diff --git a/.gitignore b/.gitignore index 63f689e875..9a17c8ef79 100644 --- a/.gitignore +++ b/.gitignore @@ -79,4 +79,12 @@ node_modules npm-debug.log # ignore qmlc files generated from qml as cache -*.qmlc \ No newline at end of file +*.qmlc +# Android studio files +*___jb_old___ + +# Generated assets for Android +android/app/src/main/assets + +# Resource binary file +interface/compiledResources diff --git a/BUILD_LINUX_CHEATSHEET.md b/BUILD_LINUX_CHEATSHEET.md index 7d77f5d685..9e7534418a 100644 --- a/BUILD_LINUX_CHEATSHEET.md +++ b/BUILD_LINUX_CHEATSHEET.md @@ -1,5 +1,7 @@ - # this guide is specific to Ubuntu 16.04. - # deb packages of High Fidelity domain server and assignment client are stored on debian.highfidelity.com +## This guide is specific to Ubuntu 16.04. +Deb packages of High Fidelity domain server and assignment client are stored on debian.highfidelity.com + +``` sudo su - apt-get -y update apt-get install -y software-properties-common @@ -8,20 +10,27 @@ add-apt-repository "deb http://debian.highfidelity.com stable main" apt-get -y update apt-get install -y hifi-domain-server apt-get install -y hifi-assignment-client +``` - # When installing master/dev builds, the packages are slightly different and you just need to change the last 2 steps to: +When installing master/dev builds, the packages are slightly different and you just need to change the last 2 steps to: +``` apt-get install -y hifi-dev-domain-server apt-get install -y hifi-dev-assignment-client +``` - # domain server and assignment clients should already be running. The processes are controlled via: +Domain server and assignment clients should already be running. The processes are controlled via: +``` systemctl start hifi-domain-server systemctl stop hifi-domain-server +``` - # Once the machine is setup and processes are running you should ensure that your firewall exposes port 40100 on TCP and all UDP ports. This will get your domain up and running and you could connect to it (for now) by using High Fidelity Interface and typing in the IP for the place name. (further customizations can be done via http://IPAddress:40100). - - # The server always depends on both hifi-domain-server and hifi-assignment-client running at the same time. - # As an additional step, you should ensure that your packages are automatically updated when a new version goes out. You could, for example, set the automatic update checks to happen every hour (though this could potentially result in the domain being unreachable for a whole hour by new clients when they are released - adjust the update checks accordingly). +Once the machine is setup and processes are running, you should ensure that your firewall exposes port 40100 on TCP and all UDP ports. This will get your domain up and running and you could connect to it (for now) by using High Fidelity Interface and typing in the IP for the place name. (Further customizations can be done via http://IPAddress:40100). + +The server always depends on both hifi-domain-server and hifi-assignment-client running at the same time. +As an additional step, you should ensure that your packages are automatically updated when a new version goes out. You could, for example, set the automatic update checks to happen every hour (though this could potentially result in the domain being unreachable for a whole hour by new clients when they are released - adjust the update checks accordingly). To do this you can modify /etc/crontab by adding the following lines +``` 0 */1 * * * root apt-get update 1 */1 * * * root apt-get install --only-upgrade -y hifi-domain-server 2 */1 * * * root apt-get install --only-upgrade -y hifi-assignment-client +``` diff --git a/CMakeLists.txt b/CMakeLists.txt index 34ff672067..bbec4a0884 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,9 +26,11 @@ endif() if (ANDROID OR UWP) option(BUILD_SERVER "Build server components" OFF) option(BUILD_TOOLS "Build tools" OFF) + option(BUILD_INSTALLER "Build installer" OFF) else() option(BUILD_SERVER "Build server components" ON) option(BUILD_TOOLS "Build tools" ON) + option(BUILD_INSTALLER "Build installer" ON) endif() if (SERVER_ONLY) @@ -40,25 +42,53 @@ else() endif() if (ANDROID) - set(PLATFORM_QT_COMPONENTS AndroidExtras WebView) - set(PLATFORM_GL_BACKEND gpu-gles) + option(USE_GLES "Use OpenGL ES" ON) + set(PLATFORM_QT_COMPONENTS AndroidExtras WebView) else () - set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets) - set(PLATFORM_GL_BACKEND gpu-gl) + option(USE_GLES "Use OpenGL ES" OFF) + set(PLATFORM_QT_COMPONENTS WebEngine WebEngineWidgets) endif () +if (USE_GLES AND (NOT ANDROID)) + option(DISABLE_QML "Disable QML" ON) +else() + option(DISABLE_QML "Disable QML" OFF) +endif() +option(DISABLE_KTX_CACHE "Disable KTX Cache" OFF) + + + +set(PLATFORM_QT_GL OpenGL) + +if (USE_GLES) + add_definitions(-DUSE_GLES) + set(PLATFORM_GL_BACKEND gpu-gles) +else() + set(PLATFORM_GL_BACKEND gpu-gl) +endif() + + foreach(PLATFORM_QT_COMPONENT ${PLATFORM_QT_COMPONENTS}) list(APPEND PLATFORM_QT_LIBRARIES "Qt5::${PLATFORM_QT_COMPONENT}") endforeach() -option(BUILD_INSTALLER "Build installer" ON) - MESSAGE(STATUS "Build server: " ${BUILD_SERVER}) MESSAGE(STATUS "Build client: " ${BUILD_CLIENT}) MESSAGE(STATUS "Build tests: " ${BUILD_TESTS}) MESSAGE(STATUS "Build tools: " ${BUILD_TOOLS}) MESSAGE(STATUS "Build installer: " ${BUILD_INSTALLER}) +MESSAGE(STATUS "GL ES: " ${USE_GLES}) + +if (DISABLE_QML) +MESSAGE(STATUS "QML disabled!") +add_definitions(-DDISABLE_QML) +endif() + +if (DISABLE_KTX_CACHE) +MESSAGE(STATUS "KTX cache disabled!") +add_definitions(-DDISABLE_KTX_CACHE) +endif() if (UNIX AND DEFINED ENV{HIFI_MEMORY_DEBUGGING}) MESSAGE(STATUS "Memory debugging is enabled") diff --git a/android/app/CMakeLists.txt b/android/app/CMakeLists.txt index 4411b7b1bb..4c50cd8609 100644 --- a/android/app/CMakeLists.txt +++ b/android/app/CMakeLists.txt @@ -1,26 +1,28 @@ set(TARGET_NAME native-lib) -setup_hifi_library(Gui Qml Quick) - -# Minimal dependencies for testing UI compositing -#link_hifi_libraries(shared networking gl ui) - -link_hifi_libraries( - shared networking octree - script-engine recording trackers - gl ktx image gpu gpu-gles render render-utils - physics - audio audio-client - ui midi controllers pointers - model model-networking fbx animation - entities entities-renderer - avatars avatars-renderer - ui-plugins input-plugins - # display-plugins - # auto-updater -) - - -target_link_libraries(native-lib android log m) +setup_hifi_library() +link_hifi_libraries(shared networking gl gpu image fbx render-utils physics entities octree ${PLATFORM_GL_BACKEND}) target_opengl() target_bullet() +set(INTERFACE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../interface") +add_subdirectory("${INTERFACE_DIR}" "libraries/interface") +include_directories("${INTERFACE_DIR}/src") + +target_link_libraries(native-lib android log m interface) + +set(GVR_ROOT "${HIFI_ANDROID_PRECOMPILED}/gvr/gvr-android-sdk-1.101.0/") +target_include_directories(native-lib PRIVATE "${GVR_ROOT}/libraries/headers" "libraries/ui/src") +target_link_libraries(native-lib "${GVR_ROOT}/libraries/libgvr.so" ui) + +# finished libraries +# core -> qt +# networking -> openssl, tbb +# fbx -> draco +# physics -> bullet +# entities-renderer -> polyvox + +# unfinished libraries +# image -> nvtt (doesn't look good, but can be made optional) +# script-engine -> quazip (probably not required for the android client) + + diff --git a/android/app/build.gradle b/android/app/build.gradle index 066eb7da3d..1cff708c3b 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,10 +1,11 @@ apply plugin: 'com.android.application' - android { compileSdkVersion 26 + //buildToolsVersion '27.0.3' + defaultConfig { - applicationId "com.highfidelity.iface" + applicationId "io.highfidelity.hifiinterface" minSdkVersion 24 targetSdkVersion 26 versionCode 1 @@ -21,24 +22,19 @@ android { '-DHIFI_ANDROID_PRECOMPILED=' + HIFI_ANDROID_PRECOMPILED, '-DRELEASE_NUMBER=' + RELEASE_NUMBER, '-DRELEASE_TYPE=' + RELEASE_TYPE, - '-DBUILD_BRANCH=' + BUILD_BRANCH + '-DBUILD_BRANCH=' + BUILD_BRANCH, + '-DDISABLE_QML=ON', + '-DDISABLE_KTX_CACHE=ON' } } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } } - buildTypes { - applicationVariants.all { variant -> - variant.outputs.all { - if (RELEASE_NUMBER != '0') { - outputFileName = "app_" + RELEASE_NUMBER + "_" + RELEASE_TYPE + ".apk" - } - } - } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' @@ -50,11 +46,58 @@ android { path '../../CMakeLists.txt' } } + + applicationVariants.all { variant -> + // Our asset contents depend on items produced in the CMake build + // so our merge has to depend on the external native build + variant.externalNativeBuildTasks.each { task -> + variant.mergeResources.dependsOn(task) + } + + variant.mergeAssets.doLast { + def assetList = new LinkedList() + def youngestLastModified = 0 + + // Copy the compiled resources generated by the external native build + copy { + from new File(projectDir, "../../interface/compiledResources") + into outputDir + duplicatesStrategy DuplicatesStrategy.INCLUDE + eachFile { details -> + youngestLastModified = Math.max(youngestLastModified, details.lastModified) + assetList.add(details.path) + } + } + + // Copy the scripts directory + copy { + from new File(projectDir, "../../scripts") + into new File(outputDir, "scripts") + duplicatesStrategy DuplicatesStrategy.INCLUDE + eachFile { details-> + youngestLastModified = Math.max(youngestLastModified, details.lastModified) + assetList.add("scripts/" + details.path) + } + } + + // Write a list of files to be unpacked to the cache folder + new File(outputDir, 'cache_assets.txt').withWriter { out -> + out.println(Long.toString(youngestLastModified)) + assetList.each { file -> out.println(file) } + } + } + + variant.outputs.all { + if (RELEASE_NUMBER != '0') { + outputFileName = "app_" + RELEASE_NUMBER + "_" + RELEASE_TYPE + ".apk" + } + } + } } dependencies { implementation 'com.google.vr:sdk-audio:1.80.0' implementation 'com.google.vr:sdk-base:1.80.0' - implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation fileTree(include: ['*.jar'], dir: 'libs') } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 2160f7b591..efb7abbb93 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ - + + @@ -7,11 +8,13 @@ + - + + + + + + + + + android:launchMode="singleTop" + android:enableVrMode="com.google.vr.vrcore/com.google.vr.vrcore.common.VrCoreListenerService" + > - - + + @@ -43,4 +62,8 @@ - \ No newline at end of file + + + + + diff --git a/android/app/src/main/cpp/+android/simple.qml b/android/app/src/main/cpp/+android/simple.qml deleted file mode 100644 index fc722d7e2c..0000000000 --- a/android/app/src/main/cpp/+android/simple.qml +++ /dev/null @@ -1,19 +0,0 @@ -import QtQuick 2.7 -import QtWebView 1.1 - -Rectangle { - id: window - anchors.fill: parent - color: "red" - ColorAnimation on color { from: "blue"; to: "yellow"; duration: 1000; loops: Animation.Infinite } - - Text { - text: "Hello" - anchors.top: parent.top - } - WebView { - anchors.fill: parent - anchors.margins: 10 - url: "http://doc.qt.io/qt-5/qml-qtwebview-webview.html" - } -} diff --git a/android/app/src/main/cpp/main.cpp b/android/app/src/main/cpp/main.cpp deleted file mode 100644 index b947323720..0000000000 --- a/android/app/src/main/cpp/main.cpp +++ /dev/null @@ -1,153 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -Q_LOGGING_CATEGORY(gpugllogging, "hifi.gl") - -bool checkGLError(const char* name) { - GLenum error = glGetError(); - if (!error) { - return false; - } else { - switch (error) { - case GL_INVALID_ENUM: - qCDebug(gpugllogging) << "GLBackend::" << name << ": An unacceptable value is specified for an enumerated argument.The offending command is ignored and has no other side effect than to set the error flag."; - break; - case GL_INVALID_VALUE: - qCDebug(gpugllogging) << "GLBackend" << name << ": A numeric argument is out of range.The offending command is ignored and has no other side effect than to set the error flag"; - break; - case GL_INVALID_OPERATION: - qCDebug(gpugllogging) << "GLBackend" << name << ": The specified operation is not allowed in the current state.The offending command is ignored and has no other side effect than to set the error flag.."; - break; - case GL_INVALID_FRAMEBUFFER_OPERATION: - qCDebug(gpugllogging) << "GLBackend" << name << ": The framebuffer object is not complete.The offending command is ignored and has no other side effect than to set the error flag."; - break; - case GL_OUT_OF_MEMORY: - qCDebug(gpugllogging) << "GLBackend" << name << ": There is not enough memory left to execute the command.The state of the GL is undefined, except for the state of the error flags, after this error is recorded."; - break; - default: - qCDebug(gpugllogging) << "GLBackend" << name << ": Unknown error: " << error; - break; - } - return true; - } -} - -bool checkGLErrorDebug(const char* name) { - return checkGLError(name); -} - - -int QtMsgTypeToAndroidPriority(QtMsgType type) { - int priority = ANDROID_LOG_UNKNOWN; - switch (type) { - case QtDebugMsg: priority = ANDROID_LOG_DEBUG; break; - case QtWarningMsg: priority = ANDROID_LOG_WARN; break; - case QtCriticalMsg: priority = ANDROID_LOG_ERROR; break; - case QtFatalMsg: priority = ANDROID_LOG_FATAL; break; - case QtInfoMsg: priority = ANDROID_LOG_INFO; break; - default: break; - } - return priority; -} - -void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) { - __android_log_write(QtMsgTypeToAndroidPriority(type), "Interface", message.toStdString().c_str()); -} - -void qt_gl_set_global_share_context(QOpenGLContext *context); - -int main(int argc, char* argv[]) -{ - qInstallMessageHandler(messageHandler); - QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); - QGuiApplication app(argc,argv); - app.setOrganizationName("QtProject"); - app.setOrganizationDomain("qt-project.org"); - app.setApplicationName(QFileInfo(app.applicationFilePath()).baseName()); - QtWebView::initialize(); - qputenv("QSG_RENDERER_DEBUG", (QStringList() << "render" << "build" << "change" << "upload" << "roots" << "dump").join(';').toUtf8()); - - OffscreenGLCanvas sharedCanvas; - if (!sharedCanvas.create()) { - qFatal("Unable to create primary offscreen context"); - } - qt_gl_set_global_share_context(sharedCanvas.getContext()); - auto globalContext = QOpenGLContext::globalShareContext(); - - GLWindow window; - window.create(); - window.setGeometry(qApp->primaryScreen()->availableGeometry()); - window.createContext(globalContext); - if (!window.makeCurrent()) { - qFatal("Unable to make primary window GL context current"); - } - - GLuint fbo = 0; - glGenFramebuffers(1, &fbo); - - static const ivec2 offscreenSize { 640, 480 }; - - OffscreenQmlSurface::setSharedContext(sharedCanvas.getContext()); - OffscreenQmlSurface* qmlSurface = new OffscreenQmlSurface(); - qmlSurface->create(); - qmlSurface->resize(fromGlm(offscreenSize)); - qmlSurface->load("qrc:///simple.qml"); - qmlSurface->resume(); - - auto discardLambda = qmlSurface->getDiscardLambda(); - - window.showFullScreen(); - QTimer timer; - timer.setInterval(10); - timer.setSingleShot(false); - OffscreenQmlSurface::TextureAndFence currentTextureAndFence; - timer.connect(&timer, &QTimer::timeout, &app, [&]{ - window.makeCurrent(); - glClearColor(0, 1, 0, 1); - glClear(GL_COLOR_BUFFER_BIT); - - OffscreenQmlSurface::TextureAndFence newTextureAndFence; - if (qmlSurface->fetchTexture(newTextureAndFence)) { - if (currentTextureAndFence.first) { - discardLambda(currentTextureAndFence.first, currentTextureAndFence.second); - } - currentTextureAndFence = newTextureAndFence; - } - checkGLErrorDebug(__FUNCTION__); - - if (currentTextureAndFence.second) { - glWaitSync((GLsync)currentTextureAndFence.second, 0, GL_TIMEOUT_IGNORED); - glDeleteSync((GLsync)currentTextureAndFence.second); - currentTextureAndFence.second = nullptr; - } - - if (currentTextureAndFence.first) { - glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); - glFramebufferTexture(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, currentTextureAndFence.first, 0); - glBlitFramebuffer(0, 0, offscreenSize.x, offscreenSize.y, 100, 100, offscreenSize.x + 100, offscreenSize.y + 100, GL_COLOR_BUFFER_BIT, GL_NEAREST); - glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - } - - window.swapBuffers(); - window.doneCurrent(); - }); - timer.start(); - return app.exec(); -} diff --git a/android/app/src/main/cpp/main.qrc b/android/app/src/main/cpp/main.qrc deleted file mode 100644 index 81cf8ef111..0000000000 --- a/android/app/src/main/cpp/main.qrc +++ /dev/null @@ -1,6 +0,0 @@ - - - simple.qml - +android/simple.qml - - diff --git a/android/app/src/main/cpp/native.cpp b/android/app/src/main/cpp/native.cpp new file mode 100644 index 0000000000..13daf4c471 --- /dev/null +++ b/android/app/src/main/cpp/native.cpp @@ -0,0 +1,160 @@ +#include + + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +void tempMessageHandler(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(); + } + } +} + +AAssetManager* g_assetManager = nullptr; + +void withAssetData(const char* filename, const std::function& callback) { + auto asset = AAssetManager_open(g_assetManager, filename, AASSET_MODE_BUFFER); + if (!asset) { + throw std::runtime_error("Failed to open file"); + } + auto buffer = AAsset_getBuffer(asset); + off64_t size = AAsset_getLength64(asset); + callback(size, buffer); + AAsset_close(asset); +} + +QStringList readAssetLines(const char* filename) { + QStringList result; + withAssetData(filename, [&](off64_t size, const void* data){ + QByteArray buffer = QByteArray::fromRawData((const char*)data, size); + { + QTextStream in(&buffer); + while (!in.atEnd()) { + QString line = in.readLine(); + result << line; + } + } + }); + return result; +} + +void copyAsset(const char* sourceAsset, const QString& destFilename) { + withAssetData(sourceAsset, [&](off64_t size, const void* data){ + QFile file(destFilename); + if (!file.open(QFile::ReadWrite | QIODevice::Truncate)) { + throw std::runtime_error("Unable to open output file for writing"); + } + if (!file.resize(size)) { + throw std::runtime_error("Unable to resize output file"); + } + if (size != 0) { + auto mapped = file.map(0, size); + if (!mapped) { + throw std::runtime_error("Unable to map output file"); + } + memcpy(mapped, data, size); + } + file.close(); + }); +} + +void unpackAndroidAssets() { + const QString DEST_PREFIX = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/"; + + QStringList filesToCopy = readAssetLines("cache_assets.txt"); + QString dateStamp = filesToCopy.takeFirst(); + QString dateStampFilename = DEST_PREFIX + dateStamp; + qDebug() << "Checking date stamp" << dateStamp; + if (QFileInfo(dateStampFilename).exists()) { + return; + } + + auto rootDir = QDir::root(); + for (const auto& fileToCopy : filesToCopy) { + auto destFileName = DEST_PREFIX + fileToCopy; + auto destFolder = QFileInfo(destFileName).absoluteDir(); + if (!destFolder.exists()) { + qDebug() << "Creating folder" << destFolder.absolutePath(); + if (!rootDir.mkpath(destFolder.absolutePath())) { + throw std::runtime_error("Error creating output path"); + } + } + if (QFile::exists(destFileName)) { + qDebug() << "Removing old file" << destFileName; + if (!QFile::remove(destFileName)) { + throw std::runtime_error("Failed to remove existing file"); + } + } + + qDebug() << "Copying asset " << fileToCopy << "to" << destFileName; + copyAsset(fileToCopy.toStdString().c_str(), destFileName); + } + + { + qDebug() << "Writing date stamp" << dateStamp; + QFile file(dateStampFilename); + if (!file.open(QIODevice::ReadWrite | QIODevice::Truncate)) { + throw std::runtime_error("Can't write date stamp"); + } + QTextStream(&file) << "touch" << endl; + file.close(); + } +} + +extern "C" { + +JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnCreate(JNIEnv* env, jobject obj, jobject instance, jobject asset_mgr) { + qDebug() << "nativeOnCreate On thread " << QThread::currentThreadId(); + g_assetManager = AAssetManager_fromJava(env, asset_mgr); + auto oldMessageHandler = qInstallMessageHandler(tempMessageHandler); + unpackAndroidAssets(); + qInstallMessageHandler(oldMessageHandler); +} + +JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnPause(JNIEnv* env, jobject obj) { + qDebug() << "nativeOnPause"; +} + +JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnResume(JNIEnv* env, jobject obj) { + qDebug() << "nativeOnResume"; +} + +JNIEXPORT void Java_io_highfidelity_hifiinterface_InterfaceActivity_nativeOnExitVr(JNIEnv* env, jobject obj) { + qDebug() << "nativeOnCreate On thread " << QThread::currentThreadId(); +} + +} + + diff --git a/android/app/src/main/cpp/simple.qml b/android/app/src/main/cpp/simple.qml deleted file mode 100644 index 38f3c80374..0000000000 --- a/android/app/src/main/cpp/simple.qml +++ /dev/null @@ -1,10 +0,0 @@ -import QtQuick 2.0 - -Rectangle { - id: window - width: 320 - height: 480 - focus: true - color: "red" - ColorAnimation on color { from: "red"; to: "yellow"; duration: 1000; loops: Animation.Infinite } -} diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java new file mode 100644 index 0000000000..de3bcee88d --- /dev/null +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/InterfaceActivity.java @@ -0,0 +1,178 @@ +// +// InterfaceActivity.java +// android/app/src/main/java +// +// Created by Stephen Birarda on 1/26/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +package io.highfidelity.hifiinterface; + +import android.content.Intent; +import android.content.res.AssetManager; +import android.net.Uri; +import android.os.Bundle; +import android.view.WindowManager; +import android.util.Log; +import org.qtproject.qt5.android.bindings.QtActivity; + +/*import com.google.vr.cardboard.DisplaySynchronizer; +import com.google.vr.cardboard.DisplayUtils; +import com.google.vr.ndk.base.GvrApi;*/ +import android.graphics.Point; +import android.content.res.Configuration; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.view.View; + +public class InterfaceActivity extends QtActivity { + + //public static native void handleHifiURL(String hifiURLString); + private native long nativeOnCreate(InterfaceActivity instance, AssetManager assetManager); + //private native void nativeOnPause(); + //private native void nativeOnResume(); + //private native void nativeOnStop(); + //private native void nativeOnStart(); + //private native void saveRealScreenSize(int width, int height); + //private native void setAppVersion(String version); + private native long nativeOnExitVr(); + + private AssetManager assetManager; + + private static boolean inVrMode; +// private GvrApi gvrApi; + // Opaque native pointer to the Application C++ object. + // This object is owned by the InterfaceActivity instance and passed to the native methods. + //private long nativeGvrApi; + + public void enterVr() { + //Log.d("[VR]", "Entering Vr mode (java)"); + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); + inVrMode = true; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + // Get the intent that started this activity in case we have a hifi:// URL to parse + Intent intent = getIntent(); + if (intent.getAction() == Intent.ACTION_VIEW) { + Uri data = intent.getData(); + +// if (data.getScheme().equals("hifi")) { +// handleHifiURL(data.toString()); +// } + } + +/* DisplaySynchronizer displaySynchronizer = new DisplaySynchronizer(this, DisplayUtils.getDefaultDisplay(this)); + gvrApi = new GvrApi(this, displaySynchronizer); + */ +// Log.d("GVR", "gvrApi.toString(): " + gvrApi.toString()); + + assetManager = getResources().getAssets(); + + //nativeGvrApi = + nativeOnCreate(this, assetManager /*, gvrApi.getNativeGvrContext()*/); + + Point size = new Point(); + getWindowManager().getDefaultDisplay().getRealSize(size); +// saveRealScreenSize(size.x, size.y); + + try { + PackageInfo pInfo = this.getPackageManager().getPackageInfo(getPackageName(), 0); + String version = pInfo.versionName; +// setAppVersion(version); + } catch (PackageManager.NameNotFoundException e) { + Log.e("GVR", "Error getting application version", e); + } + + final View rootView = getWindow().getDecorView().findViewById(android.R.id.content); + + // This is a workaround to hide the menu bar when the virtual keyboard is shown from Qt + rootView.getViewTreeObserver().addOnGlobalLayoutListener(new android.view.ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + int heightDiff = rootView.getRootView().getHeight() - rootView.getHeight(); + if (getActionBar().isShowing()) { + getActionBar().hide(); + } + } + }); + } + + @Override + protected void onPause() { + super.onPause(); + //nativeOnPause(); + //gvrApi.pauseTracking(); + } + + @Override + protected void onStart() { + super.onStart(); +// nativeOnStart(); + } + + @Override + protected void onStop() { + Log.d("[Background]","Calling nativeOnStop from InterfaceActivity"); +// nativeOnStop(); + super.onStop(); + } + + @Override + protected void onResume() { + super.onResume(); + //nativeOnResume(); + //gvrApi.resumeTracking(); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + // Checks the orientation of the screen + if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){ +// Log.d("[VR]", "Portrait, forcing landscape"); + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + if (inVrMode) { + inVrMode = false; +// Log.d("[VR]", "Starting VR exit"); + nativeOnExitVr(); + } else { + Log.w("[VR]", "Portrait detected but not in VR mode. Should not happen"); + } + } + } + + public void openUrlInAndroidWebView(String urlString) { + Log.d("openUrl", "Received in open " + urlString); + Intent openUrlIntent = new Intent(this, WebViewActivity.class); + openUrlIntent.putExtra(WebViewActivity.WEB_VIEW_ACTIVITY_EXTRA_URL, urlString); + startActivity(openUrlIntent); + } + + /** + * Called when view focus is changed + */ + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + if (hasFocus) { + final int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE + | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY; + + getWindow().getDecorView().setSystemUiVisibility(uiOptions); + } + } + +} \ No newline at end of file diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java b/android/app/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java new file mode 100644 index 0000000000..34b087ca25 --- /dev/null +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/PermissionChecker.java @@ -0,0 +1,130 @@ +package io.highfidelity.hifiinterface; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.app.Activity; + +import android.content.DialogInterface; +import android.app.AlertDialog; + +import org.json.JSONException; +import org.json.JSONObject; +import android.util.Log; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.Arrays; +import java.util.List; + +public class PermissionChecker extends Activity { + private static final int REQUEST_PERMISSIONS = 20; + + private static final boolean CHOOSE_AVATAR_ON_STARTUP = false; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (CHOOSE_AVATAR_ON_STARTUP) { + showMenu(); + } + this.requestAppPermissions(new + String[]{ + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.RECORD_AUDIO, + Manifest.permission.CAMERA} + ,2,REQUEST_PERMISSIONS); + + } + + public void requestAppPermissions(final String[] requestedPermissions, + final int stringId, final int requestCode) { + int permissionCheck = PackageManager.PERMISSION_GRANTED; + boolean shouldShowRequestPermissionRationale = false; + for (String permission : requestedPermissions) { + permissionCheck = permissionCheck + checkSelfPermission(permission); + shouldShowRequestPermissionRationale = shouldShowRequestPermissionRationale || shouldShowRequestPermissionRationale(permission); + } + if (permissionCheck != PackageManager.PERMISSION_GRANTED) { + System.out.println("Permission was not granted. Ask for permissions"); + if (shouldShowRequestPermissionRationale) { + requestPermissions(requestedPermissions, requestCode); + } else { + requestPermissions(requestedPermissions, requestCode); + } + } else { + System.out.println("Launching the other activity.."); + launchActivityWithPermissions(); + } + } + + private void launchActivityWithPermissions(){ + finish(); + Intent i = new Intent(this, InterfaceActivity.class); + startActivity(i); + finish(); + } + + private void showMenu(){ + final List avatarOptions = Arrays.asList("\uD83D\uDC66\uD83C\uDFFB Cody","\uD83D\uDC66\uD83C\uDFFF Will","\uD83D\uDC68\uD83C\uDFFD Albert", "\uD83D\uDC7D Being of Light"); + final String[] avatarPaths = { + "http://mpassets.highfidelity.com/8c859fca-4cbd-4e82-aad1-5f4cb0ca5d53-v1/cody.fst", + "http://mpassets.highfidelity.com/d029ae8d-2905-4eb7-ba46-4bd1b8cb9d73-v1/4618d52e711fbb34df442b414da767bb.fst", + "http://mpassets.highfidelity.com/1e57c395-612e-4acd-9561-e79dbda0bc49-v1/albert.fst" }; + + final String pathForJson = "/data/data/io.highfidelity.hifiinterface/files/.config/High Fidelity - dev/"; + new AlertDialog.Builder(this) + .setTitle("Pick an avatar") + .setItems(avatarOptions.toArray(new CharSequence[avatarOptions.size()]),new DialogInterface.OnClickListener(){ + + @Override + public void onClick(DialogInterface dialog, int which) { + if(which < avatarPaths.length ) { + JSONObject obj = new JSONObject(); + try { + obj.put("firstRun",false); + obj.put("Avatar/fullAvatarURL", avatarPaths[which]); + File directory = new File(pathForJson); + + if(!directory.exists()) directory.mkdirs(); + + File file = new File(pathForJson + "Interface.json"); + Writer output = new BufferedWriter(new FileWriter(file)); + output.write(obj.toString().replace("\\","")); + output.close(); + System.out.println("I Could write config file expect to see the selected avatar"+obj.toString().replace("\\","")); + + } catch (JSONException e) { + System.out.println("JSONException something weired went wrong"); + } catch (IOException e) { + System.out.println("Could not write file :("); + } + } else { + System.out.println("Default avatar selected..."); + } + launchActivityWithPermissions(); + } + }).show(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + int permissionCheck = PackageManager.PERMISSION_GRANTED; + for (int permission : grantResults) { + permissionCheck = permissionCheck + permission; + } + if ((grantResults.length > 0) && permissionCheck == PackageManager.PERMISSION_GRANTED) { + launchActivityWithPermissions(); + } else if (grantResults.length > 0) { + System.out.println("User has deliberately denied Permissions. Launching anyways"); + launchActivityWithPermissions(); + } + } + + +} diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/WebViewActivity.java b/android/app/src/main/java/io/highfidelity/hifiinterface/WebViewActivity.java new file mode 100644 index 0000000000..4d706248d8 --- /dev/null +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/WebViewActivity.java @@ -0,0 +1,242 @@ +// +// WebViewActivity.java +// interface/java +// +// Created by Cristian Duarte and Gabriel Calero on 8/17/17. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +//package hifiinterface.highfidelity.io.mybrowserapplication; +package io.highfidelity.hifiinterface; + +import android.app.ActionBar; +import android.app.Activity; +import android.content.Intent; +import android.graphics.Bitmap; +import android.net.Uri; +import android.net.http.SslError; +import android.os.Bundle; +import android.util.Log; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.webkit.SslErrorHandler; +import android.webkit.WebChromeClient; +import android.webkit.WebResourceError; +import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.ProgressBar; +import android.widget.Toast; +import android.widget.Toolbar; +import android.os.Looper; +import java.lang.Thread; +import java.lang.Runnable; + +import java.net.MalformedURLException; +import java.net.URL; + +public class WebViewActivity extends Activity { + + public static final String WEB_VIEW_ACTIVITY_EXTRA_URL = "url"; + + private native void nativeProcessURL(String url); + + private WebView myWebView; + private ProgressBar mProgressBar; + private ActionBar mActionBar; + private String mUrl; + + enum SafenessLevel { + NOT_ANALYZED_YET(""), + NOT_SECURE(""), + SECURE("\uD83D\uDD12 "), + BAD_SECURE("\uD83D\uDD13 "); + + String icon; + SafenessLevel(String icon) { + this.icon = icon; + } + } + + private SafenessLevel safenessLevel = SafenessLevel.NOT_ANALYZED_YET; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_web_view); + + setActionBar((Toolbar) findViewById(R.id.toolbar_actionbar)); + mActionBar = getActionBar(); + mActionBar.setDisplayHomeAsUpEnabled(true); + + mProgressBar = (ProgressBar) findViewById(R.id.toolbarProgressBar); + + mUrl = getIntent().getStringExtra(WEB_VIEW_ACTIVITY_EXTRA_URL); + myWebView = (WebView) findViewById(R.id.web_view); + myWebView.setWebViewClient(new HiFiWebViewClient()); + myWebView.setWebChromeClient(new HiFiWebChromeClient()); + WebSettings webSettings = myWebView.getSettings(); + webSettings.setJavaScriptEnabled(true); + webSettings.setBuiltInZoomControls(true); + webSettings.setDisplayZoomControls(false); + myWebView.loadUrl(mUrl); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + // Check if the key event was the Back button and if there's history + if ((keyCode == KeyEvent.KEYCODE_BACK) && myWebView.canGoBack()) { + myWebView.goBack(); + return true; + } + // If it wasn't the Back key or there's no web page history, bubble up to the default + // system behavior (probably exit the activity) + return super.onKeyDown(keyCode, event); + } + + private void showSubtitleWithUrl(String url) { + try { + mActionBar.setSubtitle(safenessLevel.icon + new URL(url.toString()).getHost()); + } catch (MalformedURLException e) { + Toast.makeText(WebViewActivity.this, "Error loading page: " + "bad url", Toast.LENGTH_LONG).show(); + Log.e("openUrl", "bad url"); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.web_view_menu, menu); + return true; + } + + private String intentUrlOrWebUrl() { + return myWebView==null || myWebView.getUrl()==null?mUrl:myWebView.getUrl(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item){ + switch (item.getItemId()) { + // Respond to the action bar's Up/Home/back button + case android.R.id.home: + finish(); + break; + case R.id.action_browser: + Intent i = new Intent(Intent.ACTION_VIEW); + i.setData(Uri.parse(intentUrlOrWebUrl())); + startActivity(i); + return true; + case R.id.action_share: + Intent is = new Intent(Intent.ACTION_SEND); + is.setType("text/plain"); + is.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.web_view_action_share_subject)); + is.putExtra(Intent.EXTRA_TEXT, intentUrlOrWebUrl()); + startActivity(Intent.createChooser(is, getString(R.string.web_view_action_share_chooser))); + return true; + } + return super.onOptionsItemSelected(item); + } + + class HiFiWebViewClient extends WebViewClient { + + @Override + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + mProgressBar.setVisibility(View.GONE); + if (safenessLevel!=SafenessLevel.BAD_SECURE) { + if (url.startsWith("https:")) { + safenessLevel=SafenessLevel.SECURE; + } else { + safenessLevel=SafenessLevel.NOT_SECURE; + } + } + showSubtitleWithUrl(url); + } + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + super.onPageStarted(view, url, favicon); + safenessLevel = SafenessLevel.NOT_ANALYZED_YET; + mProgressBar.setVisibility(View.VISIBLE); + mProgressBar.setProgress(0); + showSubtitleWithUrl(url); + } + + @Override + public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { + Toast.makeText(WebViewActivity.this, "Error loading page: " + error.getDescription(), Toast.LENGTH_LONG).show(); + if (ERROR_FAILED_SSL_HANDSHAKE == error.getErrorCode()) { + safenessLevel = SafenessLevel.BAD_SECURE; + } + } + + @Override + public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) { + Toast.makeText(WebViewActivity.this, "Network Error loading page: " + errorResponse.getReasonPhrase(), Toast.LENGTH_LONG).show(); + } + + @Override + public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { + super.onReceivedSslError(view, handler, error); + Toast.makeText(WebViewActivity.this, "SSL error loading page: " + error.toString(), Toast.LENGTH_LONG).show(); + safenessLevel = SafenessLevel.BAD_SECURE; + } + + private boolean isFst(WebResourceRequest request) { + return isFst(request.getUrl().toString()); + } + + private boolean isFst(String url) { + return url.endsWith(".fst"); + } + + @Override + public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + // managing avatar selections + if (isFst(request)) { + final String url = request.getUrl().toString(); + new Thread(new Runnable() { + public void run() { + nativeProcessURL(url); + } + }).start(); // Avoid deadlock in Qt dialog + WebViewActivity.this.finish(); + return true; + } + return super.shouldOverrideUrlLoading(view, request); + } + + @Override + public void onLoadResource(WebView view, String url) { + if (isFst(url)) { + // processed separately + } else { + super.onLoadResource(view, url); + } + } + } + + class HiFiWebChromeClient extends WebChromeClient { + + @Override + public void onProgressChanged(WebView view, int newProgress) { + super.onProgressChanged(view, newProgress); + mProgressBar.setProgress(newProgress); + } + + @Override + public void onReceivedTitle(WebView view, String title) { + super.onReceivedTitle(view, title); + mActionBar.setTitle(title); + } + + } +} diff --git a/android/app/src/main/res/drawable/ic_close_black_24dp.xml b/android/app/src/main/res/drawable/ic_close_black_24dp.xml new file mode 100644 index 0000000000..ede4b7108d --- /dev/null +++ b/android/app/src/main/res/drawable/ic_close_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/layout/activity_web_view.xml b/android/app/src/main/res/layout/activity_web_view.xml new file mode 100644 index 0000000000..1e30b58676 --- /dev/null +++ b/android/app/src/main/res/layout/activity_web_view.xml @@ -0,0 +1,34 @@ + + + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/menu/web_view_menu.xml b/android/app/src/main/res/menu/web_view_menu.xml new file mode 100644 index 0000000000..074e1608c5 --- /dev/null +++ b/android/app/src/main/res/menu/web_view_menu.xml @@ -0,0 +1,10 @@ + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index cde69bccce..d376d7af88 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png index 9a078e3e1a..81a137957d 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index c133a0cbd3..88b1be5903 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png index efc028a636..d032c30014 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index bfa42f0e7b..4a018d4ed9 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png index 3af2608a44..3cccf1037b 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 324e72cdd7..e97062e0ee 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png index 9bec2e6231..8d142881da 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index aee44e1384..f01203c738 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png index 34947cd6bb..211e298961 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/android/app/src/main/res/values/dimens.xml b/android/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000000..a9ec657aa9 --- /dev/null +++ b/android/app/src/main/res/values/dimens.xml @@ -0,0 +1,12 @@ + + + + 16dp + 16dp + + + + 14dp + + 12dp + \ No newline at end of file diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 5d6a4c1b99..b8080fae0f 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -1,3 +1,8 @@ - TestApp + Interface + Open in browser + Share link + Shared a link + Share link + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 033324ac58..23fe67f029 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -11,5 +11,15 @@ - + + + + + diff --git a/android/build.gradle b/android/build.gradle index c7eefd051b..1f2c563e1b 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -40,6 +40,7 @@ ext { BUILD_BRANCH = project.hasProperty('BUILD_BRANCH') ? project.getProperty('BUILD_BRANCH') : '' EXEC_SUFFIX = Os.isFamily(Os.FAMILY_WINDOWS) ? '.exe' : '' QT5_DEPS = [ + 'Qt5Concurrent', 'Qt5Core', 'Qt5Gui', 'Qt5Multimedia', @@ -47,8 +48,11 @@ ext { 'Qt5OpenGL', 'Qt5Qml', 'Qt5Quick', + 'Qt5QuickControls2', + 'Qt5QuickTemplates2', 'Qt5Script', 'Qt5ScriptTools', + 'Qt5Svg', 'Qt5WebChannel', 'Qt5WebSockets', 'Qt5Widgets', @@ -93,6 +97,11 @@ def packages = [ versionId: 'cA3tVJSmkvb1naA3l6D_Jv2Noh.4yc4m', checksum: '617a80d213a5ec69fbfa21a1f2f738cd', ], + glad: [ + file: 'glad_armv8-libcpp.zip', + versionId: 'Q9szthzeye8fFyAA.cY26Lgn2B8kezEE', + checksum: 'a8ee8584cf1ccd34766c7ddd9d5e5449', + ], glm: [ file: 'glm-0.9.8.tgz', versionId: 'BlkJNwaYV2Gfy5XwMeU7K0uzPDRKFMt2', @@ -145,16 +154,18 @@ def options = [ def qmlRoot = new File(HIFI_ANDROID_PRECOMPILED, 'qt') -def captureOutput = { String command -> - def proc = command.execute() - def sout = new StringBuilder(), serr = new StringBuilder() - proc.consumeProcessOutput(sout, serr) - proc.waitForOrKill(10000) - def errorOutput = serr.toString() - if (!errorOutput.isEmpty()) { - throw new GradleException("Command '${command}' failed with error ${errorOutput}") +def captureOutput = { String command, List commandArgs -> + def result + new ByteArrayOutputStream().withStream { os -> + def execResult = exec { + executable = command + args = commandArgs + standardOutput = os + errorOutput = new ByteArrayOutputStream() + } + result = os.toString() } - return sout.toString() + return result; } def relativize = { File root, File absolute -> @@ -168,11 +179,13 @@ def scanQmlImports = { File qmlRootPath -> throw new GradleException('Unable to find required qmlimportscanner executable at ' + qmlImportCommandFile.parent.toString()) } - def command = qmlImportCommandFile.absolutePath + - " -rootPath ${qmlRootPath.absolutePath}" + - " -importPath ${qmlRoot.absolutePath}/qml" + def command = qmlImportCommandFile.absolutePath + def args = [ + '-rootPath', qmlRootPath.absolutePath, + '-importPath', "${qmlRoot.absolutePath}/qml" + ] - def commandResult = captureOutput(command) + def commandResult = captureOutput(command, args) new JsonSlurper().parseText(commandResult).each { if (!it.containsKey('path')) { println "Warning: QML import could not be resolved in any of the import paths: ${it.name}" @@ -257,7 +270,7 @@ def parseQtDependencies = { List qtLibs -> def generateLibsXml = { def libDestinationDirectory = jniFolder def jarDestinationDirectory = new File(appDir, 'libs') - def assetDestinationDirectory = new File(appDir, 'src/main/assets/bundled'); + def assetDestinationDirectory = new File(appDir, 'src/main/assets/--Added-by-androiddeployqt--'); def libsXmlFile = new File(appDir, 'src/main/res/values/libs.xml') def libPrefix = 'lib' + File.separator def jarPrefix = 'jar' + File.separator @@ -293,7 +306,7 @@ def generateLibsXml = { } else if (relativePath.startsWith('jar')) { destinationFile = new File(jarDestinationDirectory, relativePath.substring(jarPrefix.size())) } else { - xmlParser.createNode(bundledAssetsNode, 'item', [:]).setValue("bundled/${relativePath}:${relativePath}".replace(File.separator, '/')) + xmlParser.createNode(bundledAssetsNode, 'item', [:]).setValue("--Added-by-androiddeployqt--/${relativePath}:${relativePath}".replace(File.separator, '/')) destinationFile = new File(assetDestinationDirectory, relativePath) } @@ -427,9 +440,55 @@ task extractGvrBinaries(dependsOn: extractDependencies) { } } } - } +def generateAssetsFileList = { + def assetsPath = "${appDir}/src/main/assets/" + def addedByAndroidDeployQtName = "--Added-by-androiddeployqt--/" + + def addedByAndroidDeployQtPath = assetsPath + addedByAndroidDeployQtName + + def addedByAndroidDeployQt = new File(addedByAndroidDeployQtPath) + if (!addedByAndroidDeployQt.exists() && !addedByAndroidDeployQt.mkdirs()) { + throw new GradleScriptException("Failed to create directory " + addedByAndroidDeployQtPath, null); + } + def outputFilename = "/qt_cache_pregenerated_file_list" + def outputFile = new File(addedByAndroidDeployQtPath + outputFilename); + Map> directoryContents = new TreeMap<>(); + + def dir = new File(assetsPath) + dir.eachFileRecurse (FileType.ANY) { file -> + + def name = file.path.substring(assetsPath.length()) + int slashIndex = name.lastIndexOf('/') + def pathName = slashIndex >= 0 ? name.substring(0, slashIndex) : "/" + def fileName = slashIndex >= 0 ? name.substring(pathName.length() + 1) : name + if (!fileName.isEmpty() && file.isDirectory() && !fileName.endsWith("/")) { + fileName += "/" + } + + if (!directoryContents.containsKey(pathName)) { + directoryContents[pathName] = new ArrayList() + } + if (!fileName.isEmpty()) { + directoryContents[pathName].add(fileName); + } + } + DataOutputStream fos = new DataOutputStream(new FileOutputStream(outputFile)); + for (Map.Entry> e: directoryContents.entrySet()) { + def entryList = e.getValue() + fos.writeInt(e.key.length()*2); // 2 bytes per char + fos.writeChars(e.key); + fos.writeInt(entryList.size()); + for (String entry: entryList) { + fos.writeInt(entry.length()*2); + fos.writeChars(entry); + } + } + fos.close(); +} + + // Copy required Qt main libraries and required plugins based on the predefined list here // FIXME eventually we would like to use the readelf functionality to automatically detect dependencies // from our built applications and use that during the full build process. However doing so would mean @@ -438,10 +497,11 @@ task extractGvrBinaries(dependsOn: extractDependencies) { task qtBundle { doLast { parseQtDependencies(QT5_DEPS) - //def qmlImportFolder = new File("${appDir}/../../interface/resources/qml/") - def qmlImportFolder = new File("${projectDir}/app/src/main/cpp") + def qmlImportFolder = new File("${appDir}/../../interface/resources/qml/") + //def qmlImportFolder = new File("${projectDir}/app/src/main/cpp") scanQmlImports(qmlImportFolder) generateLibsXml() + generateAssetsFileList() } } @@ -450,10 +510,12 @@ task setupDependencies(dependsOn: [setupScribe, copyDependencies, extractGvrBina task cleanDependencies(type: Delete) { delete HIFI_ANDROID_PRECOMPILED delete 'app/src/main/jniLibs/arm64-v8a' - delete 'app/src/main/assets/bundled' + delete 'app/src/main/assets/--Added-by-androiddeployqt--' delete 'app/src/main/res/values/libs.xml' } + + // FIXME this code is prototyping the desired functionality for doing build time binary dependency resolution. // See the comment on the qtBundle task above /* diff --git a/android/build_recipes.md b/android/build_recipes.md index 78ca961f5c..cd7b0413bb 100644 --- a/android/build_recipes.md +++ b/android/build_recipes.md @@ -43,15 +43,17 @@ export LDFLAGS="-pie" * Install Python 3.6 for Windows * Open a Git Bash command prompt * Ensure the following commands are visible in the path with `which ` - * gcc - * javac - * python - * gmake + * gcc + * javac + * python + * gmake * If any of them fail, fix your path and restart the bash prompt * Fetch the pre-built OpenSSL binaries for Android/iOS from here: https://github.com/leenjewel/openssl_for_ios_and_android/releases * Grab the latest release of the 1.0.2 series * Open the archive and extract the `/android/openssl-arm64-v8a` folder +### All platforms + * Download the Qt sources * `git clone git://code.qt.io/qt/qt5.git` * `cd qt5` @@ -60,7 +62,11 @@ export LDFLAGS="-pie" * `git submodule update --recursive` * `cd ..` * Create a build directory with the command `mkdir qt5build` -* Configure the Qt5 build with the command `../qt5/configure -xplatform android-clang -android-ndk-host windows-x86_64 -confirm-license -opensource --disable-rpath -nomake tests -nomake examples -skip qttranslations -skip qtserialport -skip qt3d -skip qtwebengine -skip qtlocation -skip qtwayland -skip qtsensors -skip qtgamepad -skip qtgamepad -skip qtspeech -skip qtcharts -skip qtx11extras -skip qtmacextras -skip qtvirtualkeyboard -skip qtpurchasing -skip qtdatavis3d -android-ndk C:/Android/NDK -android-toolchain-version 4.9 -android-arch arm64-v8a -no-warnings-are-errors -android-ndk-platform android-24 -v -platform win32-g++ -prefix C:/qt5build_debug -android-sdk C:/Android/SDK -c++std c++14 -openssl-linked -L/lib -I/include` +* Configure the Qt5 build with the command `../qt5/configure -opensource -confirm-license -xplatform android-clang --disable-rpath -nomake tests -nomake examples -skip qttranslations -skip qtserialport -skip qt3d -skip qtwebengine -skip qtlocation -skip qtwayland -skip qtsensors -skip qtgamepad -skip qtgamepad -skip qtspeech -skip qtcharts -skip qtx11extras -skip qtmacextras -skip qtvirtualkeyboard -skip qtpurchasing -skip qtdatavis3d -android-toolchain-version 4.9 -android-ndk $HOME/Android/NDK -android-arch arm64-v8a -no-warnings-are-errors -android-ndk-platform android-24 -v -android-ndk-host windows-x86_64 -platform win32-g++ -prefix C:/qt5build_debug -android-sdk $HOME/Android/SDK -c++std c++14 -openssl-linked -L/lib -I/include` +* Some of those entries must be customized depending on platform. + * `-platform win32-g++` + * `-android-ndk-host windows-x86_64` + * `-prefix C:/qt5build_debug` diff --git a/android/gradle.properties b/android/gradle.properties deleted file mode 100644 index aac7c9b461..0000000000 --- a/android/gradle.properties +++ /dev/null @@ -1,17 +0,0 @@ -# Project-wide Gradle settings. - -# IDE (e.g. Android Studio) users: -# Gradle settings configured through the IDE *will override* -# any settings specified in this file. - -# For more details on how to configure your build environment visit -# http://www.gradle.org/docs/current/userguide/build_environment.html - -# Specifies the JVM arguments used for the daemon process. -# The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs=-Xmx1536m - -# When configured, Gradle will run in incubating parallel mode. -# This option should only be used with decoupled projects. More details, visit -# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true diff --git a/assignment-client/CMakeLists.txt b/assignment-client/CMakeLists.txt index e657587a7a..acdc1cbc53 100644 --- a/assignment-client/CMakeLists.txt +++ b/assignment-client/CMakeLists.txt @@ -11,7 +11,7 @@ setup_memory_debugger() # link in the shared libraries link_hifi_libraries( - audio avatars octree gpu model fbx entities + audio avatars octree gpu graphics fbx entities networking animation recording shared script-engine embedded-webserver controllers physics plugins midi image ) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 2fc905c6fd..a42b78a6fa 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -343,7 +343,6 @@ void Agent::scriptRequestFinished() { void Agent::executeScript() { _scriptEngine = scriptEngineFactory(ScriptEngine::AGENT_SCRIPT, _scriptContents, _payload); - _scriptEngine->setParent(this); // be the parent of the script engine so it gets moved when we do DependencyManager::get()->setScriptEngine(_scriptEngine); @@ -439,7 +438,7 @@ void Agent::executeScript() { encodedBuffer = audio; } - AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber, + AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber, false, audioTransform, scriptedAvatar->getWorldPosition(), glm::vec3(0), packetType, _selectedCodecName); }); diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index 49453c6fc6..2560f43337 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -275,17 +275,28 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) { if (micStreamIt == _audioStreams.end()) { // we don't have a mic stream yet, so add it - // read the channel flag to see if our stream is stereo or not + // hop past the sequence number that leads the packet message.seek(sizeof(quint16)); - quint8 channelFlag; - message.readPrimitive(&channelFlag); + // pull the codec string from the packet + auto codecString = message.readString(); - bool isStereo = channelFlag == 1; + // determine if the stream is stereo or not + bool isStereo; + if (packetType == PacketType::SilentAudioFrame + || packetType == PacketType::ReplicatedSilentAudioFrame) { + quint16 numSilentSamples; + message.readPrimitive(&numSilentSamples); + isStereo = numSilentSamples == AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; + } else { + quint8 channelFlag; + message.readPrimitive(&channelFlag); + isStereo = channelFlag == 1; + } auto avatarAudioStream = new AvatarAudioStream(isStereo, AudioMixer::getStaticJitterFrames()); - avatarAudioStream->setupCodec(_codec, _selectedCodecName, AudioConstants::MONO); - qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName; + avatarAudioStream->setupCodec(_codec, _selectedCodecName, isStereo ? AudioConstants::STEREO : AudioConstants::MONO); + qCDebug(audio) << "creating new AvatarAudioStream... codec:" << _selectedCodecName << "isStereo:" << isStereo; connect(avatarAudioStream, &InboundAudioStream::mismatchedAudioCodec, this, &AudioMixerClientData::handleMismatchAudioFormat); @@ -324,7 +335,7 @@ int AudioMixerClientData::parseData(ReceivedMessage& message) { #if INJECTORS_SUPPORT_CODECS injectorStream->setupCodec(_codec, _selectedCodecName, isStereo ? AudioConstants::STEREO : AudioConstants::MONO); - qCDebug(audio) << "creating new injectorStream... codec:" << _selectedCodecName; + qCDebug(audio) << "creating new injectorStream... codec:" << _selectedCodecName << "isStereo:" << isStereo; #endif auto emplaced = _audioStreams.emplace( @@ -567,7 +578,8 @@ void AudioMixerClientData::setupCodec(CodecPluginPointer codec, const QString& c auto avatarAudioStream = getAvatarAudioStream(); if (avatarAudioStream) { - avatarAudioStream->setupCodec(codec, codecName, AudioConstants::MONO); + avatarAudioStream->setupCodec(codec, codecName, avatarAudioStream->isStereo() ? AudioConstants::STEREO : AudioConstants::MONO); + qCDebug(audio) << "setting AvatarAudioStream... codec:" << _selectedCodecName << "isStereo:" << avatarAudioStream->isStereo(); } #if INJECTORS_SUPPORT_CODECS diff --git a/assignment-client/src/audio/AvatarAudioStream.cpp b/assignment-client/src/audio/AvatarAudioStream.cpp index 1e0c3ed9e6..42495b4dd0 100644 --- a/assignment-client/src/audio/AvatarAudioStream.cpp +++ b/assignment-client/src/audio/AvatarAudioStream.cpp @@ -11,6 +11,7 @@ #include +#include "AudioLogging.h" #include "AvatarAudioStream.h" AvatarAudioStream::AvatarAudioStream(bool isStereo, int numStaticJitterFrames) : @@ -41,6 +42,15 @@ int AvatarAudioStream::parseStreamProperties(PacketType type, const QByteArray& _ringBuffer.resizeForFrameSize(isStereo ? AudioConstants::NETWORK_FRAME_SAMPLES_STEREO : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL); + // restart the codec + if (_codec) { + if (_decoder) { + _codec->releaseDecoder(_decoder); + } + _decoder = _codec->createDecoder(AudioConstants::SAMPLE_RATE, isStereo ? AudioConstants::STEREO : AudioConstants::MONO); + } + qCDebug(audio) << "resetting AvatarAudioStream... codec:" << _selectedCodecName << "isStereo:" << isStereo; + _isStereo = isStereo; } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index acd9be0702..7a7210a0e8 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -116,8 +116,9 @@ public: void setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, const uint64_t& time); QVector& getLastOtherAvatarSentJoints(QUuid otherAvatar) { - _lastOtherAvatarSentJoints[otherAvatar].resize(_avatar->getJointCount()); - return _lastOtherAvatarSentJoints[otherAvatar]; + auto& lastOtherAvatarSentJoints = _lastOtherAvatarSentJoints[otherAvatar]; + lastOtherAvatarSentJoints.resize(_avatar->getJointCount()); + return lastOtherAvatarSentJoints; } void queuePacket(QSharedPointer message, SharedNodePointer node); diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index c9ded2d6fb..1f3f770867 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -123,12 +123,12 @@ void ScriptableAvatar::update(float deltatime) { AnimPose& absPose = absPoses[i]; if (data.rotation != absPose.rot()) { data.rotation = absPose.rot(); - data.rotationSet = true; + data.rotationIsDefaultPose = false; } AnimPose& relPose = poses[i]; if (data.translation != relPose.trans()) { data.translation = relPose.trans(); - data.translationSet = true; + data.translationIsDefaultPose = false; } } diff --git a/cmake/externals/glew/CMakeLists.txt b/cmake/externals/glad32es/CMakeLists.txt similarity index 50% rename from cmake/externals/glew/CMakeLists.txt rename to cmake/externals/glad32es/CMakeLists.txt index 6c3208981d..8ca4d44021 100644 --- a/cmake/externals/glew/CMakeLists.txt +++ b/cmake/externals/glad32es/CMakeLists.txt @@ -1,27 +1,22 @@ -set(EXTERNAL_NAME glew) - -if (ANDROID) - set(ANDROID_CMAKE_ARGS "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" "-DANDROID_NATIVE_API_LEVEL=19") -endif () +set(EXTERNAL_NAME glad32es) include(ExternalProject) +include(SelectLibraryConfigurations) + ExternalProject_Add( ${EXTERNAL_NAME} - URL http://hifi-public.s3.amazonaws.com/dependencies/glew_simple_1.13.0.zip - URL_MD5 73f833649e904257b35bf4e84f8bdfb5 - CONFIGURE_COMMAND CMAKE_ARGS ${ANDROID_CMAKE_ARGS} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_POSITION_INDEPENDENT_CODE=ON + URL https://hifi-public.s3.amazonaws.com/austin/glad/glad32es.zip + URL_MD5 6a641d8c49dee4c895fa59315f5682a6 + CONFIGURE_COMMAND CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_POSITION_INDEPENDENT_CODE=ON LOG_DOWNLOAD 1 LOG_CONFIGURE 1 LOG_BUILD 1 ) # Hide this external target (for ide users) -set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") - -ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) - string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) -set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glew include directories") +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") +ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) if (UNIX) set(LIB_PREFIX "lib") @@ -30,5 +25,9 @@ elseif (WIN32) set(LIB_EXT "lib") endif () -set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/${LIB_PREFIX}glew_d.${LIB_EXT} CACHE FILEPATH "Path to glew debug library") -set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/${LIB_PREFIX}glew.${LIB_EXT} CACHE FILEPATH "Path to glew release library") +set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/${LIB_PREFIX}glad_d.${LIB_EXT}) +set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/${LIB_PREFIX}glad.${LIB_EXT}) +select_library_configurations(${EXTERNAL_NAME_UPPER}) + +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glad include directories") +set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE INTERNAL "glad libraries") diff --git a/cmake/externals/glad41/CMakeLists.txt b/cmake/externals/glad41/CMakeLists.txt new file mode 100644 index 0000000000..2371044362 --- /dev/null +++ b/cmake/externals/glad41/CMakeLists.txt @@ -0,0 +1,33 @@ +set(EXTERNAL_NAME glad41) + +include(ExternalProject) +include(SelectLibraryConfigurations) + +ExternalProject_Add( + ${EXTERNAL_NAME} + URL https://hifi-public.s3.amazonaws.com/austin/glad/glad41.zip + URL_MD5 1324eeec33abe91e67d19ae551ba624d + CONFIGURE_COMMAND CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_POSITION_INDEPENDENT_CODE=ON + LOG_DOWNLOAD 1 + LOG_CONFIGURE 1 + LOG_BUILD 1 +) + +# Hide this external target (for ide users) +string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") +ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) + +if (UNIX) + set(LIB_PREFIX "lib") + set(LIB_EXT "a") +elseif (WIN32) + set(LIB_EXT "lib") +endif () + +set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/${LIB_PREFIX}glad_d.${LIB_EXT}) +set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/${LIB_PREFIX}glad.${LIB_EXT}) +select_library_configurations(${EXTERNAL_NAME_UPPER}) + +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glad include directories") +set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE INTERNAL "glad libraries") diff --git a/cmake/externals/glad45/CMakeLists.txt b/cmake/externals/glad45/CMakeLists.txt new file mode 100644 index 0000000000..8ad455fa7e --- /dev/null +++ b/cmake/externals/glad45/CMakeLists.txt @@ -0,0 +1,33 @@ +set(EXTERNAL_NAME glad45) + +include(ExternalProject) +include(SelectLibraryConfigurations) + +ExternalProject_Add( + ${EXTERNAL_NAME} + URL https://hifi-public.s3.amazonaws.com/austin/glad/glad45.zip + URL_MD5 cfb19b3cb5b2f8f1d1669fb3150e5f05 + CONFIGURE_COMMAND CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_POSITION_INDEPENDENT_CODE=ON + LOG_DOWNLOAD 1 + LOG_CONFIGURE 1 + LOG_BUILD 1 +) + +# Hide this external target (for ide users) +string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER) +set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals") +ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR) + +if (UNIX) + set(LIB_PREFIX "lib") + set(LIB_EXT "a") +elseif (WIN32) + set(LIB_EXT "lib") +endif () + +set(${EXTERNAL_NAME_UPPER}_LIBRARY_DEBUG ${INSTALL_DIR}/lib/${LIB_PREFIX}glad_d.${LIB_EXT}) +set(${EXTERNAL_NAME_UPPER}_LIBRARY_RELEASE ${INSTALL_DIR}/lib/${LIB_PREFIX}glad.${LIB_EXT}) +select_library_configurations(${EXTERNAL_NAME_UPPER}) + +set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/include CACHE PATH "List of glad include directories") +set(${EXTERNAL_NAME_UPPER}_LIBRARY ${${EXTERNAL_NAME_UPPER}_LIBRARY} CACHE INTERNAL "glad libraries") diff --git a/cmake/macros/GenerateQrc.cmake b/cmake/macros/GenerateQrc.cmake index 0283b3ea9b..9bf530b2a2 100644 --- a/cmake/macros/GenerateQrc.cmake +++ b/cmake/macros/GenerateQrc.cmake @@ -12,9 +12,14 @@ function(GENERATE_QRC) foreach(GLOB ${GENERATE_QRC_GLOBS}) file(GLOB_RECURSE FOUND_FILES RELATIVE ${GENERATE_QRC_PATH} ${GLOB}) foreach(FILENAME ${FOUND_FILES}) + if (${FILENAME} MATCHES "^\\.\\.") + continue() + endif() + list(APPEND ALL_FILES "${GENERATE_QRC_PATH}/${FILENAME}") set(QRC_CONTENTS "${QRC_CONTENTS}${GENERATE_QRC_PATH}/${FILENAME}\n") endforeach() endforeach() - + + set(GENERATE_QRC_DEPENDS ${ALL_FILES} PARENT_SCOPE) configure_file("${HF_CMAKE_DIR}/templates/resources.qrc.in" ${GENERATE_QRC_OUTPUT}) endfunction() diff --git a/cmake/macros/TargetGlad.cmake b/cmake/macros/TargetGlad.cmake new file mode 100644 index 0000000000..929f61c3f2 --- /dev/null +++ b/cmake/macros/TargetGlad.cmake @@ -0,0 +1,38 @@ +# +# Copyright 2015 High Fidelity, Inc. +# Created by Bradley Austin Davis on 2015/10/10 +# +# Distributed under the Apache License, Version 2.0. +# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +# +macro(TARGET_GLAD) + if (ANDROID) + set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/glad) + set(GLAD_INCLUDE_DIRS "${INSTALL_DIR}/include") + set(GLAD_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libglad_d.a) + set(GLAD_LIBRARY_RELEASE ${INSTALL_DIR}/lib/libglad.a) + select_library_configurations(GLAD) + find_library(EGL EGL) + target_link_libraries(${TARGET_NAME} ${EGL}) + else() + if (USE_GLES) + set(GLAD_VER "32es") + else() + set(GLAD_VER "45") + endif() + find_package(OpenGL REQUIRED) + list(APPEND GLAD_EXTRA_LIBRARIES ${OPENGL_LIBRARY}) + if (NOT WIN32) + list(APPEND GLAD_EXTRA_LIBRARIES dl) + endif() + set(GLAD "glad${GLAD_VER}") + string(TOUPPER ${GLAD} GLAD_UPPER) + add_dependency_external_projects(${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}) +endmacro() diff --git a/cmake/macros/TargetGlew.cmake b/cmake/macros/TargetGlew.cmake deleted file mode 100644 index bc4d5cb033..0000000000 --- a/cmake/macros/TargetGlew.cmake +++ /dev/null @@ -1,16 +0,0 @@ -# -# Copyright 2015 High Fidelity, Inc. -# Created by Bradley Austin Davis on 2015/10/10 -# -# Distributed under the Apache License, Version 2.0. -# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -# -macro(TARGET_GLEW) - if (NOT ANDROID) - add_definitions(-DGLEW_STATIC) - add_dependency_external_projects(glew) - find_package(GLEW REQUIRED) - target_include_directories(${TARGET_NAME} PUBLIC ${GLEW_INCLUDE_DIRS}) - target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARY}) - endif() -endmacro() \ No newline at end of file diff --git a/cmake/macros/TargetOpenGL.cmake b/cmake/macros/TargetOpenGL.cmake index 6ad92259bb..84fe5a7a5a 100644 --- a/cmake/macros/TargetOpenGL.cmake +++ b/cmake/macros/TargetOpenGL.cmake @@ -6,20 +6,6 @@ # See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html # macro(TARGET_OPENGL) - if (APPLE) - # link in required OS X frameworks and include the right GL headers - find_library(OpenGL OpenGL) - target_link_libraries(${TARGET_NAME} ${OpenGL}) - elseif(ANDROID) - target_link_libraries(${TARGET_NAME} GLESv3 EGL) - else() - find_package(OpenGL REQUIRED) - if (${OPENGL_INCLUDE_DIR}) - include_directories(SYSTEM "${OPENGL_INCLUDE_DIR}") - endif() - target_link_libraries(${TARGET_NAME} "${OPENGL_LIBRARY}") - target_include_directories(${TARGET_NAME} PUBLIC ${OPENGL_INCLUDE_DIR}) - endif() + target_glad() target_nsight() - target_glew() endmacro() diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 6b78826d75..383c50ed44 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -11,9 +11,20 @@ function(JOIN VALUES GLUE OUTPUT) set (${OUTPUT} "${_TMP_STR}" PARENT_SCOPE) endfunction() +set(RESOURCES_QRC ${CMAKE_CURRENT_BINARY_DIR}/resources.qrc) +set(RESOURCES_RCC ${CMAKE_CURRENT_SOURCE_DIR}/compiledResources/resources.rcc) +generate_qrc(OUTPUT ${RESOURCES_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources GLOBS *) + +add_custom_command( + OUTPUT ${RESOURCES_RCC} + DEPENDS ${RESOURCES_QRC} ${GENERATE_QRC_DEPENDS} + COMMAND "${QT_DIR}/bin/rcc" + ARGS ${RESOURCES_QRC} -binary -o ${RESOURCES_RCC} +) + +list(APPEND GENERATE_QRC_DEPENDS ${RESOURCES_RCC}) +add_custom_target(resources ALL DEPENDS ${GENERATE_QRC_DEPENDS}) -set(INTERFACE_QML_QRC ${CMAKE_CURRENT_BINARY_DIR}/qml.qrc) -generate_qrc(OUTPUT ${INTERFACE_QML_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources GLOBS *.qml *.qss *.js *.html *.ttf *.gif *.svg *.png *.jpg) # set a default root dir for each of our optional externals if it was not passed set(OPTIONAL_EXTERNALS "LeapMotion") @@ -40,6 +51,7 @@ endif() # grab the implementation and header files from src dirs file(GLOB_RECURSE INTERFACE_SRCS "src/*.cpp" "src/*.h") GroupSources("src") +list(APPEND INTERFACE_SRCS ${RESOURCES_RCC}) # Add SpeechRecognizer if on Windows or OS X, otherwise remove if (WIN32) @@ -72,16 +84,6 @@ qt5_wrap_ui(QT_UI_HEADERS "${QT_UI_FILES}") # add them to the interface source files set(INTERFACE_SRCS ${INTERFACE_SRCS} "${QT_UI_HEADERS}" "${QT_RESOURCES}") -list(APPEND INTERFACE_SRCS ${INTERFACE_QML_QRC}) - -if (UNIX) - install( - DIRECTORY "${CMAKE_SOURCE_DIR}/interface/resources/qml" - DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/resources - COMPONENT ${CLIENT_COMPONENT} - ) -endif() - # translation disabled until we strip out the line numbers # set(QM ${TARGET_NAME}_en.qm) # set(TS ${TARGET_NAME}_en.ts) @@ -89,21 +91,7 @@ endif() # setup the android parameters that will help us produce an APK if (ANDROID) - set(ANDROID_APK_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}/apk-build") - set(ANDROID_APK_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/apk") - - set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${ANDROID_APK_OUTPUT_DIR}/libs/${ANDROID_ABI}") - set(ANDROID_SDK_ROOT $ENV{ANDROID_HOME}) - set(ANDROID_APP_DISPLAY_NAME Interface) - set(ANDROID_API_LEVEL 19) - set(ANDROID_APK_PACKAGE io.highfidelity.interface) - set(ANDROID_ACTIVITY_NAME io.highfidelity.interface.InterfaceActivity) - set(ANDROID_APK_VERSION_NAME "0.1") - set(ANDROID_APK_VERSION_CODE 1) - set(ANDROID_APK_FULLSCREEN TRUE) - set(ANDROID_DEPLOY_QT_INSTALL "--install") - set(BUILD_SHARED_LIBS ON) endif () @@ -175,6 +163,8 @@ else () add_executable(${TARGET_NAME} ${INTERFACE_SRCS} ${QM}) endif () +add_dependencies(${TARGET_NAME} resources) + if (WIN32) # These are external plugins, but we need to do the 'add dependency' here so that their # binary directories get added to the fixup path @@ -201,7 +191,7 @@ endif() # link required hifi libraries link_hifi_libraries( - shared octree ktx gpu gl procedural model render + shared octree ktx gpu gl procedural graphics render pointers recording fbx networking model-networking entities avatars trackers audio audio-client animation script-engine physics @@ -258,15 +248,20 @@ endforeach() # include headers for interface and InterfaceConfig. include_directories("${PROJECT_SOURCE_DIR}/src") +if (ANDROID) + find_library(ANDROID_LOG_LIB log) + target_link_libraries(${TARGET_NAME} ${ANDROID_LOG_LIB}) +endif () + target_link_libraries( ${TARGET_NAME} Qt5::Gui Qt5::Network Qt5::Multimedia Qt5::OpenGL Qt5::Qml Qt5::Quick Qt5::Script Qt5::Svg - Qt5::WebChannel Qt5::WebEngine + Qt5::WebChannel ${PLATFORM_QT_LIBRARIES} ) -if (UNIX) +if (UNIX AND NOT ANDROID) if (CMAKE_SYSTEM_NAME MATCHES "Linux") # Linux target_link_libraries(${TARGET_NAME} pthread atomic) @@ -274,7 +269,7 @@ if (UNIX) # OSX target_link_libraries(${TARGET_NAME} pthread) endif () -endif(UNIX) +endif() # assume we are using a Qt build without bearer management add_definitions(-DQT_NO_BEARERMANAGEMENT) @@ -304,12 +299,20 @@ if (APPLE) # call the fixup_interface macro to add required bundling commands for installation fixup_interface() -else (APPLE) +else() # copy the resources files beside the executable add_custom_command(TARGET ${TARGET_NAME} POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy_if_different + "${RESOURCES_RCC}" + "$" + # FIXME, the edit script code loads HTML from the scripts folder + # which in turn relies on CSS that refers to the fonts. In theory + # we should be able to modify the CSS to reference the QRC path to + # the ttf files, but doing so generates a CORS policy violation, + # so we have to retain a copy of the fonts outside of the resources binary COMMAND "${CMAKE_COMMAND}" -E copy_directory - "${PROJECT_SOURCE_DIR}/resources" - "$/resources" + "${PROJECT_SOURCE_DIR}/resources/fonts" + "$/resources/fonts" COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/scripts" "$/scripts" @@ -335,7 +338,7 @@ else (APPLE) optional_win_executable_signing() endif() -endif (APPLE) +endif() if (SCRIPTS_INSTALL_DIR) @@ -359,20 +362,6 @@ if (WIN32) package_libraries_for_deployment() endif() -if (ANDROID) - set(HIFI_URL_INTENT "\ - \n \ - \n \ - \n \ - \n \ - \n " - ) - - set(ANDROID_EXTRA_ACTIVITY_XML "${HIFI_URL_INTENT}") - - qt_create_apk() -endif () - add_dependency_external_projects(GifCreator) find_package(GifCreator REQUIRED) target_include_directories(${TARGET_NAME} PUBLIC ${GIFCREATOR_INCLUDE_DIRS}) diff --git a/interface/compiledResources/.placeholder b/interface/compiledResources/.placeholder new file mode 100644 index 0000000000..e69de29bb2 diff --git a/interface/resources/icons/tablet-icons/EmoteAppIcon.svg b/interface/resources/icons/tablet-icons/EmoteAppIcon.svg new file mode 100644 index 0000000000..340f0fcd2f --- /dev/null +++ b/interface/resources/icons/tablet-icons/EmoteAppIcon.svg @@ -0,0 +1,21 @@ + + + + + + + + + diff --git a/interface/resources/images/+gles/Default-Sky-9-cubemap.ktx b/interface/resources/images/+gles/Default-Sky-9-cubemap.ktx new file mode 100644 index 0000000000..6d46791fa2 Binary files /dev/null and b/interface/resources/images/+gles/Default-Sky-9-cubemap.ktx differ diff --git a/interface/resources/images/preview-privacy.png b/interface/resources/images/preview-privacy.png new file mode 100644 index 0000000000..1a718de233 Binary files /dev/null and b/interface/resources/images/preview-privacy.png differ diff --git a/interface/resources/meshes/+gles/defaultAvatar_full.fst b/interface/resources/meshes/+gles/defaultAvatar_full.fst new file mode 100644 index 0000000000..7d917318dd --- /dev/null +++ b/interface/resources/meshes/+gles/defaultAvatar_full.fst @@ -0,0 +1,135 @@ +name = being_of_light +type = body+head +scale = 1 +filename = being_of_light/being_of_light.fbx +texdir = being_of_light/textures +joint = jointRoot = Hips +joint = jointLeftHand = LeftHand +joint = jointHead = HeadTop_End +joint = jointLean = Spine +joint = jointEyeLeft = LeftEye +joint = jointRightHand = RightHand +joint = jointNeck = Head +joint = jointEyeRight = RightEye +freeJoint = LeftArm +freeJoint = LeftForeArm +freeJoint = RightArm +freeJoint = RightForeArm +bs = MouthFrown_L = Frown_Left = 1 +bs = MouthLeft = Midmouth_Left = 1 +bs = BrowsU_R = BrowsUp_Right = 1 +bs = ChinUpperRaise = UpperLipUp_Right = 0.5 +bs = ChinUpperRaise = UpperLipUp_Left = 0.5 +bs = MouthSmile_R = Smile_Right = 1 +bs = MouthDimple_L = Smile_Left = 0.25 +bs = EyeBlink_L = Blink_Left = 1 +bs = BrowsD_L = BrowsDown_Left = 1 +bs = MouthFrown_R = Frown_Right = 1 +bs = MouthDimple_R = Smile_Right = 0.25 +bs = Sneer = Squint_Right = 0.5 +bs = Sneer = Squint_Left = 0.5 +bs = Sneer = NoseScrunch_Right = 0.75 +bs = Sneer = NoseScrunch_Left = 0.75 +bs = EyeSquint_L = Squint_Left = 1 +bs = EyeBlink_R = Blink_Right = 1 +bs = JawLeft = JawRotateY_Left = 0.5 +bs = BrowsD_R = BrowsDown_Right = 1 +bs = EyeSquint_R = Squint_Right = 1 +bs = Puff = CheekPuff_Right = 1 +bs = Puff = CheekPuff_Left = 1 +bs = LipsUpperClose = UpperLipIn = 1 +bs = JawOpen = MouthOpen = 0.69999999999999996 +bs = LipsUpperUp = UpperLipUp_Right = 0.69999999999999996 +bs = LipsUpperUp = UpperLipUp_Left = 0.69999999999999996 +bs = LipsLowerDown = LowerLipDown_Right = 0.69999999999999996 +bs = LipsLowerDown = LowerLipDown_Left = 0.69999999999999996 +bs = LipsLowerOpen = LowerLipOut = 1 +bs = EyeOpen_L = EyesWide_Left = 1 +bs = LipsPucker = MouthNarrow_Right = 1 +bs = LipsPucker = MouthNarrow_Left = 1 +bs = EyeOpen_R = EyesWide_Right = 1 +bs = JawRight = Jaw_Right = 1 +bs = MouthRight = Midmouth_Right = 1 +bs = ChinLowerRaise = Jaw_Up = 1 +bs = LipsUpperOpen = UpperLipOut = 1 +bs = BrowsU_C = BrowsUp_Right = 1 +bs = BrowsU_C = BrowsUp_Left = 1 +bs = JawFwd = JawForeward = 1 +bs = BrowsU_L = BrowsUp_Left = 1 +bs = MouthSmile_L = Smile_Left = 1 +bs = LipsLowerClose = LowerLipIn = 1 +bs = LipsFunnel = TongueUp = 1 +bs = LipsFunnel = MouthWhistle_NarrowAdjust_Right = 0.5 +bs = LipsFunnel = MouthWhistle_NarrowAdjust_Left = 0.5 +bs = LipsFunnel = MouthNarrow_Right = 1 +bs = LipsFunnel = MouthNarrow_Left = 1 +bs = LipsFunnel = Jaw_Down = 0.35999999999999999 +bs = LipsFunnel = JawForeward = 0.39000000000000001 +jointIndex = LeftHandIndex1 = 50 +jointIndex = LeftHandIndex2 = 51 +jointIndex = LeftHandIndex3 = 52 +jointIndex = LeftHandIndex4 = 53 +jointIndex = Spine1 = 12 +jointIndex = Spine2 = 13 +jointIndex = RightHandThumb1 = 18 +jointIndex = RightHandThumb2 = 19 +jointIndex = RightHandThumb3 = 20 +jointIndex = RightHandThumb4 = 21 +jointIndex = LeftFoot = 8 +jointIndex = LeftForeArm = 40 +jointIndex = Neck = 62 +jointIndex = Head = 63 +jointIndex = Hips = 0 +jointIndex = RightHandPinky1 = 30 +jointIndex = RightHandPinky2 = 31 +jointIndex = RightHandPinky3 = 32 +jointIndex = RightHandPinky4 = 33 +jointIndex = RightLeg = 2 +jointIndex = RightForeArm = 16 +jointIndex = LeftHandRing1 = 46 +jointIndex = LeftHandRing2 = 47 +jointIndex = LeftHandRing3 = 48 +jointIndex = LeftHandRing4 = 49 +jointIndex = LeftHandThumb1 = 54 +jointIndex = LeftHandThumb2 = 55 +jointIndex = LeftHandThumb3 = 56 +jointIndex = LeftHandThumb4 = 57 +jointIndex = HeadTop_End = 66 +jointIndex = LeftUpLeg = 6 +jointIndex = LeftToeBase = 9 +jointIndex = LeftHandPinky1 = 42 +jointIndex = LeftHandPinky2 = 43 +jointIndex = LeftHandPinky3 = 44 +jointIndex = LeftHandPinky4 = 45 +jointIndex = LeftLeg = 7 +jointIndex = RightEye = 65 +jointIndex = RightHand = 17 +jointIndex = RightToeBase = 4 +jointIndex = RightUpLeg = 1 +jointIndex = RightArm = 15 +jointIndex = RightHandRing1 = 26 +jointIndex = RightHandRing2 = 27 +jointIndex = RightHandRing3 = 28 +jointIndex = RightHandRing4 = 29 +jointIndex = RightHandIndex1 = 22 +jointIndex = RightHandIndex2 = 23 +jointIndex = RightHandIndex3 = 24 +jointIndex = RightHandIndex4 = 25 +jointIndex = LeftToe_End = 10 +jointIndex = LeftHandMiddle1 = 58 +jointIndex = LeftHandMiddle2 = 59 +jointIndex = LeftHandMiddle3 = 60 +jointIndex = LeftShoulder = 38 +jointIndex = LeftHandMiddle4 = 61 +jointIndex = RightFoot = 3 +jointIndex = LeftHand = 41 +jointIndex = RightHandMiddle1 = 34 +jointIndex = RightHandMiddle2 = 35 +jointIndex = RightHandMiddle3 = 36 +jointIndex = RightShoulder = 14 +jointIndex = LeftEye = 64 +jointIndex = RightHandMiddle4 = 37 +jointIndex = Body = 67 +jointIndex = LeftArm = 39 +jointIndex = RightToe_End = 5 +jointIndex = Spine = 11 diff --git a/interface/resources/meshes/being_of_light/being_of_light.fbx b/interface/resources/meshes/being_of_light/being_of_light.fbx new file mode 100644 index 0000000000..20e71abd6d Binary files /dev/null and b/interface/resources/meshes/being_of_light/being_of_light.fbx differ diff --git a/interface/resources/meshes/being_of_light/textures/BaseMesh_BeingofLight_DiffuseMap.png b/interface/resources/meshes/being_of_light/textures/BaseMesh_BeingofLight_DiffuseMap.png new file mode 100644 index 0000000000..8fcf588643 Binary files /dev/null and b/interface/resources/meshes/being_of_light/textures/BaseMesh_BeingofLight_DiffuseMap.png differ diff --git a/interface/resources/meshes/being_of_light/textures/BaseMesh_BeingofLight_EmissiveMap.png b/interface/resources/meshes/being_of_light/textures/BaseMesh_BeingofLight_EmissiveMap.png new file mode 100644 index 0000000000..d836aebde8 Binary files /dev/null and b/interface/resources/meshes/being_of_light/textures/BaseMesh_BeingofLight_EmissiveMap.png differ diff --git a/interface/resources/meshes/being_of_light/textures/BaseMesh_BeingofLight_NormalMap.png b/interface/resources/meshes/being_of_light/textures/BaseMesh_BeingofLight_NormalMap.png new file mode 100644 index 0000000000..f5000b86e9 Binary files /dev/null and b/interface/resources/meshes/being_of_light/textures/BaseMesh_BeingofLight_NormalMap.png differ diff --git a/interface/resources/meshes/tablet-with-home-button.fbx b/interface/resources/meshes/tablet-with-home-button.fbx index 70b8008bad..37247022f6 100644 Binary files a/interface/resources/meshes/tablet-with-home-button.fbx and b/interface/resources/meshes/tablet-with-home-button.fbx differ diff --git a/interface/resources/qml/+android/Stats.qml b/interface/resources/qml/+android/Stats.qml new file mode 100644 index 0000000000..d961285a46 --- /dev/null +++ b/interface/resources/qml/+android/Stats.qml @@ -0,0 +1,394 @@ +import Hifi 1.0 as Hifi +import QtQuick 2.3 +import '.' + +Item { + id: stats + + anchors.leftMargin: 300 + objectName: "StatsItem" + property int modality: Qt.NonModal + implicitHeight: row.height + implicitWidth: row.width + + Component.onCompleted: { + stats.parentChanged.connect(fill); + fill(); + } + Component.onDestruction: { + stats.parentChanged.disconnect(fill); + } + + function fill() { + // This will cause a warning at shutdown, need to find another way to remove + // the warning other than filling the anchors to the parent + anchors.horizontalCenter = parent.horizontalCenter + } + + Hifi.Stats { + id: root + objectName: "Stats" + implicitHeight: row.height + implicitWidth: row.width + + anchors.horizontalCenter: parent.horizontalCenter + readonly property string bgColor: "#AA111111" + + Row { + id: row + spacing: 8 + Rectangle { + width: generalCol.width + 8; + height: generalCol.height + 8; + color: root.bgColor; + + MouseArea { + anchors.fill: parent + onClicked: { root.expanded = !root.expanded; } + hoverEnabled: true + } + + Column { + id: generalCol + spacing: 4; x: 4; y: 4; + StatText { + text: "Servers: " + root.serverCount + } + StatText { + text: "Avatars: " + root.avatarCount + } + StatText { + text: "Game Rate: " + root.gameLoopRate + } + StatText { + visible: root.expanded + text: root.gameUpdateStats + } + StatText { + text: "Render Rate: " + root.renderrate.toFixed(2); + } + StatText { + text: "Present Rate: " + root.presentrate.toFixed(2); + } + StatText { + visible: root.expanded + text: " Present New Rate: " + root.presentnewrate.toFixed(2); + } + StatText { + visible: root.expanded + text: " Present Drop Rate: " + root.presentdroprate.toFixed(2); + } + StatText { + text: "Stutter Rate: " + root.stutterrate.toFixed(3); + visible: root.stutterrate != -1; + } + StatText { + text: "Missed Frame Count: " + root.appdropped; + visible: root.appdropped > 0; + } + StatText { + text: "Long Render Count: " + root.longrenders; + visible: root.longrenders > 0; + } + StatText { + text: "Long Submit Count: " + root.longsubmits; + visible: root.longsubmits > 0; + } + StatText { + text: "Long Frame Count: " + root.longframes; + visible: root.longframes > 0; + } + StatText { + text: "Packets In/Out: " + root.packetInCount + "/" + root.packetOutCount + } + StatText { + text: "Mbps In/Out: " + root.mbpsIn.toFixed(2) + "/" + root.mbpsOut.toFixed(2) + } + StatText { + visible: root.expanded + text: "Asset Mbps In/Out: " + root.assetMbpsIn.toFixed(2) + "/" + root.assetMbpsOut.toFixed(2) + } + StatText { + visible: root.expanded + text: "Avatars Updated: " + root.updatedAvatarCount + } + StatText { + visible: root.expanded + text: "Avatars NOT Updated: " + root.notUpdatedAvatarCount + } + } + } + + Rectangle { + width: pingCol.width + 8 + height: pingCol.height + 8 + color: root.bgColor; + visible: root.audioPing != -2 + MouseArea { + anchors.fill: parent + onClicked: { root.expanded = !root.expanded; } + hoverEnabled: true + } + Column { + id: pingCol + spacing: 4; x: 4; y: 4; + StatText { + text: "Audio ping/loss: " + root.audioPing + "/" + root.audioPacketLoss + "%" + } + StatText { + text: "Avatar ping: " + root.avatarPing + } + StatText { + text: "Entities avg ping: " + root.entitiesPing + } + StatText { + text: "Asset ping: " + root.assetPing + } + StatText { + visible: root.expanded; + text: "Messages max ping: " + root.messagePing + } + } + } + + Rectangle { + width: geoCol.width + 8 + height: geoCol.height + 8 + color: root.bgColor; + MouseArea { + anchors.fill: parent + onClicked: { root.expanded = !root.expanded; } + hoverEnabled: true + } + Column { + id: geoCol + spacing: 4; x: 4; y: 4; + StatText { + text: "Position: " + root.position.x.toFixed(1) + ", " + + root.position.y.toFixed(1) + ", " + root.position.z.toFixed(1) + } + StatText { + text: "Speed: " + root.speed.toFixed(1) + } + StatText { + text: "Yaw: " + root.yaw.toFixed(1) + } + StatText { + visible: root.expanded; + text: "Avatar Mixer In: " + root.avatarMixerInKbps + " kbps, " + + root.avatarMixerInPps + "pps"; + } + StatText { + visible: root.expanded; + text: "Avatar Mixer Out: " + root.avatarMixerOutKbps + " kbps, " + + root.avatarMixerOutPps + "pps, " + + root.myAvatarSendRate.toFixed(2) + "hz"; + } + StatText { + visible: root.expanded; + text: "Audio Mixer In: " + root.audioMixerInKbps + " kbps, " + + root.audioMixerInPps + "pps"; + } + StatText { + visible: root.expanded; + text: "Audio In Audio: " + root.audioAudioInboundPPS + " pps, " + + "Silent: " + root.audioSilentInboundPPS + " pps"; + } + StatText { + visible: root.expanded; + text: "Audio Mixer Out: " + root.audioMixerOutKbps + " kbps, " + + root.audioMixerOutPps + "pps"; + } + StatText { + visible: root.expanded; + text: "Audio Out Mic: " + root.audioOutboundPPS + " pps, " + + "Silent: " + root.audioSilentOutboundPPS + " pps"; + } + StatText { + visible: root.expanded; + text: "Audio Codec: " + root.audioCodec + " Noise Gate: " + + root.audioNoiseGate; + } + StatText { + visible: root.expanded; + text: "Entity Servers In: " + root.entityPacketsInKbps + " kbps"; + } + StatText { + visible: root.expanded; + text: "Downloads: " + root.downloads + "/" + root.downloadLimit + + ", Pending: " + root.downloadsPending; + } + StatText { + visible: root.expanded; + text: "Processing: " + root.processing + + ", Pending: " + root.processingPending; + } + StatText { + visible: root.expanded && root.downloadUrls.length > 0; + text: "Download URLs:" + } + ListView { + width: geoCol.width + height: root.downloadUrls.length * 15 + + visible: root.expanded && root.downloadUrls.length > 0; + + model: root.downloadUrls + delegate: StatText { + visible: root.expanded; + text: modelData.length > 30 + ? modelData.substring(0, 5) + "..." + modelData.substring(modelData.length - 22) + : modelData + } + } + } + } + Rectangle { + width: octreeCol.width + 8 + height: octreeCol.height + 8 + color: root.bgColor; + MouseArea { + anchors.fill: parent + onClicked: { root.expanded = !root.expanded; } + hoverEnabled: true + } + Column { + id: octreeCol + spacing: 4; x: 4; y: 4; + StatText { + text: "Engine: " + root.engineFrameTime.toFixed(1) + " ms" + } + StatText { + text: "Batch: " + root.batchFrameTime.toFixed(1) + " ms" + } + StatText { + text: "GPU: " + root.gpuFrameTime.toFixed(1) + " ms" + } + StatText { + text: "Triangles: " + root.triangles + + " / Material Switches: " + root.materialSwitches + } + StatText { + text: "GPU Free Memory: " + root.gpuFreeMemory + " MB"; + } + StatText { + text: "GPU Textures: "; + } + StatText { + text: " Count: " + root.gpuTextures; + } + StatText { + text: " Pressure State: " + root.gpuTextureMemoryPressureState; + } + StatText { + text: " Resource Allocated / Populated / Pending: "; + } + StatText { + text: " " + root.gpuTextureResourceMemory + " / " + root.gpuTextureResourcePopulatedMemory + " / " + root.texturePendingTransfers + " MB"; + } + StatText { + text: " Resident Memory: " + root.gpuTextureResidentMemory + " MB"; + } + StatText { + text: " Framebuffer Memory: " + root.gpuTextureFramebufferMemory + " MB"; + } + StatText { + text: " External Memory: " + root.gpuTextureExternalMemory + " MB"; + } + StatText { + text: "GPU Buffers: " + } + StatText { + text: " Count: " + root.gpuBuffers; + } + StatText { + text: " Memory: " + root.gpuBufferMemory + " MB"; + } + StatText { + text: "GL Swapchain Memory: " + root.glContextSwapchainMemory + " MB"; + } + StatText { + text: "QML Texture Memory: " + root.qmlTextureMemory + " MB"; + } + StatText { + visible: root.expanded; + text: "Items rendered / considered: " + + root.itemRendered + " / " + root.itemConsidered; + } + StatText { + visible: root.expanded; + text: " out of view: " + root.itemOutOfView + + " too small: " + root.itemTooSmall; + } + StatText { + visible: root.expanded; + text: "Shadows rendered / considered: " + + root.shadowRendered + " / " + root.shadowConsidered; + } + StatText { + visible: root.expanded; + text: " out of view: " + root.shadowOutOfView + + " too small: " + root.shadowTooSmall; + } + StatText { + visible: !root.expanded + text: "Octree Elements Server: " + root.serverElements + + " Local: " + root.localElements; + } + StatText { + visible: root.expanded + text: "Octree Sending Mode: " + root.sendingMode; + } + StatText { + visible: root.expanded + text: "Octree Packets to Process: " + root.packetStats; + } + StatText { + visible: root.expanded + text: "Octree Elements - "; + } + StatText { + visible: root.expanded + text: "\tServer: " + root.serverElements + + " Internal: " + root.serverInternal + + " Leaves: " + root.serverLeaves; + } + StatText { + visible: root.expanded + text: "\tLocal: " + root.localElements + + " Internal: " + root.localInternal + + " Leaves: " + root.localLeaves; + } + StatText { + visible: root.expanded + text: "LOD: " + root.lodStatus; + } + } + } + } + + Rectangle { + y: 250 + visible: root.timingExpanded + width: perfText.width + 8 + height: perfText.height + 8 + color: root.bgColor; + StatText { + x: 4; y: 4 + id: perfText + font.family: root.monospaceFont + text: "------------------------------------------ Function " + + "--------------------------------------- --msecs- -calls--\n" + + root.timingStats; + } + } + + Connections { + target: root.parent + onWidthChanged: { + root.x = root.parent.width - root.width; + } + } + } + +} diff --git a/interface/resources/qml/CurrentAPI.qml b/interface/resources/qml/CurrentAPI.qml new file mode 100644 index 0000000000..37984965d9 --- /dev/null +++ b/interface/resources/qml/CurrentAPI.qml @@ -0,0 +1,691 @@ +// +// ScriptAPI.qml +// +// Created by Luis Cuenca on 12/18/2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import "styles-uit" +import "controls-uit" as HifiControls + +Item { + id: root + width: parent.width + height: parent.height + + property var hideQtMethods: true + property var maxUpdateValues: 20 + property var maxReloadValues: 200 + property var apiMembers: [] + property var membersWithValues: [] + property var isReloading: false + property var evaluatingIdx: -1 + property Component scrollSlider + property Component keyboard + + Rectangle { + color: hifi.colors.baseGray + width: parent.width + height: parent.height + } + + FontLoader { id: ralewayRegular; source: pathToFonts + "fonts/Raleway-Regular.ttf"; } + + Timer { + id: updateList + interval: 200 + repeat: false + running: false + onTriggered: { + scrollSlider.y = 0; + list.contentY = 0; + } + } + + Row { + id: topBar + anchors.left: parent.left + anchors.leftMargin: 30 + anchors.top: parent.top + anchors.topMargin: 30 + width: parent.width + height: 40 + + HifiControls.GlyphButton { + id: back; + enabled: true; + color: hifi.buttons.black + glyph: hifi.glyphs.backward + size: 40 + width: 40 + height: 40 + anchors.left: search.right + anchors.leftMargin: 12 + onClicked: { + var text = searchBar.text; + var chain = text.split("."); + if (chain.length > 2) { + var result = chain[0]+"."; + for (var i = 1; i < chain.length-2; i++) { + result += chain[i] + "."; + } + result += chain[chain.length-2]; + searchBar.text = result; + } else { + searchBar.text = (chain.length > 1) ? chain[0] : ""; + } + if (chain.length > 1) { + addListElements(searchBar.text); + } else { + addListElements(); + } + focus = true; + } + } + + HifiControls.TextField { + id: searchBar + focus: true + isSearchField: true + width: parent.width - 112 + height: 40 + colorScheme: hifi.colorSchemes.dark + anchors.left: back.right + anchors.leftMargin: 10 + font.family: firaSansSemiBold.name + placeholderText: "Search" + onAccepted: { + console.log("Enter Pressed"); + addListElements(searchBar.text); + } + onActiveFocusChanged: { + if (activeFocus && HMD.mounted) { + keyboard.raised = true; + } else { + keyboard.raised = false; + } + } + + } + } + + Row { + id: topBar2 + anchors.left: parent.left + anchors.leftMargin: 30 + anchors.top: topBar.bottom + anchors.topMargin: 30 + width: parent.width -60 + height: 40 + + HifiControls.GlyphButton { + id: addMember; + enabled: true; + color: hifi.buttons.black + glyph: hifi.glyphs.maximize + width: 40 + height: 40 + anchors.top: parent.top + anchors.left: parent.left + onClicked: { + addNewMember(); + updateList.start(); + focus = true; + } + } + + HifiControls.Button { + id: evaluate; + enabled: true; + color: hifi.buttons.black + text: "Eval" + width: 40 + height: 40 + anchors.left: addMember.right + anchors.leftMargin: 12 + onClicked: { + evaluateMember(); + focus = true; + } + } + + HifiControls.TextField { + id: valueBar + isSearchField: false + font.pixelSize: 16 + width: parent.width - 208 + height: 40 + colorScheme: hifi.colorSchemes.dark + font.family: firaSansSemiBold.name + placeholderText: "Value" + anchors.left: evaluate.right + anchors.leftMargin: 12 + onActiveFocusChanged: { + if (activeFocus && HMD.mounted) { + keyboard.raised = true; + } else { + keyboard.raised = false; + } + } + } + + HifiControls.GlyphButton { + id: reload; + enabled: false; + color: hifi.buttons.black + glyph: hifi.glyphs.reload + size: 40 + width: 40 + height: 40 + anchors.right: update.left + anchors.rightMargin: 12 + onClicked: { + reloadListValues(); + focus = true; + } + } + + HifiControls.GlyphButton { + id: update; + enabled: false; + color: hifi.buttons.black + glyph: hifi.glyphs.playback_play + size: 40 + width: 40 + height: 40 + anchors.right: parent.right + onClicked: { + if (isReloading) { + update.glyph = hifi.glyphs.playback_play + isReloading = false; + stopReload(); + } else { + update.glyph = hifi.glyphs.stop_square + isReloading = true; + startReload(); + } + focus = true; + } + } + } + + Rectangle { + id: membersBackground + anchors { + left: parent.left; right: parent.right; top: topBar2.bottom; bottom: bottomBar.top; + margins: 30 + } + color: hifi.colors.tableBackgroundDark + border.color: hifi.colors.lightGray + border.width: 2 + radius: 5 + + ListModel { + id: memberModel + } + + Component { + id: memberDelegate + Item { + id: item + width: parent.width + anchors.left: parent.left + height: 26 + clip: true + + Rectangle { + width: parent.width + height: parent.height + color: index % 2 == 0 ? hifi.colors.tableRowDarkEven : hifi.colors.tableRowDarkOdd + anchors.verticalCenter: parent.verticalCenter + + Row { + id: memberRow + anchors.bottom: parent.bottom + anchors.verticalCenter: parent.verticalCenter + spacing: 10 + + FiraSansSemiBold { + property var isMainKey: apiType === "class"; + text: apiMember + size: isMainKey ? 17 : 15 + font.bold: true + anchors.verticalCenter: parent.verticalCenter + color: isMainKey ? hifi.colors.faintGray : hifi.colors.lightGrayText + MouseArea { + width: list.width + height: parent.height + onClicked: { + searchBar.text = apiType=="function()" ? apiMember + "()" : apiMember; + valueBar.text = !apiValue ? "" : apiValue; + list.currentIndex = index; + evaluatingIdx = index; + } + onDoubleClicked: { + if (apiType === "class") { + addListElements(apiMember+"."); + } else { + isolateElement(evaluatingIdx); + } + } + } + } + + FiraSansRegular { + text: apiType + anchors.left: apiMember.right + anchors.verticalCenter: parent.verticalCenter + size: 13 + color: hifi.colors.lightGrayText + } + + FiraSansRegular { + text: !apiValue ? "" : apiValue; + anchors.left: apiType.right + anchors.verticalCenter: parent.verticalCenter + size: 14 + color: hifi.colors.primaryHighlight + } + } + } + } + } + + Component { + id: highlight + Rectangle { + anchors { + left: list.left + right: scrollBar.left + leftMargin: 2 + rightMargin: 2 + } + color: hifi.colors.primaryHighlight + radius: 4 + z: 10 + opacity: 0.5 + } + } + + ListView { + id: list + anchors { + top: parent.top + left: parent.left + right: scrollBar.left + bottom: parent.bottom + topMargin: 2 + leftMargin: 2 + bottomMargin: 2 + } + clip: true + cacheBuffer: 4000 + model: memberModel + delegate: memberDelegate + highlightMoveDuration: 0 + highlight: highlight + onMovementStarted: { + scrollSlider.manual = true; + } + onMovementEnded: { + if (list.contentHeight > list.height) { + var range = list.contentY/(list.contentHeight-list.height); + range = range > 1 ? 1 : range; + var idx = Math.round((list.count-1)*range); + scrollSlider.positionSlider(idx); + } + scrollSlider.manual = false; + returnToBounds() + } + } + + Rectangle { + id: scrollBar + + property bool scrolling: list.contentHeight > list.height + + anchors { + top: parent.top + right: parent.right + bottom: parent.bottom + margins: 2 + } + width: 22 + height: parent.height - 4 + color: hifi.colors.tableScrollBackgroundDark + + MouseArea { + anchors.fill: parent + + onClicked: { + var index = scrollSlider.y * (list.count - 1) / (scrollBar.height - scrollSlider.height); + index = Math.round(index); + var scrollAmount = Math.round(list.count/10); + index = index + (mouse.y <= scrollSlider.y ? -scrollAmount : scrollAmount); + if (index < 0) { + index = 0; + } + if (index > list.count - 1) { + index = list.count - 1; + } + scrollSlider.positionSlider(index); + } + } + + Rectangle { + id: scrollSlider + + property var manual: false + + function positionSlider(index){ + y = index*(scrollBar.height - scrollSlider.height)/(list.count - 1); + } + + anchors.right: parent.right + anchors.margins: 2 + width: 18 + height: ((list.height / list.contentHeight) * list.height) < 15 ? 15 : (list.height / list.contentHeight) * list.height + radius: 5 + color: hifi.colors.tableScrollHandleDark + + visible: scrollBar.scrolling; + + onYChanged: { + var index = y * (list.count - 1) / (scrollBar.height - scrollSlider.height); + index = Math.round(index); + if (!manual) { + list.positionViewAtIndex(index, ListView.Visible); + } + } + + MouseArea { + anchors.fill: parent + drag.target: scrollSlider + drag.axis: Drag.YAxis + drag.minimumY: 0 + drag.maximumY: scrollBar.height - scrollSlider.height + } + } + } + } + + Row { + id: bottomBar + anchors.left: parent.left + anchors.leftMargin: 30 + anchors.bottom: parent.bottom + anchors.bottomMargin: 30 + width: parent.width + height: 40 + + HifiControls.GlyphButton { + id: clipboard; + enabled: true; + color: hifi.buttons.black + glyph: hifi.glyphs.scriptNew + size: 25 + width: 40 + height: 40 + anchors.left: parent.left + onClicked: { + var buffer = ""; + for (var i = 0; i < memberModel.count; i++) { + var datarow = memberModel.get(i); + buffer += "\n" + datarow.apiMember + " " + datarow.apiType + " " + datarow.apiValue; + } + Window.copyToClipboard(buffer); + focus = true; + } + } + + HifiControls.CheckBox { + id: hideQt + colorScheme: hifi.checkbox.dark + boxSize: 25 + boxRadius: 3 + checked: true + anchors.left: clipboard.right + anchors.leftMargin: 10 + anchors.verticalCenter: clipboard.verticalCenter + onClicked: { + hideQtMethods = checked; + addListElements(); + } + } + + HifiControls.Label { + id: hideLabel + anchors.left: hideQt.right + anchors.verticalCenter: clipboard.verticalCenter + anchors.margins: 2 + font.pixelSize: 15 + text: "Hide Qt Methods" + } + + HifiControls.Button { + id: debug; + enabled: true; + color: hifi.buttons.black + text: "Debug Script" + width: 120 + height: 40 + anchors.right: parent.right + anchors.rightMargin: 60 + anchors.bottom: parent.bottom + + onClicked: { + sendToScript({type: "selectScript"}); + } + } + } + + HifiControls.Keyboard { + id: keyboard; + raised: false; + anchors { + bottom: parent.bottom; + left: parent.left; + right: parent.right; + } + + Keys.onPressed: { + console.log(event.nativeScanCode); + if (event.key == Qt.Key_Left) { + keyboard.raised = false; + } + } + } + + function addNewMember() { + apiMembers.push({member: searchBar.text, type: "user", value: valueBar.text, hasValue: true}); + var data = {'memberIndex': apiMembers.length-1, 'apiMember': searchBar.text, 'apiType':"user", 'apiValue': valueBar.text}; + memberModel.insert(0, data); + computeMembersWithValues(); + } + + function evaluateMember() { + sendToScript({type: "evaluateMember", data:{member: searchBar.text, index: evaluatingIdx}}); + } + + function getValuesToRefresh() { + var valuesToRefresh = []; + for (var i = 0; i < membersWithValues.length; i++) { + var index = membersWithValues[i]; + var row = memberModel.get(index); + valuesToRefresh.push({index: index, member: (row.apiType == "function()") ? row.apiMember+"()" : row.apiMember, value: row.apiValue}); + } + return valuesToRefresh; + } + + + function reloadListValues(){ + var valuesToRefresh = getValuesToRefresh(); + sendToScript({type: "refreshValues", data:valuesToRefresh}); + } + + function startReload(){ + var valuesToRefresh = getValuesToRefresh(); + sendToScript({type: "startRefreshValues", data:valuesToRefresh}); + } + + function stopReload(){ + sendToScript({type: "stopRefreshValues"}); + } + + function refreshValues(data) { + var buffer = ""; + for (var i = 0; i < data.length; i++) { + var row = memberModel.get(data[i].index); + row.apiValue = data[i].value; + apiMembers[row.memberIndex].value = data[i].value; + memberModel.set(data[i].index, row); + buffer += "\n" + apiMembers[row.memberIndex].member + " : " + data[i].value; + } + print(buffer); + } + + + function fromScript(message) { + if (message.type === "methods") { + apiMembers = message.data; + if (ScriptDiscoveryService.debugScriptUrl != "") { + addListElements("GlobalDebugger"); + if (memberModel.count == 0) { + addListElements(); + } + } else { + addListElements(); + } + + } else if (message.type === "debugMethods") { + addListElements("GlobalDebugger"); + } else if (message.type === "refreshValues") { + refreshValues(message.data); + } else if (message.type === "evaluateMember") { + valueBar.text = message.data.value; + var selrow = memberModel.get(message.data.index); + if (selrow.apiMember === searchBar.text || selrow.apiMember === searchBar.text + "()") { + selrow.apiValue = message.data.value; + apiMembers[selrow.memberIndex].value = message.data.value; + apiMembers[selrow.memberIndex].hasValue = true; + memberModel.set(message.data.index, selrow); + } + } else if (message.type === "selectScript") { + if (message.data.length > 0) { + ScriptDiscoveryService.debugScriptUrl = message.data; + } + } + } + + + function getFilterPairs(filter){ + var filteredArray = []; + var filterChain; + filterChain = filter.split(" "); + for (var i = 0; i < filterChain.length; i++) { + filterChain[i] = filterChain[i].toUpperCase(); + } + var matchPairs = []; + + for (var i = 0; i < apiMembers.length; i++) { + if (filterChain != undefined) { + var found = 0; + var memberComp = apiMembers[i].member.toUpperCase(); + for (var j = 0; j < filterChain.length; j++) { + found += memberComp.indexOf(filterChain[j]) >= 0 ? 1 : 0; + } + if (found === 0) { + continue; + } + matchPairs.push({index: i, found: found, member: apiMembers[i].member}); + } + } + + matchPairs.sort(function(a, b){ + if(a.found > b.found) return -1; + if(a.found < b.found) return 1; + if(a.member > b.member) return 1; + if(a.member < b.member) return -1; + return 0; + }); + + return matchPairs; + } + + function isolateElement(index) { + var oldElement = memberModel.get(index); + var newElement = {memberIndex: oldElement.memberIndex, apiMember: oldElement.apiMember, apiType: oldElement.apiType, apiValue: oldElement.apiValue}; + membersWithValues = apiMembers[oldElement.memberIndex].hasValue ? [0] : []; + memberModel.remove(0, memberModel.count); + memberModel.append(newElement); + } + + function computeMembersWithValues() { + membersWithValues = []; + for (var i = 0; i < memberModel.count; i++) { + var idx = memberModel.get(i).memberIndex; + if (apiMembers[idx].hasValue) { + membersWithValues.push(i); + } + } + update.enabled = membersWithValues.length <= maxUpdateValues; + reload.enabled = membersWithValues.length <= maxReloadValues; + } + + function addListElements(filter) { + valueBar.text = ""; + memberModel.remove(0, memberModel.count); + + var filteredArray = (filter != undefined) ? [] : apiMembers; + var matchPairs; + if (filter != undefined) { + matchPairs = getFilterPairs(filter); + for (var i = 0; i < matchPairs.length; i++) { + if (matchPairs[i].found < matchPairs[0].found) { + break; + } + var idx = matchPairs[i].index; + filteredArray.push(apiMembers[idx]); + } + } + + for (var i = 0; i < filteredArray.length; i++) { + var data = {'memberIndex': matchPairs ? matchPairs[i].index : i, + 'apiMember': filteredArray[i].member, + 'apiType': filteredArray[i].type, + 'apiValue': filteredArray[i].value}; + + if (hideQtMethods) { + var chain = data.apiMember.split("."); + var method = chain[chain.length-1]; + if (method != "destroyed" && + method != "objectName" && + method != "objectNameChanged") { + memberModel.append(data); + } + } else { + memberModel.append(data); + } + } + + computeMembersWithValues(); + + if (isReloading) { + update.glyph = hifi.glyphs.playback_play + isReloading = false; + stopReload(); + } + + if (memberModel.count > 0) { + scrollSlider.y = 0; + list.contentY = 0; + } + } + + signal sendToScript(var message); +} diff --git a/interface/resources/qml/ToolWindow.qml b/interface/resources/qml/ToolWindow.qml deleted file mode 100644 index b1120058f9..0000000000 --- a/interface/resources/qml/ToolWindow.qml +++ /dev/null @@ -1,256 +0,0 @@ -// -// ToolWindow.qml -// -// Created by Bradley Austin Davis on 12 Jan 2016 -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtQuick.Controls.Styles 1.4 -import QtWebEngine 1.1 -import QtWebChannel 1.0 -import Qt.labs.settings 1.0 - -import "windows" -import "controls-uit" -import "styles-uit" - - -ScrollingWindow { - id: toolWindow - resizable: true - objectName: "ToolWindow" - destroyOnCloseButton: false - destroyOnHidden: false - closable: true - shown: false - title: "Edit" - property alias tabView: tabView - implicitWidth: 520; implicitHeight: 695 - minSize: Qt.vector2d(456, 500) - - HifiConstants { id: hifi } - - onParentChanged: { - if (parent) { - x = 120; - y = 120; - } - } - - onShownChanged: { - keyboardEnabled = HMD.active; - } - - Settings { - category: "ToolWindow.Position" - property alias x: toolWindow.x - property alias y: toolWindow.y - } - - Item { - id: toolWindowTabViewItem - height: pane.scrollHeight - width: pane.contentWidth - anchors.left: parent.left - anchors.top: parent.top - - TabView { - id: tabView - width: pane.contentWidth - // Pane height so that don't use Window's scrollbars otherwise tabs may be scrolled out of view. - height: pane.scrollHeight - property int tabCount: 0 - - Repeater { - model: 4 - Tab { - // Force loading of the content even if the tab is not visible - // (required for letting the C++ code access the webview) - active: true - enabled: false - property string originalUrl: "" - - WebView { - id: webView - anchors.fill: parent - enabled: false - Component.onCompleted: { - webChannel.registerObject("eventBridge", eventBridge); - webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); - } - - onEnabledChanged: toolWindow.updateVisiblity() - } - } - } - - style: TabViewStyle { - - frame: Rectangle { // Background shown before content loads. - anchors.fill: parent - color: hifi.colors.baseGray - } - - frameOverlap: 0 - - tab: Rectangle { - implicitWidth: text.width - implicitHeight: 3 * text.height - color: styleData.selected ? hifi.colors.black : hifi.colors.tabBackgroundDark - - RalewayRegular { - id: text - text: styleData.title - font.capitalization: Font.AllUppercase - size: hifi.fontSizes.tabName - width: tabView.tabCount > 1 ? styleData.availableWidth / tabView.tabCount : implicitWidth + 2 * hifi.dimensions.contentSpacing.x - elide: Text.ElideRight - color: styleData.selected ? hifi.colors.primaryHighlight : hifi.colors.lightGrayText - horizontalAlignment: Text.AlignHCenter - anchors.centerIn: parent - } - - Rectangle { // Separator. - width: 1 - height: parent.height - color: hifi.colors.black - anchors.left: parent.left - anchors.top: parent.top - visible: styleData.index > 0 - - Rectangle { - width: 1 - height: 1 - color: hifi.colors.baseGray - anchors.left: parent.left - anchors.bottom: parent.bottom - } - } - - Rectangle { // Active underline. - width: parent.width - (styleData.index > 0 ? 1 : 0) - height: 1 - anchors.right: parent.right - anchors.bottom: parent.bottom - color: styleData.selected ? hifi.colors.primaryHighlight : hifi.colors.baseGray - } - } - - tabOverlap: 0 - } - } - } - - function updateVisiblity() { - if (visible) { - for (var i = 0; i < tabView.count; ++i) { - if (tabView.getTab(i).enabled) { - return; - } - } - shown = false; - } - } - - function findIndexForUrl(source) { - for (var i = 0; i < tabView.count; ++i) { - var tab = tabView.getTab(i); - if (tab.originalUrl === source) { - return i; - } - } - return -1; - } - - function findTabForUrl(source) { - var index = findIndexForUrl(source); - if (index < 0) { - return; - } - return tabView.getTab(index); - } - - function showTabForUrl(source, newVisible) { - var index = findIndexForUrl(source); - if (index < 0) { - return; - } - - var tab = tabView.getTab(index); - if (newVisible) { - toolWindow.shown = true - tab.enabled = true - } else { - tab.enabled = false; - updateVisiblity(); - } - } - - function findFreeTab() { - for (var i = 0; i < tabView.count; ++i) { - var tab = tabView.getTab(i); - if (tab && (!tab.originalUrl || tab.originalUrl === "")) { - return i; - } - } - return -1; - } - - function removeTabForUrl(source) { - var index = findIndexForUrl(source); - if (index < 0) { - return; - } - - var tab = tabView.getTab(index); - tab.title = ""; - tab.enabled = false; - tab.originalUrl = ""; - tab.item.url = "about:blank"; - tab.item.enabled = false; - tabView.tabCount--; - } - - function addWebTab(properties) { - if (!properties.source) { - console.warn("Attempted to open Web Tool Pane without URL"); - return; - } - - var existingTabIndex = findIndexForUrl(properties.source); - if (existingTabIndex >= 0) { - var tab = tabView.getTab(existingTabIndex); - return tab.item; - } - - var freeTabIndex = findFreeTab(); - if (freeTabIndex === -1) { - console.warn("Unable to add new tab"); - return; - } - - if (properties.width) { - tabView.width = Math.min(Math.max(tabView.width, properties.width), toolWindow.maxSize.x); - } - - if (properties.height) { - tabView.height = Math.min(Math.max(tabView.height, properties.height), toolWindow.maxSize.y); - } - - var tab = tabView.getTab(freeTabIndex); - tab.title = properties.title || "Unknown"; - tab.enabled = true; - tab.originalUrl = properties.source; - - var result = tab.item; - result.enabled = true; - tabView.tabCount++; - result.url = properties.source; - return result; - } -} diff --git a/interface/resources/qml/controls-uit/Key.qml b/interface/resources/qml/controls-uit/Key.qml index b2c720368d..dd77fc92dc 100644 --- a/interface/resources/qml/controls-uit/Key.qml +++ b/interface/resources/qml/controls-uit/Key.qml @@ -45,18 +45,6 @@ Item { } } - onClicked: { - mouse.accepted = true; - Tablet.playSound(TabletEnums.ButtonClick); - - webEntity.synthesizeKeyPress(glyph); - webEntity.synthesizeKeyPress(glyph, mirrorText); - - if (toggle) { - toggled = !toggled; - } - } - onDoubleClicked: { mouse.accepted = true; } @@ -94,6 +82,14 @@ Item { onReleased: { if (containsMouse) { + Tablet.playSound(TabletEnums.ButtonClick); + + webEntity.synthesizeKeyPress(glyph); + webEntity.synthesizeKeyPress(glyph, mirrorText); + + if (toggle) { + toggled = !toggled; + } keyItem.state = "mouseOver"; } else { if (toggled) { diff --git a/interface/resources/qml/controls-uit/TextField.qml b/interface/resources/qml/controls-uit/TextField.qml index e636bfc27f..a21d1f8efd 100644 --- a/interface/resources/qml/controls-uit/TextField.qml +++ b/interface/resources/qml/controls-uit/TextField.qml @@ -24,13 +24,18 @@ TextField { property bool isSearchField: false property string label: "" property real controlHeight: height + (textFieldLabel.visible ? textFieldLabel.height + 1 : 0) + property bool hasDefocusedBorder: true; property bool hasRoundedBorder: false + property int roundedBorderRadius: 4 property bool error: false; property bool hasClearButton: false; + property string leftPermanentGlyph: ""; + property string centerPlaceholderGlyph: ""; placeholderText: textField.placeholderText FontLoader { id: firaSansSemiBold; source: "../../fonts/FiraSans-SemiBold.ttf"; } + FontLoader { id: hifiGlyphs; source: "../../fonts/hifi-glyphs.ttf"; } font.family: firaSansSemiBold.name font.pixelSize: hifi.fontSizes.textFieldInput font.italic: textField.text == "" @@ -54,6 +59,7 @@ TextField { } style: TextFieldStyle { + id: style; textColor: { if (isLightColorScheme) { if (textField.activeFocus) { @@ -98,9 +104,28 @@ TextField { } } border.color: textField.error ? hifi.colors.redHighlight : - (textField.activeFocus ? hifi.colors.primaryHighlight : (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray)) + (textField.activeFocus ? hifi.colors.primaryHighlight : (hasDefocusedBorder ? (isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray) : color)) border.width: textField.activeFocus || hasRoundedBorder || textField.error ? 1 : 0 - radius: isSearchField ? textField.height / 2 : (hasRoundedBorder ? 4 : 0) + radius: isSearchField ? textField.height / 2 : (hasRoundedBorder ? roundedBorderRadius : 0) + + HiFiGlyphs { + text: textField.leftPermanentGlyph; + color: textColor; + size: hifi.fontSizes.textFieldSearchIcon; + anchors.left: parent.left; + anchors.verticalCenter: parent.verticalCenter; + anchors.leftMargin: hifi.dimensions.textPadding - 2; + visible: text; + } + + HiFiGlyphs { + text: textField.centerPlaceholderGlyph; + color: textColor; + size: parent.height; + anchors.horizontalCenter: parent.horizontalCenter; + anchors.verticalCenter: parent.verticalCenter; + visible: text && !textField.focus && textField.text === ""; + } HiFiGlyphs { text: hifi.glyphs.search @@ -132,7 +157,7 @@ TextField { placeholderTextColor: isFaintGrayColorScheme ? hifi.colors.lightGrayText : hifi.colors.lightGray selectedTextColor: hifi.colors.black selectionColor: hifi.colors.primaryHighlight - padding.left: (isSearchField ? textField.height - 2 : 0) + hifi.dimensions.textPadding + padding.left: hasRoundedBorder ? textField.height / 2 : ((isSearchField || textField.leftPermanentGlyph !== "") ? textField.height - 2 : 0) + hifi.dimensions.textPadding padding.right: (hasClearButton ? textField.height - 2 : 0) + hifi.dimensions.textPadding } diff --git a/interface/resources/qml/controls/TabletWebScreen.qml b/interface/resources/qml/controls/TabletWebScreen.qml index 501e321f0d..bb037ad478 100644 --- a/interface/resources/qml/controls/TabletWebScreen.qml +++ b/interface/resources/qml/controls/TabletWebScreen.qml @@ -41,9 +41,9 @@ Item { onNewViewRequestedCallback: { // desktop is not defined for web-entities or tablet if (typeof desktop !== "undefined") { - desktop.openBrowserWindow(request, profile); + desktop.openBrowserWindow(request, webViewCoreProfile); } else { - tabletRoot.openBrowserWindow(request, profile); + tabletRoot.openBrowserWindow(request, webViewCoreProfile); } } diff --git a/interface/resources/qml/dialogs/FileDialog.qml b/interface/resources/qml/dialogs/FileDialog.qml index b9633104d5..7b1a177c86 100644 --- a/interface/resources/qml/dialogs/FileDialog.qml +++ b/interface/resources/qml/dialogs/FileDialog.qml @@ -70,7 +70,15 @@ ModalWindow { signal selectedFile(var file); signal canceled(); - + signal selected(int button); + function click(button) { + clickedButton = button; + selected(button); + destroy(); + } + + property int clickedButton: OriginalDialogs.StandardButton.NoButton; + Component.onCompleted: { console.log("Helper " + helper + " drives " + drives); @@ -628,7 +636,10 @@ ModalWindow { case Qt.Key_Backtab: event.accepted = false; break; - + case Qt.Key_Escape: + event.accepted = true; + root.click(OriginalDialogs.StandardButton.Cancel); + break; default: if (addToPrefix(event)) { event.accepted = true @@ -793,7 +804,11 @@ ModalWindow { case Qt.Key_Home: event.accepted = d.navigateHome(); break; - - } + + case Qt.Key_Escape: + event.accepted = true; + root.click(OriginalDialogs.StandardButton.Cancel); + break; + } } } diff --git a/interface/resources/qml/dialogs/preferences/AvatarBrowser.qml b/interface/resources/qml/dialogs/preferences/AvatarBrowser.qml deleted file mode 100644 index 5949adffca..0000000000 --- a/interface/resources/qml/dialogs/preferences/AvatarBrowser.qml +++ /dev/null @@ -1,85 +0,0 @@ -// -// AvatarBrowser.qml -// -// Created by Bradley Austin Davis on 30 Aug 2015 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtWebChannel 1.0 -import QtWebEngine 1.2 - -import "../../windows" -import "../../controls-uit" -import "../../styles-uit" - -Window { - id: root - HifiConstants { id: hifi } - width: 900; height: 700 - resizable: true - modality: Qt.ApplicationModal - - Item { - anchors.fill: parent - - property bool keyboardEnabled: false - property bool keyboardRaised: true - property bool punctuationMode: false - - BaseWebView { - id: webview - url: Account.metaverseServerURL + "/marketplace?category=avatars" - focus: true - - anchors { - top: parent.top - left: parent.left - right: parent.right - bottom: keyboard.top - } - - // Create a global EventBridge object for raiseAndLowerKeyboard. - WebEngineScript { - id: createGlobalEventBridge - sourceCode: eventBridgeJavaScriptToInject - injectionPoint: WebEngineScript.DocumentCreation - worldId: WebEngineScript.MainWorld - } - - // Detect when may want to raise and lower keyboard. - WebEngineScript { - id: raiseAndLowerKeyboard - injectionPoint: WebEngineScript.Deferred - sourceUrl: resourceDirectoryUrl + "html/raiseAndLowerKeyboard.js" - worldId: WebEngineScript.MainWorld - } - - userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ] - - Component.onCompleted: { - webChannel.registerObject("eventBridge", eventBridge); - webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); - } - } - - Keyboard { - id: keyboard - raised: parent.keyboardEnabled && parent.keyboardRaised - numeric: parent.punctuationMode - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - } - - Component.onCompleted: { - keyboardEnabled = HMD.active; - } - } -} diff --git a/interface/resources/qml/dialogs/preferences/AvatarPreference.qml b/interface/resources/qml/dialogs/preferences/AvatarPreference.qml index b27827d9d7..0efc3776b3 100644 --- a/interface/resources/qml/dialogs/preferences/AvatarPreference.qml +++ b/interface/resources/qml/dialogs/preferences/AvatarPreference.qml @@ -99,25 +99,9 @@ Preference { leftMargin: dataTextField.acceptableInput ? hifi.dimensions.contentSpacing.x : 0 } onClicked: { - if (typeof desktop !== "undefined") { - // Load dialog via OffscreenUi so that JavaScript EventBridge is available. - root.browser = OffscreenUi.load("dialogs/preferences/AvatarBrowser.qml"); - root.browser.windowDestroyed.connect(function(){ - root.browser = null; - }); - } else { - root.browser = tabletAvatarBrowserBuilder.createObject(tabletRoot); - - // Make dialog modal. - tabletRoot.openModal = root.browser; - } + ApplicationInterface.loadAvatarBrowser(); } } - Component { - id: tabletAvatarBrowserBuilder; - TabletAvatarBrowser { } - } - } } diff --git a/interface/resources/qml/hifi/+android/Desktop.qml b/interface/resources/qml/hifi/+android/Desktop.qml new file mode 100644 index 0000000000..99d792b664 --- /dev/null +++ b/interface/resources/qml/hifi/+android/Desktop.qml @@ -0,0 +1,63 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 + +import Qt.labs.settings 1.0 + +import "../desktop" as OriginalDesktop +import ".." +import "." +import "./toolbars" + +OriginalDesktop.Desktop { + id: desktop + + MouseArea { + id: hoverWatch + anchors.fill: parent + hoverEnabled: true + propagateComposedEvents: true + scrollGestureEnabled: false // we don't need/want these + onEntered: ApplicationCompositor.reticleOverDesktop = true + onExited: ApplicationCompositor.reticleOverDesktop = false + acceptedButtons: Qt.NoButton + + + } + + + Component { id: toolbarBuilder; Toolbar { } } + // This used to create sysToolbar dynamically with a call to getToolbar() within onCompleted. + // Beginning with QT 5.6, this stopped working, as anything added to toolbars too early got + // wiped during startup. + Toolbar { + id: sysToolbar; + objectName: "com.highfidelity.interface.toolbar.system"; + // Magic: sysToolbar.x and y come from settings, and are bound before the properties specified here are applied. + x: sysToolbar.x; + y: sysToolbar.y; + } + property var toolbars: (function (map) { // answer dictionary preloaded with sysToolbar + map[sysToolbar.objectName] = sysToolbar; + return map; })({}); + + Component.onCompleted: { + } + + // Accept a download through the webview + property bool webViewProfileSetup: false + property string currentUrl: "" + property string adaptedPath: "" + property string tempDir: "" + + // Create or fetch a toolbar with the given name + function getToolbar(name) { + var result = toolbars[name]; + if (!result) { + result = toolbars[name] = toolbarBuilder.createObject(desktop, {}); + result.objectName = name; + } + return result; + } +} + + diff --git a/interface/resources/qml/hifi/Desktop.qml b/interface/resources/qml/hifi/Desktop.qml index 896b3cf10e..8c732aac40 100644 --- a/interface/resources/qml/hifi/Desktop.qml +++ b/interface/resources/qml/hifi/Desktop.qml @@ -22,10 +22,6 @@ OriginalDesktop.Desktop { acceptedButtons: Qt.NoButton } - // The tool window, one instance - property alias toolWindow: toolWindow - ToolWindow { id: toolWindow } - Action { text: "Open Browser" shortcut: "Ctrl+B" diff --git a/interface/resources/qml/hifi/Feed.qml b/interface/resources/qml/hifi/Feed.qml index 8576e26fcd..a9629c0e4e 100644 --- a/interface/resources/qml/hifi/Feed.qml +++ b/interface/resources/qml/hifi/Feed.qml @@ -102,7 +102,7 @@ Column { 'include_actions=' + actions, 'restriction=' + (Account.isLoggedIn() ? 'open,hifi' : 'open'), 'require_online=true', - 'protocol=' + encodeURIComponent(AddressManager.protocolVersion()), + 'protocol=' + encodeURIComponent(Window.protocolSignature()), 'page=' + pageNumber ]; var url = metaverseBase + 'user_stories?' + options.join('&'); diff --git a/interface/resources/qml/hifi/NameCard.qml b/interface/resources/qml/hifi/NameCard.qml index 3f5cad655d..e08fdc53ff 100644 --- a/interface/resources/qml/hifi/NameCard.qml +++ b/interface/resources/qml/hifi/NameCard.qml @@ -50,7 +50,7 @@ Item { id: avatarImage visible: profileUrl !== "" && userName !== ""; // Size - height: isMyCard ? 70 : 42; + height: isMyCard ? 84 : 42; width: visible ? height : 0; anchors.top: parent.top; anchors.topMargin: isMyCard ? 0 : 8; @@ -520,7 +520,7 @@ Item { Slider { id: gainSlider // Size - width: thisNameCard.width; + width: isMyCard ? thisNameCard.width - 20 : thisNameCard.width; height: 14 // Anchors anchors.verticalCenter: nameCardVUMeter.verticalCenter; diff --git a/interface/resources/qml/hifi/Pal.qml b/interface/resources/qml/hifi/Pal.qml index 64f61f0d69..02971cc984 100644 --- a/interface/resources/qml/hifi/Pal.qml +++ b/interface/resources/qml/hifi/Pal.qml @@ -28,7 +28,7 @@ Rectangle { // Properties property bool debug: false; property int myCardWidth: width - upperRightInfoContainer.width; - property int myCardHeight: 80; + property int myCardHeight: 100; property int rowHeight: 60; property int actionButtonWidth: 55; property int locationColumnWidth: 170; diff --git a/interface/resources/qml/hifi/commerce/common/SortableListModel.qml b/interface/resources/qml/hifi/commerce/common/SortableListModel.qml index 2d82e42ddb..0951d25950 100644 --- a/interface/resources/qml/hifi/commerce/common/SortableListModel.qml +++ b/interface/resources/qml/hifi/commerce/common/SortableListModel.qml @@ -17,6 +17,7 @@ ListModel { id: root; property string sortColumnName: ""; property bool isSortingDescending: true; + property bool valuesAreNumerical: false; function swap(a, b) { if (a < b) { @@ -29,26 +30,51 @@ ListModel { } function partition(begin, end, pivot) { - var piv = get(pivot)[sortColumnName]; - swap(pivot, end - 1); - var store = begin; + if (valuesAreNumerical) { + var piv = get(pivot)[sortColumnName]; + swap(pivot, end - 1); + var store = begin; - for (var i = begin; i < end - 1; ++i) { - if (isSortingDescending) { - if (get(i)[sortColumnName] < piv) { - swap(store, i); - ++store; - } - } else { - if (get(i)[sortColumnName] > piv) { - swap(store, i); - ++store; + for (var i = begin; i < end - 1; ++i) { + var currentElement = get(i)[sortColumnName]; + if (isSortingDescending) { + if (currentElement < piv) { + swap(store, i); + ++store; + } + } else { + if (currentElement > piv) { + swap(store, i); + ++store; + } } } - } - swap(end - 1, store); + swap(end - 1, store); - return store; + return store; + } else { + var piv = get(pivot)[sortColumnName].toLowerCase(); + swap(pivot, end - 1); + var store = begin; + + for (var i = begin; i < end - 1; ++i) { + var currentElement = get(i)[sortColumnName].toLowerCase(); + if (isSortingDescending) { + if (currentElement < piv) { + swap(store, i); + ++store; + } + } else { + if (currentElement > piv) { + swap(store, i); + ++store; + } + } + } + swap(end - 1, store); + + return store; + } } function qsort(begin, end) { diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml index 28c32c59de..c331532f5e 100644 --- a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml +++ b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml @@ -145,7 +145,7 @@ Rectangle { // Title text RalewayRegular { id: popText; - text: "PROOF OF PURCHASE"; + text: "Proof of Provenance"; // Text size size: 16; // Anchors @@ -155,7 +155,7 @@ Rectangle { anchors.right: titleBarText.right; height: paintedHeight; // Style - color: hifi.colors.baseGray; + color: hifi.colors.darkGray; } // @@ -182,7 +182,7 @@ Rectangle { anchors.rightMargin: 16; height: paintedHeight; // Style - color: hifi.colors.baseGray; + color: hifi.colors.darkGray; } RalewaySemiBold { id: itemName; @@ -196,7 +196,7 @@ Rectangle { anchors.right: itemNameHeader.right; height: paintedHeight; // Style - color: hifi.colors.blueAccent; + color: hifi.colors.white; elide: Text.ElideRight; MouseArea { anchors.fill: parent; @@ -205,7 +205,7 @@ Rectangle { sendToScript({method: 'inspectionCertificate_showInMarketplaceClicked', marketplaceUrl: root.marketplaceUrl}); } onEntered: itemName.color = hifi.colors.blueHighlight; - onExited: itemName.color = hifi.colors.blueAccent; + onExited: itemName.color = hifi.colors.white; } } @@ -223,7 +223,7 @@ Rectangle { anchors.rightMargin: 16; height: paintedHeight; // Style - color: hifi.colors.lightGray; + color: hifi.colors.darkGray; } RalewayRegular { id: ownedBy; @@ -236,7 +236,7 @@ Rectangle { anchors.left: ownedByHeader.left; height: paintedHeight; // Style - color: hifi.colors.darkGray; + color: hifi.colors.white; elide: Text.ElideRight; } AnonymousProRegular { @@ -252,7 +252,7 @@ Rectangle { anchors.leftMargin: 6; anchors.right: ownedByHeader.right; // Style - color: hifi.colors.lightGray; + color: hifi.colors.white; elide: Text.ElideRight; verticalAlignment: Text.AlignVCenter; } @@ -271,7 +271,7 @@ Rectangle { anchors.rightMargin: 16; height: paintedHeight; // Style - color: hifi.colors.lightGray; + color: hifi.colors.darkGray; } AnonymousProRegular { id: edition; @@ -285,7 +285,7 @@ Rectangle { anchors.right: editionHeader.right; height: paintedHeight; // Style - color: hifi.colors.darkGray; + color: hifi.colors.white; } RalewayRegular { @@ -302,7 +302,7 @@ Rectangle { anchors.rightMargin: 16; height: paintedHeight; // Style - color: hifi.colors.lightGray; + color: hifi.colors.darkGray; } AnonymousProRegular { id: dateOfPurchase; @@ -316,7 +316,7 @@ Rectangle { anchors.right: dateOfPurchaseHeader.right; height: paintedHeight; // Style - color: hifi.colors.darkGray; + color: hifi.colors.white; } RalewayRegular { @@ -349,7 +349,7 @@ Rectangle { // "Cancel" button HifiControlsUit.Button { - color: hifi.buttons.noneBorderless; + color: hifi.buttons.noneBorderlessWhite; colorScheme: hifi.colorSchemes.light; anchors.top: parent.top; anchors.left: parent.left; diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/images/cert-bg.jpg b/interface/resources/qml/hifi/commerce/inspectionCertificate/images/cert-bg.jpg index 9cecc79869..b39a55e4e8 100644 Binary files a/interface/resources/qml/hifi/commerce/inspectionCertificate/images/cert-bg.jpg and b/interface/resources/qml/hifi/commerce/inspectionCertificate/images/cert-bg.jpg differ diff --git a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml index de66be4a88..a9961dc17e 100644 --- a/interface/resources/qml/hifi/commerce/purchases/Purchases.qml +++ b/interface/resources/qml/hifi/commerce/purchases/Purchases.qml @@ -36,6 +36,7 @@ Rectangle { property bool pendingInventoryReply: true; property bool isShowingMyItems: false; property bool isDebuggingFirstUseTutorial: false; + property int pendingItemCount: 0; // Style color: hifi.colors.white; Connections { @@ -79,18 +80,22 @@ Rectangle { onInventoryResult: { purchasesReceived = true; - if (root.pendingInventoryReply) { - inventoryTimer.start(); - } - if (result.status !== 'success') { console.log("Failed to get purchases", result.message); - } else { + } else if (!purchasesContentsList.dragging) { // Don't modify the view if the user's scrolling var inventoryResult = processInventoryResult(result.data.assets); + var currentIndex = purchasesContentsList.currentIndex === -1 ? 0 : purchasesContentsList.currentIndex; purchasesModel.clear(); purchasesModel.append(inventoryResult); + root.pendingItemCount = 0; + for (var i = 0; i < purchasesModel.count; i++) { + if (purchasesModel.get(i).status === "pending") { + root.pendingItemCount++; + } + } + if (previousPurchasesModel.count !== 0) { checkIfAnyItemStatusChanged(); } else { @@ -103,6 +108,12 @@ Rectangle { previousPurchasesModel.append(inventoryResult); buildFilteredPurchasesModel(); + + purchasesContentsList.positionViewAtIndex(currentIndex, ListView.Beginning); + } + + if (root.pendingInventoryReply && root.pendingItemCount > 0) { + inventoryTimer.start(); } root.pendingInventoryReply = false; @@ -419,6 +430,8 @@ Rectangle { visible: (root.isShowingMyItems && filteredPurchasesModel.count !== 0) || (!root.isShowingMyItems && filteredPurchasesModel.count !== 0); clip: true; model: filteredPurchasesModel; + snapMode: ListView.SnapToItem; + highlightRangeMode: ListView.StrictlyEnforceRange; // Anchors anchors.top: root.canRezCertifiedItems ? separator.bottom : cantRezCertified.bottom; anchors.topMargin: 12; @@ -664,7 +677,7 @@ Rectangle { } } - if (sameItemCount !== tempPurchasesModel.count) { + if (sameItemCount !== tempPurchasesModel.count || filterBar.text !== "") { filteredPurchasesModel.clear(); for (var i = 0; i < tempPurchasesModel.count; i++) { filteredPurchasesModel.append(tempPurchasesModel.get(i)); diff --git a/interface/resources/qml/hifi/commerce/wallet/Help.qml b/interface/resources/qml/hifi/commerce/wallet/Help.qml index 409833df98..f0f123f6c0 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Help.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Help.qml @@ -25,7 +25,6 @@ Item { id: root; property string keyFilePath; - property bool showDebugButtons: true; Connections { target: Commerce; @@ -55,37 +54,6 @@ Item { // Style color: hifi.colors.blueHighlight; } - HifiControlsUit.Button { - id: clearCachedPassphraseButton; - visible: root.showDebugButtons; - color: hifi.buttons.black; - colorScheme: hifi.colorSchemes.dark; - anchors.top: parent.top; - anchors.left: helpTitleText.right; - anchors.leftMargin: 20; - height: 40; - width: 150; - text: "DBG: Clear Pass"; - onClicked: { - Commerce.setPassphrase(""); - sendSignalToWallet({method: 'passphraseReset'}); - } - } - HifiControlsUit.Button { - id: resetButton; - visible: root.showDebugButtons; - color: hifi.buttons.red; - colorScheme: hifi.colorSchemes.dark; - anchors.top: clearCachedPassphraseButton.top; - anchors.left: clearCachedPassphraseButton.right; - height: 40; - width: 150; - text: "DBG: RST Wallet"; - onClicked: { - Commerce.reset(); - sendSignalToWallet({method: 'walletReset'}); - } - } ListModel { id: helpModel; diff --git a/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml b/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml index 1f5e67eaa5..c6c6056605 100644 --- a/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml +++ b/interface/resources/qml/hifi/commerce/wallet/SecurityImageSelection.qml @@ -99,6 +99,10 @@ Item { function initModel() { gridModel.initModel(); } + + function resetSelection() { + securityImageGrid.currentIndex = -1; + } // // FUNCTION DEFINITIONS END // diff --git a/interface/resources/qml/hifi/commerce/wallet/SendMoney.qml b/interface/resources/qml/hifi/commerce/wallet/SendMoney.qml deleted file mode 100644 index 11a6c99b0c..0000000000 --- a/interface/resources/qml/hifi/commerce/wallet/SendMoney.qml +++ /dev/null @@ -1,73 +0,0 @@ -// -// SendMoney.qml -// qml/hifi/commerce/wallet -// -// SendMoney -// -// Created by Zach Fox on 2017-08-18 -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import Hifi 1.0 as Hifi -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import "../../../styles-uit" -import "../../../controls-uit" as HifiControlsUit -import "../../../controls" as HifiControls - -// references XXX from root context - -Item { - HifiConstants { id: hifi; } - - id: root; - - Connections { - target: Commerce; - } - - // "Unavailable" - RalewayRegular { - text: "You currently cannot send money to other High Fidelity users."; - // Anchors - anchors.fill: parent; - // Text size - size: 24; - // Style - color: hifi.colors.faintGray; - wrapMode: Text.WordWrap; - // Alignment - horizontalAlignment: Text.AlignHCenter; - verticalAlignment: Text.AlignVCenter; - } - - // - // FUNCTION DEFINITIONS START - // - // - // Function Name: fromScript() - // - // Relevant Variables: - // None - // - // Arguments: - // message: The message sent from the JavaScript. - // Messages are in format "{method, params}", like json-rpc. - // - // Description: - // Called when a message is received from a script. - // - function fromScript(message) { - switch (message.method) { - default: - console.log('Unrecognized message from wallet.js:', JSON.stringify(message)); - } - } - signal sendSignalToWallet(var msg); - // - // FUNCTION DEFINITIONS END - // -} diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index ef2b007dc8..ae42b8e3e1 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -19,8 +19,7 @@ import "../../../styles-uit" import "../../../controls-uit" as HifiControlsUit import "../../../controls" as HifiControls import "../common" as HifiCommerceCommon - -// references XXX from root context +import "./sendMoney" Rectangle { HifiConstants { id: hifi; } @@ -316,18 +315,29 @@ Rectangle { Connections { onSendSignalToWallet: { - sendToScript(msg); + if (msg.method === 'transactionHistory_usernameLinkClicked') { + userInfoViewer.url = msg.usernameLink; + userInfoViewer.visible = true; + } else { + sendToScript(msg); + } } } } SendMoney { id: sendMoney; + z: 997; visible: root.activeView === "sendMoney"; - anchors.top: titleBarContainer.bottom; - anchors.bottom: tabButtonsContainer.top; - anchors.left: parent.left; - anchors.right: parent.right; + anchors.fill: parent; + parentAppTitleBarHeight: titleBarContainer.height; + parentAppNavBarHeight: tabButtonsContainer.height; + + Connections { + onSendSignalToWallet: { + sendToScript(msg); + } + } } Security { @@ -379,7 +389,7 @@ Rectangle { // Item { id: tabButtonsContainer; - visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && root.activeView !== "securityImageChange"; + visible: !needsLogIn.visible && root.activeView !== "passphraseChange" && root.activeView !== "securityImageChange" && sendMoney.currentActiveView !== "sendMoneyStep"; property int numTabs: 5; // Size width: root.width; @@ -497,7 +507,7 @@ Rectangle { Rectangle { id: sendMoneyButtonContainer; visible: !walletSetup.visible; - color: hifi.colors.black; + color: root.activeView === "sendMoney" ? hifi.colors.blueAccent : hifi.colors.black; anchors.top: parent.top; anchors.left: exchangeMoneyButtonContainer.right; anchors.bottom: parent.bottom; @@ -513,7 +523,7 @@ Rectangle { anchors.top: parent.top; anchors.topMargin: -2; // Style - color: hifi.colors.lightGray50; + color: root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; } RalewaySemiBold { @@ -528,12 +538,24 @@ Rectangle { anchors.right: parent.right; anchors.rightMargin: 4; // Style - color: hifi.colors.lightGray50; + color: root.activeView === "sendMoney" || sendMoneyTabMouseArea.containsMouse ? hifi.colors.white : hifi.colors.blueHighlight; wrapMode: Text.WordWrap; // Alignment horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignTop; } + + MouseArea { + id: sendMoneyTabMouseArea; + anchors.fill: parent; + hoverEnabled: enabled; + onClicked: { + root.activeView = "sendMoney"; + tabButtonsContainer.resetTabButtonColors(); + } + onEntered: parent.color = hifi.colors.blueHighlight; + onExited: parent.color = root.activeView === "sendMoney" ? hifi.colors.blueAccent : hifi.colors.black; + } } // "SECURITY" tab button @@ -665,9 +687,16 @@ Rectangle { // TAB BUTTONS END // + HifiControls.TabletWebView { + id: userInfoViewer; + z: 998; + anchors.fill: parent; + visible: false; + } + Item { id: keyboardContainer; - z: 998; + z: 999; visible: keyboard.raised; property bool punctuationMode: false; anchors { @@ -713,6 +742,13 @@ Rectangle { case 'inspectionCertificate_resetCert': // NOP break; + case 'updateConnections': + sendMoney.updateConnections(message.connections); + break; + case 'selectRecipient': + case 'updateSelectedRecipientUsername': + sendMoney.fromScript(message); + break; default: console.log('Unrecognized message from wallet.js:', JSON.stringify(message)); } diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml index 42ee44d584..b980c13e3c 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml @@ -19,14 +19,30 @@ import "../../../styles-uit" import "../../../controls-uit" as HifiControlsUit import "../../../controls" as HifiControls -// references XXX from root context - Item { HifiConstants { id: hifi; } id: root; - property bool historyReceived: false; + property bool initialHistoryReceived: false; + property bool historyRequestPending: true; + property bool noMoreHistoryData: false; property int pendingCount: 0; + property int currentHistoryPage: 1; + property var pagesAlreadyAdded: new Array(); + + onVisibleChanged: { + if (visible) { + transactionHistoryModel.clear(); + Commerce.balance(); + initialHistoryReceived = false; + root.currentHistoryPage = 1; + root.noMoreHistoryData = false; + root.historyRequestPending = true; + Commerce.history(root.currentHistoryPage); + } else { + refreshTimer.stop(); + } + } Connections { target: Commerce; @@ -36,32 +52,86 @@ Item { } onHistoryResult : { - historyReceived = true; - if (result.status === 'success') { - var sameItemCount = 0; - tempTransactionHistoryModel.clear(); - - tempTransactionHistoryModel.append(result.data.history); - - for (var i = 0; i < tempTransactionHistoryModel.count; i++) { - if (!transactionHistoryModel.get(i)) { - sameItemCount = -1; - break; - } else if (tempTransactionHistoryModel.get(i).transaction_type === transactionHistoryModel.get(i).transaction_type && - tempTransactionHistoryModel.get(i).text === transactionHistoryModel.get(i).text) { - sameItemCount++; - } - } + root.initialHistoryReceived = true; + root.historyRequestPending = false; - if (sameItemCount !== tempTransactionHistoryModel.count) { - transactionHistoryModel.clear(); + if (result.status === 'success') { + var currentPage = parseInt(result.current_page); + + if (result.data.history.length === 0) { + root.noMoreHistoryData = true; + console.log("No more data to retrieve from Commerce.history() endpoint.") + } else if (root.currentHistoryPage === 1) { + var sameItemCount = 0; + tempTransactionHistoryModel.clear(); + + tempTransactionHistoryModel.append(result.data.history); + for (var i = 0; i < tempTransactionHistoryModel.count; i++) { - transactionHistoryModel.append(tempTransactionHistoryModel.get(i)); + if (!transactionHistoryModel.get(i)) { + sameItemCount = -1; + break; + } else if (tempTransactionHistoryModel.get(i).transaction_type === transactionHistoryModel.get(i).transaction_type && + tempTransactionHistoryModel.get(i).text === transactionHistoryModel.get(i).text) { + sameItemCount++; + } + } + + if (sameItemCount !== tempTransactionHistoryModel.count) { + transactionHistoryModel.clear(); + for (var i = 0; i < tempTransactionHistoryModel.count; i++) { + transactionHistoryModel.append(tempTransactionHistoryModel.get(i)); + } + calculatePendingAndInvalidated(); + } + } else { + if (root.pagesAlreadyAdded.indexOf(currentPage) !== -1) { + console.log("Page " + currentPage + " of history has already been added to the list."); + } else { + // First, add the history result to a temporary model + tempTransactionHistoryModel.clear(); + tempTransactionHistoryModel.append(result.data.history); + + // Make a note that we've already added this page to the model... + root.pagesAlreadyAdded.push(currentPage); + + var insertionIndex = 0; + // If there's nothing in the model right now, we don't need to modify insertionIndex. + if (transactionHistoryModel.count !== 0) { + var currentIteratorPage; + // Search through the whole transactionHistoryModel and look for the insertion point. + // The insertion point is found when the result page from the server is less than + // the page that the current item came from, OR when we've reached the end of the whole model. + for (var i = 0; i < transactionHistoryModel.count; i++) { + currentIteratorPage = transactionHistoryModel.get(i).resultIsFromPage; + + if (currentPage < currentIteratorPage) { + insertionIndex = i; + break; + } else if (i === transactionHistoryModel.count - 1) { + insertionIndex = i + 1; + break; + } + } + } + + // Go through the results we just got back from the server, setting the "resultIsFromPage" + // property of those results and adding them to the main model. + for (var i = 0; i < tempTransactionHistoryModel.count; i++) { + tempTransactionHistoryModel.setProperty(i, "resultIsFromPage", currentPage); + transactionHistoryModel.insert(i + insertionIndex, tempTransactionHistoryModel.get(i)) + } + + calculatePendingAndInvalidated(); } - calculatePendingAndInvalidated(); } } - refreshTimer.start(); + + // Only auto-refresh if the user hasn't scrolled + // and there is more data to grab + if (transactionHistory.atYBeginning && !root.noMoreHistoryData) { + refreshTimer.start(); + } } } @@ -131,16 +201,6 @@ Item { color: hifi.colors.white; // Alignment verticalAlignment: Text.AlignVCenter; - - onVisibleChanged: { - if (visible) { - historyReceived = false; - Commerce.balance(); - Commerce.history(); - } else { - refreshTimer.stop(); - } - } } // "balance" text below field @@ -164,9 +224,12 @@ Item { id: refreshTimer; interval: 4000; onTriggered: { - console.log("Refreshing Wallet Home..."); - Commerce.balance(); - Commerce.history(); + if (transactionHistory.atYBeginning) { + console.log("Refreshing 1st Page of Recent Activity..."); + root.historyRequestPending = true; + Commerce.balance(); + Commerce.history(1); + } } } @@ -241,7 +304,7 @@ Item { anchors.right: parent.right; Item { - visible: transactionHistoryModel.count === 0 && root.historyReceived; + visible: transactionHistoryModel.count === 0 && root.initialHistoryReceived; anchors.centerIn: parent; width: parent.width - 12; height: parent.height; @@ -319,8 +382,8 @@ Item { height: visible ? parent.height : 0; AnonymousProRegular { - id: dateText; - text: model.created_at ? getFormattedDate(model.created_at * 1000) : ""; + id: hfcText; + text: model.hfc_text || ''; // Style size: 18; anchors.left: parent.left; @@ -328,7 +391,6 @@ Item { anchors.topMargin: 15; width: 118; height: paintedHeight; - color: hifi.colors.blueAccent; wrapMode: Text.WordWrap; // Alignment horizontalAlignment: Text.AlignRight; @@ -336,20 +398,25 @@ Item { AnonymousProRegular { id: transactionText; - text: model.text ? (model.status === "invalidated" ? ("INVALIDATED: " + model.text) : model.text) : ""; + text: model.transaction_text ? (model.status === "invalidated" ? ("INVALIDATED: " + model.transaction_text) : model.transaction_text) : ""; size: 18; anchors.top: parent.top; anchors.topMargin: 15; - anchors.left: dateText.right; + anchors.left: hfcText.right; anchors.leftMargin: 20; anchors.right: parent.right; height: paintedHeight; color: model.status === "invalidated" ? hifi.colors.redAccent : hifi.colors.baseGrayHighlight; + linkColor: hifi.colors.blueAccent; wrapMode: Text.WordWrap; font.strikeout: model.status === "invalidated"; onLinkActivated: { - sendSignalToWallet({method: 'transactionHistory_linkClicked', marketplaceLink: link}); + if (link.indexOf("users/") !== -1) { + sendSignalToWallet({method: 'transactionHistory_usernameLinkClicked', usernameLink: link}); + } else { + sendSignalToWallet({method: 'transactionHistory_linkClicked', marketplaceLink: link}); + } } } @@ -364,7 +431,12 @@ Item { onAtYEndChanged: { if (transactionHistory.atYEnd) { console.log("User scrolled to the bottom of 'Recent Activity'."); - // Grab next page of results and append to model + if (!root.historyRequestPending && !root.noMoreHistoryData) { + // Grab next page of results and append to model + root.historyRequestPending = true; + Commerce.history(++root.currentHistoryPage); + console.log("Fetching Page " + root.currentHistoryPage + " of Recent Activity..."); + } } } } diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml index 21da38def0..fd74b07465 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletSetup.qml @@ -348,6 +348,7 @@ Item { width: 200; text: "Back" onClicked: { + securityImageSelection.resetSelection(); root.activeView = "step_1"; } } @@ -516,6 +517,7 @@ Item { width: 200; text: "Back" onClicked: { + securityImageSelection.resetSelection(); root.lastPage = "step_3"; root.activeView = "step_2"; } diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/ConnectionItem.qml b/interface/resources/qml/hifi/commerce/wallet/sendMoney/ConnectionItem.qml new file mode 100644 index 0000000000..33cd43bb05 --- /dev/null +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/ConnectionItem.qml @@ -0,0 +1,128 @@ +// +// ConnectionItem.qml +// qml/hifi/commerce/wallet/sendMoney +// +// ConnectionItem +// +// Created by Zach Fox on 2018-01-09 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 as Hifi +import QtQuick 2.5 +import QtGraphicalEffects 1.0 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import "../../../../styles-uit" +import "../../../../controls-uit" as HifiControlsUit +import "../../../../controls" as HifiControls +import "../../wallet" as HifiWallet + +Item { + HifiConstants { id: hifi; } + + id: root; + property bool isSelected: false; + property string userName; + property string profilePicUrl; + + height: 75; + width: parent.width; + + Rectangle { + id: mainContainer; + // Style + color: root.isSelected ? hifi.colors.faintGray80 : hifi.colors.white; + // Size + anchors.left: parent.left; + anchors.right: parent.right; + anchors.top: parent.top; + height: root.height; + + Item { + id: avatarImage; + visible: profileUrl !== "" && userName !== ""; + // Size + anchors.verticalCenter: parent.verticalCenter; + anchors.left: parent.left; + anchors.leftMargin: 36; + height: 50; + width: visible ? height : 0; + clip: true; + Image { + id: userImage; + source: root.profilePicUrl !== "" ? ((0 === root.profilePicUrl.indexOf("http")) ? + root.profilePicUrl : (Account.metaverseServerURL + root.profilePicUrl)) : ""; + mipmap: true; + // Anchors + anchors.fill: parent + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Item { + width: userImage.width; + height: userImage.height; + Rectangle { + anchors.centerIn: parent; + width: userImage.width; // This works because userImage is square + height: width; + radius: width; + } + } + } + } + AnimatedImage { + source: "../../../../../icons/profilePicLoading.gif" + anchors.fill: parent; + visible: userImage.status != Image.Ready; + } + } + + RalewaySemiBold { + id: userName; + anchors.left: avatarImage.right; + anchors.leftMargin: 12; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + anchors.right: chooseButton.visible ? chooseButton.left : parent.right; + anchors.rightMargin: chooseButton.visible ? 10 : 0; + // Text size + size: 18; + // Style + color: hifi.colors.blueAccent; + text: root.userName; + elide: Text.ElideRight; + // Alignment + horizontalAlignment: Text.AlignLeft; + verticalAlignment: Text.AlignVCenter; + } + + // "Choose" button + HifiControlsUit.Button { + id: chooseButton; + visible: root.isSelected; + color: hifi.buttons.blue; + colorScheme: hifi.colorSchemes.dark; + anchors.verticalCenter: parent.verticalCenter; + anchors.right: parent.right; + anchors.rightMargin: 28; + height: 35; + width: 100; + text: "CHOOSE"; + onClicked: { + var msg = { method: 'chooseConnection', userName: root.userName, profilePicUrl: root.profilePicUrl }; + sendToSendMoney(msg); + } + } + } + + // + // FUNCTION DEFINITIONS START + // + signal sendToSendMoney(var msg); + // + // FUNCTION DEFINITIONS END + // +} diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/RecipientDisplay.qml b/interface/resources/qml/hifi/commerce/wallet/sendMoney/RecipientDisplay.qml new file mode 100644 index 0000000000..43636d47ca --- /dev/null +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/RecipientDisplay.qml @@ -0,0 +1,117 @@ +// +// RecipientDisplay.qml +// qml/hifi/commerce/wallet/sendMoney +// +// RecipientDisplay +// +// Created by Zach Fox on 2018-01-11 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 as Hifi +import QtQuick 2.6 +import QtQuick.Controls 2.2 +import QtGraphicalEffects 1.0 +import "../../../../styles-uit" +import "../../../../controls-uit" as HifiControlsUit +import "../../../../controls" as HifiControls +import "../../common" as HifiCommerceCommon + +Item { + HifiConstants { id: hifi; } + + id: root; + + property bool isDisplayingNearby; // as opposed to 'connections' + property string displayName; + property string userName; + property string profilePic; + property string textColor: hifi.colors.white; + + Item { + visible: root.isDisplayingNearby; + anchors.fill: parent; + + RalewaySemiBold { + id: recipientDisplayName; + text: root.displayName; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.rightMargin: 12; + height: parent.height/2; + // Text size + size: 18; + // Style + color: root.textColor; + verticalAlignment: Text.AlignBottom; + elide: Text.ElideRight; + } + + RalewaySemiBold { + text: root.userName; + // Anchors + anchors.bottom: parent.bottom; + anchors.left: recipientDisplayName.anchors.left; + anchors.leftMargin: recipientDisplayName.anchors.leftMargin; + anchors.right: recipientDisplayName.anchors.right; + anchors.rightMargin: recipientDisplayName.anchors.rightMargin; + height: parent.height/2; + // Text size + size: 16; + // Style + color: root.textColor; + verticalAlignment: Text.AlignTop; + elide: Text.ElideRight; + } + } + + Item { + visible: !root.isDisplayingNearby; + anchors.fill: parent; + + Image { + id: userImage; + source: root.profilePic; + mipmap: true; + // Anchors + anchors.left: parent.left; + anchors.verticalCenter: parent.verticalCenter; + height: parent.height - 36; + width: height; + layer.enabled: true; + layer.effect: OpacityMask { + maskSource: Item { + width: userImage.width; + height: userImage.height; + Rectangle { + anchors.centerIn: parent; + width: userImage.width; // This works because userImage is square + height: width; + radius: width; + } + } + } + } + + RalewaySemiBold { + text: root.userName; + // Anchors + anchors.left: userImage.right; + anchors.leftMargin: 8; + anchors.right: parent.right; + anchors.verticalCenter: parent.verticalCenter; + height: parent.height - 4; + // Text size + size: 16; + // Style + color: root.textColor; + verticalAlignment: Text.AlignVCenter; + elide: Text.ElideRight; + } + } +} diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml b/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml new file mode 100644 index 0000000000..f66781c919 --- /dev/null +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/SendMoney.qml @@ -0,0 +1,1575 @@ +// +// SendMoney.qml +// qml/hifi/commerce/wallet/sendMoney +// +// SendMoney +// +// Created by Zach Fox on 2018-01-09 +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +import Hifi 1.0 as Hifi +import QtQuick 2.6 +import QtQuick.Controls 2.2 +import QtGraphicalEffects 1.0 +import "../../../../styles-uit" +import "../../../../controls-uit" as HifiControlsUit +import "../../../../controls" as HifiControls +import "../../common" as HifiCommerceCommon + +Item { + HifiConstants { id: hifi; } + + id: root; + + property int parentAppTitleBarHeight; + property int parentAppNavBarHeight; + property string currentActiveView: "sendMoneyHome"; + property string nextActiveView: ""; + property bool shouldShowTopAndBottomOfWallet: chooseRecipientConnection.visible || + chooseRecipientNearby.visible || paymentSuccess.visible || paymentFailure.visible; + property bool shouldShowTopOfWallet: sendMoneyStep.visible; + property bool isCurrentlySendingMoney: false; + + // This object is always used in a popup or full-screen Wallet section. + // This MouseArea is used to prevent a user from being + // able to click on a button/mouseArea underneath the popup/section. + MouseArea { + x: 0; + y: (root.shouldShowTopAndBottomOfWallet && !root.shouldShowTopOfWallet) ? 0 : root.parentAppTitleBarHeight; + width: parent.width; + height: (root.shouldShowTopAndBottomOfWallet || root.shouldShowTopOfWallet) ? parent.height : parent.height - root.parentAppTitleBarHeight - root.parentAppNavBarHeight; + propagateComposedEvents: false; + } + + Connections { + target: Commerce; + + onBalanceResult : { + balanceText.text = result.data.balance; + } + + onTransferHfcToNodeResult: { + root.isCurrentlySendingMoney = false; + + if (result.status === 'success') { + root.nextActiveView = 'paymentSuccess'; + } else { + root.nextActiveView = 'paymentFailure'; + } + } + + onTransferHfcToUsernameResult: { + root.isCurrentlySendingMoney = false; + + if (result.status === 'success') { + root.nextActiveView = 'paymentSuccess'; + } else { + root.nextActiveView = 'paymentFailure'; + } + } + } + + Connections { + target: GlobalServices + onMyUsernameChanged: { + usernameText.text = Account.username; + } + } + + Component.onCompleted: { + Commerce.balance(); + } + + onNextActiveViewChanged: { + if (root.currentActiveView === 'chooseRecipientNearby') { + sendSignalToWallet({method: 'disable_ChooseRecipientNearbyMode'}); + } + + root.currentActiveView = root.nextActiveView; + + if (root.currentActiveView === 'chooseRecipientConnection') { + // Refresh connections model + connectionsLoading.visible = false; + connectionsLoading.visible = true; + sendSignalToWallet({method: 'refreshConnections'}); + } else if (root.currentActiveView === 'sendMoneyHome') { + Commerce.balance(); + } else if (root.currentActiveView === 'chooseRecipientNearby') { + sendSignalToWallet({method: 'enable_ChooseRecipientNearbyMode'}); + } + } + + // Send Money Home BEGIN + Item { + id: sendMoneyHome; + z: 996; + visible: root.currentActiveView === "sendMoneyHome" || root.currentActiveView === "chooseRecipientConnection" || root.currentActiveView === "chooseRecipientNearby"; + anchors.fill: parent; + anchors.topMargin: root.parentAppTitleBarHeight; + anchors.bottomMargin: root.parentAppNavBarHeight; + + // Username Text + RalewayRegular { + id: usernameText; + text: Account.username; + // Text size + size: 24; + // Style + color: hifi.colors.white; + elide: Text.ElideRight; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.leftMargin: 20; + width: parent.width/2; + height: 80; + } + + // HFC Balance Container + Item { + id: hfcBalanceContainer; + // Anchors + anchors.top: parent.top; + anchors.right: parent.right; + anchors.leftMargin: 20; + width: parent.width/2; + height: 80; + + // "HFC" balance label + HiFiGlyphs { + id: balanceLabel; + text: hifi.glyphs.hfc; + // Size + size: 40; + // Anchors + anchors.left: parent.left; + anchors.top: parent.top; + anchors.bottom: parent.bottom; + // Style + color: hifi.colors.white; + } + + // Balance Text + FiraSansRegular { + id: balanceText; + text: "--"; + // Text size + size: 28; + // Anchors + anchors.top: balanceLabel.top; + anchors.bottom: balanceLabel.bottom; + anchors.left: balanceLabel.right; + anchors.leftMargin: 10; + anchors.right: parent.right; + anchors.rightMargin: 4; + // Style + color: hifi.colors.white; + // Alignment + verticalAlignment: Text.AlignVCenter; + } + + // "balance" text below field + RalewayRegular { + text: "BALANCE (HFC)"; + // Text size + size: 14; + // Anchors + anchors.top: balanceLabel.top; + anchors.topMargin: balanceText.paintedHeight + 20; + anchors.bottom: balanceLabel.bottom; + anchors.left: balanceText.left; + anchors.right: balanceText.right; + height: paintedHeight; + // Style + color: hifi.colors.white; + } + } + + // Send Money + Rectangle { + id: sendMoneyContainer; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.bottom: parent.bottom; + height: 440; + + + LinearGradient { + anchors.fill: parent; + start: Qt.point(0, 0); + end: Qt.point(0, height); + gradient: Gradient { + GradientStop { position: 0.0; color: hifi.colors.white } + GradientStop { position: 1.0; color: hifi.colors.faintGray } + } + } + + RalewaySemiBold { + id: sendMoneyText; + text: "Send Money To:"; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 26; + anchors.left: parent.left; + anchors.leftMargin: 20; + width: paintedWidth; + height: 30; + // Text size + size: 22; + // Style + color: hifi.colors.baseGray; + } + + Item { + id: connectionButton; + // Anchors + anchors.top: sendMoneyText.bottom; + anchors.topMargin: 40; + anchors.left: parent.left; + anchors.leftMargin: 75; + height: 95; + width: 95; + + Image { + anchors.top: parent.top; + source: "./images/connection.svg"; + height: 70; + width: parent.width; + fillMode: Image.PreserveAspectFit; + horizontalAlignment: Image.AlignHCenter; + verticalAlignment: Image.AlignTop; + mipmap: true; + } + + RalewaySemiBold { + text: "Connection"; + // Anchors + anchors.bottom: parent.bottom; + height: 15; + width: parent.width; + // Text size + size: 18; + // Style + color: hifi.colors.baseGray; + horizontalAlignment: Text.AlignHCenter; + } + + MouseArea { + anchors.fill: parent; + onClicked: { + root.nextActiveView = "chooseRecipientConnection"; + filterBar.text = ""; + } + } + } + + Item { + id: nearbyButton; + // Anchors + anchors.top: sendMoneyText.bottom; + anchors.topMargin: connectionButton.anchors.topMargin; + anchors.right: parent.right; + anchors.rightMargin: connectionButton.anchors.leftMargin; + height: connectionButton.height; + width: connectionButton.width; + + Image { + anchors.top: parent.top; + source: "./images/nearby.svg"; + height: 70; + width: parent.width; + fillMode: Image.PreserveAspectFit; + horizontalAlignment: Image.AlignHCenter; + verticalAlignment: Image.AlignTop; + mipmap: true; + } + + RalewaySemiBold { + text: "Someone Nearby"; + // Anchors + anchors.bottom: parent.bottom; + height: 15; + width: parent.width; + // Text size + size: 18; + // Style + color: hifi.colors.baseGray; + horizontalAlignment: Text.AlignHCenter; + } + + MouseArea { + anchors.fill: parent; + onClicked: { + root.nextActiveView = "chooseRecipientNearby"; + } + } + } + } + } + // Send Money Home END + + // Choose Recipient Connection BEGIN + Rectangle { + id: chooseRecipientConnection; + z: 997; + visible: root.currentActiveView === "chooseRecipientConnection"; + anchors.fill: parent; + color: "#80000000"; + + // This object is always used in a popup or full-screen Wallet section. + // This MouseArea is used to prevent a user from being + // able to click on a button/mouseArea underneath the popup/section. + MouseArea { + anchors.fill: parent; + propagateComposedEvents: false; + } + + ListModel { + id: connectionsModel; + } + ListModel { + id: filteredConnectionsModel; + } + + Rectangle { + anchors.centerIn: parent; + width: parent.width - 30; + height: parent.height - 30; + color: "#FFFFFF"; + radius: 8; + + RalewaySemiBold { + id: chooseRecipientText_connections; + text: "Choose Recipient:"; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 26; + anchors.left: parent.left; + anchors.leftMargin: 36; + width: paintedWidth; + height: 30; + // Text size + size: 18; + // Style + color: hifi.colors.baseGray; + } + + HiFiGlyphs { + id: closeGlyphButton_connections; + text: hifi.glyphs.close; + color: hifi.colors.lightGrayText; + size: 26; + anchors.top: parent.top; + anchors.topMargin: 10; + anchors.right: parent.right; + anchors.rightMargin: 10; + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + onEntered: { + parent.text = hifi.glyphs.closeInverted; + } + onExited: { + parent.text = hifi.glyphs.close; + } + onClicked: { + root.nextActiveView = "sendMoneyHome"; + } + } + } + + // + // FILTER BAR START + // + Item { + id: filterBarContainer; + // Size + height: 40; + // Anchors + anchors.left: parent.left; + anchors.leftMargin: 36; + anchors.right: parent.right; + anchors.rightMargin: 16; + anchors.top: chooseRecipientText_connections.bottom; + anchors.topMargin: 12; + + HifiControlsUit.TextField { + id: filterBar; + colorScheme: hifi.colorSchemes.faintGray; + hasClearButton: true; + hasRoundedBorder: true; + hasDefocusedBorder: false; + roundedBorderRadius: filterBar.height/2; + anchors.fill: parent; + centerPlaceholderGlyph: hifi.glyphs.search; + + onTextChanged: { + buildFilteredConnectionsModel(); + } + + onAccepted: { + focus = false; + } + } + } + // + // FILTER BAR END + // + + Item { + id: connectionsContainer; + anchors.top: filterBarContainer.bottom; + anchors.topMargin: 16; + anchors.left: parent.left; + anchors.right: parent.right; + anchors.bottom: parent.bottom; + + AnimatedImage { + id: connectionsLoading; + source: "../../../../../icons/profilePicLoading.gif" + width: 120; + height: width; + anchors.top: parent.top; + anchors.topMargin: 185; + anchors.horizontalCenter: parent.horizontalCenter; + } + + ListView { + id: connectionsList; + ScrollBar.vertical: ScrollBar { + policy: connectionsList.contentHeight > parent.parent.height ? ScrollBar.AlwaysOn : ScrollBar.AsNeeded; + parent: connectionsList.parent; + anchors.top: connectionsList.top; + anchors.right: connectionsList.right; + anchors.bottom: connectionsList.bottom; + width: 20; + } + visible: !connectionsLoading.visible; + clip: true; + model: filteredConnectionsModel; + snapMode: ListView.SnapToItem; + // Anchors + anchors.fill: parent; + delegate: ConnectionItem { + isSelected: connectionsList.currentIndex === index; + userName: model.userName; + profilePicUrl: model.profileUrl; + anchors.topMargin: 6; + anchors.bottomMargin: 6; + + Connections { + onSendToSendMoney: { + sendMoneyStep.referrer = "connections"; + sendMoneyStep.selectedRecipientNodeID = ''; + sendMoneyStep.selectedRecipientDisplayName = 'connection'; + sendMoneyStep.selectedRecipientUserName = msg.userName; + sendMoneyStep.selectedRecipientProfilePic = msg.profilePicUrl; + + root.nextActiveView = "sendMoneyStep"; + } + } + + MouseArea { + enabled: connectionsList.currentIndex !== index; + anchors.fill: parent; + propagateComposedEvents: false; + onClicked: { + connectionsList.currentIndex = index; + } + } + } + } + } + } + } + // Choose Recipient Connection END + + // Choose Recipient Nearby BEGIN + Rectangle { + id: chooseRecipientNearby; + z: 997; + color: "#80000000"; + + property string selectedRecipient; + + visible: root.currentActiveView === "chooseRecipientNearby"; + anchors.fill: parent; + + // This object is always used in a popup or full-screen Wallet section. + // This MouseArea is used to prevent a user from being + // able to click on a button/mouseArea underneath the popup/section. + MouseArea { + anchors.fill: parent; + propagateComposedEvents: false; + } + + Rectangle { + anchors.centerIn: parent; + width: parent.width - 30; + height: parent.height - 30; + color: "#FFFFFF"; + radius: 8; + + RalewaySemiBold { + text: "Choose Recipient:"; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 26; + anchors.left: parent.left; + anchors.leftMargin: 20; + width: paintedWidth; + height: 30; + // Text size + size: 18; + // Style + color: hifi.colors.baseGray; + } + + HiFiGlyphs { + id: closeGlyphButton_nearby; + text: hifi.glyphs.close; + color: hifi.colors.lightGrayText; + size: 26; + anchors.top: parent.top; + anchors.topMargin: 10; + anchors.right: parent.right; + anchors.rightMargin: 10; + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + onEntered: { + parent.text = hifi.glyphs.closeInverted; + } + onExited: { + parent.text = hifi.glyphs.close; + } + onClicked: { + root.nextActiveView = "sendMoneyHome"; + resetSendMoneyData(); + } + } + } + + RalewaySemiBold { + id: selectionInstructions; + text: chooseRecipientNearby.selectedRecipient === "" ? "Trigger or click on\nsomeone nearby to select them" : + "Trigger or click on\nsomeone else to select again"; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 100; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: anchors.leftMargin; + height: paintedHeight; + // Text size + size: 20; + // Style + color: hifi.colors.baseGray; + horizontalAlignment: Text.AlignHCenter; + wrapMode: Text.Wrap; + } + + Image { + anchors.top: selectionInstructions.bottom; + anchors.topMargin: 20; + anchors.bottom: selectionMadeContainer.top; + anchors.bottomMargin: 30; + source: "./images/p2p-nearby-unselected.svg"; + width: parent.width; + fillMode: Image.PreserveAspectFit; + horizontalAlignment: Image.AlignHCenter; + verticalAlignment: Image.AlignVCenter; + mipmap: true; + } + + Rectangle { + id: selectionMadeContainer; + visible: chooseRecipientNearby.selectedRecipient !== ""; + anchors.bottom: parent.bottom; + anchors.left: parent.left; + anchors.right: parent.right; + height: 190; + color: "#F3F3F3"; + radius: 8; + + // Used to square off the top left and top right edges of this container + Rectangle { + color: "#F3F3F3"; + height: selectionMadeContainer.radius; + anchors.top: selectionMadeContainer.top; + anchors.left: selectionMadeContainer.left; + anchors.right: selectionMadeContainer.right; + } + + RalewaySemiBold { + id: sendToText; + text: "Send to:"; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 36; + anchors.left: parent.left; + anchors.leftMargin: 36; + width: paintedWidth; + height: paintedHeight; + // Text size + size: 18; + // Style + color: hifi.colors.baseGray; + } + + Image { + id: selectedImage; + anchors.top: parent.top; + anchors.topMargin: 24; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 32; + anchors.left: sendToText.right; + anchors.leftMargin: 4; + source: "./images/p2p-nearby-selected.svg"; + width: 50; + fillMode: Image.PreserveAspectFit; + horizontalAlignment: Image.AlignHCenter; + verticalAlignment: Image.AlignVCenter; + mipmap: true; + } + + RalewaySemiBold { + id: avatarDisplayName; + text: '"' + AvatarList.getAvatar(chooseRecipientNearby.selectedRecipient).sessionDisplayName + '"'; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 34; + anchors.left: selectedImage.right; + anchors.leftMargin: 10; + anchors.right: parent.right; + anchors.rightMargin: 10; + height: paintedHeight; + // Text size + size: 20; + // Style + color: hifi.colors.blueAccent; + } + + RalewaySemiBold { + id: avatarUserName; + text: sendMoneyStep.selectedRecipientUserName; + // Anchors + anchors.top: avatarDisplayName.bottom; + anchors.topMargin: 16; + anchors.left: selectedImage.right; + anchors.leftMargin: 10; + anchors.right: parent.right; + anchors.rightMargin: 10; + height: paintedHeight; + // Text size + size: 18; + // Style + color: hifi.colors.baseGray; + } + + // "CHOOSE" button + HifiControlsUit.Button { + id: chooseButton; + color: hifi.buttons.blue; + colorScheme: hifi.colorSchemes.dark; + anchors.horizontalCenter: parent.horizontalCenter; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 20; + height: 40; + width: 110; + text: "CHOOSE"; + onClicked: { + sendMoneyStep.referrer = "nearby"; + sendMoneyStep.selectedRecipientNodeID = chooseRecipientNearby.selectedRecipient; + chooseRecipientNearby.selectedRecipient = ""; + + root.nextActiveView = "sendMoneyStep"; + } + } + } + } + } + // Choose Recipient Nearby END + + // Send Money Screen BEGIN + Item { + id: sendMoneyStep; + z: 996; + + property string referrer; // either "connections" or "nearby" + property string selectedRecipientNodeID; + property string selectedRecipientDisplayName; + property string selectedRecipientUserName; + property string selectedRecipientProfilePic; + + visible: root.currentActiveView === "sendMoneyStep"; + anchors.fill: parent; + anchors.topMargin: root.parentAppTitleBarHeight; + + RalewaySemiBold { + id: sendMoneyText_sendMoneyStep; + text: "Send Money"; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 26; + anchors.left: parent.left; + anchors.leftMargin: 20; + width: paintedWidth; + height: 30; + // Text size + size: 22; + // Style + color: hifi.colors.white; + } + + Item { + id: sendToContainer; + anchors.top: sendMoneyText_sendMoneyStep.bottom; + anchors.topMargin: 20; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 20; + height: 80; + + RalewaySemiBold { + id: sendToText_sendMoneyStep; + text: "Send to:"; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.bottom: parent.bottom; + width: 90; + // Text size + size: 18; + // Style + color: hifi.colors.white; + verticalAlignment: Text.AlignVCenter; + } + + RecipientDisplay { + anchors.top: parent.top; + anchors.left: sendToText_sendMoneyStep.right; + anchors.right: changeButton.left; + anchors.rightMargin: 12; + height: parent.height; + + displayName: sendMoneyStep.selectedRecipientDisplayName; + userName: sendMoneyStep.selectedRecipientUserName; + profilePic: sendMoneyStep.selectedRecipientProfilePic !== "" ? ((0 === sendMoneyStep.selectedRecipientProfilePic.indexOf("http")) ? + sendMoneyStep.selectedRecipientProfilePic : (Account.metaverseServerURL + sendMoneyStep.selectedRecipientProfilePic)) : ""; + isDisplayingNearby: sendMoneyStep.referrer === "nearby"; + } + + // "CHANGE" button + HifiControlsUit.Button { + id: changeButton; + color: hifi.buttons.none; + colorScheme: hifi.colorSchemes.dark; + anchors.right: parent.right; + anchors.verticalCenter: parent.verticalCenter; + height: 35; + width: 100; + text: "CHANGE"; + onClicked: { + if (sendMoneyStep.referrer === "connections") { + root.nextActiveView = "chooseRecipientConnection"; + } else if (sendMoneyStep.referrer === "nearby") { + root.nextActiveView = "chooseRecipientNearby"; + } + resetSendMoneyData(); + } + } + } + + Item { + id: amountContainer; + anchors.top: sendToContainer.bottom; + anchors.topMargin: 2; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 20; + height: 80; + + RalewaySemiBold { + id: amountText; + text: "Amount:"; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.bottom: parent.bottom; + width: 90; + // Text size + size: 18; + // Style + color: hifi.colors.white; + verticalAlignment: Text.AlignVCenter; + } + + HifiControlsUit.TextField { + id: amountTextField; + colorScheme: hifi.colorSchemes.dark; + inputMethodHints: Qt.ImhDigitsOnly; + // Anchors + anchors.verticalCenter: parent.verticalCenter; + anchors.left: amountText.right; + anchors.right: parent.right; + height: 50; + // Style + leftPermanentGlyph: hifi.glyphs.hfc; + activeFocusOnPress: true; + activeFocusOnTab: true; + + validator: IntValidator { bottom: 0; } + + onAccepted: { + optionalMessage.focus = true; + } + } + + FiraSansSemiBold { + visible: amountTextFieldError.text === ""; + text: "Balance: "; + // Anchors + anchors.top: amountTextField.bottom; + anchors.topMargin: 2; + anchors.left: amountTextField.left; + anchors.right: sendMoneyBalanceText_HFC.left; + width: paintedWidth; + height: 40; + // Text size + size: 16; + // Style + color: hifi.colors.lightGrayText; + verticalAlignment: Text.AlignTop; + horizontalAlignment: Text.AlignRight; + } + HiFiGlyphs { + id: sendMoneyBalanceText_HFC; + visible: amountTextFieldError.text === ""; + text: hifi.glyphs.hfc; + // Size + size: 16; + // Anchors + anchors.top: amountTextField.bottom; + anchors.topMargin: 2; + anchors.right: sendMoneyBalanceText.left; + width: paintedWidth; + height: 40; + // Style + color: hifi.colors.lightGrayText; + verticalAlignment: Text.AlignTop; + horizontalAlignment: Text.AlignRight; + } + FiraSansSemiBold { + id: sendMoneyBalanceText; + visible: amountTextFieldError.text === ""; + text: balanceText.text; + // Anchors + anchors.top: amountTextField.bottom; + anchors.topMargin: 2; + anchors.right: amountTextField.right; + width: paintedWidth; + height: 40; + // Text size + size: 16; + // Style + color: hifi.colors.lightGrayText; + verticalAlignment: Text.AlignTop; + horizontalAlignment: Text.AlignRight; + } + + RalewaySemiBold { + id: amountTextFieldError; + // Anchors + anchors.top: amountTextField.bottom; + anchors.topMargin: 2; + anchors.left: amountTextField.left; + anchors.right: amountTextField.right; + height: 40; + // Text size + size: 16; + // Style + color: hifi.colors.white; + verticalAlignment: Text.AlignTop; + horizontalAlignment: Text.AlignRight; + } + } + + Item { + id: messageContainer; + anchors.top: amountContainer.bottom; + anchors.topMargin: 16; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 20; + height: 140; + + FontLoader { id: firaSansSemiBold; source: "../../../../../fonts/FiraSans-SemiBold.ttf"; } + TextArea { + id: optionalMessage; + property int maximumLength: 72; + property string previousText: text; + placeholderText: "Optional Message (" + maximumLength + " character limit)"; + font.family: firaSansSemiBold.name; + font.pixelSize: 20; + // Anchors + anchors.fill: parent; + // Style + background: Rectangle { + anchors.fill: parent; + color: optionalMessage.activeFocus ? hifi.colors.black : hifi.colors.baseGrayShadow; + border.width: optionalMessage.activeFocus ? 1 : 0; + border.color: optionalMessage.activeFocus ? hifi.colors.primaryHighlight : hifi.colors.textFieldLightBackground; + } + color: hifi.colors.white; + textFormat: TextEdit.PlainText; + wrapMode: TextEdit.Wrap; + activeFocusOnPress: true; + activeFocusOnTab: true; + // Workaround for no max length on TextAreas + onTextChanged: { + if (text.length > maximumLength) { + var cursor = cursorPosition; + text = previousText; + if (cursor > text.length) { + cursorPosition = text.length; + } else { + cursorPosition = cursor-1; + } + } + previousText = text; + } + } + FiraSansSemiBold { + id: optionalMessageCharacterCount; + text: optionalMessage.text.length + "/" + optionalMessage.maximumLength; + // Anchors + anchors.top: optionalMessage.bottom; + anchors.topMargin: 4; + anchors.right: optionalMessage.right; + height: paintedHeight; + // Text size + size: 16; + // Style + color: optionalMessage.text.length === optionalMessage.maximumLength ? "#ea89a5" : hifi.colors.lightGrayText; + verticalAlignment: Text.AlignTop; + horizontalAlignment: Text.AlignRight; + } + } + + HifiControlsUit.CheckBox { + id: sendPubliclyCheckbox; + visible: false; // FIXME ONCE PARTICLE EFFECTS ARE IN + text: "Send Publicly" + // Anchors + anchors.top: messageContainer.bottom; + anchors.topMargin: 16; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 16; + boxSize: 24; + } + + Item { + id: bottomBarContainer; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 20; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 20; + height: 60; + + // "CANCEL" button + HifiControlsUit.Button { + id: cancelButton_sendMoneyStep; + color: hifi.buttons.noneBorderlessWhite; + colorScheme: hifi.colorSchemes.dark; + anchors.left: parent.left; + anchors.leftMargin: 24; + anchors.verticalCenter: parent.verticalCenter; + height: 40; + width: 150; + text: "CANCEL"; + onClicked: { + resetSendMoneyData(); + root.nextActiveView = "sendMoneyHome"; + } + } + + // "SEND" button + HifiControlsUit.Button { + id: sendButton; + color: hifi.buttons.blue; + colorScheme: hifi.colorSchemes.dark; + anchors.right: parent.right; + anchors.rightMargin: 24; + anchors.verticalCenter: parent.verticalCenter; + height: 40; + width: 150; + text: "SUBMIT"; + onClicked: { + if (parseInt(amountTextField.text) > parseInt(balanceText.text)) { + amountTextField.focus = true; + amountTextField.error = true; + amountTextFieldError.text = "amount exceeds available funds"; + } else if (amountTextField.text === "" || parseInt(amountTextField.text) < 1) { + amountTextField.focus = true; + amountTextField.error = true; + amountTextFieldError.text = "invalid amount"; + } else { + amountTextFieldError.text = ""; + amountTextField.error = false; + root.isCurrentlySendingMoney = true; + amountTextField.focus = false; + optionalMessage.focus = false; + if (sendMoneyStep.referrer === "connections") { + Commerce.transferHfcToUsername(sendMoneyStep.selectedRecipientUserName, parseInt(amountTextField.text), optionalMessage.text); + } else if (sendMoneyStep.referrer === "nearby") { + Commerce.transferHfcToNode(sendMoneyStep.selectedRecipientNodeID, parseInt(amountTextField.text), optionalMessage.text); + } + } + } + } + } + } + // Send Money Screen END + + // Sending Money Overlay START + Rectangle { + id: sendingMoneyOverlay; + z: 998; + + visible: root.isCurrentlySendingMoney; + anchors.fill: parent; + color: Qt.rgba(0.0, 0.0, 0.0, 0.8); + + // This object is always used in a popup or full-screen Wallet section. + // This MouseArea is used to prevent a user from being + // able to click on a button/mouseArea underneath the popup/section. + MouseArea { + anchors.fill: parent; + propagateComposedEvents: false; + } + + AnimatedImage { + id: sendingMoneyImage; + source: "./images/loader.gif" + width: 96; + height: width; + anchors.verticalCenter: parent.verticalCenter; + anchors.horizontalCenter: parent.horizontalCenter; + } + + RalewaySemiBold { + text: "Sending"; + // Anchors + anchors.top: sendingMoneyImage.bottom; + anchors.topMargin: 4; + anchors.horizontalCenter: parent.horizontalCenter; + width: paintedWidth; + // Text size + size: 26; + // Style + color: hifi.colors.white; + verticalAlignment: Text.AlignVCenter; + } + } + // Sending Money Overlay END + + // Payment Success BEGIN + Item { + id: paymentSuccess; + + visible: root.currentActiveView === "paymentSuccess"; + anchors.fill: parent; + + Rectangle { + anchors.centerIn: parent; + width: parent.width - 30; + height: parent.height - 30; + color: "#FFFFFF"; + + RalewaySemiBold { + id: paymentSentText; + text: "Payment Sent"; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 26; + anchors.left: parent.left; + anchors.leftMargin: 20; + width: paintedWidth; + height: 30; + // Text size + size: 22; + // Style + color: hifi.colors.baseGray; + } + + HiFiGlyphs { + id: closeGlyphButton_paymentSuccess; + text: hifi.glyphs.close; + color: hifi.colors.lightGrayText; + size: 26; + anchors.top: parent.top; + anchors.topMargin: 10; + anchors.right: parent.right; + anchors.rightMargin: 10; + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + onEntered: { + parent.text = hifi.glyphs.closeInverted; + } + onExited: { + parent.text = hifi.glyphs.close; + } + onClicked: { + root.nextActiveView = "sendMoneyHome"; + resetSendMoneyData(); + } + } + } + + Item { + id: sendToContainer_paymentSuccess; + anchors.top: paymentSentText.bottom; + anchors.topMargin: 20; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 20; + height: 80; + + RalewaySemiBold { + id: sendToText_paymentSuccess; + text: "Sent To:"; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.bottom: parent.bottom; + width: 90; + // Text size + size: 18; + // Style + color: hifi.colors.baseGray; + verticalAlignment: Text.AlignVCenter; + } + + RecipientDisplay { + anchors.top: parent.top; + anchors.left: sendToText_paymentSuccess.right; + anchors.right: parent.right; + height: parent.height; + textColor: hifi.colors.blueAccent; + + displayName: sendMoneyStep.selectedRecipientDisplayName; + userName: sendMoneyStep.selectedRecipientUserName; + profilePic: sendMoneyStep.selectedRecipientProfilePic !== "" ? ((0 === sendMoneyStep.selectedRecipientProfilePic.indexOf("http")) ? + sendMoneyStep.selectedRecipientProfilePic : (Account.metaverseServerURL + sendMoneyStep.selectedRecipientProfilePic)) : ""; + isDisplayingNearby: sendMoneyStep.referrer === "nearby"; + } + } + + Item { + id: amountContainer_paymentSuccess; + anchors.top: sendToContainer_paymentSuccess.bottom; + anchors.topMargin: 16; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 20; + height: 80; + + RalewaySemiBold { + id: amountText_paymentSuccess; + text: "Amount:"; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.bottom: parent.bottom; + width: 90; + // Text size + size: 18; + // Style + color: hifi.colors.baseGray; + verticalAlignment: Text.AlignVCenter; + } + + // "HFC" balance label + HiFiGlyphs { + id: amountSentLabel; + text: hifi.glyphs.hfc; + // Size + size: 32; + // Anchors + anchors.left: amountText_paymentSuccess.right; + anchors.verticalCenter: parent.verticalCenter; + height: 50; + // Style + color: hifi.colors.blueAccent; + } + + FiraSansSemiBold { + id: amountSentText; + text: amountTextField.text; + // Anchors + anchors.verticalCenter: parent.verticalCenter; + anchors.left: amountSentLabel.right; + anchors.leftMargin: 20; + anchors.right: parent.right; + height: 50; + // Style + size: 22; + color: hifi.colors.blueAccent; + } + } + + RalewaySemiBold { + id: optionalMessage_paymentSuccess; + text: optionalMessage.text; + // Anchors + anchors.top: amountContainer_paymentSuccess.bottom; + anchors.left: parent.left; + anchors.leftMargin: 110; + anchors.right: parent.right; + anchors.rightMargin: 16; + anchors.bottom: closeButton.top; + anchors.bottomMargin: 40; + // Text size + size: 22; + // Style + color: hifi.colors.blueAccent; + wrapMode: Text.Wrap; + verticalAlignment: Text.AlignTop; + } + + // "Close" button + HifiControlsUit.Button { + id: closeButton; + color: hifi.buttons.blue; + colorScheme: hifi.colorSchemes.dark; + anchors.horizontalCenter: parent.horizontalCenter; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 80; + height: 50; + width: 120; + text: "Close"; + onClicked: { + root.nextActiveView = "sendMoneyHome"; + resetSendMoneyData(); + } + } + } + } + // Payment Success END + + // Payment Failure BEGIN + Item { + id: paymentFailure; + + visible: root.currentActiveView === "paymentFailure"; + anchors.fill: parent; + + Rectangle { + anchors.centerIn: parent; + width: parent.width - 30; + height: parent.height - 30; + color: "#FFFFFF"; + + RalewaySemiBold { + id: paymentFailureText; + text: "Payment Failed"; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 26; + anchors.left: parent.left; + anchors.leftMargin: 20; + width: paintedWidth; + height: 30; + // Text size + size: 22; + // Style + color: hifi.colors.baseGray; + } + + HiFiGlyphs { + id: closeGlyphButton_paymentFailure; + text: hifi.glyphs.close; + color: hifi.colors.lightGrayText; + size: 26; + anchors.top: parent.top; + anchors.topMargin: 10; + anchors.right: parent.right; + anchors.rightMargin: 10; + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + onEntered: { + parent.text = hifi.glyphs.closeInverted; + } + onExited: { + parent.text = hifi.glyphs.close; + } + onClicked: { + root.nextActiveView = "sendMoneyHome"; + resetSendMoneyData(); + } + } + } + + RalewaySemiBold { + id: paymentFailureDetailText; + text: "The recipient you specified was unable to receive your payment."; + anchors.top: paymentFailureText.bottom; + anchors.topMargin: 20; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 20; + height: 80; + // Text size + size: 18; + // Style + color: hifi.colors.baseGray; + verticalAlignment: Text.AlignVCenter; + wrapMode: Text.Wrap; + } + + Item { + id: sendToContainer_paymentFailure; + anchors.top: paymentFailureDetailText.bottom; + anchors.topMargin: 8; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 20; + height: 80; + + RalewaySemiBold { + id: sentToText_paymentFailure; + text: "Sent To:"; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.bottom: parent.bottom; + width: 90; + // Text size + size: 18; + // Style + color: hifi.colors.baseGray; + verticalAlignment: Text.AlignVCenter; + } + + RecipientDisplay { + anchors.top: parent.top; + anchors.left: sentToText_paymentFailure.right; + anchors.right: parent.right; + height: parent.height; + textColor: hifi.colors.baseGray; + + displayName: sendMoneyStep.selectedRecipientDisplayName; + userName: sendMoneyStep.selectedRecipientUserName; + profilePic: sendMoneyStep.selectedRecipientProfilePic !== "" ? ((0 === sendMoneyStep.selectedRecipientProfilePic.indexOf("http")) ? + sendMoneyStep.selectedRecipientProfilePic : (Account.metaverseServerURL + sendMoneyStep.selectedRecipientProfilePic)) : ""; + isDisplayingNearby: sendMoneyStep.referrer === "nearby"; + } + } + + Item { + id: amountContainer_paymentFailure; + anchors.top: sendToContainer_paymentFailure.bottom; + anchors.topMargin: 16; + anchors.left: parent.left; + anchors.leftMargin: 20; + anchors.right: parent.right; + anchors.rightMargin: 20; + height: 80; + + RalewaySemiBold { + id: amountText_paymentFailure; + text: "Amount:"; + // Anchors + anchors.top: parent.top; + anchors.left: parent.left; + anchors.bottom: parent.bottom; + width: 90; + // Text size + size: 18; + // Style + color: hifi.colors.baseGray; + verticalAlignment: Text.AlignVCenter; + } + + // "HFC" balance label + HiFiGlyphs { + id: amountSentLabel_paymentFailure; + text: hifi.glyphs.hfc; + // Size + size: 32; + // Anchors + anchors.left: amountText_paymentFailure.right; + anchors.verticalCenter: parent.verticalCenter; + height: 50; + // Style + color: hifi.colors.baseGray; + } + + FiraSansSemiBold { + id: amountSentText_paymentFailure; + text: amountTextField.text; + // Anchors + anchors.verticalCenter: parent.verticalCenter; + anchors.left: amountSentLabel_paymentFailure.right; + anchors.leftMargin: 20; + anchors.right: parent.right; + height: 50; + // Style + size: 22; + color: hifi.colors.baseGray; + } + } + + RalewaySemiBold { + id: optionalMessage_paymentFailuire; + text: optionalMessage.text; + // Anchors + anchors.top: amountContainer_paymentFailure.bottom; + anchors.left: parent.left; + anchors.leftMargin: 110; + anchors.right: parent.right; + anchors.rightMargin: 16; + anchors.bottom: closeButton_paymentFailure.top; + anchors.bottomMargin: 40; + // Text size + size: 22; + // Style + color: hifi.colors.baseGray; + wrapMode: Text.Wrap; + verticalAlignment: Text.AlignTop; + } + + // "Close" button + HifiControlsUit.Button { + id: closeButton_paymentFailure; + color: hifi.buttons.noneBorderless; + colorScheme: hifi.colorSchemes.dark; + anchors.horizontalCenter: parent.horizontalCenter; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 80; + height: 50; + width: 120; + text: "Cancel"; + onClicked: { + root.nextActiveView = "sendMoneyHome"; + resetSendMoneyData(); + } + } + + // "Retry" button + HifiControlsUit.Button { + id: retryButton_paymentFailure; + color: hifi.buttons.blue; + colorScheme: hifi.colorSchemes.dark; + anchors.right: parent.right; + anchors.rightMargin: 12; + anchors.bottom: parent.bottom; + anchors.bottomMargin: 80; + height: 50; + width: 120; + text: "Retry"; + onClicked: { + root.isCurrentlySendingMoney = true; + if (sendMoneyStep.referrer === "connections") { + Commerce.transferHfcToUsername(sendMoneyStep.selectedRecipientUserName, parseInt(amountTextField.text), optionalMessage.text); + } else if (sendMoneyStep.referrer === "nearby") { + Commerce.transferHfcToNode(sendMoneyStep.selectedRecipientNodeID, parseInt(amountTextField.text), optionalMessage.text); + } + } + } + } + } + // Payment Failure END + + + // + // FUNCTION DEFINITIONS START + // + + function updateConnections(connections) { + connectionsModel.clear(); + connectionsModel.append(connections); + buildFilteredConnectionsModel(); + connectionsLoading.visible = false; + } + + function buildFilteredConnectionsModel() { + filteredConnectionsModel.clear(); + for (var i = 0; i < connectionsModel.count; i++) { + if (connectionsModel.get(i).userName.toLowerCase().indexOf(filterBar.text.toLowerCase()) !== -1) { + filteredConnectionsModel.append(connectionsModel.get(i)); + } + } + } + + function resetSendMoneyData() { + amountTextField.focus = false; + optionalMessage.focus = false; + amountTextFieldError.text = ""; + amountTextField.error = false; + chooseRecipientNearby.selectedRecipient = ""; + sendMoneyStep.selectedRecipientNodeID = ""; + sendMoneyStep.selectedRecipientDisplayName = ""; + sendMoneyStep.selectedRecipientUserName = ""; + sendMoneyStep.selectedRecipientProfilePic = ""; + amountTextField.text = ""; + optionalMessage.text = ""; + } + + // + // Function Name: fromScript() + // + // Relevant Variables: + // None + // + // Arguments: + // message: The message sent from the JavaScript. + // Messages are in format "{method, params}", like json-rpc. + // + // Description: + // Called when a message is received from a script. + // + function fromScript(message) { + switch (message.method) { + case 'selectRecipient': + if (message.isSelected) { + chooseRecipientNearby.selectedRecipient = message.id[0]; + sendMoneyStep.selectedRecipientDisplayName = message.displayName; + sendMoneyStep.selectedRecipientUserName = message.userName; + } else { + chooseRecipientNearby.selectedRecipient = ""; + sendMoneyStep.selectedRecipientDisplayName = ''; + sendMoneyStep.selectedRecipientUserName = ''; + } + break; + case 'updateSelectedRecipientUsername': + sendMoneyStep.selectedRecipientUserName = message.userName; + break; + default: + console.log('SendMoney: Unrecognized message from wallet.js:', JSON.stringify(message)); + } + } + signal sendSignalToWallet(var msg); + // + // FUNCTION DEFINITIONS END + // +} diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/connection.svg b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/connection.svg new file mode 100644 index 0000000000..7c5403fda3 --- /dev/null +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/connection.svg @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/loader.gif b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/loader.gif new file mode 100644 index 0000000000..0536bd1884 Binary files /dev/null and b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/loader.gif differ diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/nearby.svg b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/nearby.svg new file mode 100644 index 0000000000..dec87e658d --- /dev/null +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/nearby.svg @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/p2p-nearby-selected.svg b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/p2p-nearby-selected.svg new file mode 100644 index 0000000000..59635a99b1 --- /dev/null +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/p2p-nearby-selected.svg @@ -0,0 +1,27 @@ + + + + + + + + diff --git a/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/p2p-nearby-unselected.svg b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/p2p-nearby-unselected.svg new file mode 100644 index 0000000000..bba07b9567 --- /dev/null +++ b/interface/resources/qml/hifi/commerce/wallet/sendMoney/images/p2p-nearby-unselected.svg @@ -0,0 +1,30 @@ + + + + + + + + + diff --git a/interface/resources/qml/hifi/overlays/ImageOverlay.qml b/interface/resources/qml/hifi/overlays/ImageOverlay.qml index 6899c38e67..cbcf6c7910 100644 --- a/interface/resources/qml/hifi/overlays/ImageOverlay.qml +++ b/interface/resources/qml/hifi/overlays/ImageOverlay.qml @@ -7,7 +7,7 @@ import "." Overlay { id: root - Image { + AnimatedImage { id: image property bool scaleFix: true property real xStart: 0 diff --git a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml index 217adc5083..f3cda533e9 100644 --- a/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml +++ b/interface/resources/qml/hifi/tablet/TabletAddressDialog.qml @@ -62,7 +62,8 @@ StackView { var callback = rpcCalls[message.id]; if (!callback) { - console.log('No callback for message fromScript', JSON.stringify(message)); + // FIXME: We often recieve very long messages here, the logging of which is drastically slowing down the main thread + //console.log('No callback for message fromScript', JSON.stringify(message)); return; } delete rpcCalls[message.id]; diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index e934f18ab6..524bbf5f4d 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -127,9 +127,15 @@ Item { GridView { id: gridView + keyNavigationEnabled: false highlightFollowsCurrentItem: false + property int previousGridIndex: -1 + + // true if any of the buttons contains mouse + property bool containsMouse: false + anchors { fill: parent topMargin: 20 @@ -162,15 +168,31 @@ Item { flow: GridView.LeftToRight model: page.proxyModel - delegate: Item { + delegate: Control { id: wrapper width: gridView.cellWidth height: gridView.cellHeight + hoverEnabled: true + + property bool containsMouse: gridView.containsMouse + onHoveredChanged: { + if (hovered && !gridView.containsMouse) { + gridView.containsMouse = true + } else { + gridView.containsMouse = false + } + } + property var proxy: modelData TabletButton { id: tabletButton + + // Temporarily disable magnification + // scale: wrapper.hovered ? 1.25 : wrapper.containsMouse ? 0.75 : 1.0 + // Behavior on scale { NumberAnimation { duration: 200; easing.type: Easing.Linear } } + anchors.centerIn: parent gridView: wrapper.GridView.view buttonIndex: page.proxyModel.buttonIndex(uuid); @@ -224,6 +246,7 @@ Item { PageIndicator { id: pageIndicator currentIndex: swipeView.currentIndex + visible: swipeView.count > 1 delegate: Item { width: 15 diff --git a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml index ff653b6457..4fa29de9a3 100644 --- a/interface/resources/qml/hifi/tablet/TabletMenuStack.qml +++ b/interface/resources/qml/hifi/tablet/TabletMenuStack.qml @@ -22,7 +22,6 @@ Item { anchors.fill: parent id: d objectName: "stack" - initialItem: topMenu property var menuStack: [] property var topMenu: null; diff --git a/interface/resources/qml/hifi/tablet/TabletRoot.qml b/interface/resources/qml/hifi/tablet/TabletRoot.qml index da544c2114..e7614e11b7 100644 --- a/interface/resources/qml/hifi/tablet/TabletRoot.qml +++ b/interface/resources/qml/hifi/tablet/TabletRoot.qml @@ -106,7 +106,7 @@ Item { if (isWebPage) { var webUrl = tabletApps.get(currentApp).appWebUrl; var scriptUrl = tabletApps.get(currentApp).scriptUrl; - loadSource("TabletWebView.qml"); + loadSource("hifi/tablet/TabletWebView.qml"); loadWebUrl(webUrl, scriptUrl); } else { loader.load(tabletApps.get(currentApp).appUrl); diff --git a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletAvatarBrowser.qml b/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletAvatarBrowser.qml deleted file mode 100644 index 2ea12f1d3d..0000000000 --- a/interface/resources/qml/hifi/tablet/tabletWindows/preferences/TabletAvatarBrowser.qml +++ /dev/null @@ -1,111 +0,0 @@ -// -// TabletAvatarBrowser.qml -// -// Created by David Rowe on 14 Mar 2017 -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.5 -import QtQuick.Controls 1.4 -import QtWebChannel 1.0 -import QtWebEngine 1.2 - -import "../../../../windows" -import "../../../../controls-uit" -import "../../../../styles-uit" - -Item { - id: root - objectName: "ModelBrowserDialog" - - property string title: "Attachment Model" - - property bool keyboardEnabled: false - property bool keyboardRaised: false - property bool punctuationMode: false - - anchors.fill: parent - - BaseWebView { - id: webview - url: (Account.metaverseServerURL + "/marketplace?category=avatars") - focus: true - - anchors { - top: parent.top - left: parent.left - right: parent.right - bottom: footer.top - } - - // Create a global EventBridge object for raiseAndLowerKeyboard. - WebEngineScript { - id: createGlobalEventBridge - sourceCode: eventBridgeJavaScriptToInject - injectionPoint: WebEngineScript.DocumentCreation - worldId: WebEngineScript.MainWorld - } - - // Detect when may want to raise and lower keyboard. - WebEngineScript { - id: raiseAndLowerKeyboard - injectionPoint: WebEngineScript.Deferred - sourceUrl: resourceDirectoryUrl + "html/raiseAndLowerKeyboard.js" - worldId: WebEngineScript.MainWorld - } - - userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard ] - - Component.onCompleted: { - webChannel.registerObject("eventBridge", eventBridge); - webChannel.registerObject("eventBridgeWrapper", eventBridgeWrapper); - } - } - - Rectangle { - id: footer - height: 40 - - anchors { - left: parent.left - right: parent.right - bottom: keyboard.top - } - - color: hifi.colors.baseGray - - Row { - anchors { - verticalCenter: parent.verticalCenter - right: parent.right - rightMargin: hifi.dimensions.contentMargin.x - } - - Button { - text: "Cancel" - color: hifi.buttons.white - onClicked: root.destroy(); - } - } - } - - Keyboard { - id: keyboard - - raised: parent.keyboardEnabled && parent.keyboardRaised - numeric: parent.punctuationMode - - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - } - - Component.onCompleted: { - keyboardEnabled = HMD.active; - } -} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 32002812a5..d3e6dcfb24 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -14,17 +14,20 @@ #include #include -#include #include #include #include #include #include +#include + +#include #include #include #include #include +#include #include #include @@ -49,6 +52,7 @@ #include +#include #include #include #include @@ -157,7 +161,7 @@ #include "scripting/AssetMappingsScriptingInterface.h" #include "scripting/ClipboardScriptingInterface.h" #include "scripting/DesktopScriptingInterface.h" -#include "scripting/GlobalServicesScriptingInterface.h" +#include "scripting/AccountServicesScriptingInterface.h" #include "scripting/HMDScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" #include "scripting/SettingsScriptingInterface.h" @@ -209,9 +213,8 @@ #include "commerce/QmlCommerce.h" #include "webbrowser/WebBrowserSuggestionsEngine.h" +#include -// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU -// FIXME seems to be broken. #if defined(Q_OS_WIN) #include @@ -226,11 +229,17 @@ #undef QT_BOOTSTRAPPED #endif +// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU +// FIXME seems to be broken. extern "C" { _declspec(dllexport) DWORD NvOptimusEnablement = 0x00000001; } #endif +#if defined(Q_OS_ANDROID) +#include +#endif + enum ApplicationEvent { // Execute a lambda function Lambda = QEvent::User + 1, @@ -240,7 +249,6 @@ enum ApplicationEvent { Idle, }; - class RenderEventHandler : public QObject { using Parent = QObject; Q_OBJECT @@ -304,9 +312,19 @@ static QTimer locationUpdateTimer; static QTimer identityPacketTimer; static QTimer pingTimer; -static const QString DISABLE_WATCHDOG_FLAG("HIFI_DISABLE_WATCHDOG"); +#if defined(Q_OS_ANDROID) +static bool DISABLE_WATCHDOG = true; +#else +static const QString DISABLE_WATCHDOG_FLAG{ "HIFI_DISABLE_WATCHDOG" }; static bool DISABLE_WATCHDOG = QProcessEnvironment::systemEnvironment().contains(DISABLE_WATCHDOG_FLAG); +#endif +#if defined(USE_GLES) +static bool DISABLE_DEFERRED = true; +#else +static const QString RENDER_FORWARD{ "HIFI_RENDER_FORWARD" }; +static bool DISABLE_DEFERRED = QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD); +#endif static const int MAX_CONCURRENT_RESOURCE_DOWNLOADS = 16; @@ -333,7 +351,7 @@ static const float MIRROR_FULLSCREEN_DISTANCE = 0.389f; static const quint64 TOO_LONG_SINCE_LAST_SEND_DOWNSTREAM_AUDIO_STATS = 1 * USECS_PER_SECOND; static const QString INFO_EDIT_ENTITIES_PATH = "html/edit-commands.html"; -static const QString INFO_HELP_PATH = "../../../html/tabletHelp.html"; +static const QString INFO_HELP_PATH = "html/tabletHelp.html"; static const unsigned int THROTTLED_SIM_FRAMERATE = 15; static const int THROTTLED_SIM_FRAME_PERIOD_MS = MSECS_PER_SECOND / THROTTLED_SIM_FRAMERATE; @@ -503,7 +521,13 @@ public: } if (message->message == WM_DEVICECHANGE) { - Midi::USBchanged(); // re-scan the MIDI bus + const float MIN_DELTA_SECONDS = 2.0f; // de-bounce signal + static float lastTriggerTime = 0.0f; + const float deltaSeconds = secTimestampNow() - lastTriggerTime; + lastTriggerTime = secTimestampNow(); + if (deltaSeconds > MIN_DELTA_SECONDS) { + Midi::USBchanged(); // re-scan the MIDI bus + } } } return false; @@ -530,6 +554,26 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt #ifdef Q_OS_WIN OutputDebugStringA(logMessage.toLocal8Bit().constData()); OutputDebugStringA("\n"); +#elif defined Q_OS_ANDROID + const char * local=logMessage.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(); + } #endif qApp->getLogger()->addMessage(qPrintable(logMessage + "\n")); } @@ -573,8 +617,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { } }; reportAndQuit("--protocolVersion", [&](FILE* fp) { - DependencyManager::set(); - auto version = DependencyManager::get()->protocolVersion(); + auto version = protocolVersionsSignatureBase64(); fputs(version.toLatin1().data(), fp); }); reportAndQuit("--version", [&](FILE* fp) { @@ -594,6 +637,24 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { qApp->setProperty(hifi::properties::APP_LOCAL_DATA_PATH, cacheDir); } + // FIXME fix the OSX installer to install the resources.rcc binary instead of resource files and remove + // this conditional exclusion +#if !defined(Q_OS_OSX) + { +#if defined(Q_OS_ANDROID) + const QString resourcesBinaryFile = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/resources.rcc"; +#else + const QString resourcesBinaryFile = QCoreApplication::applicationDirPath() + "/resources.rcc"; +#endif + if (!QFile::exists(resourcesBinaryFile)) { + throw std::runtime_error("Unable to find primary resources"); + } + if (!QResource::registerResource(resourcesBinaryFile)) { + throw std::runtime_error("Unable to load primary resources"); + } + } +#endif + Setting::init(); // Tell the plugin manager about our statically linked plugins @@ -632,15 +693,16 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(std::bind(&Application::getUserAgent, qApp)); DependencyManager::set(); DependencyManager::set(ScriptEngine::CLIENT_SCRIPT); DependencyManager::set(); - DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(NodeType::Agent, listenPort); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -729,8 +791,10 @@ OverlayID _keyboardFocusHighlightID{ UNKNOWN_OVERLAY_ID }; // // So instead we create a new offscreen context to share with the QGLWidget, // and manually set THAT to be the shared context for the Chromium helper +#if !defined(DISABLE_QML) OffscreenGLCanvas* _chromiumShareContext { nullptr }; Q_GUI_EXPORT void qt_gl_set_global_share_context(QOpenGLContext *context); +#endif Setting::Handle sessionRunTime{ "sessionRunTime", 0 }; @@ -776,6 +840,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _sampleSound(nullptr) { + auto steamClient = PluginManager::getInstance()->getSteamClientPlugin(); setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning())); setProperty(hifi::properties::CRASHED, _previousSessionCrashed); @@ -996,8 +1061,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo DependencyManager::get().data(), &AddressManager::storeCurrentAddress); // Inititalize sample before registering - QFileInfo infSample = QFileInfo(PathUtils::resourcesPath() + "sounds/sample.wav"); - _sampleSound = DependencyManager::get()->getSound(QUrl::fromLocalFile(infSample.absoluteFilePath())); + _sampleSound = DependencyManager::get()->getSound(PathUtils::resourcesUrl("sounds/sample.wav")); auto scriptEngines = DependencyManager::get().data(); scriptEngines->registerScriptInitializer([this](ScriptEnginePointer engine){ @@ -1082,9 +1146,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Make sure we don't time out during slow operations at startup updateHeartbeat(); - // Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success. - DependencyManager::get()->initializeShapePipelines(); - // sessionRunTime will be reset soon by loadSettings. Grab it now to get previous session value. // The value will be 0 if the user blew away settings this session, which is both a feature and a bug. static const QString TESTER = "HIFI_TESTER"; @@ -1730,7 +1791,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo static int lastCountOfNearbyAvatars = -1; QTimer* checkNearbyAvatarsTimer = new QTimer(this); checkNearbyAvatarsTimer->setInterval(CHECK_NEARBY_AVATARS_INTERVAL_MS); // 10 seconds, Qt::CoarseTimer ok - connect(checkNearbyAvatarsTimer, &QTimer::timeout, this, [this]() { + connect(checkNearbyAvatarsTimer, &QTimer::timeout, this, []() { auto avatarManager = DependencyManager::get(); int nearbyAvatars = avatarManager->numberOfAvatarsInRange(avatarManager->getMyAvatar()->getWorldPosition(), NEARBY_AVATAR_RADIUS_METERS) - 1; @@ -1769,9 +1830,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo PROFILE_RANGE(render, "Process Default Skybox"); auto textureCache = DependencyManager::get(); - auto skyboxUrl = PathUtils::resourcesPath().toStdString() + "images/Default-Sky-9-cubemap.ktx"; + QFileSelector fileSelector; + fileSelector.setExtraSelectors(FileUtils::getFileSelectors()); + auto skyboxUrl = fileSelector.select(PathUtils::resourcesPath() + "images/Default-Sky-9-cubemap.ktx"); - _defaultSkyboxTexture = gpu::Texture::unserialize(skyboxUrl); + _defaultSkyboxTexture = gpu::Texture::unserialize(skyboxUrl.toStdString()); _defaultSkyboxAmbientTexture = _defaultSkyboxTexture; _defaultSkybox->setCubemap(_defaultSkyboxTexture); @@ -1782,8 +1845,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo return entityServerNode && !isPhysicsEnabled(); }); - QFileInfo infSnap = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav"); - _snapshotSound = DependencyManager::get()->getSound(QUrl::fromLocalFile(infSnap.absoluteFilePath())); + _snapshotSound = DependencyManager::get()->getSound(PathUtils::resourcesUrl("sounds/snap.wav")); QVariant testProperty = property(hifi::properties::TEST); qDebug() << testProperty; @@ -1851,6 +1913,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo DependencyManager::get()->setSetPrecisionPickingOperator([&](unsigned int rayPickID, bool value) { DependencyManager::get()->setPrecisionPicking(rayPickID, value); }); + EntityTreeRenderer::setRenderDebugHullsOperator([] { + return Menu::getInstance()->isOptionChecked(MenuOption::PhysicsShowHulls); + }); // Preload Tablet sounds DependencyManager::get()->preloadSounds(); @@ -2090,6 +2155,11 @@ void Application::cleanupBeforeQuit() { DependencyManager::destroy(); DependencyManager::destroy(); + // The PointerManager must be destroyed before the PickManager because when a Pointer is deleted, + // it accesses the PickManager to delete its associated Pick + DependencyManager::destroy(); + DependencyManager::destroy(); + qCDebug(interfaceapp) << "Application::cleanupBeforeQuit() complete"; } @@ -2173,11 +2243,16 @@ void Application::initializeGL() { } _glWidget->makeCurrent(); - _chromiumShareContext = new OffscreenGLCanvas(); - _chromiumShareContext->setObjectName("ChromiumShareContext"); - _chromiumShareContext->create(_glWidget->qglContext()); - _chromiumShareContext->makeCurrent(); - qt_gl_set_global_share_context(_chromiumShareContext->getContext()); + +#if !defined(DISABLE_QML) + if (!nsightActive()) { + _chromiumShareContext = new OffscreenGLCanvas(); + _chromiumShareContext->setObjectName("ChromiumShareContext"); + _chromiumShareContext->create(_glWidget->qglContext()); + _chromiumShareContext->makeCurrent(); + qt_gl_set_global_share_context(_chromiumShareContext->getContext()); + } +#endif _glWidget->makeCurrent(); gpu::Context::init(); @@ -2194,13 +2269,17 @@ void Application::initializeGL() { // Set up the render engine render::CullFunctor cullFunctor = LODManager::shouldRender; static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD"; - bool isDeferred = !QProcessEnvironment::systemEnvironment().contains(RENDER_FORWARD); _renderEngine->addJob("UpdateScene"); - _renderEngine->addJob("SecondaryCameraJob", cullFunctor); - _renderEngine->addJob("RenderMainView", cullFunctor, isDeferred); +#ifndef Q_OS_ANDROID + _renderEngine->addJob("SecondaryCameraJob", cullFunctor, !DISABLE_DEFERRED); +#endif + _renderEngine->addJob("RenderMainView", cullFunctor, !DISABLE_DEFERRED); _renderEngine->load(); _renderEngine->registerScene(_main3DScene); + // Now that OpenGL is initialized, we are sure we have a valid context and can create the various pipeline shaders with success. + DependencyManager::get()->initializeShapePipelines(); + _offscreenContext = new OffscreenGLCanvas(); _offscreenContext->setObjectName("MainThreadContext"); _offscreenContext->create(_glWidget->qglContext()); @@ -2284,7 +2363,7 @@ void Application::initializeUi() { QUrl{ "hifi/commerce/wallet/SecurityImageChange.qml" }, QUrl{ "hifi/commerce/wallet/SecurityImageModel.qml" }, QUrl{ "hifi/commerce/wallet/SecurityImageSelection.qml" }, - QUrl{ "hifi/commerce/wallet/SendMoney.qml" }, + QUrl{ "hifi/commerce/wallet/sendMoney/SendMoney.qml" }, QUrl{ "hifi/commerce/wallet/Wallet.qml" }, QUrl{ "hifi/commerce/wallet/WalletHome.qml" }, QUrl{ "hifi/commerce/wallet/WalletSetup.qml" }, @@ -2305,8 +2384,7 @@ void Application::initializeUi() { offscreenUi->setProxyWindow(_window->windowHandle()); // OffscreenUi is a subclass of OffscreenQmlSurface specifically designed to // support the window management and scripting proxies for VR use - offscreenUi->createDesktop(QString("hifi/Desktop.qml")); - + offscreenUi->createDesktop(PathUtils::qmlUrl("hifi/Desktop.qml")); // FIXME either expose so that dialogs can set this themselves or // do better detection in the offscreen UI of what has focus offscreenUi->setNavigationFocused(false); @@ -2373,9 +2451,11 @@ void Application::initializeUi() { surfaceContext->setContextProperty("SoundCache", DependencyManager::get().data()); surfaceContext->setContextProperty("InputConfiguration", DependencyManager::get().data()); - surfaceContext->setContextProperty("Account", GlobalServicesScriptingInterface::getInstance()); + surfaceContext->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED + surfaceContext->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED + surfaceContext->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance()); + surfaceContext->setContextProperty("DialogsManager", _dialogsManagerScriptingInterface); - surfaceContext->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance()); surfaceContext->setContextProperty("FaceTracker", DependencyManager::get().data()); surfaceContext->setContextProperty("AvatarManager", DependencyManager::get().data()); surfaceContext->setContextProperty("UndoStack", &_undoStackScriptingInterface); @@ -2441,7 +2521,7 @@ void Application::initializeUi() { offscreenSurfaceCache->reserve(Web3DOverlay::QML, 2); } -void Application::updateCamera(RenderArgs& renderArgs) { +void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { PROFILE_RANGE(render, __FUNCTION__); PerformanceTimer perfTimer("updateCamera"); @@ -2456,6 +2536,7 @@ void Application::updateCamera(RenderArgs& renderArgs) { // Using the latter will cause the camera to wobble with idle animations, // or with changes from the face tracker if (_myCamera.getMode() == CAMERA_MODE_FIRST_PERSON) { + _thirdPersonHMDCameraBoomValid= false; if (isHMDMode()) { mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); _myCamera.setPosition(extractTranslation(camMat)); @@ -2468,12 +2549,25 @@ void Application::updateCamera(RenderArgs& renderArgs) { } else if (_myCamera.getMode() == CAMERA_MODE_THIRD_PERSON) { if (isHMDMode()) { - auto hmdWorldMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); - _myCamera.setOrientation(glm::normalize(glmExtractRotation(hmdWorldMat))); - _myCamera.setPosition(extractTranslation(hmdWorldMat) + - myAvatar->getWorldOrientation() * boomOffset); + + if (!_thirdPersonHMDCameraBoomValid) { + const glm::vec3 CAMERA_OFFSET = glm::vec3(0.0f, 0.0f, 0.7f); + _thirdPersonHMDCameraBoom = cancelOutRollAndPitch(myAvatar->getHMDSensorOrientation()) * CAMERA_OFFSET; + _thirdPersonHMDCameraBoomValid = true; + } + + glm::mat4 thirdPersonCameraSensorToWorldMatrix = myAvatar->getSensorToWorldMatrix(); + + const glm::vec3 cameraPos = myAvatar->getHMDSensorPosition() + _thirdPersonHMDCameraBoom * myAvatar->getBoomLength(); + glm::mat4 sensorCameraMat = createMatFromQuatAndPos(myAvatar->getHMDSensorOrientation(), cameraPos); + glm::mat4 worldCameraMat = thirdPersonCameraSensorToWorldMatrix * sensorCameraMat; + + _myCamera.setOrientation(glm::normalize(glmExtractRotation(worldCameraMat))); + _myCamera.setPosition(extractTranslation(worldCameraMat)); } else { + _thirdPersonHMDCameraBoomValid = false; + _myCamera.setOrientation(myAvatar->getHead()->getOrientation()); if (Menu::getInstance()->isOptionChecked(MenuOption::CenterPlayerInView)) { _myCamera.setPosition(myAvatar->getDefaultEyePosition() @@ -2486,6 +2580,7 @@ void Application::updateCamera(RenderArgs& renderArgs) { } } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { + _thirdPersonHMDCameraBoomValid= false; if (isHMDMode()) { auto mirrorBodyOrientation = myAvatar->getWorldOrientation() * glm::quat(glm::vec3(0.0f, PI + _rotateMirror, 0.0f)); @@ -2520,6 +2615,7 @@ void Application::updateCamera(RenderArgs& renderArgs) { renderArgs._renderMode = RenderArgs::MIRROR_RENDER_MODE; } else if (_myCamera.getMode() == CAMERA_MODE_ENTITY) { + _thirdPersonHMDCameraBoomValid= false; EntityItemPointer cameraEntity = _myCamera.getCameraEntityPointer(); if (cameraEntity != nullptr) { if (isHMDMode()) { @@ -2627,7 +2723,8 @@ void Application::showHelp() { queryString.addQueryItem("defaultTab", defaultTab); auto tabletScriptingInterface = DependencyManager::get(); TabletProxy* tablet = dynamic_cast(tabletScriptingInterface->getTablet(SYSTEM_TABLET)); - tablet->gotoWebScreen(INFO_HELP_PATH + "?" + queryString.toString()); + tablet->gotoWebScreen(PathUtils::resourcesPath() + INFO_HELP_PATH + "?" + queryString.toString()); + DependencyManager::get()->openTablet(); //InfoView::show(INFO_HELP_PATH, false, queryString.toString()); } @@ -3883,12 +3980,18 @@ void Application::idle() { PROFILE_COUNTER_IF_CHANGED(app, "pendingProcessing", int, DependencyManager::get()->getStat("PendingProcessing").toInt()); auto renderConfig = _renderEngine->getConfiguration(); PROFILE_COUNTER_IF_CHANGED(render, "gpuTime", float, (float)_gpuContext->getFrameTimerGPUAverage()); + auto opaqueRangeTimer = renderConfig->getConfig("OpaqueRangeTimer"); + auto linearDepth = renderConfig->getConfig("LinearDepth"); + auto surfaceGeometry = renderConfig->getConfig("SurfaceGeometry"); + auto renderDeferred = renderConfig->getConfig("RenderDeferred"); + auto toneAndPostRangeTimer = renderConfig->getConfig("ToneAndPostRangeTimer"); + PROFILE_COUNTER(render_detail, "gpuTimes", { - { "OpaqueRangeTimer", renderConfig->getConfig("OpaqueRangeTimer")->property("gpuRunTime") }, - { "LinearDepth", renderConfig->getConfig("LinearDepth")->property("gpuRunTime") }, - { "SurfaceGeometry", renderConfig->getConfig("SurfaceGeometry")->property("gpuRunTime") }, - { "RenderDeferred", renderConfig->getConfig("RenderDeferred")->property("gpuRunTime") }, - { "ToneAndPostRangeTimer", renderConfig->getConfig("ToneAndPostRangeTimer")->property("gpuRunTime") } + { "OpaqueRangeTimer", opaqueRangeTimer ? opaqueRangeTimer->property("gpuRunTime") : 0 }, + { "LinearDepth", linearDepth ? linearDepth->property("gpuRunTime") : 0 }, + { "SurfaceGeometry", surfaceGeometry ? surfaceGeometry->property("gpuRunTime") : 0 }, + { "RenderDeferred", renderDeferred ? renderDeferred->property("gpuRunTime") : 0 }, + { "ToneAndPostRangeTimer", toneAndPostRangeTimer ? toneAndPostRangeTimer->property("gpuRunTime") : 0 } }); PROFILE_RANGE(app, __FUNCTION__); @@ -4253,9 +4356,9 @@ void Application::init() { // Make sure Login state is up to date DependencyManager::get()->toggleLoginDialog(); - - DependencyManager::get()->init(); - + if (!DISABLE_DEFERRED) { + DependencyManager::get()->init(); + } DependencyManager::get()->init(); _timerStart.start(); @@ -4330,8 +4433,9 @@ void Application::updateLOD(float deltaTime) const { float presentTime = getActiveDisplayPlugin()->getAveragePresentTime(); float engineRunTime = (float)(_renderEngine->getConfiguration().get()->getCPURunTime()); float gpuTime = getGPUContext()->getFrameTimerGPUAverage(); - float maxRenderTime = glm::max(gpuTime, glm::max(presentTime, engineRunTime)); - DependencyManager::get()->autoAdjustLOD(maxRenderTime, deltaTime); + auto lodManager = DependencyManager::get(); + lodManager->setRenderTimes(presentTime, engineRunTime, gpuTime); + lodManager->autoAdjustLOD(deltaTime); } else { DependencyManager::get()->resetLODAdjust(); } @@ -5004,8 +5108,6 @@ void Application::update(float deltaTime) { } } - PerformanceTimer perfTimer("misc"); - bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "Application::update()"); @@ -5014,11 +5116,13 @@ void Application::update(float deltaTime) { // TODO: break these out into distinct perfTimers when they prove interesting { PROFILE_RANGE(app, "PickManager"); + PerformanceTimer perfTimer("pickManager"); DependencyManager::get()->update(); } { PROFILE_RANGE(app, "PointerManager"); + PerformanceTimer perfTimer("pointerManager"); DependencyManager::get()->update(); } @@ -5044,8 +5148,8 @@ void Application::update(float deltaTime) { // Update my voxel servers with my current voxel query... { PROFILE_RANGE_EX(app, "QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount()); - QMutexLocker viewLocker(&_viewMutex); PerformanceTimer perfTimer("queryOctree"); + QMutexLocker viewLocker(&_viewMutex); quint64 sinceLastQuery = now - _lastQueriedTime; const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND; bool queryIsDue = sinceLastQuery > TOO_LONG_SINCE_LAST_QUERY; @@ -5081,12 +5185,14 @@ void Application::update(float deltaTime) { } } - avatarManager->postUpdate(deltaTime, getMain3DScene()); - + { + PerformanceTimer perfTimer("avatarManager/postUpdate"); + avatarManager->postUpdate(deltaTime, getMain3DScene()); + } { - PROFILE_RANGE_EX(app, "PreRenderLambdas", 0xffff0000, (uint64_t)0); - + PROFILE_RANGE_EX(app, "PostUpdateLambdas", 0xffff0000, (uint64_t)0); + PerformanceTimer perfTimer("postUpdateLambdas"); std::unique_lock guard(_postUpdateLambdasLock); for (auto& iter : _postUpdateLambdas) { iter.second(); @@ -5094,7 +5200,9 @@ void Application::update(float deltaTime) { _postUpdateLambdas.clear(); } - editRenderArgs([this](AppRenderArgs& appRenderArgs) { + + editRenderArgs([this, deltaTime](AppRenderArgs& appRenderArgs) { + PerformanceTimer perfTimer("editRenderArgs"); appRenderArgs._headPose= getHMDSensorPose(); auto myAvatar = getMyAvatar(); @@ -5140,7 +5248,7 @@ void Application::update(float deltaTime) { resizeGL(); } - updateCamera(appRenderArgs._renderArgs); + this->updateCamera(appRenderArgs._renderArgs, deltaTime); appRenderArgs._eyeToWorld = _myCamera.getTransform(); appRenderArgs._isStereo = false; @@ -5208,12 +5316,20 @@ void Application::update(float deltaTime) { } }); - AnimDebugDraw::getInstance().update(); + { + PerformanceTimer perfTimer("limitless"); + AnimDebugDraw::getInstance().update(); + } - DependencyManager::get()->update(); + { + PerformanceTimer perfTimer("limitless"); + DependencyManager::get()->update(); + } - // Game loop is done, mark the end of the frame for the scene transactions and the render loop to take over - getMain3DScene()->enqueueFrame(); + { // Game loop is done, mark the end of the frame for the scene transactions and the render loop to take over + PerformanceTimer perfTimer("enqueueFrame"); + getMain3DScene()->enqueueFrame(); + } } void Application::sendAvatarViewFrustum() { @@ -5434,7 +5550,7 @@ void Application::clearDomainOctreeDetails() { auto skyStage = DependencyManager::get()->getSkyStage(); - skyStage->setBackgroundMode(model::SunSkyStage::SKY_DEFAULT); + skyStage->setBackgroundMode(graphics::SunSkyStage::SKY_DEFAULT); DependencyManager::get()->clearUnusedResources(); DependencyManager::get()->clearUnusedResources(); @@ -5721,10 +5837,13 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe scriptEngine->registerGetterSetter("location", LocationScriptingInterface::locationGetter, LocationScriptingInterface::locationSetter); +#if !defined(Q_OS_ANDROID) scriptEngine->registerFunction("OverlayWebWindow", QmlWebWindowClass::constructor); +#endif scriptEngine->registerFunction("OverlayWindow", QmlWindowClass::constructor); scriptEngine->registerGlobalObject("Menu", MenuScriptingInterface::getInstance()); + scriptEngine->registerGlobalObject("DesktopPreviewProvider", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Stats", Stats::getInstance()); scriptEngine->registerGlobalObject("Settings", SettingsScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("Snapshot", DependencyManager::get().data()); @@ -5744,10 +5863,11 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe scriptEngine->registerGlobalObject("ModelCache", DependencyManager::get().data()); scriptEngine->registerGlobalObject("SoundCache", DependencyManager::get().data()); - scriptEngine->registerGlobalObject("Account", GlobalServicesScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("DialogsManager", _dialogsManagerScriptingInterface); - scriptEngine->registerGlobalObject("GlobalServices", GlobalServicesScriptingInterface::getInstance()); + scriptEngine->registerGlobalObject("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED + scriptEngine->registerGlobalObject("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED + scriptEngine->registerGlobalObject("AccountServices", AccountServicesScriptingInterface::getInstance()); qScriptRegisterMetaType(scriptEngine.data(), DownloadInfoResultToScriptValue, DownloadInfoResultFromScriptValue); scriptEngine->registerGlobalObject("FaceTracker", DependencyManager::get().data()); @@ -6147,7 +6267,7 @@ void Application::showAssetServerWidget(QString filePath) { if (!hmd->getShouldShowTablet() && !isHMDMode()) { DependencyManager::get()->show(url, "AssetServer", startUpload); } else { - static const QUrl url("hifi/dialogs/TabletAssetServer.qml"); + static const QUrl url("../dialogs/TabletAssetServer.qml"); tablet->pushOntoStack(url); } } @@ -6776,6 +6896,15 @@ void Application::loadAddAvatarBookmarkDialog() const { avatarBookmarks->addBookmark(); } +void Application::loadAvatarBrowser() const { + auto tablet = dynamic_cast(DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system")); + // construct the url to the marketplace item + QString url = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace?category=avatars"; + QString MARKETPLACES_INJECT_SCRIPT_PATH = "file:///" + qApp->applicationDirPath() + "/scripts/system/html/js/marketplacesInject.js"; + tablet->gotoWebScreen(url, MARKETPLACES_INJECT_SCRIPT_PATH); + DependencyManager::get()->openTablet(); +} + void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) { postLambdaEvent([notify, includeAnimated, aspectRatio, this] { // Get a screenshot and save it @@ -6793,7 +6922,8 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa void Application::takeSecondaryCameraSnapshot() { postLambdaEvent([this] { - Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot()); + QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot()); + emit DependencyManager::get()->stillSnapshotTaken(snapshotPath, true); }); } @@ -7357,11 +7487,13 @@ void Application::updateThreadPoolCount() const { } void Application::updateSystemTabletMode() { - qApp->setProperty(hifi::properties::HMD, isHMDMode()); - if (isHMDMode()) { - DependencyManager::get()->setToolbarMode(getHmdTabletBecomesToolbarSetting()); - } else { - DependencyManager::get()->setToolbarMode(getDesktopTabletBecomesToolbarSetting()); + if (_settingsLoaded) { + qApp->setProperty(hifi::properties::HMD, isHMDMode()); + if (isHMDMode()) { + DependencyManager::get()->setToolbarMode(getHmdTabletBecomesToolbarSetting()); + } else { + DependencyManager::get()->setToolbarMode(getDesktopTabletBecomesToolbarSetting()); + } } } diff --git a/interface/src/Application.h b/interface/src/Application.h index 479ce919a3..ddb8ce11e5 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -71,7 +71,7 @@ #include "UndoStackScriptingInterface.h" #include -#include +#include #include #include "FrameTimingsScriptingInterface.h" @@ -146,7 +146,7 @@ public: void initializeGL(); void initializeUi(); - void updateCamera(RenderArgs& renderArgs); + void updateCamera(RenderArgs& renderArgs, float deltaTime); void paintGL(); void resizeGL(); @@ -270,7 +270,7 @@ public: void takeSecondaryCameraSnapshot(); void shareSnapshot(const QString& filename, const QUrl& href = QUrl("")); - model::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; } + graphics::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; } gpu::TexturePointer getDefaultSkyboxTexture() const { return _defaultSkyboxTexture; } gpu::TexturePointer getDefaultSkyboxAmbientTexture() const { return _defaultSkyboxAmbientTexture; } @@ -309,6 +309,7 @@ public slots: void toggleEntityScriptServerLogDialog(); Q_INVOKABLE void showAssetServerWidget(QString filePath = ""); Q_INVOKABLE void loadAddAvatarBookmarkDialog() const; + Q_INVOKABLE void loadAvatarBrowser() const; Q_INVOKABLE SharedSoundPointer getSampleSound() const; void showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const QString& name) const; @@ -666,7 +667,7 @@ private: ConnectionMonitor _connectionMonitor; - model::SkyboxPointer _defaultSkybox { new ProceduralSkybox() } ; + graphics::SkyboxPointer _defaultSkybox { new ProceduralSkybox() } ; gpu::TexturePointer _defaultSkyboxTexture; gpu::TexturePointer _defaultSkyboxAmbientTexture; @@ -695,6 +696,9 @@ private: void startHMDStandBySession(); void endHMDSession(); + glm::vec3 _thirdPersonHMDCameraBoom { 0.0f, 0.0f, -1.0f }; + bool _thirdPersonHMDCameraBoomValid { true }; + QUrl _avatarOverrideUrl; bool _saveAvatarOverrideUrl { false }; QObject* _renderEventHandler{ nullptr }; diff --git a/interface/src/LODManager.cpp b/interface/src/LODManager.cpp index 9e6fabd439..73547cdb5e 100644 --- a/interface/src/LODManager.cpp +++ b/interface/src/LODManager.cpp @@ -26,43 +26,50 @@ Setting::Handle hmdLODDecreaseFPS("hmdLODDecreaseFPS", DEFAULT_HMD_LOD_DO LODManager::LODManager() { } -float LODManager::getLODDecreaseFPS() { +float LODManager::getLODDecreaseFPS() const { if (qApp->isHMDMode()) { return getHMDLODDecreaseFPS(); } return getDesktopLODDecreaseFPS(); } -float LODManager::getLODIncreaseFPS() { +float LODManager::getLODIncreaseFPS() const { if (qApp->isHMDMode()) { return getHMDLODIncreaseFPS(); } return getDesktopLODIncreaseFPS(); } -// We use a "time-weighted running average" of the renderTime and compare it against min/max thresholds +// We use a "time-weighted running average" of the maxRenderTime and compare it against min/max thresholds // to determine if we should adjust the level of detail (LOD). // // A time-weighted running average has a timescale which determines how fast the average tracks the measured // value in real-time. Given a step-function in the mesured value, and assuming measurements happen // faster than the runningAverage is computed, the error between the value and its runningAverage will be // reduced by 1/e every timescale of real-time that passes. -const float LOD_ADJUST_RUNNING_AVG_TIMESCALE = 0.1f; // sec +const float LOD_ADJUST_RUNNING_AVG_TIMESCALE = 0.08f; // sec // // Assuming the measured value is affected by logic invoked by the runningAverage bumping up against its -// thresholds, we expect the adjustment to introduce a step-function. We want the runningAverage settle +// thresholds, we expect the adjustment to introduce a step-function. We want the runningAverage to settle // to the new value BEFORE we test it aginst its thresholds again. Hence we test on a period that is a few // multiples of the running average timescale: -const uint64_t LOD_AUTO_ADJUST_PERIOD = 5 * (uint64_t)(LOD_ADJUST_RUNNING_AVG_TIMESCALE * (float)USECS_PER_MSEC); // usec +const uint64_t LOD_AUTO_ADJUST_PERIOD = 4 * (uint64_t)(LOD_ADJUST_RUNNING_AVG_TIMESCALE * (float)USECS_PER_MSEC); // usec const float LOD_AUTO_ADJUST_DECREMENT_FACTOR = 0.8f; const float LOD_AUTO_ADJUST_INCREMENT_FACTOR = 1.2f; -void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) { - // compute time-weighted running average renderTime +void LODManager::setRenderTimes(float presentTime, float engineRunTime, float gpuTime) { + _presentTime = presentTime; + _engineRunTime = engineRunTime; + _gpuTime = gpuTime; +} + +void LODManager::autoAdjustLOD(float realTimeDelta) { + float maxRenderTime = glm::max(glm::max(_presentTime, _engineRunTime), _gpuTime); + // compute time-weighted running average maxRenderTime // Note: we MUST clamp the blend to 1.0 for stability float blend = (realTimeDelta < LOD_ADJUST_RUNNING_AVG_TIMESCALE) ? realTimeDelta / LOD_ADJUST_RUNNING_AVG_TIMESCALE : 1.0f; - _avgRenderTime = (1.0f - blend) * _avgRenderTime + blend * renderTime; // msec + _avgRenderTime = (1.0f - blend) * _avgRenderTime + blend * maxRenderTime; // msec if (!_automaticLODAdjust) { // early exit return; @@ -84,6 +91,10 @@ void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) { << "targetFPS =" << getLODDecreaseFPS() << "octreeSizeScale =" << _octreeSizeScale; emit LODDecreased(); + // Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime + // to provide an FPS just above the decrease threshold. It will drift close to its + // true value after a few LOD_ADJUST_TIMESCALEs and we'll adjust again as necessary. + _avgRenderTime = (float)MSECS_PER_SECOND / (getLODDecreaseFPS() + 1.0f); } _decreaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD; } @@ -105,6 +116,10 @@ void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) { << "targetFPS =" << getLODDecreaseFPS() << "octreeSizeScale =" << _octreeSizeScale; emit LODIncreased(); + // Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime + // to provide an FPS just below the increase threshold. It will drift close to its + // true value after a few LOD_ADJUST_TIMESCALEs and we'll adjust again as necessary. + _avgRenderTime = (float)MSECS_PER_SECOND / (getLODIncreaseFPS() - 1.0f); } _increaseFPSExpiry = now + LOD_AUTO_ADJUST_PERIOD; } @@ -119,11 +134,6 @@ void LODManager::autoAdjustLOD(float renderTime, float realTimeDelta) { if (lodToolsDialog) { lodToolsDialog->reloadSliders(); } - // Assuming the LOD adjustment will work: we optimistically reset _avgRenderTime - // to be at middle of target zone. It will drift close to its true value within - // about three few LOD_ADJUST_TIMESCALEs and we'll adjust again as necessary. - float expectedFPS = 0.5f * (getLODIncreaseFPS() + getLODDecreaseFPS()); - _avgRenderTime = MSECS_PER_SECOND / expectedFPS; } } @@ -131,6 +141,18 @@ void LODManager::resetLODAdjust() { _decreaseFPSExpiry = _increaseFPSExpiry = usecTimestampNow() + LOD_AUTO_ADJUST_PERIOD; } +float LODManager::getLODLevel() const { + // simpleLOD is a linearized and normalized number that represents how much LOD is being applied. + // It ranges from: + // 1.0 = normal (max) level of detail + // 0.0 = min level of detail + // In other words: as LOD "drops" the value of simpleLOD will also "drop", and it cannot go lower than 0.0. + const float LOG_MIN_LOD_RATIO = logf(ADJUST_LOD_MIN_SIZE_SCALE / ADJUST_LOD_MAX_SIZE_SCALE); + float power = logf(_octreeSizeScale / ADJUST_LOD_MAX_SIZE_SCALE); + float simpleLOD = (LOG_MIN_LOD_RATIO - power) / LOG_MIN_LOD_RATIO; + return simpleLOD; +} + const float MIN_DECREASE_FPS = 0.5f; void LODManager::setDesktopLODDecreaseFPS(float fps) { diff --git a/interface/src/LODManager.h b/interface/src/LODManager.h index cf38342db0..ca6be9380b 100644 --- a/interface/src/LODManager.h +++ b/interface/src/LODManager.h @@ -37,7 +37,7 @@ class AABox; class LODManager : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY - + public: Q_INVOKABLE void setAutomaticLODAdjust(bool value) { _automaticLODAdjust = value; } Q_INVOKABLE bool getAutomaticLODAdjust() const { return _automaticLODAdjust; } @@ -49,34 +49,56 @@ public: Q_INVOKABLE void setHMDLODDecreaseFPS(float value); Q_INVOKABLE float getHMDLODDecreaseFPS() const; Q_INVOKABLE float getHMDLODIncreaseFPS() const; - + // User Tweakable LOD Items Q_INVOKABLE QString getLODFeedbackText(); Q_INVOKABLE void setOctreeSizeScale(float sizeScale); Q_INVOKABLE float getOctreeSizeScale() const { return _octreeSizeScale; } - + Q_INVOKABLE void setBoundaryLevelAdjust(int boundaryLevelAdjust); Q_INVOKABLE int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; } - - Q_INVOKABLE float getLODDecreaseFPS(); - Q_INVOKABLE float getLODIncreaseFPS(); - + + Q_INVOKABLE float getLODDecreaseFPS() const; + Q_INVOKABLE float getLODIncreaseFPS() const; + + Q_PROPERTY(float presentTime READ getPresentTime) + Q_PROPERTY(float engineRunTime READ getEngineRunTime) + Q_PROPERTY(float gpuTime READ getGPUTime) + Q_PROPERTY(float avgRenderTime READ getAverageRenderTime) + Q_PROPERTY(float fps READ getMaxTheoreticalFPS) + Q_PROPERTY(float lodLevel READ getLODLevel) + + Q_PROPERTY(float lodDecreaseFPS READ getLODDecreaseFPS) + Q_PROPERTY(float lodIncreaseFPS READ getLODIncreaseFPS) + + float getPresentTime() const { return _presentTime; } + float getEngineRunTime() const { return _engineRunTime; } + float getGPUTime() const { return _gpuTime; } + static bool shouldRender(const RenderArgs* args, const AABox& bounds); - void autoAdjustLOD(float renderTime, float realTimeDelta); - + void setRenderTimes(float presentTime, float engineRunTime, float gpuTime); + void autoAdjustLOD(float realTimeDelta); + void loadSettings(); void saveSettings(); void resetLODAdjust(); - + + float getAverageRenderTime() const { return _avgRenderTime; }; + float getMaxTheoreticalFPS() const { return (float)MSECS_PER_SECOND / _avgRenderTime; }; + float getLODLevel() const; + signals: void LODIncreased(); void LODDecreased(); - + private: LODManager(); - + bool _automaticLODAdjust = true; - float _avgRenderTime { 0.0f }; + float _presentTime { 0.0f }; // msec + float _engineRunTime { 0.0f }; // msec + float _gpuTime { 0.0f }; // msec + float _avgRenderTime { 0.0f }; // msec float _desktopMaxRenderTime { DEFAULT_DESKTOP_MAX_RENDER_TIME }; float _hmdMaxRenderTime { DEFAULT_HMD_MAX_RENDER_TIME }; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index d295e96867..2a33ee6f7b 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -574,8 +574,6 @@ Menu::Menu() { avatar.get(), SLOT(setEnableMeshVisible(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::DisableEyelidAdjustment, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::TurnWithHead, 0, false); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::UseAnimPreAndPostRotations, 0, true, - avatar.get(), SLOT(setUseAnimPreAndPostRotations(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::EnableInverseKinematics, 0, true, avatar.get(), SLOT(setEnableInverseKinematics(bool))); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderSensorToWorldMatrix, 0, false, @@ -698,7 +696,7 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowOwned, 0, false, drawStatusConfig, SLOT(setShowNetwork(bool))); } - addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowHulls); + addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowHulls, 0, false, qApp->getEntities().data(), SIGNAL(setRenderDebugHulls())); // Developer > Ask to Reset Settings addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::AskToResetSettings, 0, false); @@ -758,6 +756,14 @@ Menu::Menu() { // Developer > Stats addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::Stats); + // Developer > API Debugger + action = addActionToQMenuAndActionHash(developerMenu, "API Debugger"); + connect(action, &QAction::triggered, [] { + auto scriptEngines = DependencyManager::get(); + QUrl defaultScriptsLoc = PathUtils::defaultScriptsLocation(); + defaultScriptsLoc.setPath(defaultScriptsLoc.path() + "developer/utilities/tools/currentAPI.js"); + scriptEngines->loadScript(defaultScriptsLoc.toString()); + }); #if 0 /// -------------- REMOVED FOR NOW -------------- addDisabledActionAndSeparator(navigateMenu, "History"); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 854f8d8c2b..11a27ff7f5 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -194,7 +194,6 @@ namespace MenuOption { const QString TurnWithHead = "Turn using Head"; const QString UseAudioForMouth = "Use Audio for Mouth"; const QString UseCamera = "Use Camera"; - const QString UseAnimPreAndPostRotations = "Use Anim Pre and Post Rotations"; const QString VelocityFilter = "Velocity Filter"; const QString VisibleToEveryone = "Everyone"; const QString VisibleToFriends = "Friends"; diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp index a4fd89e576..9b4bae493d 100644 --- a/interface/src/SecondaryCamera.cpp +++ b/interface/src/SecondaryCamera.cpp @@ -203,10 +203,14 @@ public: } }; -void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor) { +void SecondaryCameraRenderTask::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred) { const auto cachedArg = task.addJob("SecondaryCamera"); const auto items = task.addJob("FetchCullSort", cullFunctor); assert(items.canCast()); - task.addJob("RenderDeferredTask", items); + if (!isDeferred) { + task.addJob("Forward", items); + } else { + task.addJob("RenderDeferredTask", items); + } task.addJob("EndSecondaryCamera", cachedArg); } \ No newline at end of file diff --git a/interface/src/SecondaryCamera.h b/interface/src/SecondaryCamera.h index 38d75c391e..026b72d865 100644 --- a/interface/src/SecondaryCamera.h +++ b/interface/src/SecondaryCamera.h @@ -71,7 +71,7 @@ public: using JobModel = render::Task::Model; SecondaryCameraRenderTask() {} void configure(const Config& config) {} - void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor); + void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs, render::CullFunctor cullFunctor, bool isDeferred = true); }; #endif diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 68e417ba1d..2e633986a0 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -537,6 +537,7 @@ void MyAvatar::simulate(float deltaTime) { // we've achived our final adjusted position and rotation for the avatar // and all of its joints, now update our attachements. Avatar::simulateAttachments(deltaTime); + relayJointDataToChildren(); if (!_skeletonModel->hasSkeleton()) { // All the simulation that can be done has been done @@ -1061,11 +1062,6 @@ void MyAvatar::setEnableMeshVisible(bool isEnabled) { _skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene()); } -void MyAvatar::setUseAnimPreAndPostRotations(bool isEnabled) { - AnimClip::usePreAndPostPoseFromAnim = isEnabled; - reset(true); -} - void MyAvatar::setEnableInverseKinematics(bool isEnabled) { _skeletonModel->getRig().setEnableInverseKinematics(isEnabled); } @@ -1801,7 +1797,7 @@ void MyAvatar::initAnimGraph() { } else if (!_fstAnimGraphOverrideUrl.isEmpty()) { graphUrl = _fstAnimGraphOverrideUrl; } else { - graphUrl = QUrl::fromLocalFile(PathUtils::resourcesPath() + "avatar/avatar-animation.json"); + graphUrl = PathUtils::resourcesUrl("avatar/avatar-animation.json"); } _skeletonModel->getRig().initAnimGraph(graphUrl); @@ -1929,7 +1925,7 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) { _prevShouldDrawHead = shouldDrawHead; } -const float RENDER_HEAD_CUTOFF_DISTANCE = 0.3f; +const float RENDER_HEAD_CUTOFF_DISTANCE = 0.47f; bool MyAvatar::cameraInsideHead(const glm::vec3& cameraPosition) const { return glm::length(cameraPosition - getHeadPosition()) < (RENDER_HEAD_CUTOFF_DISTANCE * getModelScale()); @@ -2023,8 +2019,7 @@ void MyAvatar::updateOrientation(float deltaTime) { _smoothOrientationTimer = 0.0f; } - getHead()->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime); - + Head* head = getHead(); auto headPose = getControllerPoseInAvatarFrame(controller::Action::HEAD); if (headPose.isValid()) { glm::quat localOrientation = headPose.rotation * Quaternions::Y_180; @@ -2036,6 +2031,10 @@ void MyAvatar::updateOrientation(float deltaTime) { head->setBaseYaw(YAW(euler)); head->setBasePitch(PITCH(euler)); head->setBaseRoll(ROLL(euler)); + } else { + head->setBaseYaw(0.0f); + head->setBasePitch(getHead()->getBasePitch() + getDriveKey(PITCH) * _pitchSpeed * deltaTime); + head->setBaseRoll(0.0f); } } @@ -2100,7 +2099,7 @@ void MyAvatar::updateActionMotor(float deltaTime) { _actionMotorVelocity = motorSpeed * direction; } else { // we're interacting with a floor --> simple horizontal speed and exponential decay - _actionMotorVelocity = getSensorToWorldScale() * DEFAULT_AVATAR_MAX_WALKING_SPEED * direction; + _actionMotorVelocity = getSensorToWorldScale() * _walkSpeed.get() * direction; } float boomChange = getDriveKey(ZOOM); @@ -2692,6 +2691,14 @@ float MyAvatar::getUserEyeHeight() const { return userHeight - userHeight * ratio; } +float MyAvatar::getWalkSpeed() const { + return _walkSpeed.get(); +} + +void MyAvatar::setWalkSpeed(float value) { + _walkSpeed.set(value); +} + glm::vec3 MyAvatar::getPositionForAudio() { switch (_audioListenerMode) { case AudioListenerMode::FROM_HEAD: @@ -2799,14 +2806,9 @@ void MyAvatar::FollowHelper::decrementTimeRemaining(float dt) { } bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { - auto cameraMode = qApp->getCamera().getMode(); - if (cameraMode == CAMERA_MODE_THIRD_PERSON) { - return false; - } else { - const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees - glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix); - return glm::dot(-myAvatar.getHeadControllerFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD; - } + const float FOLLOW_ROTATION_THRESHOLD = cosf(PI / 6.0f); // 30 degrees + glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix); + return glm::dot(-myAvatar.getHeadControllerFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD; } bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index ab74460d4e..58b49b61ff 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -163,6 +163,8 @@ class MyAvatar : public Avatar { Q_PROPERTY(QUuid SELF_ID READ getSelfID CONSTANT) + Q_PROPERTY(float walkSpeed READ getWalkSpeed WRITE setWalkSpeed); + const QString DOMINANT_LEFT_HAND = "left"; const QString DOMINANT_RIGHT_HAND = "right"; @@ -557,6 +559,9 @@ public: const QUuid& getSelfID() const { return AVATAR_SELF_ID; } + void setWalkSpeed(float value); + float getWalkSpeed() const; + public slots: void increaseSize(); void decreaseSize(); @@ -594,7 +599,6 @@ public slots: bool getEnableMeshVisible() const { return _skeletonModel->isVisible(); } void setEnableMeshVisible(bool isEnabled); - void setUseAnimPreAndPostRotations(bool isEnabled); void setEnableInverseKinematics(bool isEnabled); QUrl getAnimGraphOverrideUrl() const; // thread-safe @@ -841,6 +845,9 @@ private: // height of user in sensor space, when standing erect. ThreadSafeValueCache _userHeight { DEFAULT_AVATAR_HEIGHT }; + + // max unscaled forward movement speed + ThreadSafeValueCache _walkSpeed { DEFAULT_AVATAR_MAX_WALKING_SPEED }; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 8d07a878b9..9280899a1e 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -34,7 +34,6 @@ Rig::CharacterControllerState convertCharacterControllerState(CharacterControlle } static AnimPose computeHipsInSensorFrame(MyAvatar* myAvatar, bool isFlying) { - glm::mat4 worldToSensorMat = glm::inverse(myAvatar->getSensorToWorldMatrix()); // check for pinned hips. @@ -106,7 +105,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { if (avatarHeadPose.isValid()) { AnimPose pose(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = avatarToRigPose * pose; - params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Head] = true; + params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = (uint8_t)Rig::ControllerFlags::Enabled; } else { // even though full head IK is disabled, the rig still needs the head orientation to rotate the head up and // down in desktop mode. @@ -114,7 +113,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // postMult 180 is necessary to convert head from -z forward to z forward. glm::quat headRot = Quaternions::Y_180 * head->getFinalOrientationInLocalFrame() * Quaternions::Y_180; params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = AnimPose(glm::vec3(1.0f), headRot, glm::vec3(0.0f)); - params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Head] = false; + params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = 0; } // @@ -135,10 +134,10 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { if (controllerPose.isValid()) { AnimPose pose(controllerPose.getRotation(), controllerPose.getTranslation()); params.primaryControllerPoses[pair.second] = avatarToRigPose * pose; - params.primaryControllerActiveFlags[pair.second] = true; + params.primaryControllerFlags[pair.second] = (uint8_t)Rig::ControllerFlags::Enabled; } else { params.primaryControllerPoses[pair.second] = AnimPose::identity; - params.primaryControllerActiveFlags[pair.second] = false; + params.primaryControllerFlags[pair.second] = 0; } } @@ -166,15 +165,15 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { if (controllerPose.isValid()) { AnimPose pose(controllerPose.getRotation(), controllerPose.getTranslation()); params.secondaryControllerPoses[pair.second] = avatarToRigPose * pose; - params.secondaryControllerActiveFlags[pair.second] = true; + params.secondaryControllerFlags[pair.second] = (uint8_t)Rig::ControllerFlags::Enabled; } else { params.secondaryControllerPoses[pair.second] = AnimPose::identity; - params.secondaryControllerActiveFlags[pair.second] = false; + params.secondaryControllerFlags[pair.second] = 0; } } // if hips are not under direct control, estimate the hips position. - if (avatarHeadPose.isValid() && !params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Hips]) { + 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); if (!_prevHipsValid) { @@ -200,7 +199,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { AnimPose sensorToRigPose(invRigMat * myAvatar->getSensorToWorldMatrix()); params.primaryControllerPoses[Rig::PrimaryControllerType_Hips] = sensorToRigPose * hips; - params.primaryControllerActiveFlags[Rig::PrimaryControllerType_Hips] = true; + params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; } else { _prevHipsValid = false; diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index d7d36dabf6..10ddd4c110 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -17,6 +17,7 @@ #include "Ledger.h" #include "CommerceLogging.h" #include +#include // inventory answers {status: 'success', data: {assets: [{id: "guid", title: "name", preview: "url"}....]}} // balance answers {status: 'success', data: {balance: integer}} @@ -46,6 +47,8 @@ Handler(buy) Handler(receiveAt) Handler(balance) Handler(inventory) +Handler(transferHfcToNode) +Handler(transferHfcToUsername) void Ledger::send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request) { auto accountManager = DependencyManager::get(); @@ -72,11 +75,16 @@ void Ledger::signedSend(const QString& propertyName, const QByteArray& text, con send(endpoint, success, fail, QNetworkAccessManager::PutOperation, AccountManagerAuth::Required, request); } -void Ledger::keysQuery(const QString& endpoint, const QString& success, const QString& fail) { +void Ledger::keysQuery(const QString& endpoint, const QString& success, const QString& fail, QJsonObject& requestParams) { auto wallet = DependencyManager::get(); - QJsonObject request; - request["public_keys"] = QJsonArray::fromStringList(wallet->listPublicKeys()); - send(endpoint, success, fail, QNetworkAccessManager::PostOperation, AccountManagerAuth::Required, request); + requestParams["public_keys"] = QJsonArray::fromStringList(wallet->listPublicKeys()); + + send(endpoint, success, fail, QNetworkAccessManager::PostOperation, AccountManagerAuth::Required, requestParams); +} + +void Ledger::keysQuery(const QString& endpoint, const QString& success, const QString& fail) { + QJsonObject requestParams; + keysQuery(endpoint, success, fail, requestParams); } void Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const bool controlled_failure) { @@ -111,23 +119,69 @@ void Ledger::inventory(const QStringList& keys) { keysQuery("inventory", "inventorySuccess", "inventoryFailure"); } -QString amountString(const QString& label, const QString&color, const QJsonValue& moneyValue, const QJsonValue& certsValue) { - int money = moneyValue.toInt(); - int certs = certsValue.toInt(); - if (money <= 0 && certs <= 0) { - return QString(); +QString hfcString(const QJsonValue& sentValue, const QJsonValue& receivedValue) { + int sent = sentValue.toInt(); + int received = receivedValue.toInt(); + if (sent <= 0 && received <= 0) { + return QString("0 HFC"); } - QString result(QString(" %2").arg(color, label)); - if (money > 0) { - result += QString(" %1 HFC").arg(money); - } - if (certs > 0) { - if (money > 0) { - result += QString(","); + QString result; + if (sent > 0) { + result += QString("-%1 HFC").arg(sent); + if (received > 0) { + result += QString("
"); } - result += QString((certs == 1) ? " %1 certificate" : " %1 certificates").arg(certs); } - return result + QString("
"); + if (received > 0) { + result += QString("%1 HFC").arg(received); + } + return result; +} +static const QString USER_PAGE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/users/"; +static const QString PLACE_PAGE_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/places/"; +static const QStringList KNOWN_USERS(QStringList() << "highfidelity" << "marketplace"); +QString userLink(const QString& username, const QString& placename) { + if (username.isEmpty()) { + if (placename.isEmpty()) { + return QString("someone"); + } else { + return QString("someone nearby").arg(PLACE_PAGE_BASE_URL, placename); + } + } + if (KNOWN_USERS.contains(username)) { + return username; + } + return QString("%2").arg(USER_PAGE_BASE_URL, username); +} + +QString transactionString(const QJsonObject& valueObject) { + int sentCerts = valueObject["sent_certs"].toInt(); + int receivedCerts = valueObject["received_certs"].toInt(); + int sent = valueObject["sent_money"].toInt(); + int dateInteger = valueObject["created_at"].toInt(); + QString message = valueObject["message"].toString(); + QDateTime createdAt(QDateTime::fromSecsSinceEpoch(dateInteger, Qt::UTC)); + QString result; + + if (sentCerts <= 0 && receivedCerts <= 0 && !KNOWN_USERS.contains(valueObject["sender_name"].toString())) { + // this is an hfc transfer. + if (sent > 0) { + QString recipient = userLink(valueObject["recipient_name"].toString(), valueObject["place_name"].toString()); + result += QString("Money sent to %1").arg(recipient); + } else { + QString sender = userLink(valueObject["sender_name"].toString(), valueObject["place_name"].toString()); + result += QString("Money from %1").arg(sender); + } + if (!message.isEmpty()) { + result += QString("
with memo: \"%1\"").arg(message); + } + } else { + result += valueObject["message"].toString(); + } + + // no matter what we append a smaller date to the bottom of this... + result += QString("
%1").arg(createdAt.toLocalTime().toString(Qt::DefaultLocaleShortDate)); + return result; } static const QString MARKETPLACE_ITEMS_BASE_URL = NetworkingConstants::METAVERSE_SERVER_URL().toString() + "/marketplace/items/"; @@ -150,16 +204,13 @@ void Ledger::historySuccess(QNetworkReply& reply) { // TODO: do this with 0 copies if possible for (auto it = historyArray.begin(); it != historyArray.end(); it++) { + // We have 2 text fields to synthesize, the one on the left is a listing + // of the HFC in/out of your wallet. The one on the right contains an explaination + // of the transaction. That could be just the memo (if it is a regular purchase), or + // more text (plus the optional memo) if an hfc transfer auto valueObject = (*it).toObject(); - QString sent = amountString("sent", "EA4C5F", valueObject["sent_money"], valueObject["sent_certs"]); - QString received = amountString("received", "1FC6A6", valueObject["received_money"], valueObject["received_certs"]); - - // turns out on my machine, toLocalTime convert to some weird timezone, yet the - // systemTimeZone is correct. To avoid a strange bug with other's systems too, lets - // be explicit - QDateTime createdAt = QDateTime::fromSecsSinceEpoch(valueObject["created_at"].toInt(), Qt::UTC); - QDateTime localCreatedAt = createdAt.toTimeZone(QTimeZone::systemTimeZone()); - valueObject["text"] = QString("%1%2%3").arg(valueObject["message"].toString(), sent, received); + valueObject["hfc_text"] = hfcString(valueObject["sent_money"], valueObject["received_money"]); + valueObject["transaction_text"] = transactionString(valueObject); newHistoryArray.push_back(valueObject); } // now copy the rest of the json -- this is inefficient @@ -169,6 +220,7 @@ void Ledger::historySuccess(QNetworkReply& reply) { QJsonObject newDataData; newDataData["history"] = newHistoryArray; newData["data"] = newDataData; + newData["current_page"] = data["current_page"].toInt(); emit historyResult(newData); } @@ -176,15 +228,11 @@ void Ledger::historyFailure(QNetworkReply& reply) { failResponse("history", reply); } -void Ledger::history(const QStringList& keys) { - keysQuery("history", "historySuccess", "historyFailure"); -} - -// The api/failResponse is called just for the side effect of logging. -void Ledger::resetSuccess(QNetworkReply& reply) { apiResponse("reset", reply); } -void Ledger::resetFailure(QNetworkReply& reply) { failResponse("reset", reply); } -void Ledger::reset() { - send("reset_user_hfc_account", "resetSuccess", "resetFailure", QNetworkAccessManager::PutOperation, AccountManagerAuth::Required, QJsonObject()); +void Ledger::history(const QStringList& keys, const int& pageNumber) { + QJsonObject params; + params["per_page"] = 100; + params["page"] = pageNumber; + keysQuery("history", "historySuccess", "historyFailure", params); } void Ledger::accountSuccess(QNetworkReply& reply) { @@ -196,11 +244,17 @@ void Ledger::accountSuccess(QNetworkReply& reply) { auto salt = QByteArray::fromBase64(data["salt"].toString().toUtf8()); auto iv = QByteArray::fromBase64(data["iv"].toString().toUtf8()); auto ckey = QByteArray::fromBase64(data["ckey"].toString().toUtf8()); + QString remotePublicKey = data["public_key"].toString(); wallet->setSalt(salt); wallet->setIv(iv); wallet->setCKey(ckey); + QStringList localPublicKeys = wallet->listPublicKeys(); + if (remotePublicKey.isEmpty() && !localPublicKeys.isEmpty()) { + receiveAt(localPublicKeys.first(), ""); + } + // none of the hfc account info should be emitted emit accountResult(QJsonObject{ {"status", "success"} }); } @@ -259,3 +313,26 @@ void Ledger::certificateInfo(const QString& certificateId) { request["certificate_id"] = certificateId; send(endpoint, "certificateInfoSuccess", "certificateInfoFailure", QNetworkAccessManager::PutOperation, AccountManagerAuth::None, request); } + +void Ledger::transferHfcToNode(const QString& hfc_key, const QString& nodeID, const int& amount, const QString& optionalMessage) { + QJsonObject transaction; + transaction["public_key"] = hfc_key; + transaction["node_id"] = nodeID; + transaction["quantity"] = amount; + transaction["message"] = optionalMessage; + transaction["place_name"] = DependencyManager::get()->getPlaceName(); + QJsonDocument transactionDoc{ transaction }; + auto transactionString = transactionDoc.toJson(QJsonDocument::Compact); + signedSend("transaction", transactionString, hfc_key, "transfer_hfc_to_node", "transferHfcToNodeSuccess", "transferHfcToNodeFailure"); +} + +void Ledger::transferHfcToUsername(const QString& hfc_key, const QString& username, const int& amount, const QString& optionalMessage) { + QJsonObject transaction; + transaction["public_key"] = hfc_key; + transaction["username"] = username; + transaction["quantity"] = amount; + transaction["message"] = optionalMessage; + QJsonDocument transactionDoc{ transaction }; + auto transactionString = transactionDoc.toJson(QJsonDocument::Compact); + signedSend("transaction", transactionString, hfc_key, "transfer_hfc_to_user", "transferHfcToUsernameSuccess", "transferHfcToUsernameFailure"); +} diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index 42eb0ffc49..1b56c893ab 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -29,11 +29,12 @@ public: bool receiveAt(const QString& hfc_key, const QString& old_key); void balance(const QStringList& keys); void inventory(const QStringList& keys); - void history(const QStringList& keys); + void history(const QStringList& keys, const int& pageNumber); void account(); - void reset(); void updateLocation(const QString& asset_id, const QString location, const bool controlledFailure = false); void certificateInfo(const QString& certificateId); + void transferHfcToNode(const QString& hfc_key, const QString& nodeID, const int& amount, const QString& optionalMessage); + void transferHfcToUsername(const QString& hfc_key, const QString& username, const int& amount, const QString& optionalMessage); enum CertificateStatus { CERTIFICATE_STATUS_UNKNOWN = 0, @@ -52,6 +53,8 @@ signals: void accountResult(QJsonObject result); void locationUpdateResult(QJsonObject result); void certificateInfoResult(QJsonObject result); + void transferHfcToNodeResult(QJsonObject result); + void transferHfcToUsernameResult(QJsonObject result); void updateCertificateStatus(const QString& certID, uint certStatus); @@ -66,19 +69,22 @@ public slots: void inventoryFailure(QNetworkReply& reply); void historySuccess(QNetworkReply& reply); void historyFailure(QNetworkReply& reply); - void resetSuccess(QNetworkReply& reply); - void resetFailure(QNetworkReply& reply); void accountSuccess(QNetworkReply& reply); void accountFailure(QNetworkReply& reply); void updateLocationSuccess(QNetworkReply& reply); void updateLocationFailure(QNetworkReply& reply); void certificateInfoSuccess(QNetworkReply& reply); void certificateInfoFailure(QNetworkReply& reply); + void transferHfcToNodeSuccess(QNetworkReply& reply); + void transferHfcToNodeFailure(QNetworkReply& reply); + void transferHfcToUsernameSuccess(QNetworkReply& reply); + void transferHfcToUsernameFailure(QNetworkReply& reply); private: QJsonObject apiResponse(const QString& label, QNetworkReply& reply); QJsonObject failResponse(const QString& label, QNetworkReply& reply); void send(const QString& endpoint, const QString& success, const QString& fail, QNetworkAccessManager::Operation method, AccountManagerAuth::Type authType, QJsonObject request); + void keysQuery(const QString& endpoint, const QString& success, const QString& fail, QJsonObject& extraRequestParams); void keysQuery(const QString& endpoint, const QString& success, const QString& fail); void signedSend(const QString& propertyName, const QByteArray& text, const QString& key, const QString& endpoint, const QString& success, const QString& fail, const bool controlled_failure = false); }; diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 62e87f9c66..c9caa393ce 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -29,6 +29,8 @@ QmlCommerce::QmlCommerce() { connect(wallet.data(), &Wallet::walletStatusResult, this, &QmlCommerce::walletStatusResult); connect(ledger.data(), &Ledger::certificateInfoResult, this, &QmlCommerce::certificateInfoResult); connect(ledger.data(), &Ledger::updateCertificateStatus, this, &QmlCommerce::updateCertificateStatus); + connect(ledger.data(), &Ledger::transferHfcToNodeResult, this, &QmlCommerce::transferHfcToNodeResult); + connect(ledger.data(), &Ledger::transferHfcToUsernameResult, this, &QmlCommerce::transferHfcToUsernameResult); auto accountManager = DependencyManager::get(); connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() { @@ -96,12 +98,12 @@ void QmlCommerce::inventory() { } } -void QmlCommerce::history() { +void QmlCommerce::history(const int& pageNumber) { auto ledger = DependencyManager::get(); auto wallet = DependencyManager::get(); QStringList cachedPublicKeys = wallet->listPublicKeys(); if (!cachedPublicKeys.isEmpty()) { - ledger->history(cachedPublicKeys); + ledger->history(cachedPublicKeys, pageNumber); } } @@ -128,18 +130,6 @@ void QmlCommerce::generateKeyPair() { getWalletAuthenticatedStatus(); } -void QmlCommerce::reset() { - auto ledger = DependencyManager::get(); - auto wallet = DependencyManager::get(); - ledger->reset(); - wallet->reset(); -} - -void QmlCommerce::resetLocalWalletOnly() { - auto wallet = DependencyManager::get(); - wallet->reset(); -} - void QmlCommerce::account() { auto ledger = DependencyManager::get(); ledger->account(); @@ -149,3 +139,27 @@ void QmlCommerce::certificateInfo(const QString& certificateId) { auto ledger = DependencyManager::get(); ledger->certificateInfo(certificateId); } + +void QmlCommerce::transferHfcToNode(const QString& nodeID, const int& amount, const QString& optionalMessage) { + auto ledger = DependencyManager::get(); + auto wallet = DependencyManager::get(); + QStringList keys = wallet->listPublicKeys(); + if (keys.count() == 0) { + QJsonObject result{ { "status", "fail" },{ "message", "Uninitialized Wallet." } }; + return emit buyResult(result); + } + QString key = keys[0]; + ledger->transferHfcToNode(key, nodeID, amount, optionalMessage); +} + +void QmlCommerce::transferHfcToUsername(const QString& username, const int& amount, const QString& optionalMessage) { + auto ledger = DependencyManager::get(); + auto wallet = DependencyManager::get(); + QStringList keys = wallet->listPublicKeys(); + if (keys.count() == 0) { + QJsonObject result{ { "status", "fail" },{ "message", "Uninitialized Wallet." } }; + return emit buyResult(result); + } + QString key = keys[0]; + ledger->transferHfcToUsername(key, username, amount, optionalMessage); +} diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index c53e73d565..b261ee6de6 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -45,6 +45,9 @@ signals: void updateCertificateStatus(const QString& certID, uint certStatus); + void transferHfcToNodeResult(QJsonObject result); + void transferHfcToUsernameResult(QJsonObject result); + protected: Q_INVOKABLE void getWalletStatus(); @@ -60,13 +63,14 @@ protected: Q_INVOKABLE void buy(const QString& assetId, int cost, const bool controlledFailure = false); Q_INVOKABLE void balance(); Q_INVOKABLE void inventory(); - Q_INVOKABLE void history(); + Q_INVOKABLE void history(const int& pageNumber); Q_INVOKABLE void generateKeyPair(); - Q_INVOKABLE void reset(); - Q_INVOKABLE void resetLocalWalletOnly(); Q_INVOKABLE void account(); Q_INVOKABLE void certificateInfo(const QString& certificateId); + + Q_INVOKABLE void transferHfcToNode(const QString& nodeID, const int& amount, const QString& optionalMessage); + Q_INVOKABLE void transferHfcToUsername(const QString& username, const int& amount, const QString& optionalMessage); }; #endif // hifi_QmlCommerce_h diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index c3c91e82a8..8b73042ada 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -144,15 +144,13 @@ bool writeKeys(const char* filename, EC_KEY* keys) { if ((fp = fopen(filename, "wt"))) { if (!PEM_write_EC_PUBKEY(fp, keys)) { fclose(fp); - qCDebug(commerce) << "failed to write public key"; - QFile(QString(filename)).remove(); + qCCritical(commerce) << "failed to write public key"; return retval; } if (!PEM_write_ECPrivateKey(fp, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) { fclose(fp); - qCDebug(commerce) << "failed to write private key"; - QFile(QString(filename)).remove(); + qCCritical(commerce) << "failed to write private key"; return retval; } @@ -168,7 +166,8 @@ bool writeKeys(const char* filename, EC_KEY* keys) { QPair generateECKeypair() { EC_KEY* keyPair = EC_KEY_new_by_curve_name(NID_secp256k1); - QPair retval; + QPair retval{}; + EC_KEY_set_asn1_flag(keyPair, OPENSSL_EC_NAMED_CURVE); if (!EC_KEY_generate_key(keyPair)) { qCDebug(commerce) << "Error generating EC Keypair -" << ERR_get_error(); @@ -517,6 +516,9 @@ bool Wallet::generateKeyPair() { qCInfo(commerce) << "Generating keypair."; auto keyPair = generateECKeypair(); + if (!keyPair.first) { + return false; + } writeBackupInstructions(); @@ -653,20 +655,6 @@ QString Wallet::getKeyFilePath() { } } -void Wallet::reset() { - _publicKeys.clear(); - - delete _securityImage; - _securityImage = nullptr; - - // tell the provider we got nothing - updateImageProvider(); - _passphrase->clear(); - - - QFile keyFile(keyFilePath()); - keyFile.remove(); -} bool Wallet::writeWallet(const QString& newPassphrase) { EC_KEY* keys = readKeys(keyFilePath().toStdString().c_str()); if (keys) { diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h index ed145df451..fe3a9f1d5f 100644 --- a/interface/src/commerce/Wallet.h +++ b/interface/src/commerce/Wallet.h @@ -49,8 +49,6 @@ public: bool walletIsAuthenticatedWithPassphrase(); bool changePassphrase(const QString& newPassphrase); - void reset(); - void getWalletStatus(); enum WalletStatus { WALLET_STATUS_NOT_LOGGED_IN = 0, diff --git a/interface/src/main.cpp b/interface/src/main.cpp index 5c07bebc23..80ec78f118 100644 --- a/interface/src/main.cpp +++ b/interface/src/main.cpp @@ -20,7 +20,6 @@ #include #include -#include #include #include #include @@ -53,6 +52,14 @@ int main(int argc, const char* argv[]) { QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar); #endif +#if defined(USE_GLES) && defined(Q_OS_WIN) + // When using GLES on Windows, we can't create normal GL context in Qt, so + // we force Qt to use angle. This will cause the QML to be unable to be used + // in the output window, so QML should be disabled. + qputenv("QT_ANGLE_PLATFORM", "d3d11"); + QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); +#endif + disableQtBearerPoll(); // Fixes wifi ping spikes QElapsedTimer startupTime; diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index fad6fe47ae..8c0fb59106 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -44,7 +44,7 @@ OverlayID StylusPointer::buildStylusOverlay(const QVariantMap& properties) { QVariantMap overlayProperties; // TODO: make these configurable per pointer overlayProperties["name"] = "stylus"; - overlayProperties["url"] = PathUtils::resourcesPath() + "/meshes/tablet-stylus-fat.fbx"; + overlayProperties["url"] = PathUtils::resourcesUrl() + "/meshes/tablet-stylus-fat.fbx"; overlayProperties["loadPriority"] = 10.0f; overlayProperties["solid"] = true; overlayProperties["visible"] = false; diff --git a/interface/src/scripting/AccountScriptingInterface.cpp b/interface/src/scripting/AccountScriptingInterface.cpp deleted file mode 100644 index fd54198b3c..0000000000 --- a/interface/src/scripting/AccountScriptingInterface.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// -// AccountScriptingInterface.cpp -// interface/src/scripting -// -// Created by Stojce Slavkovski on 6/07/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "AccountManager.h" - -#include "AccountScriptingInterface.h" -#include "GlobalServicesScriptingInterface.h" - -AccountScriptingInterface* AccountScriptingInterface::getInstance() { - static AccountScriptingInterface sharedInstance; - return &sharedInstance; -} - -bool AccountScriptingInterface::isLoggedIn() { - return GlobalServicesScriptingInterface::getInstance()->isLoggedIn(); -} - -void AccountScriptingInterface::logOut() { - GlobalServicesScriptingInterface::getInstance()->logOut(); -} - -bool AccountScriptingInterface::loggedIn() const { - return GlobalServicesScriptingInterface::getInstance()->loggedIn(); -} - -QString AccountScriptingInterface::getUsername() { - return GlobalServicesScriptingInterface::getInstance()->getUsername(); -} diff --git a/interface/src/scripting/AccountScriptingInterface.h b/interface/src/scripting/AccountScriptingInterface.h deleted file mode 100644 index 10d33ffa36..0000000000 --- a/interface/src/scripting/AccountScriptingInterface.h +++ /dev/null @@ -1,60 +0,0 @@ -// -// AccountScriptingInterface.h -// interface/src/scripting -// -// Created by Stojce Slavkovski on 6/07/14. -// Copyright 2014 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_AccountScriptingInterface_h -#define hifi_AccountScriptingInterface_h - -#include - -class AccountScriptingInterface : public QObject { - Q_OBJECT - - /**jsdoc - * @namespace Account - * @property username {String} username if user is logged in, otherwise it returns "Unknown user" - */ - Q_PROPERTY(QString username READ getUsername) - Q_PROPERTY(bool loggedIn READ loggedIn) - -signals: - - /**jsdoc - * Triggered when username has changed. - * @function Account.usernameChanged - * @return {Signal} - */ - void usernameChanged(); - void loggedInChanged(bool loggedIn); - -public slots: - static AccountScriptingInterface* getInstance(); - - /**jsdoc - * Returns the username for the currently logged in High Fidelity metaverse account. - * @function Account.getUsername - * @return {string} username if user is logged in, otherwise it returns "Unknown user" - */ - QString getUsername(); - - /**jsdoc - * Determine if the user is logged into the High Fidleity metaverse. - * @function Account.isLoggedIn - * @return {bool} true when user is logged into the High Fidelity metaverse. - */ - bool isLoggedIn(); - void logOut(); - -public: - AccountScriptingInterface(QObject* parent = nullptr) {} - bool loggedIn() const; -}; - -#endif // hifi_AccountScriptingInterface_h diff --git a/interface/src/scripting/GlobalServicesScriptingInterface.cpp b/interface/src/scripting/AccountServicesScriptingInterface.cpp similarity index 71% rename from interface/src/scripting/GlobalServicesScriptingInterface.cpp rename to interface/src/scripting/AccountServicesScriptingInterface.cpp index 305bfa3e79..fc293098bb 100644 --- a/interface/src/scripting/GlobalServicesScriptingInterface.cpp +++ b/interface/src/scripting/AccountServicesScriptingInterface.cpp @@ -1,5 +1,5 @@ // -// GlobalServicesScriptingInterface.cpp +// AccountServicesScriptingInterface.cpp // interface/src/scripting // // Created by Thijs Wenker on 9/10/14. @@ -14,41 +14,41 @@ #include "DiscoverabilityManager.h" #include "ResourceCache.h" -#include "GlobalServicesScriptingInterface.h" +#include "AccountServicesScriptingInterface.h" -GlobalServicesScriptingInterface::GlobalServicesScriptingInterface() { +AccountServicesScriptingInterface::AccountServicesScriptingInterface() { auto accountManager = DependencyManager::get(); - connect(accountManager.data(), &AccountManager::usernameChanged, this, &GlobalServicesScriptingInterface::onUsernameChanged); - connect(accountManager.data(), &AccountManager::logoutComplete, this, &GlobalServicesScriptingInterface::loggedOut); - connect(accountManager.data(), &AccountManager::loginComplete, this, &GlobalServicesScriptingInterface::connected); + connect(accountManager.data(), &AccountManager::usernameChanged, this, &AccountServicesScriptingInterface::onUsernameChanged); + connect(accountManager.data(), &AccountManager::logoutComplete, this, &AccountServicesScriptingInterface::loggedOut); + connect(accountManager.data(), &AccountManager::loginComplete, this, &AccountServicesScriptingInterface::connected); _downloading = false; QTimer* checkDownloadTimer = new QTimer(this); - connect(checkDownloadTimer, &QTimer::timeout, this, &GlobalServicesScriptingInterface::checkDownloadInfo); + connect(checkDownloadTimer, &QTimer::timeout, this, &AccountServicesScriptingInterface::checkDownloadInfo); const int CHECK_DOWNLOAD_INTERVAL = MSECS_PER_SECOND / 2; checkDownloadTimer->start(CHECK_DOWNLOAD_INTERVAL); auto discoverabilityManager = DependencyManager::get(); connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged, - this, &GlobalServicesScriptingInterface::discoverabilityModeChanged); + this, &AccountServicesScriptingInterface::discoverabilityModeChanged); _loggedIn = isLoggedIn(); emit loggedInChanged(_loggedIn); } -GlobalServicesScriptingInterface::~GlobalServicesScriptingInterface() { +AccountServicesScriptingInterface::~AccountServicesScriptingInterface() { auto accountManager = DependencyManager::get(); - disconnect(accountManager.data(), &AccountManager::usernameChanged, this, &GlobalServicesScriptingInterface::onUsernameChanged); - disconnect(accountManager.data(), &AccountManager::logoutComplete, this, &GlobalServicesScriptingInterface::loggedOut); - disconnect(accountManager.data(), &AccountManager::loginComplete, this, &GlobalServicesScriptingInterface::connected); + disconnect(accountManager.data(), &AccountManager::usernameChanged, this, &AccountServicesScriptingInterface::onUsernameChanged); + disconnect(accountManager.data(), &AccountManager::logoutComplete, this, &AccountServicesScriptingInterface::loggedOut); + disconnect(accountManager.data(), &AccountManager::loginComplete, this, &AccountServicesScriptingInterface::connected); } -GlobalServicesScriptingInterface* GlobalServicesScriptingInterface::getInstance() { - static GlobalServicesScriptingInterface sharedInstance; +AccountServicesScriptingInterface* AccountServicesScriptingInterface::getInstance() { + static AccountServicesScriptingInterface sharedInstance; return &sharedInstance; } -const QString GlobalServicesScriptingInterface::getUsername() const { +const QString AccountServicesScriptingInterface::getUsername() const { auto accountManager = DependencyManager::get(); if (accountManager->isLoggedIn()) { return accountManager->getAccountInfo().getUsername(); @@ -57,31 +57,31 @@ const QString GlobalServicesScriptingInterface::getUsername() const { } } -bool GlobalServicesScriptingInterface::isLoggedIn() { +bool AccountServicesScriptingInterface::isLoggedIn() { auto accountManager = DependencyManager::get(); return accountManager->isLoggedIn(); } -bool GlobalServicesScriptingInterface::checkAndSignalForAccessToken() { +bool AccountServicesScriptingInterface::checkAndSignalForAccessToken() { auto accountManager = DependencyManager::get(); return accountManager->checkAndSignalForAccessToken(); } -void GlobalServicesScriptingInterface::logOut() { +void AccountServicesScriptingInterface::logOut() { auto accountManager = DependencyManager::get(); return accountManager->logout(); } -void GlobalServicesScriptingInterface::loggedOut() { - emit GlobalServicesScriptingInterface::disconnected(QString("logout")); +void AccountServicesScriptingInterface::loggedOut() { + emit AccountServicesScriptingInterface::disconnected(QString("logout")); } -QString GlobalServicesScriptingInterface::getFindableBy() const { +QString AccountServicesScriptingInterface::getFindableBy() const { auto discoverabilityManager = DependencyManager::get(); return DiscoverabilityManager::findableByString(discoverabilityManager->getDiscoverabilityMode()); } -void GlobalServicesScriptingInterface::setFindableBy(const QString& discoverabilityMode) { +void AccountServicesScriptingInterface::setFindableBy(const QString& discoverabilityMode) { auto discoverabilityManager = DependencyManager::get(); if (discoverabilityMode.toLower() == "none") { discoverabilityManager->setDiscoverabilityMode(Discoverability::None); @@ -96,11 +96,11 @@ void GlobalServicesScriptingInterface::setFindableBy(const QString& discoverabil } } -void GlobalServicesScriptingInterface::discoverabilityModeChanged(Discoverability::Mode discoverabilityMode) { +void AccountServicesScriptingInterface::discoverabilityModeChanged(Discoverability::Mode discoverabilityMode) { emit findableByChanged(DiscoverabilityManager::findableByString(discoverabilityMode)); } -void GlobalServicesScriptingInterface::onUsernameChanged(const QString& username) { +void AccountServicesScriptingInterface::onUsernameChanged(const QString& username) { _loggedIn = (username != QString()); emit myUsernameChanged(username); emit loggedInChanged(_loggedIn); @@ -135,7 +135,7 @@ void DownloadInfoResultFromScriptValue(const QScriptValue& object, DownloadInfoR result.pending = object.property("pending").toVariant().toFloat(); } -DownloadInfoResult GlobalServicesScriptingInterface::getDownloadInfo() { +DownloadInfoResult AccountServicesScriptingInterface::getDownloadInfo() { DownloadInfoResult result; foreach(const auto& resource, ResourceCache::getLoadingRequests()) { result.downloading.append(resource->getProgress() * 100.0f); @@ -144,7 +144,7 @@ DownloadInfoResult GlobalServicesScriptingInterface::getDownloadInfo() { return result; } -void GlobalServicesScriptingInterface::checkDownloadInfo() { +void AccountServicesScriptingInterface::checkDownloadInfo() { DownloadInfoResult downloadInfo = getDownloadInfo(); bool downloading = downloadInfo.downloading.count() > 0 || downloadInfo.pending > 0; @@ -155,7 +155,7 @@ void GlobalServicesScriptingInterface::checkDownloadInfo() { } } -void GlobalServicesScriptingInterface::updateDownloadInfo() { +void AccountServicesScriptingInterface::updateDownloadInfo() { emit downloadInfoChanged(getDownloadInfo()); } diff --git a/interface/src/scripting/GlobalServicesScriptingInterface.h b/interface/src/scripting/AccountServicesScriptingInterface.h similarity index 84% rename from interface/src/scripting/GlobalServicesScriptingInterface.h rename to interface/src/scripting/AccountServicesScriptingInterface.h index 93d35e9ce8..012c37305d 100644 --- a/interface/src/scripting/GlobalServicesScriptingInterface.h +++ b/interface/src/scripting/AccountServicesScriptingInterface.h @@ -1,5 +1,5 @@ // -// GlobalServicesScriptingInterface.h +// AccountServicesScriptingInterface.h // interface/src/scripting // // Created by Thijs Wenker on 9/10/14. @@ -9,8 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_GlobalServicesScriptingInterface_h -#define hifi_GlobalServicesScriptingInterface_h +#ifndef hifi_AccountServicesScriptingInterface_h +#define hifi_AccountServicesScriptingInterface_h #include #include @@ -32,7 +32,7 @@ Q_DECLARE_METATYPE(DownloadInfoResult) QScriptValue DownloadInfoResultToScriptValue(QScriptEngine* engine, const DownloadInfoResult& result); void DownloadInfoResultFromScriptValue(const QScriptValue& object, DownloadInfoResult& result); -class GlobalServicesScriptingInterface : public QObject { +class AccountServicesScriptingInterface : public QObject { Q_OBJECT Q_PROPERTY(QString username READ getUsername NOTIFY myUsernameChanged) @@ -41,7 +41,7 @@ class GlobalServicesScriptingInterface : public QObject { Q_PROPERTY(QUrl metaverseServerURL READ getMetaverseServerURL) public: - static GlobalServicesScriptingInterface* getInstance(); + static AccountServicesScriptingInterface* getInstance(); const QString getUsername() const; bool loggedIn() const { return _loggedIn; } @@ -74,11 +74,11 @@ signals: void loggedInChanged(bool loggedIn); private: - GlobalServicesScriptingInterface(); - ~GlobalServicesScriptingInterface(); + AccountServicesScriptingInterface(); + ~AccountServicesScriptingInterface(); bool _downloading; bool _loggedIn{ false }; }; -#endif // hifi_GlobalServicesScriptingInterface_h +#endif // hifi_AccountServicesScriptingInterface_h diff --git a/interface/src/scripting/AssetMappingsScriptingInterface.cpp b/interface/src/scripting/AssetMappingsScriptingInterface.cpp index 72b6f401e9..c13b5b2e0d 100644 --- a/interface/src/scripting/AssetMappingsScriptingInterface.cpp +++ b/interface/src/scripting/AssetMappingsScriptingInterface.cpp @@ -39,8 +39,7 @@ AssetMappingsScriptingInterface::AssetMappingsScriptingInterface() { void AssetMappingsScriptingInterface::setMapping(QString path, QString hash, QJSValue callback) { auto assetClient = DependencyManager::get(); auto request = assetClient->createSetMappingRequest(path, hash); - - connect(request, &SetMappingRequest::finished, this, [this, callback](SetMappingRequest* request) mutable { + connect(request, &SetMappingRequest::finished, this, [callback](SetMappingRequest* request) mutable { if (callback.isCallable()) { QJSValueList args { request->getErrorString(), request->getPath() }; callback.call(args); @@ -48,15 +47,13 @@ void AssetMappingsScriptingInterface::setMapping(QString path, QString hash, QJS request->deleteLater(); }); - request->start(); } void AssetMappingsScriptingInterface::getMapping(QString path, QJSValue callback) { auto assetClient = DependencyManager::get(); auto request = assetClient->createGetMappingRequest(path); - - connect(request, &GetMappingRequest::finished, this, [this, callback](GetMappingRequest* request) mutable { + connect(request, &GetMappingRequest::finished, this, [callback](GetMappingRequest* request) mutable { auto hash = request->getHash(); if (callback.isCallable()) { diff --git a/interface/src/scripting/LimitlessConnection.h b/interface/src/scripting/LimitlessConnection.h index ee049aff8e..9ed39bd653 100644 --- a/interface/src/scripting/LimitlessConnection.h +++ b/interface/src/scripting/LimitlessConnection.h @@ -12,9 +12,11 @@ #ifndef hifi_LimitlessConnection_h #define hifi_LimitlessConnection_h +#include +#include +#include + #include -#include -#include class LimitlessConnection : public QObject { Q_OBJECT diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index b4247fd0b0..fc45555bf9 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -49,6 +49,8 @@ void CustomPromptResultFromScriptValue(const QScriptValue& object, CustomPromptR WindowScriptingInterface::WindowScriptingInterface() { const DomainHandler& domainHandler = DependencyManager::get()->getDomainHandler(); connect(&domainHandler, &DomainHandler::connectedToDomain, this, &WindowScriptingInterface::domainChanged); + connect(&domainHandler, &DomainHandler::disconnectedFromDomain, this, &WindowScriptingInterface::disconnectedFromDomain); + connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &WindowScriptingInterface::domainConnectionRefused); connect(qApp, &Application::svoImportRequested, [this](const QString& urlString) { @@ -134,6 +136,10 @@ void WindowScriptingInterface::promptAsync(const QString& message, const QString }); } +void WindowScriptingInterface::disconnectedFromDomain() { + emit domainChanged(""); +} + CustomPromptResult WindowScriptingInterface::customPrompt(const QVariant& config) { CustomPromptResult result; bool ok = false; @@ -176,10 +182,6 @@ bool WindowScriptingInterface::isPointOnDesktopWindow(QVariant point) { return offscreenUi->isPointOnDesktopWindow(point); } -glm::vec2 WindowScriptingInterface::getDeviceSize() const { - return qApp->getDeviceSize(); -} - /// Makes sure that the reticle is visible, use this in blocking forms that require a reticle and /// might be in same thread as a script that sets the reticle to invisible void WindowScriptingInterface::ensureReticleVisible() const { @@ -275,7 +277,8 @@ void WindowScriptingInterface::browseAsync(const QString& title, const QString& if (!result.isEmpty()) { setPreviousBrowseLocation(QFileInfo(result).absolutePath()); } - emit openFileChanged(result); + emit browseChanged(result); + emit openFileChanged(result); // Deprecated signal; to be removed in due course. }); } @@ -388,12 +391,20 @@ QString WindowScriptingInterface::checkVersion() { return QCoreApplication::applicationVersion(); } +QString WindowScriptingInterface::protocolSignature() { + return protocolVersionsSignatureBase64(); +} + int WindowScriptingInterface::getInnerWidth() { - return qApp->getWindow()->geometry().width(); + return qApp->getDeviceSize().x; } int WindowScriptingInterface::getInnerHeight() { - return qApp->getWindow()->geometry().height(); + return qApp->getDeviceSize().y; +} + +glm::vec2 WindowScriptingInterface::getDeviceSize() const { + return qApp->getDeviceSize(); } int WindowScriptingInterface::getX() { @@ -405,6 +416,11 @@ int WindowScriptingInterface::getY() { } void WindowScriptingInterface::copyToClipboard(const QString& text) { + if (QThread::currentThread() != qApp->thread()) { + QMetaObject::invokeMethod(this, "copyToClipboard", Q_ARG(QString, text)); + return; + } + qDebug() << "Copying"; QApplication::clipboard()->setText(text); } diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 5cd75ee941..dc71611c5b 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -1,5 +1,5 @@ // -// WindowScriptingInterface.cpp +// WindowScriptingInterface.h // interface/src/scripting // // Created by Ryan Huffman on 4/29/14. @@ -42,7 +42,7 @@ void CustomPromptResultFromScriptValue(const QScriptValue& object, CustomPromptR * @property {number} innerWidth - The width of the drawable area of the Interface window (i.e., without borders or other * chrome), in pixels. Read-only. * @property {number} innerHeight - The height of the drawable area of the Interface window (i.e., without borders or other - * chrome) plus the height of the menu bar, in pixels. Read-only. + * chrome), in pixels. Read-only. * @property {object} location - Provides facilities for working with your current metaverse location. See {@link location}. * @property {number} x - The x coordinate of the top left corner of the Interface window on the display. Read-only. * @property {number} y - The y coordinate of the top left corner of the Interface window on the display. Read-only. @@ -54,6 +54,7 @@ class WindowScriptingInterface : public QObject, public Dependency { Q_PROPERTY(int innerHeight READ getInnerHeight) Q_PROPERTY(int x READ getX) Q_PROPERTY(int y READ getY) + public: WindowScriptingInterface(); ~WindowScriptingInterface(); @@ -143,7 +144,7 @@ public slots: /**jsdoc * Prompt the user for input in a custom, modal dialog. - * @deprecated This funtion is deprecated and will be removed. + * @deprecated This function is deprecated and will soon be removed. * @function Window.customPrompt * @param {object} config - Configures the modal dialog. * @returns {object} The user's response. @@ -196,18 +197,19 @@ public slots: /**jsdoc * Prompt the user to choose a file. Displays a non-modal dialog that navigates the directory tree. A - * {@link Window.openFileChanged|openFileChanged} signal is emitted when a file is chosen; no signal is emitted if the user + * {@link Window.browseChanged|browseChanged} signal is emitted when a file is chosen; no signal is emitted if the user * cancels the dialog. + * @deprecated A deprecated {@link Window.openFileChanged|openFileChanged} signal is also emitted when a file is chosen. * @function Window.browseAsync * @param {string} title="" - The title to display at the top of the dialog. * @param {string} directory="" - The initial directory to start browsing at. * @param {string} nameFilter="" - The types of files to display. Examples: "*.json" and * "Images (*.png *.jpg *.svg)". All files are displayed if a filter isn't specified. * @example Ask the user to choose an image file without waiting for the answer. - * function onOpenFileChanged(filename) { + * function onBrowseChanged(filename) { * print("File: " + filename); * } - * Window.openFileChanged.connect(onOpenFileChanged); + * Window.browseChanged.connect(onBrowseChanged); * * Window.browseAsync("Select Image File", Paths.resources, "Images (*.png *.jpg *.svg)"); * print("Script continues without waiting"); @@ -300,10 +302,17 @@ public slots: /**jsdoc * Get Interface's build number. * @function Window.checkVersion - * @returns {string} - Interface's build number. + * @returns {string} Interface's build number. */ QString checkVersion(); + /**jsdoc + * Get the signature for Interface's protocol version. + * @function Window.protocolSignature + * @returns {string} A string uniquely identifying the version of the metaverse protocol that Interface is using. + */ + QString protocolSignature(); + /**jsdoc * Copies text to the operating system's clipboard. * @function Window.copyToClipboard @@ -326,7 +335,7 @@ public slots: * full resolution is used (window dimensions in desktop mode; HMD display dimensions in HMD mode), otherwise one of the * dimensions is adjusted in order to match the aspect ratio. * @example Using the snapshot function and signals. - * function onStillSnapshottaken(path, notify) { + * function onStillSnapshotTaken(path, notify) { * print("Still snapshot taken: " + path); * print("Notify: " + notify); * } @@ -339,7 +348,7 @@ public slots: * print("Animated snapshot taken: " + animatedPath); * } * - * Window.stillSnapshotTaken.connect(onStillSnapshottaken); + * Window.stillSnapshotTaken.connect(onStillSnapshotTaken); * Window.processingGifStarted.connect(onProcessingGifStarted); * Window.processingGifCompleted.connect(onProcessingGifCompleted); * @@ -515,6 +524,7 @@ public slots: private slots: void onMessageBoxSelected(int button); + void disconnectedFromDomain(); signals: @@ -553,7 +563,7 @@ signals: /**jsdoc * Triggered when a still snapshot has been taken by calling {@link Window.takeSnapshot|takeSnapshot} with - * includeAnimated = false. + * includeAnimated = false or {@link Window.takeSecondaryCameraSnapshot|takeSecondaryCameraSnapshot}. * @function Window.stillSnapshotTaken * @param {string} pathStillSnapshot - The path and name of the snapshot image file. * @param {boolean} notify - The value of the notify parameter that {@link Window.takeSnapshot|takeSnapshot} @@ -650,9 +660,18 @@ signals: */ void saveFileChanged(QString filename); + /**jsdoc + * Triggered when the user chooses a file in a {@link Window.browseAsync|browseAsync} dialog. + * @function Window.browseChanged + * @param {string} filename - The path and name of the file the user chose in the dialog. + * @returns {Signal} + */ + void browseChanged(QString filename); + /**jsdoc * Triggered when the user chooses a file in a {@link Window.browseAsync|browseAsync} dialog. * @function Window.openFileChanged + * @deprecated This signal is being replaced with {@link Window.browseChanged|browseChanged} and will be removed. * @param {string} filename - The path and name of the file the user chose in the dialog. * @returns {Signal} */ diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 9b97563233..9a7ebdf784 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -9,6 +9,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "ApplicationOverlay.h" + #include #include @@ -17,12 +19,10 @@ #include #include #include -#include #include "AudioClient.h" #include "audio/AudioScope.h" #include "Application.h" -#include "ApplicationOverlay.h" #include "Util.h" #include "ui/Stats.h" diff --git a/interface/src/ui/ApplicationOverlay.h b/interface/src/ui/ApplicationOverlay.h index 0d30123c61..b36b4f02df 100644 --- a/interface/src/ui/ApplicationOverlay.h +++ b/interface/src/ui/ApplicationOverlay.h @@ -13,6 +13,7 @@ #define hifi_ApplicationOverlay_h #include +#include // Handles the drawing of the overlays to the screen diff --git a/interface/src/ui/ResourceImageItem.cpp b/interface/src/ui/ResourceImageItem.cpp index 5b7c1896fe..f14f4ca78e 100644 --- a/interface/src/ui/ResourceImageItem.cpp +++ b/interface/src/ui/ResourceImageItem.cpp @@ -10,10 +10,9 @@ #include "ResourceImageItem.h" +#include + #include -#include -#include -#include #include @@ -89,11 +88,9 @@ QOpenGLFramebufferObject* ResourceImageItemRenderer::createFramebufferObject(con } void ResourceImageItemRenderer::render() { - auto f = QOpenGLContext::currentContext()->extraFunctions(); - if (_fenceSync) { - f->glWaitSync(_fenceSync, 0, GL_TIMEOUT_IGNORED); - f->glDeleteSync(_fenceSync); + glWaitSync(_fenceSync, 0, GL_TIMEOUT_IGNORED); + glDeleteSync(_fenceSync); _fenceSync = 0; } if (_ready) { diff --git a/interface/src/ui/ResourceImageItem.h b/interface/src/ui/ResourceImageItem.h index 985ab5a66e..d154588094 100644 --- a/interface/src/ui/ResourceImageItem.h +++ b/interface/src/ui/ResourceImageItem.h @@ -14,6 +14,8 @@ #include "Application.h" +#include + #include #include #include diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index f991420fe8..c157898e74 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -42,8 +42,9 @@ using namespace std; static Stats* INSTANCE{ nullptr }; +#if !defined (Q_OS_ANDROID) QString getTextureMemoryPressureModeString(); - +#endif Stats* Stats::getInstance() { if (!INSTANCE) { Stats::registerType(); @@ -359,7 +360,9 @@ void Stats::updateStats(bool force) { STAT_UPDATE(gpuTextureResourceMemory, (int)BYTES_TO_MB(gpu::Context::getTextureResourceGPUMemSize())); STAT_UPDATE(gpuTextureResourcePopulatedMemory, (int)BYTES_TO_MB(gpu::Context::getTextureResourcePopulatedGPUMemSize())); STAT_UPDATE(gpuTextureExternalMemory, (int)BYTES_TO_MB(gpu::Context::getTextureExternalGPUMemSize())); +#if !defined(Q_OS_ANDROID) STAT_UPDATE(gpuTextureMemoryPressureState, getTextureMemoryPressureModeString()); +#endif STAT_UPDATE(gpuFreeMemory, (int)BYTES_TO_MB(gpu::Context::getFreeGPUMemSize())); STAT_UPDATE(rectifiedTextureCount, (int)RECTIFIED_TEXTURE_COUNT.load()); STAT_UPDATE(decimatedTextureCount, (int)DECIMATED_TEXTURE_COUNT.load()); diff --git a/interface/src/ui/overlays/Base3DOverlay.cpp b/interface/src/ui/overlays/Base3DOverlay.cpp index 84a6fe1da4..23e09fe5ca 100644 --- a/interface/src/ui/overlays/Base3DOverlay.cpp +++ b/interface/src/ui/overlays/Base3DOverlay.cpp @@ -210,8 +210,8 @@ void Base3DOverlay::setProperties(const QVariantMap& originalProperties) { * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, - * filled, and filed. Antonyms: isWire and wire. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. + * Antonyms: isWire and wire. * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, diff --git a/interface/src/ui/overlays/Circle3DOverlay.cpp b/interface/src/ui/overlays/Circle3DOverlay.cpp index ff267adc1f..5e38f28a06 100644 --- a/interface/src/ui/overlays/Circle3DOverlay.cpp +++ b/interface/src/ui/overlays/Circle3DOverlay.cpp @@ -386,8 +386,6 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) { * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and - * rotating as you move your avatar. * * @property {string} name="" - A friendly name for the overlay. * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and @@ -397,10 +395,8 @@ void Circle3DOverlay::setProperties(const QVariantMap& properties) { * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, - * filled, and filed. Antonyms: isWire and wire. - * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will - * be removed. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled + * Antonyms: isWire and wire. * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index e34e68e053..f13f782482 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -154,8 +154,6 @@ void Cube3DOverlay::setProperties(const QVariantMap& properties) { * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and - * rotating as you move your avatar. * * @property {string} name="" - A friendly name for the overlay. * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and @@ -165,10 +163,8 @@ void Cube3DOverlay::setProperties(const QVariantMap& properties) { * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, - * filled, and filed. Antonyms: isWire and wire. - * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will - * be removed. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. + * Antonyms: isWire and wire. * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, diff --git a/interface/src/ui/overlays/Grid3DOverlay.cpp b/interface/src/ui/overlays/Grid3DOverlay.cpp index e06fa22692..621c19944b 100644 --- a/interface/src/ui/overlays/Grid3DOverlay.cpp +++ b/interface/src/ui/overlays/Grid3DOverlay.cpp @@ -132,8 +132,6 @@ void Grid3DOverlay::setProperties(const QVariantMap& properties) { * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and - * rotating as you move your avatar. * * @property {string} name="" - A friendly name for the overlay. * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and @@ -143,10 +141,8 @@ void Grid3DOverlay::setProperties(const QVariantMap& properties) { * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, - * filled, and filed. Antonyms: isWire and wire. - * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will - * be removed. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. + * Antonyms: isWire and wire. * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, diff --git a/interface/src/ui/overlays/Image3DOverlay.cpp b/interface/src/ui/overlays/Image3DOverlay.cpp index 454945d31c..df93245922 100644 --- a/interface/src/ui/overlays/Image3DOverlay.cpp +++ b/interface/src/ui/overlays/Image3DOverlay.cpp @@ -208,8 +208,6 @@ void Image3DOverlay::setProperties(const QVariantMap& properties) { * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and - * rotating as you move your avatar. * * @property {string} name="" - A friendly name for the overlay. * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and @@ -219,10 +217,8 @@ void Image3DOverlay::setProperties(const QVariantMap& properties) { * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, - * filled, and filed. Antonyms: isWire and wire. - * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will - * be removed. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. + * Antonyms: isWire and wire. * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, diff --git a/interface/src/ui/overlays/Line3DOverlay.cpp b/interface/src/ui/overlays/Line3DOverlay.cpp index 62675bacea..7200abf74e 100644 --- a/interface/src/ui/overlays/Line3DOverlay.cpp +++ b/interface/src/ui/overlays/Line3DOverlay.cpp @@ -275,8 +275,6 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) { * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and - * rotating as you move your avatar. * * @property {string} name="" - A friendly name for the overlay. * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and @@ -286,10 +284,8 @@ void Line3DOverlay::setProperties(const QVariantMap& originalProperties) { * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, - * filled, and filed. Antonyms: isWire and wire. - * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will - * be removed. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. + * Antonyms: isWire and wire. * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 3bd4f84cd6..4804a2db3d 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -306,8 +306,6 @@ vectorType ModelOverlay::mapJoints(mapFunction function) const { * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and - * rotating as you move your avatar. * * @property {string} name="" - A friendly name for the overlay. * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and @@ -317,10 +315,8 @@ vectorType ModelOverlay::mapJoints(mapFunction function) const { * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, - * filled, and filed. Antonyms: isWire and wire. - * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will - * be removed. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. + * Antonyms: isWire and wire. * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, @@ -568,9 +564,9 @@ void ModelOverlay::animate() { rotationMat * fbxJoints[index].postTransform); auto& jointData = jointsData[j]; jointData.translation = extractTranslation(finalMat); - jointData.translationSet = true; + jointData.translationIsDefaultPose = false; jointData.rotation = glmExtractRotation(finalMat); - jointData.rotationSet = true; + jointData.rotationIsDefaultPose = false; } } // Set the data in the model diff --git a/interface/src/ui/overlays/Overlay.cpp b/interface/src/ui/overlays/Overlay.cpp index 5cf9fc7c8b..3c952b8338 100644 --- a/interface/src/ui/overlays/Overlay.cpp +++ b/interface/src/ui/overlays/Overlay.cpp @@ -31,8 +31,7 @@ Overlay::Overlay() : _alphaPulse(0.0f), _colorPulse(0.0f), _color(DEFAULT_OVERLAY_COLOR), - _visible(true), - _anchor(NO_ANCHOR) + _visible(true) { } @@ -49,8 +48,7 @@ Overlay::Overlay(const Overlay* overlay) : _alphaPulse(overlay->_alphaPulse), _colorPulse(overlay->_colorPulse), _color(overlay->_color), - _visible(overlay->_visible), - _anchor(overlay->_anchor) + _visible(overlay->_visible) { } @@ -92,13 +90,6 @@ void Overlay::setProperties(const QVariantMap& properties) { bool visible = properties["visible"].toBool(); setVisible(visible); } - - if (properties["anchor"].isValid()) { - QString property = properties["anchor"].toString(); - if (property == "MyAvatar") { - setAnchor(MY_AVATAR); - } - } } // JSDoc for copying to @typedefs of overlay types that inherit Overlay. @@ -119,8 +110,6 @@ void Overlay::setProperties(const QVariantMap& properties) { * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and - * rotating as you move your avatar. */ QVariant Overlay::getProperty(const QString& property) { if (property == "type") { @@ -150,9 +139,6 @@ QVariant Overlay::getProperty(const QString& property) { if (property == "visible") { return _visible; } - if (property == "anchor") { - return _anchor == MY_AVATAR ? "MyAvatar" : ""; - } return QVariant(); } diff --git a/interface/src/ui/overlays/Overlay.h b/interface/src/ui/overlays/Overlay.h index 9b07f24600..e9271f3c3f 100644 --- a/interface/src/ui/overlays/Overlay.h +++ b/interface/src/ui/overlays/Overlay.h @@ -26,11 +26,6 @@ class Overlay : public QObject { Q_OBJECT public: - enum Anchor { - NO_ANCHOR, - MY_AVATAR - }; - typedef std::shared_ptr Pointer; typedef render::Payload Payload; typedef std::shared_ptr PayloadPointer; @@ -63,7 +58,6 @@ public: virtual bool isTransparent() { return getAlphaPulse() != 0.0f || getAlpha() != 1.0f; }; xColor getColor(); float getAlpha(); - Anchor getAnchor() const { return _anchor; } float getPulseMax() const { return _pulseMax; } float getPulseMin() const { return _pulseMin; } @@ -78,7 +72,6 @@ public: void setDrawHUDLayer(bool drawHUDLayer); void setColor(const xColor& color) { _color = color; } void setAlpha(float alpha) { _alpha = alpha; } - void setAnchor(Anchor anchor) { _anchor = anchor; } void setPulseMax(float value) { _pulseMax = value; } void setPulseMin(float value) { _pulseMin = value; } @@ -118,7 +111,6 @@ protected: xColor _color; bool _visible; // should the overlay be drawn at all - Anchor _anchor; unsigned int _stackOrder { 0 }; diff --git a/interface/src/ui/overlays/OverlayPanel.cpp b/interface/src/ui/overlays/OverlayPanel.cpp deleted file mode 100644 index 06480109ce..0000000000 --- a/interface/src/ui/overlays/OverlayPanel.cpp +++ /dev/null @@ -1,190 +0,0 @@ -// -// OverlayPanel.cpp -// interface/src/ui/overlays -// -// Created by Zander Otavka on 7/2/15. -// 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 "OverlayPanel.h" - -#if OVERLAY_PANELS - -#include -#include -#include -#include - -#include "avatar/AvatarManager.h" -#include "avatar/MyAvatar.h" -#include "Base3DOverlay.h" - -PropertyBinding::PropertyBinding(QString avatar, QUuid entity) : - avatar(avatar), - entity(entity) -{ -} - -QVariant propertyBindingToVariant(const PropertyBinding& value) { - QVariantMap obj; - - if (value.avatar == "MyAvatar") { - obj["avatar"] = "MyAvatar"; - } else if (!value.entity.isNull()) { - obj["entity"] = value.entity; - } - - return obj; -} - -void propertyBindingFromVariant(const QVariant& objectVar, PropertyBinding& value) { - auto object = objectVar.toMap(); - auto avatar = object["avatar"]; - auto entity = object["entity"]; - - if (avatar.isValid() && !avatar.isNull()) { - value.avatar = avatar.toString(); - } else if (entity.isValid() && !entity.isNull()) { - value.entity = entity.toUuid(); - } -} - - -void OverlayPanel::addChild(OverlayID childId) { - if (!_children.contains(childId)) { - _children.append(childId); - } -} - -void OverlayPanel::removeChild(OverlayID childId) { - if (_children.contains(childId)) { - _children.removeOne(childId); - } -} - -QVariant OverlayPanel::getProperty(const QString &property) { - if (property == "anchorPosition") { - return vec3toVariant(getAnchorPosition()); - } - if (property == "anchorPositionBinding") { - return propertyBindingToVariant(PropertyBinding(_anchorPositionBindMyAvatar ? - "MyAvatar" : "", - _anchorPositionBindEntity)); - } - if (property == "anchorRotation") { - return quatToVariant(getAnchorRotation()); - } - if (property == "anchorRotationBinding") { - return propertyBindingToVariant(PropertyBinding(_anchorRotationBindMyAvatar ? - "MyAvatar" : "", - _anchorRotationBindEntity)); - } - if (property == "anchorScale") { - return vec3toVariant(getAnchorScale()); - } - if (property == "visible") { - return getVisible(); - } - if (property == "children") { - QVariantList array; - for (int i = 0; i < _children.length(); i++) { - array.append(OverlayIDtoScriptValue(nullptr, _children[i]).toVariant()); - } - return array; - } - - auto value = Billboardable::getProperty(property); - if (value.isValid()) { - return value; - } - return PanelAttachable::getProperty(property); -} - -void OverlayPanel::setProperties(const QVariantMap& properties) { - PanelAttachable::setProperties(properties); - Billboardable::setProperties(properties); - - auto anchorPosition = properties["anchorPosition"]; - if (anchorPosition.isValid()) { - setAnchorPosition(vec3FromVariant(anchorPosition)); - } - - auto anchorPositionBinding = properties["anchorPositionBinding"]; - if (anchorPositionBinding.isValid()) { - PropertyBinding binding = {}; - propertyBindingFromVariant(anchorPositionBinding, binding); - _anchorPositionBindMyAvatar = binding.avatar == "MyAvatar"; - _anchorPositionBindEntity = binding.entity; - } - - auto anchorRotation = properties["anchorRotation"]; - if (anchorRotation.isValid()) { - setAnchorRotation(quatFromVariant(anchorRotation)); - } - - auto anchorRotationBinding = properties["anchorRotationBinding"]; - if (anchorRotationBinding.isValid()) { - PropertyBinding binding = {}; - propertyBindingFromVariant(anchorPositionBinding, binding); - _anchorRotationBindMyAvatar = binding.avatar == "MyAvatar"; - _anchorRotationBindEntity = binding.entity; - } - - auto anchorScale = properties["anchorScale"]; - if (anchorScale.isValid()) { - setAnchorScale(vec3FromVariant(anchorScale)); - } - - auto visible = properties["visible"]; - if (visible.isValid()) { - setVisible(visible.toBool()); - } -} - -void OverlayPanel::applyTransformTo(Transform& transform, bool force) { - if (force || usecTimestampNow() > _transformExpiry) { - PanelAttachable::applyTransformTo(transform, true); - if (!getParentPanel()) { - if (_anchorPositionBindMyAvatar) { - transform.setTranslation(DependencyManager::get()->getMyAvatar() - ->getPosition()); - } else if (!_anchorPositionBindEntity.isNull()) { - EntityTreePointer entityTree = DependencyManager::get()->getEntityTree(); - entityTree->withReadLock([&] { - EntityItemPointer foundEntity = entityTree->findEntityByID(_anchorPositionBindEntity); - if (foundEntity) { - transform.setTranslation(foundEntity->getPosition()); - } - }); - } else { - transform.setTranslation(getAnchorPosition()); - } - - if (_anchorRotationBindMyAvatar) { - transform.setRotation(DependencyManager::get()->getMyAvatar() - ->getOrientation()); - } else if (!_anchorRotationBindEntity.isNull()) { - EntityTreePointer entityTree = DependencyManager::get()->getEntityTree(); - entityTree->withReadLock([&] { - EntityItemPointer foundEntity = entityTree->findEntityByID(_anchorRotationBindEntity); - if (foundEntity) { - transform.setRotation(foundEntity->getRotation()); - } - }); - } else { - transform.setRotation(getAnchorRotation()); - } - - transform.setScale(getAnchorScale()); - - transform.postTranslate(getOffsetPosition()); - transform.postRotate(getOffsetRotation()); - transform.postScale(getOffsetScale()); - } - pointTransformAtCamera(transform, getOffsetRotation()); - } -} -#endif \ No newline at end of file diff --git a/interface/src/ui/overlays/OverlayPanel.h b/interface/src/ui/overlays/OverlayPanel.h deleted file mode 100644 index cff2bc224d..0000000000 --- a/interface/src/ui/overlays/OverlayPanel.h +++ /dev/null @@ -1,86 +0,0 @@ -// -// OverlayPanel.h -// interface/src/ui/overlays -// -// Created by Zander Otavka on 7/2/15. -// 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 -// - -#ifndef hifi_OverlayPanel_h -#define hifi_OverlayPanel_h - -#include - -#include -#include -#include - -#include "PanelAttachable.h" -#include "Billboardable.h" -#include "Overlay.h" - -#if OVERLAY_PANELS -class PropertyBinding { -public: - PropertyBinding() {} - PropertyBinding(QString avatar, QUuid entity); - QString avatar; - QUuid entity; -}; - -QVariant propertyBindingToVariant(const PropertyBinding& value); -void propertyBindingFromVariant(const QVariant& object, PropertyBinding& value); - - -class OverlayPanel : public QObject, public PanelAttachable, public Billboardable { - Q_OBJECT - -public: - typedef std::shared_ptr Pointer; - - void init(QScriptEngine* scriptEngine) { _scriptEngine = scriptEngine; } - - // getters - glm::vec3 getAnchorPosition() const { return _anchorTransform.getTranslation(); } - glm::quat getAnchorRotation() const { return _anchorTransform.getRotation(); } - glm::vec3 getAnchorScale() const { return _anchorTransform.getScale(); } - bool getVisible() const { return _visible; } - - // setters - void setAnchorPosition(const glm::vec3& position) { _anchorTransform.setTranslation(position); } - void setAnchorRotation(const glm::quat& rotation) { _anchorTransform.setRotation(rotation); } - void setAnchorScale(float scale) { _anchorTransform.setScale(scale); } - void setAnchorScale(const glm::vec3& scale) { _anchorTransform.setScale(scale); } - void setVisible(bool visible) { _visible = visible; } - - const QList& getChildren() { return _children; } - void addChild(OverlayID childId); - void removeChild(OverlayID childId); - OverlayID popLastChild() { return _children.takeLast(); } - - void setProperties(const QVariantMap& properties); - QVariant getProperty(const QString& property); - - virtual void applyTransformTo(Transform& transform, bool force = false) override; - -private: - Transform _anchorTransform; - - bool _anchorPositionBindMyAvatar = false; - QUuid _anchorPositionBindEntity; - - bool _anchorRotationBindMyAvatar = false; - QUuid _anchorRotationBindEntity; - - bool _visible = true; - QList _children; - - QScriptEngine* _scriptEngine; -}; - -#endif - -#endif // hifi_OverlayPanel_h diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 052ef0b6d8..7f897aee4a 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -68,16 +68,10 @@ void Overlays::cleanupAllOverlays() { foreach(Overlay::Pointer overlay, overlaysWorld) { _overlaysToDelete.push_back(overlay); } -#if OVERLAY_PANELS - _panels.clear(); -#endif cleanupOverlaysToDelete(); } void Overlays::init() { -#if OVERLAY_PANELS - _scriptEngine = new QScriptEngine(); -#endif } void Overlays::update(float deltatime) { @@ -300,12 +294,6 @@ OverlayID Overlays::cloneOverlay(OverlayID id) { if (thisOverlay) { OverlayID cloneId = addOverlay(Overlay::Pointer(thisOverlay->createClone(), [](Overlay* ptr) { ptr->deleteLater(); })); -#if OVERLAY_PANELS - auto attachable = std::dynamic_pointer_cast(thisOverlay); - if (attachable && attachable->getParentPanel()) { - attachable->getParentPanel()->addChild(cloneId); - } -#endif return cloneId; } @@ -381,15 +369,6 @@ void Overlays::deleteOverlay(OverlayID id) { } } -#if OVERLAY_PANELS - auto attachable = std::dynamic_pointer_cast(overlayToDelete); - if (attachable && attachable->getParentPanel()) { - attachable->getParentPanel()->removeChild(id); - attachable->setParentPanel(nullptr); - } -#endif - - _overlaysToDelete.push_back(overlayToDelete); emit overlayDeleted(id); } @@ -424,49 +403,6 @@ QObject* Overlays::getOverlayObject(OverlayID id) { return nullptr; } -#if OVERLAY_PANELS -OverlayID Overlays::getParentPanel(OverlayID childId) const { - Overlay::Pointer overlay = getOverlay(childId); - auto attachable = std::dynamic_pointer_cast(overlay); - if (attachable) { - return _panels.key(attachable->getParentPanel()); - } else if (_panels.contains(childId)) { - return _panels.key(getPanel(childId)->getParentPanel()); - } - return UNKNOWN_OVERLAY_ID; -} - -void Overlays::setParentPanel(OverlayID childId, OverlayID panelId) { - auto attachable = std::dynamic_pointer_cast(getOverlay(childId)); - if (attachable) { - if (_panels.contains(panelId)) { - auto panel = getPanel(panelId); - panel->addChild(childId); - attachable->setParentPanel(panel); - } else { - auto panel = attachable->getParentPanel(); - if (panel) { - panel->removeChild(childId); - attachable->setParentPanel(nullptr); - } - } - } else if (_panels.contains(childId)) { - OverlayPanel::Pointer child = getPanel(childId); - if (_panels.contains(panelId)) { - auto panel = getPanel(panelId); - panel->addChild(childId); - child->setParentPanel(panel); - } else { - auto panel = child->getParentPanel(); - if (panel) { - panel->removeChild(childId); - child->setParentPanel(0); - } - } - } -} -#endif - OverlayID Overlays::getOverlayAtPoint(const glm::vec2& point) { if (!_enabled) { return UNKNOWN_OVERLAY_ID; @@ -717,62 +653,6 @@ QSizeF Overlays::textSize(OverlayID id, const QString& text) { return QSizeF(0.0f, 0.0f); } -#if OVERLAY_PANELS -OverlayID Overlays::addPanel(OverlayPanel::Pointer panel) { - QWriteLocker lock(&_lock); - - OverlayID thisID = QUuid::createUuid(); - _panels[thisID] = panel; - - return thisID; -} - -OverlayID Overlays::addPanel(const QVariant& properties) { - OverlayPanel::Pointer panel = std::make_shared(); - panel->init(_scriptEngine); - panel->setProperties(properties.toMap()); - return addPanel(panel); -} - -void Overlays::editPanel(OverlayID panelId, const QVariant& properties) { - if (_panels.contains(panelId)) { - _panels[panelId]->setProperties(properties.toMap()); - } -} - -OverlayPropertyResult Overlays::getPanelProperty(OverlayID panelId, const QString& property) { - OverlayPropertyResult result; - if (_panels.contains(panelId)) { - OverlayPanel::Pointer thisPanel = getPanel(panelId); - QReadLocker lock(&_lock); - result.value = thisPanel->getProperty(property); - } - return result; -} - - -void Overlays::deletePanel(OverlayID panelId) { - OverlayPanel::Pointer panelToDelete; - - { - QWriteLocker lock(&_lock); - if (_panels.contains(panelId)) { - panelToDelete = _panels.take(panelId); - } else { - return; - } - } - - while (!panelToDelete->getChildren().isEmpty()) { - OverlayID childId = panelToDelete->popLastChild(); - deleteOverlay(childId); - deletePanel(childId); - } - - emit panelDeleted(panelId); -} -#endif - bool Overlays::isAddedOverlay(OverlayID id) { if (QThread::currentThread() != thread()) { bool result; diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index abd657074c..fd57869048 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -27,7 +27,6 @@ #include "Overlay.h" #include "PanelAttachable.h" -#include "OverlayPanel.h" class PickRay; @@ -93,9 +92,6 @@ public: void enable(); Overlay::Pointer getOverlay(OverlayID id) const; -#if OVERLAY_PANELS - OverlayPanel::Pointer getPanel(OverlayID id) const { return _panels[id]; } -#endif /// adds an overlay that's already been created OverlayID addOverlay(Overlay* overlay) { return addOverlay(Overlay::Pointer(overlay)); } @@ -233,6 +229,7 @@ public slots: /**jsdoc * Get the overlay script object. * @function Overlays.getOverlayObject + * @deprecated This function is deprecated and will soon be removed. * @param {Uuid} overlayID - The ID of the overlay to get the script object of. * @returns {object} The script object for the overlay if found. */ @@ -467,30 +464,6 @@ public slots: */ bool isAddedOverlay(OverlayID id); -#if OVERLAY_PANELS - OverlayID getParentPanel(OverlayID childId) const; - void setParentPanel(OverlayID childId, OverlayID panelId); - - /// adds a panel that has already been created - OverlayID addPanel(OverlayPanel::Pointer panel); - - /// creates and adds a panel based on a set of properties - OverlayID addPanel(const QVariant& properties); - - /// edit the properties of a panel - void editPanel(OverlayID panelId, const QVariant& properties); - - /// get a property of a panel - OverlayPropertyResult getPanelProperty(OverlayID panelId, const QString& property); - - /// deletes a panel and all child overlays - void deletePanel(OverlayID panelId); - - /// return true if there is a panel with that id else false - bool isAddedPanel(OverlayID id) { return _panels.contains(id); } - -#endif - /**jsdoc * Generate a mouse press event on an overlay. * @function Overlays.sendMousePressOnOverlay @@ -611,10 +584,6 @@ signals: */ void overlayDeleted(OverlayID id); -#if OVERLAY_PANELS - void panelDeleted(OverlayID id); -#endif - /**jsdoc * Triggered when a mouse press event occurs on an overlay. Only occurs for 3D overlays (unless you use * {@link Overlays.sendMousePressOnOverlay|sendMousePressOnOverlay} for a 2D overlay). @@ -731,15 +700,9 @@ private: QMap _overlaysHUD; QMap _overlaysWorld; -#if OVERLAY_PANELS - QMap _panels; -#endif QList _overlaysToDelete; unsigned int _stackOrder { 1 }; -#if OVERLAY_PANELS - QScriptEngine* _scriptEngine; -#endif bool _enabled = true; PointerEvent calculateOverlayPointerEvent(OverlayID overlayID, PickRay ray, RayToOverlayIntersectionResult rayPickResult, diff --git a/interface/src/ui/overlays/OverlaysPayload.cpp b/interface/src/ui/overlays/OverlaysPayload.cpp index fceb261503..449ac62998 100644 --- a/interface/src/ui/overlays/OverlaysPayload.cpp +++ b/interface/src/ui/overlays/OverlaysPayload.cpp @@ -65,23 +65,7 @@ namespace render { } template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) { if (args) { - if (overlay->getAnchor() == Overlay::MY_AVATAR) { - auto batch = args->_batch; - auto avatar = DependencyManager::get()->getMyAvatar(); - glm::quat myAvatarRotation = avatar->getWorldOrientation(); - glm::vec3 myAvatarPosition = avatar->getWorldPosition(); - float angle = glm::degrees(glm::angle(myAvatarRotation)); - glm::vec3 axis = glm::axis(myAvatarRotation); - float myAvatarScale = avatar->getModelScale(); - Transform transform = Transform(); - transform.setTranslation(myAvatarPosition); - transform.setRotation(glm::angleAxis(angle, axis)); - transform.setScale(myAvatarScale); - batch->setModelTransform(transform); - overlay->render(args); - } else { - overlay->render(args); - } + overlay->render(args); } } template <> const ShapeKey shapeGetShapeKey(const Overlay::Pointer& overlay) { diff --git a/interface/src/ui/overlays/PanelAttachable.cpp b/interface/src/ui/overlays/PanelAttachable.cpp index bcd32b2850..b53474390c 100644 --- a/interface/src/ui/overlays/PanelAttachable.cpp +++ b/interface/src/ui/overlays/PanelAttachable.cpp @@ -13,18 +13,8 @@ #include -#include "OverlayPanel.h" - bool PanelAttachable::getParentVisible() const { -#if OVERLAY_PANELS - if (getParentPanel()) { - return getParentPanel()->getVisible() && getParentPanel()->getParentVisible(); - } else { - return true; - } -#else return true; -#endif } // JSDoc for copying to @typedefs of overlay types that inherit PanelAttachable. @@ -67,15 +57,6 @@ bool PanelAttachable::applyTransformTo(Transform& transform, bool force) { if (force || usecTimestampNow() > _transformExpiry) { const quint64 TRANSFORM_UPDATE_PERIOD = 100000; // frequency is 10 Hz _transformExpiry = usecTimestampNow() + TRANSFORM_UPDATE_PERIOD; -#if OVERLAY_PANELS - if (getParentPanel()) { - getParentPanel()->applyTransformTo(transform, true); - transform.postTranslate(getOffsetPosition()); - transform.postRotate(getOffsetRotation()); - transform.postScale(getOffsetScale()); - return true; - } -#endif } return false; } diff --git a/interface/src/ui/overlays/PanelAttachable.h b/interface/src/ui/overlays/PanelAttachable.h index 1598aa4700..95faf38cf2 100644 --- a/interface/src/ui/overlays/PanelAttachable.h +++ b/interface/src/ui/overlays/PanelAttachable.h @@ -30,8 +30,6 @@ #ifndef hifi_PanelAttachable_h #define hifi_PanelAttachable_h -#define OVERLAY_PANELS 0 - #include #include @@ -44,18 +42,12 @@ class OverlayPanel; class PanelAttachable { public: // getters -#if OVERLAY_PANELS - std::shared_ptr getParentPanel() const { return _parentPanel; } -#endif glm::vec3 getOffsetPosition() const { return _offset.getTranslation(); } glm::quat getOffsetRotation() const { return _offset.getRotation(); } glm::vec3 getOffsetScale() const { return _offset.getScale(); } bool getParentVisible() const; // setters -#if OVERLAY_PANELS - void setParentPanel(std::shared_ptr panel) { _parentPanel = panel; } -#endif void setOffsetPosition(const glm::vec3& position) { _offset.setTranslation(position); } void setOffsetRotation(const glm::quat& rotation) { _offset.setRotation(rotation); } void setOffsetScale(float scale) { _offset.setScale(scale); } @@ -71,9 +63,6 @@ protected: quint64 _transformExpiry = 0; private: -#if OVERLAY_PANELS - std::shared_ptr _parentPanel = nullptr; -#endif Transform _offset; }; diff --git a/interface/src/ui/overlays/Rectangle3DOverlay.cpp b/interface/src/ui/overlays/Rectangle3DOverlay.cpp index 425b7e8155..e765f3fc18 100644 --- a/interface/src/ui/overlays/Rectangle3DOverlay.cpp +++ b/interface/src/ui/overlays/Rectangle3DOverlay.cpp @@ -127,8 +127,6 @@ const render::ShapeKey Rectangle3DOverlay::getShapeKey() { * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and - * rotating as you move your avatar. * * @property {string} name="" - A friendly name for the overlay. * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and @@ -138,10 +136,8 @@ const render::ShapeKey Rectangle3DOverlay::getShapeKey() { * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, - * filled, and filed. Antonyms: isWire and wire. - * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will - * be removed. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. + * Antonyms: isWire and wire. * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, diff --git a/interface/src/ui/overlays/Shape3DOverlay.cpp b/interface/src/ui/overlays/Shape3DOverlay.cpp index 92431d7716..97342a80ab 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.cpp +++ b/interface/src/ui/overlays/Shape3DOverlay.cpp @@ -128,8 +128,6 @@ void Shape3DOverlay::setProperties(const QVariantMap& properties) { * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and - * rotating as you move your avatar. * * @property {string} name="" - A friendly name for the overlay. * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and @@ -139,10 +137,8 @@ void Shape3DOverlay::setProperties(const QVariantMap& properties) { * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, - * filled, and filed. Antonyms: isWire and wire. - * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will - * be removed. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. + * Antonyms: isWire and wire. * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index 8cb7f7757b..3021aa4404 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -47,8 +47,6 @@ Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) : * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and - * rotating as you move your avatar. * * @property {string} name="" - A friendly name for the overlay. * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and @@ -58,10 +56,8 @@ Sphere3DOverlay::Sphere3DOverlay(const Sphere3DOverlay* Sphere3DOverlay) : * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, - * filled, and filed. Antonyms: isWire and wire. - * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will - * be removed. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. + * Antonyms: isWire and wire. * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, diff --git a/interface/src/ui/overlays/Text3DOverlay.cpp b/interface/src/ui/overlays/Text3DOverlay.cpp index a66b57c365..bed20be698 100644 --- a/interface/src/ui/overlays/Text3DOverlay.cpp +++ b/interface/src/ui/overlays/Text3DOverlay.cpp @@ -224,8 +224,6 @@ void Text3DOverlay::setProperties(const QVariantMap& properties) { * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and - * rotating as you move your avatar. * * @property {string} name="" - A friendly name for the overlay. * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and @@ -235,10 +233,8 @@ void Text3DOverlay::setProperties(const QVariantMap& properties) { * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, - * filled, and filed. Antonyms: isWire and wire. - * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will - * be removed. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. + * Antonyms: isWire and wire. * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, diff --git a/interface/src/ui/overlays/Web3DOverlay.cpp b/interface/src/ui/overlays/Web3DOverlay.cpp index a5da5e99b6..8121b985cb 100644 --- a/interface/src/ui/overlays/Web3DOverlay.cpp +++ b/interface/src/ui/overlays/Web3DOverlay.cpp @@ -50,7 +50,7 @@ #include "ui/DomainConnectionModel.h" #include "ui/AvatarInputs.h" #include "avatar/AvatarManager.h" -#include "scripting/GlobalServicesScriptingInterface.h" +#include "scripting/AccountServicesScriptingInterface.h" #include #include "ui/Snapshot.h" #include "SoundCache.h" @@ -192,7 +192,10 @@ void Web3DOverlay::setupQmlSurface() { _webSurface->getSurfaceContext()->setContextProperty("offscreenFlags", flags); _webSurface->getSurfaceContext()->setContextProperty("AddressManager", DependencyManager::get().data()); - _webSurface->getSurfaceContext()->setContextProperty("Account", GlobalServicesScriptingInterface::getInstance()); + + _webSurface->getSurfaceContext()->setContextProperty("Account", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED + _webSurface->getSurfaceContext()->setContextProperty("GlobalServices", AccountServicesScriptingInterface::getInstance()); // DEPRECATED - TO BE REMOVED + _webSurface->getSurfaceContext()->setContextProperty("AccountServices", AccountServicesScriptingInterface::getInstance()); // in Qt 5.10.0 there is already an "Audio" object in the QML context // though I failed to find it (from QtMultimedia??). So.. let it be "AudioScriptingInterface" @@ -208,7 +211,6 @@ void Web3DOverlay::setupQmlSurface() { _webSurface->getSurfaceContext()->setContextProperty("OctreeStats", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("DCModel", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("AvatarInputs", AvatarInputs::getInstance()); - _webSurface->getSurfaceContext()->setContextProperty("GlobalServices", GlobalServicesScriptingInterface::getInstance()); _webSurface->getSurfaceContext()->setContextProperty("AvatarList", DependencyManager::get().data()); _webSurface->getSurfaceContext()->setContextProperty("DialogsManager", DialogsManagerScriptingInterface::getInstance()); _webSurface->getSurfaceContext()->setContextProperty("InputConfiguration", DependencyManager::get().data()); @@ -222,10 +224,6 @@ void Web3DOverlay::setupQmlSurface() { _webSurface->getSurfaceContext()->setContextProperty("pathToFonts", "../../"); - tabletScriptingInterface->setQmlTabletRoot("com.highfidelity.interface.tablet.system", _webSurface.data()); - // mark the TabletProxy object as cpp ownership. - QObject* tablet = tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"); - _webSurface->getSurfaceContext()->engine()->setObjectOwnership(tablet, QQmlEngine::CppOwnership); // Override min fps for tablet UI, for silky smooth scrolling setMaxFPS(90); } @@ -486,8 +484,6 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) { * the pulse multiplier is applied out of phase with the pulse period. (The magnitude of the property isn't otherwise * used.) * @property {boolean} visible=true - If true, the overlay is rendered, otherwise it is not rendered. - * @property {string} anchor="" - If set to "MyAvatar" then the overlay is attached to your avatar, moving and - * rotating as you move your avatar. * * @property {string} name="" - A friendly name for the overlay. * @property {Vec3} position - The position of the overlay center. Synonyms: p1, point, and @@ -497,10 +493,8 @@ void Web3DOverlay::setProperties(const QVariantMap& properties) { * @property {Quat} rotation - The orientation of the overlay. Synonym: orientation. * @property {Quat} localRotation - The orientation of the overlay relative to its parent if the overlay has a * parentID set, otherwise the same value as rotation. - * @property {boolean} isSolid=false - Synonyms: solid, isFilled, - * filled, and filed. Antonyms: isWire and wire. - * Deprecated: The erroneous property spelling "filed" is deprecated and support for it will - * be removed. + * @property {boolean} isSolid=false - Synonyms: solid, isFilled, and filled. + * Antonyms: isWire and wire. * @property {boolean} isDashedLine=false - If true, a dashed line is drawn on the overlay's edges. Synonym: * dashed. * @property {boolean} ignoreRayIntersection=false - If true, diff --git a/libraries/animation/CMakeLists.txt b/libraries/animation/CMakeLists.txt index b9fef19fbb..1ec6194afd 100644 --- a/libraries/animation/CMakeLists.txt +++ b/libraries/animation/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME animation) setup_hifi_library(Network Script) -link_hifi_libraries(shared model fbx) +link_hifi_libraries(shared graphics fbx) include_hifi_library_headers(networking) include_hifi_library_headers(gpu) diff --git a/libraries/animation/src/AnimClip.cpp b/libraries/animation/src/AnimClip.cpp index 1118e21c91..2ead4558fe 100644 --- a/libraries/animation/src/AnimClip.cpp +++ b/libraries/animation/src/AnimClip.cpp @@ -13,8 +13,6 @@ #include "AnimationLogging.h" #include "AnimUtil.h" -bool AnimClip::usePreAndPostPoseFromAnim = true; - AnimClip::AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag) : AnimNode(AnimNode::Type::Clip, id), _startFrame(startFrame), @@ -138,14 +136,8 @@ void AnimClip::copyFromNetworkAnim() { if (skeletonJoint >= 0 && skeletonJoint < skeletonJointCount) { AnimPose preRot, postRot; - if (usePreAndPostPoseFromAnim) { - preRot = animSkeleton.getPreRotationPose(animJoint); - postRot = animSkeleton.getPostRotationPose(animJoint); - } else { - // In order to support Blender, which does not have preRotation FBX support, we use the models defaultPose as the reference frame for the animations. - preRot = AnimPose(glm::vec3(1.0f), _skeleton->getRelativeBindPose(skeletonJoint).rot(), glm::vec3()); - postRot = AnimPose::identity; - } + preRot = animSkeleton.getPreRotationPose(animJoint); + postRot = animSkeleton.getPostRotationPose(animJoint); // cancel out scale preRot.scale() = glm::vec3(1.0f); diff --git a/libraries/animation/src/AnimClip.h b/libraries/animation/src/AnimClip.h index c7e7ebf3ee..717972ca26 100644 --- a/libraries/animation/src/AnimClip.h +++ b/libraries/animation/src/AnimClip.h @@ -25,8 +25,6 @@ class AnimClip : public AnimNode { public: friend class AnimTests; - static bool usePreAndPostPoseFromAnim; - AnimClip(const QString& id, const QString& url, float startFrame, float endFrame, float timeScale, bool loopFlag, bool mirrorFlag); virtual ~AnimClip() override; diff --git a/libraries/animation/src/AnimInverseKinematics.cpp b/libraries/animation/src/AnimInverseKinematics.cpp index d17fbebf3a..e9f9f8818f 100644 --- a/libraries/animation/src/AnimInverseKinematics.cpp +++ b/libraries/animation/src/AnimInverseKinematics.cpp @@ -591,19 +591,31 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const glm::vec3 d = basePose.trans() - topPose.trans(); float dLen = glm::length(d); if (dLen > EPSILON) { + glm::vec3 dUnit = d / dLen; glm::vec3 e = midPose.xformVector(target.getPoleReferenceVector()); + + // if mid joint is straight use the reference vector to compute eProj, otherwise use reference vector. + // however if mid joint angle is in between the two blend between both solutions. + vec3 u = normalize(basePose.trans() - midPose.trans()); + vec3 v = normalize(topPose.trans() - midPose.trans()); + + const float LERP_THRESHOLD = 3.05433f; // 175 deg + const float BENT_THRESHOLD = 2.96706f; // 170 deg + + float jointAngle = acos(dot(u, v)); + if (jointAngle < BENT_THRESHOLD) { + glm::vec3 midPoint = topPose.trans() + d * 0.5f; + e = normalize(midPose.trans() - midPoint); + } else if (jointAngle < LERP_THRESHOLD) { + glm::vec3 midPoint = topPose.trans() + d * 0.5f; + float alpha = (jointAngle - LERP_THRESHOLD) / (BENT_THRESHOLD - LERP_THRESHOLD); + e = lerp(e, normalize(midPose.trans() - midPoint), alpha); + } + glm::vec3 eProj = e - glm::dot(e, dUnit) * dUnit; float eProjLen = glm::length(eProj); - const float MIN_EPROJ_LEN = 0.5f; - if (eProjLen < MIN_EPROJ_LEN) { - glm::vec3 midPoint = topPose.trans() + d * 0.5f; - e = midPose.trans() - midPoint; - eProj = e - glm::dot(e, dUnit) * dUnit; - eProjLen = glm::length(eProj); - } - glm::vec3 p = target.getPoleVector(); glm::vec3 pProj = p - glm::dot(p, dUnit) * dUnit; float pProjLen = glm::length(pProj); @@ -634,16 +646,27 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const AnimPose geomToWorldPose = AnimPose(context.getRigToWorldMatrix() * context.getGeometryToRigMatrix()); - glm::vec3 dUnit = d / dLen; glm::vec3 e = midPose.xformVector(target.getPoleReferenceVector()); - glm::vec3 eProj = e - glm::dot(e, dUnit) * dUnit; - float eProjLen = glm::length(eProj); - const float MIN_EPROJ_LEN = 0.5f; - if (eProjLen < MIN_EPROJ_LEN) { + + // if mid joint is straight use the reference vector to compute eProj, otherwise use reference vector. + // however if mid joint angle is in between the two blend between both solutions. + vec3 u = normalize(basePose.trans() - midPose.trans()); + vec3 v = normalize(topPose.trans() - midPose.trans()); + + const float LERP_THRESHOLD = 3.05433f; // 175 deg + const float BENT_THRESHOLD = 2.96706f; // 170 deg + + float jointAngle = acos(dot(u, v)); + glm::vec4 eColor = RED; + if (jointAngle < BENT_THRESHOLD) { glm::vec3 midPoint = topPose.trans() + d * 0.5f; - e = midPose.trans() - midPoint; - eProj = e - glm::dot(e, dUnit) * dUnit; - eProjLen = glm::length(eProj); + e = normalize(midPose.trans() - midPoint); + eColor = GREEN; + } else if (jointAngle < LERP_THRESHOLD) { + glm::vec3 midPoint = topPose.trans() + d * 0.5f; + float alpha = (jointAngle - LERP_THRESHOLD) / (BENT_THRESHOLD - LERP_THRESHOLD); + e = lerp(e, normalize(midPose.trans() - midPoint), alpha); + eColor = YELLOW; } glm::vec3 p = target.getPoleVector(); @@ -655,7 +678,7 @@ void AnimInverseKinematics::solveTargetWithCCD(const AnimContext& context, const YELLOW); DebugDraw::getInstance().drawRay(geomToWorldPose.xformPoint(midPoint), geomToWorldPose.xformPoint(midPoint + PROJ_VECTOR_LEN * glm::normalize(e)), - RED); + eColor); DebugDraw::getInstance().drawRay(geomToWorldPose.xformPoint(midPoint), geomToWorldPose.xformPoint(midPoint + POLE_VECTOR_LEN * glm::normalize(p)), BLUE); @@ -1232,7 +1255,7 @@ void AnimInverseKinematics::initConstraints() { // / / // O--O O--O - loadDefaultPoses(_skeleton->getRelativeBindPoses()); + loadDefaultPoses(_skeleton->getRelativeDefaultPoses()); int numJoints = (int)_defaultRelativePoses.size(); diff --git a/libraries/animation/src/AnimManipulator.cpp b/libraries/animation/src/AnimManipulator.cpp index 070949ab3b..46b3cf1c28 100644 --- a/libraries/animation/src/AnimManipulator.cpp +++ b/libraries/animation/src/AnimManipulator.cpp @@ -33,7 +33,7 @@ AnimManipulator::~AnimManipulator() { } const AnimPoseVec& AnimManipulator::evaluate(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut) { - return overlay(animVars, context, dt, triggersOut, _skeleton->getRelativeBindPoses()); + return overlay(animVars, context, dt, triggersOut, _skeleton->getRelativeDefaultPoses()); } const AnimPoseVec& AnimManipulator::overlay(const AnimVariantMap& animVars, const AnimContext& context, float dt, Triggers& triggersOut, const AnimPoseVec& underPoses) { diff --git a/libraries/animation/src/AnimPose.cpp b/libraries/animation/src/AnimPose.cpp index 470bbab8b6..a0b8fba1da 100644 --- a/libraries/animation/src/AnimPose.cpp +++ b/libraries/animation/src/AnimPose.cpp @@ -76,3 +76,5 @@ AnimPose::operator glm::mat4() const { return glm::mat4(glm::vec4(xAxis, 0.0f), glm::vec4(yAxis, 0.0f), glm::vec4(zAxis, 0.0f), glm::vec4(_trans, 1.0f)); } + + diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 804ffb0583..2bce16c5ca 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -56,14 +56,6 @@ int AnimSkeleton::getChainDepth(int jointIndex) const { } } -const AnimPose& AnimSkeleton::getAbsoluteBindPose(int jointIndex) const { - return _absoluteBindPoses[jointIndex]; -} - -const AnimPose& AnimSkeleton::getRelativeBindPose(int jointIndex) const { - return _relativeBindPoses[jointIndex]; -} - const AnimPose& AnimSkeleton::getRelativeDefaultPose(int jointIndex) const { return _relativeDefaultPoses[jointIndex]; } @@ -164,8 +156,6 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) _joints = joints; _jointsSize = (int)joints.size(); // build a cache of bind poses - _absoluteBindPoses.reserve(_jointsSize); - _relativeBindPoses.reserve(_jointsSize); // build a chache of default poses _absoluteDefaultPoses.reserve(_jointsSize); @@ -192,28 +182,6 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints) } else { _absoluteDefaultPoses.push_back(relDefaultPose); } - - // build relative and absolute bind poses - if (_joints[i].bindTransformFoundInCluster) { - // Use the FBXJoint::bindTransform, which is absolute model coordinates - // i.e. not relative to it's parent. - AnimPose absoluteBindPose(_joints[i].bindTransform); - _absoluteBindPoses.push_back(absoluteBindPose); - if (parentIndex >= 0) { - AnimPose inverseParentAbsoluteBindPose = _absoluteBindPoses[parentIndex].inverse(); - _relativeBindPoses.push_back(inverseParentAbsoluteBindPose * absoluteBindPose); - } else { - _relativeBindPoses.push_back(absoluteBindPose); - } - } else { - // use default transform instead - _relativeBindPoses.push_back(relDefaultPose); - if (parentIndex >= 0) { - _absoluteBindPoses.push_back(_absoluteBindPoses[parentIndex] * relDefaultPose); - } else { - _absoluteBindPoses.push_back(relDefaultPose); - } - } } for (int i = 0; i < _jointsSize; i++) { @@ -251,8 +219,6 @@ void AnimSkeleton::dump(bool verbose) const { qCDebug(animation) << " {"; qCDebug(animation) << " index =" << i; qCDebug(animation) << " name =" << getJointName(i); - qCDebug(animation) << " absBindPose =" << getAbsoluteBindPose(i); - qCDebug(animation) << " relBindPose =" << getRelativeBindPose(i); qCDebug(animation) << " absDefaultPose =" << getAbsoluteDefaultPose(i); qCDebug(animation) << " relDefaultPose =" << getRelativeDefaultPose(i); if (verbose) { @@ -287,8 +253,6 @@ void AnimSkeleton::dump(const AnimPoseVec& poses) const { qCDebug(animation) << " {"; qCDebug(animation) << " index =" << i; qCDebug(animation) << " name =" << getJointName(i); - qCDebug(animation) << " absBindPose =" << getAbsoluteBindPose(i); - qCDebug(animation) << " relBindPose =" << getRelativeBindPose(i); qCDebug(animation) << " absDefaultPose =" << getAbsoluteDefaultPose(i); qCDebug(animation) << " relDefaultPose =" << getRelativeDefaultPose(i); qCDebug(animation) << " pose =" << poses[i]; diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 99c9a148f7..664358f414 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -30,13 +30,6 @@ public: int getNumJoints() const; int getChainDepth(int jointIndex) const; - // absolute pose, not relative to parent - const AnimPose& getAbsoluteBindPose(int jointIndex) const; - - // relative to parent pose - const AnimPose& getRelativeBindPose(int jointIndex) const; - const AnimPoseVec& getRelativeBindPoses() const { return _relativeBindPoses; } - // the default poses are the orientations of the joints on frame 0. const AnimPose& getRelativeDefaultPose(int jointIndex) const; const AnimPoseVec& getRelativeDefaultPoses() const { return _relativeDefaultPoses; } @@ -72,8 +65,6 @@ protected: std::vector _joints; int _jointsSize { 0 }; - AnimPoseVec _absoluteBindPoses; - AnimPoseVec _relativeBindPoses; AnimPoseVec _relativeDefaultPoses; AnimPoseVec _absoluteDefaultPoses; AnimPoseVec _relativePreRotationPoses; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 44745c5c2d..b1b41775a8 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -179,7 +179,7 @@ void Rig::restoreRoleAnimation(const QString& role) { } else { qCWarning(animation) << "Rig::restoreRoleAnimation could not find role " << role; } - + auto statesIter = _roleAnimStates.find(role); if (statesIter != _roleAnimStates.end()) { _roleAnimStates.erase(statesIter); @@ -1050,52 +1050,6 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons } } -void Rig::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority, - const QVector& freeLineage, glm::mat4 rootTransform) { - ASSERT(false); -} - -bool Rig::restoreJointPosition(int jointIndex, float fraction, float priority, const QVector& freeLineage) { - ASSERT(false); - return false; -} - -float Rig::getLimbLength(int jointIndex, const QVector& freeLineage, - const glm::vec3 scale, const QVector& fbxJoints) const { - ASSERT(false); - return 1.0f; -} - -glm::quat Rig::setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority) { - ASSERT(false); - return glm::quat(); -} - -glm::vec3 Rig::getJointDefaultTranslationInConstrainedFrame(int jointIndex) { - ASSERT(false); - return glm::vec3(); -} - -glm::quat Rig::setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, float priority, float mix) { - ASSERT(false); - return glm::quat(); -} - -bool Rig::getJointRotationInConstrainedFrame(int jointIndex, glm::quat& quatOut) const { - ASSERT(false); - return false; -} - -void Rig::clearJointStatePriorities() { - ASSERT(false); -} - -glm::quat Rig::getJointDefaultRotationInParentFrame(int jointIndex) { - ASSERT(false); - return glm::quat(); -} - - void Rig::updateFromEyeParameters(const EyeParameters& params) { updateEyeJoint(params.leftEyeJointIndex, params.modelTranslation, params.modelRotation, params.eyeLookAt, params.eyeSaccade); updateEyeJoint(params.rightEyeJointIndex, params.modelTranslation, params.modelRotation, params.eyeLookAt, params.eyeSaccade); @@ -1265,7 +1219,8 @@ glm::vec3 Rig::deflectHandFromTorso(const glm::vec3& handPosition, const FBXJoin return position; } -void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool leftArmEnabled, bool rightArmEnabled, float dt, +void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated, + bool leftArmEnabled, bool rightArmEnabled, float dt, const AnimPose& leftHandPose, const AnimPose& rightHandPose, const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo, const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo) { @@ -1279,7 +1234,7 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab glm::vec3 handPosition = leftHandPose.trans(); glm::quat handRotation = leftHandPose.rot(); - if (!hipsEnabled) { + if (!hipsEnabled || hipsEstimated) { // prevent the hand IK targets from intersecting the torso handPosition = deflectHandFromTorso(handPosition, hipsShapeInfo, spineShapeInfo, spine1ShapeInfo, spine2ShapeInfo); } @@ -1326,7 +1281,7 @@ void Rig::updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnab glm::vec3 handPosition = rightHandPose.trans(); glm::quat handRotation = rightHandPose.rot(); - if (!hipsEnabled) { + if (!hipsEnabled || hipsEstimated) { // prevent the hand IK targets from intersecting the torso handPosition = deflectHandFromTorso(handPosition, hipsShapeInfo, spineShapeInfo, spine1ShapeInfo, spine2ShapeInfo); } @@ -1550,20 +1505,20 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo _animVars.set("isTalking", params.isTalking); _animVars.set("notIsTalking", !params.isTalking); - bool headEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_Head]; - bool leftHandEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_LeftHand]; - bool rightHandEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_RightHand]; - bool hipsEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_Hips]; - bool leftFootEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_LeftFoot]; - bool rightFootEnabled = params.primaryControllerActiveFlags[PrimaryControllerType_RightFoot]; - bool spine2Enabled = params.primaryControllerActiveFlags[PrimaryControllerType_Spine2]; - - bool leftArmEnabled = params.secondaryControllerActiveFlags[SecondaryControllerType_LeftArm]; - bool rightArmEnabled = params.secondaryControllerActiveFlags[SecondaryControllerType_RightArm]; + bool headEnabled = params.primaryControllerFlags[PrimaryControllerType_Head] & (uint8_t)ControllerFlags::Enabled; + bool leftHandEnabled = params.primaryControllerFlags[PrimaryControllerType_LeftHand] & (uint8_t)ControllerFlags::Enabled; + bool rightHandEnabled = params.primaryControllerFlags[PrimaryControllerType_RightHand] & (uint8_t)ControllerFlags::Enabled; + bool hipsEnabled = params.primaryControllerFlags[PrimaryControllerType_Hips] & (uint8_t)ControllerFlags::Enabled; + bool hipsEstimated = params.primaryControllerFlags[PrimaryControllerType_Hips] & (uint8_t)ControllerFlags::Estimated; + bool leftFootEnabled = params.primaryControllerFlags[PrimaryControllerType_LeftFoot] & (uint8_t)ControllerFlags::Enabled; + bool rightFootEnabled = params.primaryControllerFlags[PrimaryControllerType_RightFoot] & (uint8_t)ControllerFlags::Enabled; + bool spine2Enabled = params.primaryControllerFlags[PrimaryControllerType_Spine2] & (uint8_t)ControllerFlags::Enabled; + bool leftArmEnabled = params.secondaryControllerFlags[SecondaryControllerType_LeftArm] & (uint8_t)ControllerFlags::Enabled; + bool rightArmEnabled = params.secondaryControllerFlags[SecondaryControllerType_RightArm] & (uint8_t)ControllerFlags::Enabled; updateHead(headEnabled, hipsEnabled, params.primaryControllerPoses[PrimaryControllerType_Head]); - updateHands(leftHandEnabled, rightHandEnabled, hipsEnabled, leftArmEnabled, rightArmEnabled, dt, + updateHands(leftHandEnabled, rightHandEnabled, hipsEnabled, hipsEstimated, leftArmEnabled, rightArmEnabled, dt, params.primaryControllerPoses[PrimaryControllerType_LeftHand], params.primaryControllerPoses[PrimaryControllerType_RightHand], params.hipsShapeInfo, params.spineShapeInfo, params.spine1ShapeInfo, params.spine2ShapeInfo); @@ -1623,7 +1578,7 @@ void Rig::updateFromControllerParameters(const ControllerParameters& params, flo for (int i = 0; i < (int)NumSecondaryControllerTypes; i++) { int index = indexOfJoint(secondaryControllerJointNames[i]); if (index >= 0) { - if (params.secondaryControllerActiveFlags[i]) { + if (params.secondaryControllerFlags[i] & (uint8_t)ControllerFlags::Enabled) { ikNode->setSecondaryTargetInRigFrame(index, params.secondaryControllerPoses[i]); } else { ikNode->clearSecondaryTarget(index); @@ -1731,6 +1686,14 @@ glm::mat4 Rig::getJointTransform(int jointIndex) const { } } +AnimPose Rig::getJointPose(int jointIndex) const { + if (isIndexValid(jointIndex)) { + return _internalPoseSet._absolutePoses[jointIndex]; + } else { + return AnimPose::identity; + } +} + void Rig::copyJointsIntoJointData(QVector& jointDataVec) const { const AnimPose geometryToRigPose(_geometryToRigTransform); @@ -1742,16 +1705,16 @@ void Rig::copyJointsIntoJointData(QVector& jointDataVec) const { // rotations are in absolute rig frame. glm::quat defaultAbsRot = geometryToRigPose.rot() * _animSkeleton->getAbsoluteDefaultPose(i).rot(); data.rotation = _internalPoseSet._absolutePoses[i].rot(); - data.rotationSet = !isEqual(data.rotation, defaultAbsRot); + data.rotationIsDefaultPose = isEqual(data.rotation, defaultAbsRot); // translations are in relative frame but scaled so that they are in meters, // instead of geometry units. glm::vec3 defaultRelTrans = _geometryOffset.scale() * _animSkeleton->getRelativeDefaultPose(i).trans(); data.translation = _geometryOffset.scale() * _internalPoseSet._relativePoses[i].trans(); - data.translationSet = !isEqual(data.translation, defaultRelTrans); + data.translationIsDefaultPose = isEqual(data.translation, defaultRelTrans); } else { - data.translationSet = false; - data.rotationSet = false; + data.translationIsDefaultPose = true; + data.rotationIsDefaultPose = true; } } } @@ -1776,11 +1739,11 @@ void Rig::copyJointsFromJointData(const QVector& jointDataVec) { const glm::quat rigToGeometryRot(glmExtractRotation(_rigToGeometryTransform)); for (int i = 0; i < numJoints; i++) { const JointData& data = jointDataVec.at(i); - if (data.rotationSet) { + if (data.rotationIsDefaultPose) { + rotations.push_back(absoluteDefaultPoses[i].rot()); + } else { // JointData rotations are in absolute rig-frame so we rotate them to absolute geometry-frame rotations.push_back(rigToGeometryRot * data.rotation); - } else { - rotations.push_back(absoluteDefaultPoses[i].rot()); } } @@ -1796,11 +1759,11 @@ void Rig::copyJointsFromJointData(const QVector& jointDataVec) { const JointData& data = jointDataVec.at(i); _internalPoseSet._relativePoses[i].scale() = Vectors::ONE; _internalPoseSet._relativePoses[i].rot() = rotations[i]; - if (data.translationSet) { + if (data.translationIsDefaultPose) { + _internalPoseSet._relativePoses[i].trans() = relativeDefaultPoses[i].trans(); + } else { // JointData translations are in scaled relative-frame so we scale back to regular relative-frame _internalPoseSet._relativePoses[i].trans() = _invGeometryOffset.scale() * data.translation; - } else { - _internalPoseSet._relativePoses[i].trans() = relativeDefaultPoses[i].trans(); } } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 1ec4d9527f..87277af754 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -69,11 +69,16 @@ public: NumSecondaryControllerTypes }; + enum class ControllerFlags : uint8_t { + Enabled = 0x01, + Estimated = 0x02 + }; + struct ControllerParameters { AnimPose primaryControllerPoses[NumPrimaryControllerTypes]; // rig space - bool primaryControllerActiveFlags[NumPrimaryControllerTypes]; + uint8_t primaryControllerFlags[NumPrimaryControllerTypes]; AnimPose secondaryControllerPoses[NumSecondaryControllerTypes]; // rig space - bool secondaryControllerActiveFlags[NumSecondaryControllerTypes]; + uint8_t secondaryControllerFlags[NumSecondaryControllerTypes]; bool isTalking; FBXJointShapeInfo hipsShapeInfo; FBXJointShapeInfo spineShapeInfo; @@ -159,6 +164,7 @@ public: // rig space glm::mat4 getJointTransform(int jointIndex) const; + AnimPose getJointPose(int jointIndex) const; // Start or stop animations as needed. void computeMotionAnimationState(float deltaTime, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation, CharacterControllerState ccState); @@ -166,36 +172,6 @@ public: // Regardless of who started the animations or how many, update the joints. void updateAnimations(float deltaTime, const glm::mat4& rootTransform, const glm::mat4& rigToWorldTransform); - // legacy - void inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority, - const QVector& freeLineage, glm::mat4 rootTransform); - - // legacy - bool restoreJointPosition(int jointIndex, float fraction, float priority, const QVector& freeLineage); - - // legacy - float getLimbLength(int jointIndex, const QVector& freeLineage, - const glm::vec3 scale, const QVector& fbxJoints) const; - - // legacy - glm::quat setJointRotationInBindFrame(int jointIndex, const glm::quat& rotation, float priority); - - // legacy - glm::vec3 getJointDefaultTranslationInConstrainedFrame(int jointIndex); - - // legacy - glm::quat setJointRotationInConstrainedFrame(int jointIndex, glm::quat targetRotation, - float priority, float mix = 1.0f); - - // legacy - bool getJointRotationInConstrainedFrame(int jointIndex, glm::quat& rotOut) const; - - // legacy - glm::quat getJointDefaultRotationInParentFrame(int jointIndex); - - // legacy - void clearJointStatePriorities(); - void updateFromControllerParameters(const ControllerParameters& params, float dt); void updateFromEyeParameters(const EyeParameters& params); @@ -251,7 +227,8 @@ protected: void buildAbsoluteRigPoses(const AnimPoseVec& relativePoses, AnimPoseVec& absolutePosesOut); void updateHead(bool headEnabled, bool hipsEnabled, const AnimPose& headMatrix); - void updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool leftArmEnabled, bool rightArmEnabled, float dt, + void updateHands(bool leftHandEnabled, bool rightHandEnabled, bool hipsEnabled, bool hipsEstimated, + bool leftArmEnabled, bool rightArmEnabled, float dt, const AnimPose& leftHandPose, const AnimPose& rightHandPose, const FBXJointShapeInfo& hipsShapeInfo, const FBXJointShapeInfo& spineShapeInfo, const FBXJointShapeInfo& spine1ShapeInfo, const FBXJointShapeInfo& spine2ShapeInfo); @@ -338,7 +315,7 @@ protected: float firstFrame; float lastFrame; }; - + struct RoleAnimState { RoleAnimState() {} RoleAnimState(const QString& roleId, const QString& urlIn, float fpsIn, bool loopIn, float firstFrameIn, float lastFrameIn) : diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index af86499101..579910d9f7 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -782,7 +782,7 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) { _selectedCodecName = selectedCodecName; - qCDebug(audioclient) << "Selected Codec:" << _selectedCodecName; + qCDebug(audioclient) << "Selected Codec:" << _selectedCodecName << "isStereoInput:" << _isStereoInput; // release any old codec encoder/decoder first... if (_codec && _encoder) { @@ -797,7 +797,7 @@ void AudioClient::selectAudioFormat(const QString& selectedCodecName) { if (_selectedCodecName == plugin->getName()) { _codec = plugin; _receivedAudioStream.setupCodec(plugin, _selectedCodecName, AudioConstants::STEREO); - _encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, AudioConstants::MONO); + _encoder = plugin->createEncoder(AudioConstants::SAMPLE_RATE, _isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO); qCDebug(audioclient) << "Selected Codec Plugin:" << _codec.get(); break; } @@ -1079,7 +1079,7 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) { encodedBuffer = audioBuffer; } - emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, + emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), _outgoingAvatarAudioSequenceNumber, _isStereoInput, audioTransform, avatarBoundingBoxCorner, avatarBoundingBoxScale, packetType, _selectedCodecName); _stats.sentPacket(); @@ -1382,7 +1382,16 @@ void AudioClient::setIsStereoInput(bool isStereoInput) { _desiredInputFormat.setChannelCount(1); } - // change in channel count for desired input format, restart the input device + // restart the codec + if (_codec) { + if (_encoder) { + _codec->releaseEncoder(_encoder); + } + _encoder = _codec->createEncoder(AudioConstants::SAMPLE_RATE, _isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO); + } + qCDebug(audioclient) << "Reset Codec:" << _selectedCodecName << "isStereoInput:" << _isStereoInput; + + // restart the input device switchInputToAudioDevice(_inputDeviceInfo); } } @@ -1418,7 +1427,7 @@ void AudioClient::outputFormatChanged() { _receivedAudioStream.outputFormatChanged(_outputFormat.sampleRate(), OUTPUT_CHANNEL_COUNT); } -bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo, bool isShutdownRequest) { +bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest) { qCDebug(audioclient) << __FUNCTION__ << "inputDeviceInfo: [" << inputDeviceInfo.deviceName() << "]"; bool supportedFormat = false; @@ -1601,7 +1610,7 @@ void AudioClient::outputNotify() { } } -bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo, bool isShutdownRequest) { +bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest) { qCDebug(audioclient) << "AudioClient::switchOutputToAudioDevice() outputDeviceInfo: [" << outputDeviceInfo.deviceName() << "]"; bool supportedFormat = false; diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 0ceb9c4dc3..0643b8e52a 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -378,8 +378,8 @@ private: void handleLocalEchoAndReverb(QByteArray& inputByteArray); - bool switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo, bool isShutdownRequest = false); - bool switchOutputToAudioDevice(const QAudioDeviceInfo& outputDeviceInfo, bool isShutdownRequest = false); + bool switchInputToAudioDevice(const QAudioDeviceInfo inputDeviceInfo, bool isShutdownRequest = false); + bool switchOutputToAudioDevice(const QAudioDeviceInfo outputDeviceInfo, bool isShutdownRequest = false); // Callback acceleration dependent calculations int calculateNumberOfInputCallbackBytes(const QAudioFormat& format) const; diff --git a/libraries/audio/src/AbstractAudioInterface.cpp b/libraries/audio/src/AbstractAudioInterface.cpp index 4def97596f..376ecddd34 100644 --- a/libraries/audio/src/AbstractAudioInterface.cpp +++ b/libraries/audio/src/AbstractAudioInterface.cpp @@ -19,7 +19,7 @@ #include "AudioConstants.h" -void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, +void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, bool isStereo, const Transform& transform, glm::vec3 avatarBoundingBoxCorner, glm::vec3 avatarBoundingBoxScale, PacketType packetType, QString codecName) { static std::mutex _mutex; @@ -30,9 +30,6 @@ void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes Locker lock(_mutex); auto audioPacket = NLPacket::create(packetType); - // FIXME - this is not a good way to determine stereoness with codecs.... - quint8 isStereo = bytes == AudioConstants::NETWORK_FRAME_BYTES_STEREO ? 1 : 0; - // write sequence number auto sequence = sequenceNumber++; audioPacket->writePrimitive(sequence); @@ -48,7 +45,8 @@ void AbstractAudioInterface::emitAudioPacket(const void* audioData, size_t bytes audioPacket->writePrimitive(numSilentSamples); } else { // set the mono/stereo byte - audioPacket->writePrimitive(isStereo); + quint8 channelFlag = isStereo ? 1 : 0; + audioPacket->writePrimitive(channelFlag); } // pack the three float positions diff --git a/libraries/audio/src/AbstractAudioInterface.h b/libraries/audio/src/AbstractAudioInterface.h index 8b48b55206..37731c31f7 100644 --- a/libraries/audio/src/AbstractAudioInterface.h +++ b/libraries/audio/src/AbstractAudioInterface.h @@ -29,7 +29,7 @@ class AbstractAudioInterface : public QObject { public: AbstractAudioInterface(QObject* parent = 0) : QObject(parent) {}; - static void emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, + static void emitAudioPacket(const void* audioData, size_t bytes, quint16& sequenceNumber, bool isStereo, const Transform& transform, glm::vec3 avatarBoundingBoxCorner, glm::vec3 avatarBoundingBoxScale, PacketType packetType, QString codecName = QString("")); diff --git a/libraries/audio/src/AudioInjector.cpp b/libraries/audio/src/AudioInjector.cpp index 9f32372a8e..d1b919292a 100644 --- a/libraries/audio/src/AudioInjector.cpp +++ b/libraries/audio/src/AudioInjector.cpp @@ -427,28 +427,11 @@ AudioInjectorPointer AudioInjector::playSound(SharedSoundPointer sound, const fl options.stereo = sound->isStereo(); options.position = position; options.volume = volume; + options.pitch = 1.0f / stretchFactor; QByteArray samples = sound->getByteArray(); - if (stretchFactor == 1.0f) { - return playSoundAndDelete(samples, options); - } - const int standardRate = AudioConstants::SAMPLE_RATE; - const int resampledRate = standardRate * stretchFactor; - const int channelCount = sound->isStereo() ? 2 : 1; - - AudioSRC resampler(standardRate, resampledRate, channelCount); - - const int nInputFrames = samples.size() / (channelCount * sizeof(int16_t)); - const int maxOutputFrames = resampler.getMaxOutput(nInputFrames); - QByteArray resampled(maxOutputFrames * channelCount * sizeof(int16_t), '\0'); - - int nOutputFrames = resampler.render(reinterpret_cast(samples.data()), - reinterpret_cast(resampled.data()), - nInputFrames); - - Q_UNUSED(nOutputFrames); - return playSoundAndDelete(resampled, options); + return playSoundAndDelete(samples, options); } AudioInjectorPointer AudioInjector::playSoundAndDelete(const QByteArray& buffer, const AudioInjectorOptions options) { @@ -461,12 +444,40 @@ AudioInjectorPointer AudioInjector::playSoundAndDelete(const QByteArray& buffer, return sound; } - AudioInjectorPointer AudioInjector::playSound(const QByteArray& buffer, const AudioInjectorOptions options) { - AudioInjectorPointer injector = AudioInjectorPointer::create(buffer, options); - if (!injector->inject(&AudioInjectorManager::threadInjector)) { - qWarning() << "AudioInjector::playSound failed to thread injector"; + if (options.pitch == 1.0f) { + + AudioInjectorPointer injector = AudioInjectorPointer::create(buffer, options); + + if (!injector->inject(&AudioInjectorManager::threadInjector)) { + qWarning() << "AudioInjector::playSound failed to thread injector"; + } + return injector; + + } else { + + const int standardRate = AudioConstants::SAMPLE_RATE; + const int resampledRate = AudioConstants::SAMPLE_RATE / glm::clamp(options.pitch, 1/16.0f, 16.0f); // limit to 4 octaves + const int numChannels = options.ambisonic ? AudioConstants::AMBISONIC : + (options.stereo ? AudioConstants::STEREO : AudioConstants::MONO); + + AudioSRC resampler(standardRate, resampledRate, numChannels); + + // create a resampled buffer that is guaranteed to be large enough + const int nInputFrames = buffer.size() / (numChannels * sizeof(int16_t)); + const int maxOutputFrames = resampler.getMaxOutput(nInputFrames); + QByteArray resampledBuffer(maxOutputFrames * numChannels * sizeof(int16_t), '\0'); + + resampler.render(reinterpret_cast(buffer.data()), + reinterpret_cast(resampledBuffer.data()), + nInputFrames); + + AudioInjectorPointer injector = AudioInjectorPointer::create(resampledBuffer, options); + + if (!injector->inject(&AudioInjectorManager::threadInjector)) { + qWarning() << "AudioInjector::playSound failed to thread pitch-shifted injector"; + } + return injector; } - return injector; } diff --git a/libraries/audio/src/AudioInjectorOptions.cpp b/libraries/audio/src/AudioInjectorOptions.cpp index 0af74a796c..2f82cae137 100644 --- a/libraries/audio/src/AudioInjectorOptions.cpp +++ b/libraries/audio/src/AudioInjectorOptions.cpp @@ -26,7 +26,8 @@ AudioInjectorOptions::AudioInjectorOptions() : ambisonic(false), ignorePenumbra(false), localOnly(false), - secondOffset(0.0f) + secondOffset(0.0f), + pitch(1.0f) { } @@ -40,6 +41,7 @@ QScriptValue injectorOptionsToScriptValue(QScriptEngine* engine, const AudioInje obj.setProperty("ignorePenumbra", injectorOptions.ignorePenumbra); obj.setProperty("localOnly", injectorOptions.localOnly); obj.setProperty("secondOffset", injectorOptions.secondOffset); + obj.setProperty("pitch", injectorOptions.pitch); return obj; } @@ -87,6 +89,12 @@ void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOpt } else { qCWarning(audio) << "Audio injector options: secondOffset is not a number"; } + } else if (it.name() == "pitch") { + if (it.value().isNumber()) { + injectorOptions.pitch = it.value().toNumber(); + } else { + qCWarning(audio) << "Audio injector options: pitch is not a number"; + } } else { qCWarning(audio) << "Unknown audio injector option:" << it.name(); } diff --git a/libraries/audio/src/AudioInjectorOptions.h b/libraries/audio/src/AudioInjectorOptions.h index 40a3f343bd..4dd38ce915 100644 --- a/libraries/audio/src/AudioInjectorOptions.h +++ b/libraries/audio/src/AudioInjectorOptions.h @@ -29,6 +29,7 @@ public: bool ignorePenumbra; bool localOnly; float secondOffset; + float pitch; // multiplier, where 2.0f shifts up one octave }; Q_DECLARE_METATYPE(AudioInjectorOptions); diff --git a/libraries/avatars-renderer/CMakeLists.txt b/libraries/avatars-renderer/CMakeLists.txt index 148835965e..53edc692f2 100644 --- a/libraries/avatars-renderer/CMakeLists.txt +++ b/libraries/avatars-renderer/CMakeLists.txt @@ -1,7 +1,7 @@ set(TARGET_NAME avatars-renderer) -AUTOSCRIBE_SHADER_LIB(gpu model render render-utils) +AUTOSCRIBE_SHADER_LIB(gpu graphics render render-utils) setup_hifi_library(Widgets Network Script) -link_hifi_libraries(shared gpu model animation model-networking script-engine render render-utils image trackers entities-renderer) +link_hifi_libraries(shared gpu graphics animation model-networking script-engine render render-utils image trackers entities-renderer) include_hifi_library_headers(avatars) include_hifi_library_headers(networking) include_hifi_library_headers(fbx) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index c532e7659f..500a24763d 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -32,6 +32,8 @@ #include #include #include +#include "ModelEntityItem.h" +#include "RenderableModelEntityItem.h" #include "Logging.h" @@ -326,13 +328,15 @@ void Avatar::updateAvatarEntities() { AvatarEntityIDs recentlyDettachedAvatarEntities = getAndClearRecentlyDetachedIDs(); if (!recentlyDettachedAvatarEntities.empty()) { // only lock this thread when absolutely necessary + AvatarEntityMap avatarEntityData; _avatarEntitiesLock.withReadLock([&] { - foreach (auto entityID, recentlyDettachedAvatarEntities) { - if (!_avatarEntityData.contains(entityID)) { - entityTree->deleteEntity(entityID, true, true); - } - } + avatarEntityData = _avatarEntityData; }); + foreach (auto entityID, recentlyDettachedAvatarEntities) { + if (!avatarEntityData.contains(entityID)) { + entityTree->deleteEntity(entityID, true, true); + } + } // remove stale data hashes foreach (auto entityID, recentlyDettachedAvatarEntities) { @@ -347,6 +351,65 @@ void Avatar::updateAvatarEntities() { setAvatarEntityDataChanged(false); } +void Avatar::relayJointDataToChildren() { + forEachChild([&](SpatiallyNestablePointer child) { + if (child->getNestableType() == NestableType::Entity) { + auto modelEntity = std::dynamic_pointer_cast(child); + if (modelEntity) { + if (modelEntity->getRelayParentJoints()) { + if (!modelEntity->getJointMapCompleted() || _reconstructSoftEntitiesJointMap) { + QStringList modelJointNames = modelEntity->getJointNames(); + int numJoints = modelJointNames.count(); + std::vector map; + map.reserve(numJoints); + for (int jointIndex = 0; jointIndex < numJoints; jointIndex++) { + QString jointName = modelJointNames.at(jointIndex); + int avatarJointIndex = getJointIndex(jointName); + glm::quat jointRotation; + glm::vec3 jointTranslation; + if (avatarJointIndex < 0) { + jointRotation = modelEntity->getAbsoluteJointRotationInObjectFrame(jointIndex); + jointTranslation = modelEntity->getAbsoluteJointTranslationInObjectFrame(jointIndex); + map.push_back(-1); + } else { + int jointIndex = getJointIndex(jointName); + jointRotation = getJointRotation(jointIndex); + jointTranslation = getJointTranslation(jointIndex); + map.push_back(avatarJointIndex); + } + modelEntity->setLocalJointRotation(jointIndex, jointRotation); + modelEntity->setLocalJointTranslation(jointIndex, jointTranslation); + } + modelEntity->setJointMap(map); + } else { + QStringList modelJointNames = modelEntity->getJointNames(); + int numJoints = modelJointNames.count(); + for (int jointIndex = 0; jointIndex < numJoints; jointIndex++) { + int avatarJointIndex = modelEntity->avatarJointIndex(jointIndex); + glm::quat jointRotation; + glm::vec3 jointTranslation; + if (avatarJointIndex >=0) { + jointRotation = getJointRotation(avatarJointIndex); + jointTranslation = getJointTranslation(avatarJointIndex); + } else { + jointRotation = modelEntity->getAbsoluteJointRotationInObjectFrame(jointIndex); + jointTranslation = modelEntity->getAbsoluteJointTranslationInObjectFrame(jointIndex); + } + modelEntity->setLocalJointRotation(jointIndex, jointRotation); + modelEntity->setLocalJointTranslation(jointIndex, jointTranslation); + } + } + Transform avatarTransform = _skeletonModel->getTransform(); + avatarTransform.setScale(_skeletonModel->getScale()); + modelEntity->setOverrideTransform(avatarTransform, _skeletonModel->getOffset()); + modelEntity->simulateRelayedJoints(); + } + } + } + }); + _reconstructSoftEntitiesJointMap = false; +} + void Avatar::simulate(float deltaTime, bool inView) { PROFILE_RANGE(simulation, "simulate"); @@ -379,6 +442,7 @@ void Avatar::simulate(float deltaTime, bool inView) { } head->setScale(getModelScale()); head->simulate(deltaTime); + relayJointDataToChildren(); } else { // a non-full update is still required so that the position, rotation, scale and bounds of the skeletonModel are updated. _skeletonModel->simulate(deltaTime, false); @@ -727,10 +791,19 @@ bool Avatar::shouldRenderHead(const RenderArgs* renderArgs) const { // virtual void Avatar::simulateAttachments(float deltaTime) { + assert(_attachmentModels.size() == _attachmentModelsTexturesLoaded.size()); PerformanceTimer perfTimer("attachments"); for (int i = 0; i < (int)_attachmentModels.size(); i++) { const AttachmentData& attachment = _attachmentData.at(i); auto& model = _attachmentModels.at(i); + bool texturesLoaded = _attachmentModelsTexturesLoaded.at(i); + + // Watch for texture loading + if (!texturesLoaded && model->getGeometry() && model->getGeometry()->areTexturesLoaded()) { + _attachmentModelsTexturesLoaded[i] = true; + model->updateRenderItems(); + } + int jointIndex = getJointIndex(attachment.jointName); glm::vec3 jointPosition; glm::quat jointRotation; @@ -1197,6 +1270,7 @@ void Avatar::setModelURLFinished(bool success) { invalidateJointIndicesCache(); _isAnimatingScale = true; + _reconstructSoftEntitiesJointMap = true; if (!success && _skeletonModelURL != AvatarData::defaultFullAvatarModelUrl()) { const int MAX_SKELETON_DOWNLOAD_ATTEMPTS = 4; // NOTE: we don't want to be as generous as ResourceCache is, we only want 4 attempts @@ -1254,6 +1328,7 @@ void Avatar::setAttachmentData(const QVector& attachmentData) { while ((int)_attachmentModels.size() > attachmentData.size()) { auto attachmentModel = _attachmentModels.back(); _attachmentModels.pop_back(); + _attachmentModelsTexturesLoaded.pop_back(); _attachmentsToRemove.push_back(attachmentModel); } @@ -1261,11 +1336,16 @@ void Avatar::setAttachmentData(const QVector& attachmentData) { if (i == (int)_attachmentModels.size()) { // if number of attachments has been increased, we need to allocate a new model _attachmentModels.push_back(allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig(), isMyAvatar())); - } - else if (i < oldAttachmentData.size() && oldAttachmentData[i].isSoft != attachmentData[i].isSoft) { + _attachmentModelsTexturesLoaded.push_back(false); + } else if (i < oldAttachmentData.size() && oldAttachmentData[i].isSoft != attachmentData[i].isSoft) { // if the attachment has changed type, we need to re-allocate a new one. _attachmentsToRemove.push_back(_attachmentModels[i]); _attachmentModels[i] = allocateAttachmentModel(attachmentData[i].isSoft, _skeletonModel->getRig(), isMyAvatar()); + _attachmentModelsTexturesLoaded[i] = false; + } + // If the model URL has changd, we need to wait for the textures to load + if (_attachmentModels[i]->getURL() != attachmentData[i].modelURL) { + _attachmentModelsTexturesLoaded[i] = false; } _attachmentModels[i]->setURL(attachmentData[i].modelURL); } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 39081ba44b..c2b404a925 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -306,6 +306,7 @@ protected: glm::vec3 _skeletonOffset; std::vector> _attachmentModels; + std::vector _attachmentModelsTexturesLoaded; std::vector> _attachmentsToRemove; std::vector> _attachmentsToDelete; @@ -330,6 +331,7 @@ protected: // protected methods... bool isLookingAtMe(AvatarSharedPointer avatar) const; + void relayJointDataToChildren(); void fade(render::Transaction& transaction, render::Transition::Type type); @@ -382,6 +384,7 @@ protected: bool _isAnimatingScale { false }; bool _mustFadeIn { false }; bool _isFading { false }; + bool _reconstructSoftEntitiesJointMap { false }; float _modelScale { 1.0f }; static int _jointConesID; diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index a13ef07cd4..1112ccde60 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -237,30 +237,14 @@ bool SkeletonModel::getRightHandPosition(glm::vec3& position) const { return getJointPositionInWorldFrame(getRightHandJointIndex(), position); } -bool SkeletonModel::restoreLeftHandPosition(float fraction, float priority) { - return restoreJointPosition(getLeftHandJointIndex(), fraction, priority); -} - bool SkeletonModel::getLeftShoulderPosition(glm::vec3& position) const { return getJointPositionInWorldFrame(getLastFreeJointIndex(getLeftHandJointIndex()), position); } -float SkeletonModel::getLeftArmLength() const { - return getLimbLength(getLeftHandJointIndex()); -} - -bool SkeletonModel::restoreRightHandPosition(float fraction, float priority) { - return restoreJointPosition(getRightHandJointIndex(), fraction, priority); -} - bool SkeletonModel::getRightShoulderPosition(glm::vec3& position) const { return getJointPositionInWorldFrame(getLastFreeJointIndex(getRightHandJointIndex()), position); } -float SkeletonModel::getRightArmLength() const { - return getLimbLength(getRightHandJointIndex()); -} - bool SkeletonModel::getHeadPosition(glm::vec3& headPosition) const { return isActive() && getJointPositionInWorldFrame(getFBXGeometry().headJointIndex, headPosition); } diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h index f911ad0c5a..d82fce7412 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.h @@ -57,11 +57,6 @@ public: /// \return true whether or not the position was found bool getRightHandPosition(glm::vec3& position) const; - /// Restores some fraction of the default position of the left hand. - /// \param fraction the fraction of the default position to restore - /// \return whether or not the left hand joint was found - bool restoreLeftHandPosition(float fraction = 1.0f, float priority = 1.0f); - /// Gets the position of the left shoulder. /// \return whether or not the left shoulder joint was found bool getLeftShoulderPosition(glm::vec3& position) const; @@ -69,18 +64,10 @@ public: /// Returns the extended length from the left hand to its last free ancestor. float getLeftArmLength() const; - /// Restores some fraction of the default position of the right hand. - /// \param fraction the fraction of the default position to restore - /// \return whether or not the right hand joint was found - bool restoreRightHandPosition(float fraction = 1.0f, float priority = 1.0f); - /// Gets the position of the right shoulder. /// \return whether or not the right shoulder joint was found bool getRightShoulderPosition(glm::vec3& position) const; - /// Returns the extended length from the right hand to its first free ancestor. - float getRightArmLength() const; - /// Returns the position of the head joint. /// \return whether or not the head was found bool getHeadPosition(glm::vec3& headPosition) const; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index f2053e29d7..c2aadb8723 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include "AvatarLogging.h" @@ -77,6 +78,16 @@ size_t AvatarDataPacket::maxJointDataSize(size_t numJoints) { return totalSize; } +size_t AvatarDataPacket::maxJointDefaultPoseFlagsSize(size_t numJoints) { + const size_t bitVectorSize = calcBitVectorSize((int)numJoints); + size_t totalSize = sizeof(uint8_t); // numJoints + + // one set of bits for rotation and one for translation + const size_t NUM_BIT_VECTORS_IN_DEFAULT_POSE_FLAGS_SECTION = 2; + totalSize += NUM_BIT_VECTORS_IN_DEFAULT_POSE_FLAGS_SECTION * bitVectorSize; + + return totalSize; +} AvatarData::AvatarData() : SpatiallyNestable(NestableType::Avatar, QUuid()), @@ -103,7 +114,7 @@ AvatarData::~AvatarData() { QUrl AvatarData::_defaultFullAvatarModelUrl = {}; // In C++, if this initialization were in the AvatarInfo, every file would have it's own copy, even for class vars. const QUrl& AvatarData::defaultFullAvatarModelUrl() { if (_defaultFullAvatarModelUrl.isEmpty()) { - _defaultFullAvatarModelUrl = QUrl::fromLocalFile(PathUtils::resourcesPath() + "/meshes/defaultAvatar_full.fst"); + _defaultFullAvatarModelUrl = PathUtils::resourcesUrl("/meshes/defaultAvatar_full.fst"); } return _defaultFullAvatarModelUrl; } @@ -272,6 +283,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent bool hasFaceTrackerInfo = false; bool hasJointData = false; + bool hasJointDefaultPoseFlags = false; if (sendPALMinimum) { hasAudioLoudness = true; @@ -290,12 +302,14 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent hasFaceTrackerInfo = !dropFaceTracking && hasFaceTracker() && (sendAll || faceTrackerInfoChangedSince(lastSentTime)); hasJointData = sendAll || !sendMinimum; + hasJointDefaultPoseFlags = hasJointData; } const size_t byteArraySize = AvatarDataPacket::MAX_CONSTANT_HEADER_SIZE + (hasFaceTrackerInfo ? AvatarDataPacket::maxFaceTrackerInfoSize(_headData->getNumSummedBlendshapeCoefficients()) : 0) + - (hasJointData ? AvatarDataPacket::maxJointDataSize(_jointData.size()) : 0); + (hasJointData ? AvatarDataPacket::maxJointDataSize(_jointData.size()) : 0) + + (hasJointDefaultPoseFlags ? AvatarDataPacket::maxJointDefaultPoseFlagsSize(_jointData.size()) : 0); QByteArray avatarDataByteArray((int)byteArraySize, 0); unsigned char* destinationBuffer = reinterpret_cast(avatarDataByteArray.data()); @@ -314,7 +328,8 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent | (hasParentInfo ? AvatarDataPacket::PACKET_HAS_PARENT_INFO : 0) | (hasAvatarLocalPosition ? AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION : 0) | (hasFaceTrackerInfo ? AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO : 0) - | (hasJointData ? AvatarDataPacket::PACKET_HAS_JOINT_DATA : 0); + | (hasJointData ? AvatarDataPacket::PACKET_HAS_JOINT_DATA : 0) + | (hasJointDefaultPoseFlags ? AvatarDataPacket::PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS : 0); memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags)); destinationBuffer += sizeof(packetStateFlags); @@ -530,21 +545,30 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent destinationBuffer += numValidityBytes; // Move pointer past the validity bytes + // sentJointDataOut and lastSentJointData might be the same vector + // build sentJointDataOut locally and then swap it at the end. + QVector localSentJointDataOut; if (sentJointDataOut) { - sentJointDataOut->resize(_jointData.size()); // Make sure the destination is resized before using it + localSentJointDataOut.resize(numJoints); // Make sure the destination is resized before using it } + float minRotationDOT = !distanceAdjust ? AVATAR_MIN_ROTATION_DOT : getDistanceBasedMinRotationDOT(viewerPosition); for (int i = 0; i < _jointData.size(); i++) { const JointData& data = _jointData[i]; + const JointData& last = lastSentJointData[i]; - // The dot product for smaller rotations is a smaller number. - // So if the dot() is less than the value, then the rotation is a larger angle of rotation - bool largeEnoughRotation = fabsf(glm::dot(data.rotation, lastSentJointData[i].rotation)) < minRotationDOT; + if (!data.rotationIsDefaultPose) { + if (sendAll || last.rotationIsDefaultPose || last.rotation != data.rotation) { - if (sendAll || lastSentJointData[i].rotation != data.rotation) { - if (sendAll || !cullSmallChanges || largeEnoughRotation) { - if (data.rotationSet) { + bool largeEnoughRotation = true; + if (cullSmallChanges) { + // The dot product for smaller rotations is a smaller number. + // So if the dot() is less than the value, then the rotation is a larger angle of rotation + largeEnoughRotation = fabsf(glm::dot(last.rotation, data.rotation)) < minRotationDOT; + } + + if (sendAll || !cullSmallChanges || largeEnoughRotation) { validity |= (1 << validityBit); #ifdef WANT_DEBUG rotationSentCount++; @@ -552,10 +576,9 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent destinationBuffer += packOrientationQuatToSixBytes(destinationBuffer, data.rotation); if (sentJointDataOut) { - auto jointDataOut = *sentJointDataOut; - jointDataOut[i].rotation = data.rotation; + localSentJointDataOut[i].rotation = data.rotation; + localSentJointDataOut[i].rotationIsDefaultPose = false; } - } } } @@ -585,11 +608,10 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent float maxTranslationDimension = 0.0; for (int i = 0; i < _jointData.size(); i++) { const JointData& data = _jointData[i]; - if (sendAll || lastSentJointData[i].translation != data.translation) { - if (sendAll || - !cullSmallChanges || - glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) { - if (data.translationSet) { + + if (!data.translationIsDefaultPose) { + if (sendAll || lastSentJointData[i].translation != data.translation) { + if (sendAll || !cullSmallChanges || glm::distance(data.translation, lastSentJointData[i].translation) > minTranslation) { validity |= (1 << validityBit); #ifdef WANT_DEBUG translationSentCount++; @@ -602,10 +624,9 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent packFloatVec3ToSignedTwoByteFixed(destinationBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX); if (sentJointDataOut) { - auto jointDataOut = *sentJointDataOut; - jointDataOut[i].translation = data.translation; + localSentJointDataOut[i].translation = data.translation; + localSentJointDataOut[i].translationIsDefaultPose = false; } - } } } @@ -646,6 +667,35 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent if (outboundDataRateOut) { outboundDataRateOut->jointDataRate.increment(numBytes); } + + if (sentJointDataOut) { + // Push new sent joint data to sentJointDataOut + sentJointDataOut->swap(localSentJointDataOut); + } + } + + if (hasJointDefaultPoseFlags) { + auto startSection = destinationBuffer; + QReadLocker readLock(&_jointDataLock); + + // write numJoints + int numJoints = _jointData.size(); + *destinationBuffer++ = (uint8_t)numJoints; + + // write rotationIsDefaultPose bits + destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) { + return _jointData[i].rotationIsDefaultPose; + }); + + // write translationIsDefaultPose bits + destinationBuffer += writeBitVector(destinationBuffer, numJoints, [&](int i) { + return _jointData[i].translationIsDefaultPose; + }); + + if (outboundDataRateOut) { + size_t numBytes = destinationBuffer - startSection; + outboundDataRateOut->jointDefaultPoseFlagsRate.increment(numBytes); + } } int avatarDataSize = destinationBuffer - startPosition; @@ -657,6 +707,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent return avatarDataByteArray.left(avatarDataSize); } + // NOTE: This is never used in a "distanceAdjust" mode, so it's ok that it doesn't use a variable minimum rotation/translation void AvatarData::doneEncoding(bool cullSmallChanges) { // The server has finished sending this version of the joint-data to other nodes. Update _lastSentJointData. @@ -667,7 +718,7 @@ void AvatarData::doneEncoding(bool cullSmallChanges) { if (_lastSentJointData[i].rotation != data.rotation) { if (!cullSmallChanges || fabsf(glm::dot(data.rotation, _lastSentJointData[i].rotation)) <= AVATAR_MIN_ROTATION_DOT) { - if (data.rotationSet) { + if (!data.rotationIsDefaultPose) { _lastSentJointData[i].rotation = data.rotation; } } @@ -675,7 +726,7 @@ void AvatarData::doneEncoding(bool cullSmallChanges) { if (_lastSentJointData[i].translation != data.translation) { if (!cullSmallChanges || glm::distance(data.translation, _lastSentJointData[i].translation) > AVATAR_MIN_TRANSLATION) { - if (data.translationSet) { + if (!data.translationIsDefaultPose) { _lastSentJointData[i].translation = data.translation; } } @@ -723,6 +774,7 @@ const unsigned char* unpackFauxJoint(const unsigned char* sourceBuffer, ThreadSa // read data in packet starting at byte offset and return number of bytes parsed int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { + // lazily allocate memory for HeadData in case we're not an Avatar instance lazyInitHeadData(); @@ -738,18 +790,19 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { #define HAS_FLAG(B,F) ((B & F) == F) - bool hasAvatarGlobalPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_GLOBAL_POSITION); - bool hasAvatarBoundingBox = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_BOUNDING_BOX); - bool hasAvatarOrientation = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_ORIENTATION); - bool hasAvatarScale = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_SCALE); - bool hasLookAtPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_LOOK_AT_POSITION); - bool hasAudioLoudness = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AUDIO_LOUDNESS); - bool hasSensorToWorldMatrix = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_SENSOR_TO_WORLD_MATRIX); - bool hasAdditionalFlags = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_ADDITIONAL_FLAGS); - bool hasParentInfo = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_PARENT_INFO); - bool hasAvatarLocalPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION); - bool hasFaceTrackerInfo = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO); - bool hasJointData = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DATA); + bool hasAvatarGlobalPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_GLOBAL_POSITION); + bool hasAvatarBoundingBox = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_BOUNDING_BOX); + bool hasAvatarOrientation = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_ORIENTATION); + bool hasAvatarScale = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_SCALE); + bool hasLookAtPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_LOOK_AT_POSITION); + bool hasAudioLoudness = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AUDIO_LOUDNESS); + bool hasSensorToWorldMatrix = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_SENSOR_TO_WORLD_MATRIX); + bool hasAdditionalFlags = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_ADDITIONAL_FLAGS); + bool hasParentInfo = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_PARENT_INFO); + bool hasAvatarLocalPosition = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_AVATAR_LOCAL_POSITION); + bool hasFaceTrackerInfo = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_FACE_TRACKER_INFO); + bool hasJointData = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DATA); + bool hasJointDefaultPoseFlags = HAS_FLAG(packetStateFlags, AvatarDataPacket::PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS); quint64 now = usecTimestampNow(); @@ -1048,7 +1101,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { if (validRotations[i]) { sourceBuffer += unpackOrientationQuatFromSixBytes(sourceBuffer, data.rotation); _hasNewJointData = true; - data.rotationSet = true; + data.rotationIsDefaultPose = false; } } @@ -1083,7 +1136,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { if (validTranslations[i]) { sourceBuffer += unpackFloatVec3FromSignedTwoByteFixed(sourceBuffer, data.translation, TRANSLATION_COMPRESSION_RADIX); _hasNewJointData = true; - data.translationSet = true; + data.translationIsDefaultPose = false; } } @@ -1103,6 +1156,32 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { _jointDataUpdateRate.increment(); } + if (hasJointDefaultPoseFlags) { + auto startSection = sourceBuffer; + + QWriteLocker writeLock(&_jointDataLock); + + PACKET_READ_CHECK(JointDefaultPoseFlagsNumJoints, sizeof(uint8_t)); + int numJoints = (int)*sourceBuffer++; + + _jointData.resize(numJoints); + + size_t bitVectorSize = calcBitVectorSize(numJoints); + PACKET_READ_CHECK(JointDefaultPoseFlagsRotationFlags, bitVectorSize); + sourceBuffer += readBitVector(sourceBuffer, numJoints, [&](int i, bool value) { + _jointData[i].rotationIsDefaultPose = value; + }); + + PACKET_READ_CHECK(JointDefaultPoseFlagsTranslationFlags, bitVectorSize); + sourceBuffer += readBitVector(sourceBuffer, numJoints, [&](int i, bool value) { + _jointData[i].translationIsDefaultPose = value; + }); + + int numBytesRead = sourceBuffer - startSection; + _jointDefaultPoseFlagsRate.increment(numBytesRead); + _jointDefaultPoseFlagsUpdateRate.increment(); + } + int numBytesRead = sourceBuffer - startPosition; _averageBytesReceived.updateAverage(numBytesRead); @@ -1139,6 +1218,8 @@ float AvatarData::getDataRate(const QString& rateName) const { return _faceTrackerRate.rate() / BYTES_PER_KILOBIT; } else if (rateName == "jointData") { return _jointDataRate.rate() / BYTES_PER_KILOBIT; + } else if (rateName == "jointDefaultPoseFlagsRate") { + return _jointDefaultPoseFlagsRate.rate() / BYTES_PER_KILOBIT; } else if (rateName == "globalPositionOutbound") { return _outboundDataRate.globalPositionRate.rate() / BYTES_PER_KILOBIT; } else if (rateName == "localPositionOutbound") { @@ -1163,6 +1244,8 @@ float AvatarData::getDataRate(const QString& rateName) const { return _outboundDataRate.faceTrackerRate.rate() / BYTES_PER_KILOBIT; } else if (rateName == "jointDataOutbound") { return _outboundDataRate.jointDataRate.rate() / BYTES_PER_KILOBIT; + } else if (rateName == "jointDefaultPoseFlagsOutbound") { + return _outboundDataRate.jointDefaultPoseFlagsRate.rate() / BYTES_PER_KILOBIT; } return 0.0f; } @@ -1229,9 +1312,9 @@ void AvatarData::setJointData(int index, const glm::quat& rotation, const glm::v } JointData& data = _jointData[index]; data.rotation = rotation; - data.rotationSet = true; + data.rotationIsDefaultPose = false; data.translation = translation; - data.translationSet = true; + data.translationIsDefaultPose = false; } void AvatarData::clearJointData(int index) { @@ -1287,7 +1370,8 @@ void AvatarData::setJointData(const QString& name, const glm::quat& rotation, co auto& jointData = _jointData[index]; jointData.rotation = rotation; jointData.translation = translation; - jointData.rotationSet = jointData.translationSet = true; + jointData.rotationIsDefaultPose = false; + jointData.translationIsDefaultPose = false; }); } @@ -1297,7 +1381,7 @@ void AvatarData::setJointRotation(const QString& name, const glm::quat& rotation writeLockWithNamedJointIndex(name, [&](int index) { auto& data = _jointData[index]; data.rotation = rotation; - data.rotationSet = true; + data.rotationIsDefaultPose = false; }); } @@ -1307,7 +1391,7 @@ void AvatarData::setJointTranslation(const QString& name, const glm::vec3& trans writeLockWithNamedJointIndex(name, [&](int index) { auto& data = _jointData[index]; data.translation = translation; - data.translationSet = true; + data.translationIsDefaultPose = false; }); } @@ -1321,7 +1405,7 @@ void AvatarData::setJointRotation(int index, const glm::quat& rotation) { } JointData& data = _jointData[index]; data.rotation = rotation; - data.rotationSet = true; + data.rotationIsDefaultPose = false; } void AvatarData::setJointTranslation(int index, const glm::vec3& translation) { @@ -1334,7 +1418,7 @@ void AvatarData::setJointTranslation(int index, const glm::vec3& translation) { } JointData& data = _jointData[index]; data.translation = translation; - data.translationSet = true; + data.translationIsDefaultPose = false; } void AvatarData::clearJointData(const QString& name) { @@ -1390,7 +1474,7 @@ void AvatarData::setJointRotations(const QVector& jointRotations) { for (int i = 0; i < size; ++i) { auto& data = _jointData[i]; data.rotation = jointRotations[i]; - data.rotationSet = true; + data.rotationIsDefaultPose = false; } } @@ -1412,7 +1496,7 @@ void AvatarData::setJointTranslations(const QVector& jointTranslation for (int i = 0; i < size; ++i) { auto& data = _jointData[i]; data.translation = jointTranslations[i]; - data.translationSet = true; + data.translationIsDefaultPose = false; } } @@ -1989,9 +2073,9 @@ JointData jointDataFromJsonValue(const QJsonValue& json) { if (json.isArray()) { QJsonArray array = json.toArray(); result.rotation = quatFromJsonValue(array[0]); - result.rotationSet = true; + result.rotationIsDefaultPose = false; result.translation = vec3FromJsonValue(array[1]); - result.translationSet = true; + result.translationIsDefaultPose = false; } return result; } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index d7dd2837cb..92c505f5c3 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -113,18 +113,19 @@ namespace AvatarDataPacket { // Packet State Flags - we store the details about the existence of other records in this bitset: // AvatarGlobalPosition, Avatar face tracker, eye tracking, and existence of using HasFlags = uint16_t; - const HasFlags PACKET_HAS_AVATAR_GLOBAL_POSITION = 1U << 0; - const HasFlags PACKET_HAS_AVATAR_BOUNDING_BOX = 1U << 1; - const HasFlags PACKET_HAS_AVATAR_ORIENTATION = 1U << 2; - const HasFlags PACKET_HAS_AVATAR_SCALE = 1U << 3; - const HasFlags PACKET_HAS_LOOK_AT_POSITION = 1U << 4; - const HasFlags PACKET_HAS_AUDIO_LOUDNESS = 1U << 5; - const HasFlags PACKET_HAS_SENSOR_TO_WORLD_MATRIX = 1U << 6; - const HasFlags PACKET_HAS_ADDITIONAL_FLAGS = 1U << 7; - const HasFlags PACKET_HAS_PARENT_INFO = 1U << 8; - const HasFlags PACKET_HAS_AVATAR_LOCAL_POSITION = 1U << 9; - const HasFlags PACKET_HAS_FACE_TRACKER_INFO = 1U << 10; - const HasFlags PACKET_HAS_JOINT_DATA = 1U << 11; + const HasFlags PACKET_HAS_AVATAR_GLOBAL_POSITION = 1U << 0; + const HasFlags PACKET_HAS_AVATAR_BOUNDING_BOX = 1U << 1; + const HasFlags PACKET_HAS_AVATAR_ORIENTATION = 1U << 2; + const HasFlags PACKET_HAS_AVATAR_SCALE = 1U << 3; + const HasFlags PACKET_HAS_LOOK_AT_POSITION = 1U << 4; + const HasFlags PACKET_HAS_AUDIO_LOUDNESS = 1U << 5; + const HasFlags PACKET_HAS_SENSOR_TO_WORLD_MATRIX = 1U << 6; + const HasFlags PACKET_HAS_ADDITIONAL_FLAGS = 1U << 7; + const HasFlags PACKET_HAS_PARENT_INFO = 1U << 8; + const HasFlags PACKET_HAS_AVATAR_LOCAL_POSITION = 1U << 9; + const HasFlags PACKET_HAS_FACE_TRACKER_INFO = 1U << 10; + const HasFlags PACKET_HAS_JOINT_DATA = 1U << 11; + const HasFlags PACKET_HAS_JOINT_DEFAULT_POSE_FLAGS = 1U << 12; const size_t AVATAR_HAS_FLAGS_SIZE = 2; using SixByteQuat = uint8_t[6]; @@ -256,6 +257,15 @@ namespace AvatarDataPacket { }; */ size_t maxJointDataSize(size_t numJoints); + + /* + struct JointDefaultPoseFlags { + uint8_t numJoints; + uint8_t rotationIsDefaultPoseBits[ceil(numJoints / 8)]; + uint8_t translationIsDefaultPoseBits[ceil(numJoints / 8)]; + }; + */ + size_t maxJointDefaultPoseFlagsSize(size_t numJoints); } const float MAX_AUDIO_LOUDNESS = 1000.0f; // close enough for mouth animation @@ -321,6 +331,7 @@ public: RateCounter<> parentInfoRate; RateCounter<> faceTrackerRate; RateCounter<> jointDataRate; + RateCounter<> jointDefaultPoseFlagsRate; }; class AvatarPriority { @@ -358,7 +369,7 @@ class AvatarData : public QObject, public SpatiallyNestable { Q_PROPERTY(QString displayName READ getDisplayName WRITE setDisplayName NOTIFY displayNameChanged) // sessionDisplayName is sanitized, defaulted version displayName that is defined by the AvatarMixer rather than by Interface clients. // The result is unique among all avatars present at the time. - Q_PROPERTY(QString sessionDisplayName READ getSessionDisplayName WRITE setSessionDisplayName) + Q_PROPERTY(QString sessionDisplayName READ getSessionDisplayName WRITE setSessionDisplayName NOTIFY sessionDisplayNameChanged) Q_PROPERTY(bool lookAtSnappingEnabled MEMBER _lookAtSnappingEnabled NOTIFY lookAtSnappingChanged) Q_PROPERTY(QString skeletonModelURL READ getSkeletonModelURLFromScript WRITE setSkeletonModelURLFromScript) Q_PROPERTY(QVector attachmentData READ getAttachmentData WRITE setAttachmentData) @@ -685,6 +696,7 @@ public: signals: void displayNameChanged(); + void sessionDisplayNameChanged(); void lookAtSnappingChanged(bool enabled); void sessionUUIDChanged(); @@ -809,6 +821,7 @@ protected: RateCounter<> _parentInfoRate; RateCounter<> _faceTrackerRate; RateCounter<> _jointDataRate; + RateCounter<> _jointDefaultPoseFlagsRate; // Some rate data for incoming data updates RateCounter<> _parseBufferUpdateRate; @@ -824,6 +837,7 @@ protected: RateCounter<> _parentInfoUpdateRate; RateCounter<> _faceTrackerUpdateRate; RateCounter<> _jointDataUpdateRate; + RateCounter<> _jointDefaultPoseFlagsUpdateRate; // Some rate data for outgoing data AvatarDataRate _outboundDataRate; diff --git a/libraries/avatars/src/ScriptAvatarData.cpp b/libraries/avatars/src/ScriptAvatarData.cpp index 64cd534c8b..1fd001e536 100644 --- a/libraries/avatars/src/ScriptAvatarData.cpp +++ b/libraries/avatars/src/ScriptAvatarData.cpp @@ -15,6 +15,7 @@ ScriptAvatarData::ScriptAvatarData(AvatarSharedPointer avatarData) : _avatarData(avatarData) { QObject::connect(avatarData.get(), &AvatarData::displayNameChanged, this, &ScriptAvatarData::displayNameChanged); + QObject::connect(avatarData.get(), &AvatarData::sessionDisplayNameChanged, this, &ScriptAvatarData::sessionDisplayNameChanged); QObject::connect(avatarData.get(), &AvatarData::lookAtSnappingChanged, this, &ScriptAvatarData::lookAtSnappingChanged); } diff --git a/libraries/avatars/src/ScriptAvatarData.h b/libraries/avatars/src/ScriptAvatarData.h index 46dfb5325f..68074b838d 100644 --- a/libraries/avatars/src/ScriptAvatarData.h +++ b/libraries/avatars/src/ScriptAvatarData.h @@ -44,7 +44,7 @@ class ScriptAvatarData : public QObject { // Q_PROPERTY(QUuid sessionUUID READ getSessionUUID) Q_PROPERTY(QString displayName READ getDisplayName NOTIFY displayNameChanged) - Q_PROPERTY(QString sessionDisplayName READ getSessionDisplayName) + Q_PROPERTY(QString sessionDisplayName READ getSessionDisplayName NOTIFY sessionDisplayNameChanged) Q_PROPERTY(bool isReplicated READ getIsReplicated) Q_PROPERTY(bool lookAtSnappingEnabled READ getLookAtSnappingEnabled NOTIFY lookAtSnappingChanged) @@ -131,6 +131,7 @@ public: signals: void displayNameChanged(); + void sessionDisplayNameChanged(); void lookAtSnappingChanged(bool enabled); public slots: diff --git a/libraries/baking/CMakeLists.txt b/libraries/baking/CMakeLists.txt index 66cf791776..ec7caf574b 100644 --- a/libraries/baking/CMakeLists.txt +++ b/libraries/baking/CMakeLists.txt @@ -1,7 +1,7 @@ set(TARGET_NAME baking) setup_hifi_library(Concurrent) -link_hifi_libraries(shared model networking ktx image fbx) +link_hifi_libraries(shared graphics networking ktx image fbx) include_hifi_library_headers(gpu) add_dependency_external_projects(draco) diff --git a/libraries/display-plugins/CMakeLists.txt b/libraries/display-plugins/CMakeLists.txt index 1fd855e6aa..1d9d42d579 100644 --- a/libraries/display-plugins/CMakeLists.txt +++ b/libraries/display-plugins/CMakeLists.txt @@ -5,7 +5,7 @@ link_hifi_libraries(shared plugins ui-plugins gl ui render-utils ${PLATFORM_GL_B include_hifi_library_headers(gpu) include_hifi_library_headers(model-networking) include_hifi_library_headers(networking) -include_hifi_library_headers(model) +include_hifi_library_headers(graphics) include_hifi_library_headers(fbx) include_hifi_library_headers(image) include_hifi_library_headers(ktx) diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index b85668fbfd..fe3a378874 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -10,16 +10,14 @@ #include #include +#include + #include #include #include -#include #include -#include -#if defined(Q_OS_MAC) -#include -#endif +#include #include #include @@ -27,7 +25,6 @@ #include #include -#include #include #include @@ -130,6 +127,8 @@ public: OpenGLDisplayPlugin* currentPlugin{ nullptr }; Q_ASSERT(_context); _context->makeCurrent(); + CHECK_GL_ERROR(); + _context->doneCurrent(); while (!_shutdown) { if (_pendingMainThreadOperation) { PROFILE_RANGE(render, "MainThreadOp") @@ -171,20 +170,15 @@ public: QThread::setPriority(newPlugin->getPresentPriority()); bool wantVsync = newPlugin->wantVsync(); _context->makeCurrent(); -#if defined(Q_OS_WIN) - wglSwapIntervalEXT(wantVsync ? 1 : 0); - hasVsync = wglGetSwapIntervalEXT() != 0; -#elif defined(Q_OS_MAC) - GLint interval = wantVsync ? 1 : 0; + CHECK_GL_ERROR(); +#if defined(Q_OS_MAC) newPlugin->swapBuffers(); - CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval); - newPlugin->swapBuffers(); - CGLGetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval); - hasVsync = interval != 0; -#else - // TODO: Fill in for linux - Q_UNUSED(wantVsync); #endif + gl::setSwapInterval(wantVsync ? 1 : 0); +#if defined(Q_OS_MAC) + newPlugin->swapBuffers(); +#endif + hasVsync = gl::getSwapInterval() != 0; newPlugin->setVsyncEnabled(hasVsync); newPlugin->customizeContext(); CHECK_GL_ERROR(); @@ -284,6 +278,12 @@ bool OpenGLDisplayPlugin::activate() { DependencyManager::set(); presentThread = DependencyManager::get(); presentThread->setObjectName("Presentation Thread"); + if (!widget->context()->makeCurrent()) { + throw std::runtime_error("Failed to make context current"); + } + CHECK_GL_ERROR(); + widget->context()->doneCurrent(); + presentThread->setContext(widget->context()); // Start execution presentThread->start(); @@ -884,6 +884,7 @@ void OpenGLDisplayPlugin::updateCompositeFramebuffer() { } void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer networkTexture, QOpenGLFramebufferObject* target, GLsync* fenceSync) { +#if !defined(USE_GLES) auto glBackend = const_cast(*this).getGLBackend(); withMainThreadContext([&] { GLuint sourceTexture = glBackend->getTextureID(networkTexture->getGPUTexture()); @@ -924,11 +925,13 @@ void OpenGLDisplayPlugin::copyTextureToQuickFramebuffer(NetworkTexturePointer ne } else { newY = (target->height() - newHeight) / 2; } + glBlitNamedFramebuffer(fbo[0], fbo[1], 0, 0, texWidth, texHeight, newX, newY, newX + newWidth, newY + newHeight, GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT, GL_NEAREST); // don't delete the textures! glDeleteFramebuffers(2, fbo); *fenceSync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); }); +#endif } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index d67c0b8663..1176471b40 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -8,7 +8,6 @@ #pragma once #include "DisplayPlugin.h" -#include #include #include diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index c99fafff89..45b5609f30 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -33,6 +33,7 @@ #include "../Logging.h" #include "../CompositorHelper.h" +#include "DesktopPreviewProvider.h" #include "render-utils/hmd_ui_vert.h" #include "render-utils/hmd_ui_frag.h" @@ -266,17 +267,9 @@ void HmdDisplayPlugin::internalPresent() { swapBuffers(); } else if (_clearPreviewFlag) { - QImage image; - if (_vsyncEnabled) { - image = QImage(PathUtils::resourcesPath() + "images/preview.png"); - } else { - image = QImage(PathUtils::resourcesPath() + "images/preview-disabled.png"); - } - image = image.mirrored(); - image = image.convertToFormat(QImage::Format_RGBA8888); - if (!_previewTexture) { - _previewTexture = gpu::Texture::createStrict( + QImage image = DesktopPreviewProvider::getInstance()->getPreviewDisabledImage(_vsyncEnabled); + _previewTexture = gpu::Texture::createStrict( gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA), image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, @@ -286,7 +279,6 @@ void HmdDisplayPlugin::internalPresent() { _previewTexture->setStoredMipFormat(gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA)); _previewTexture->assignStoredMip(0, image.byteCount(), image.constBits()); _previewTexture->setAutoGenerateMips(true); - } auto viewport = getViewportForSourceSize(uvec2(_previewTexture->getDimensions())); diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index 19987197b8..ea75367e1e 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -1,7 +1,7 @@ set(TARGET_NAME entities-renderer) -AUTOSCRIBE_SHADER_LIB(gpu model procedural render render-utils) +AUTOSCRIBE_SHADER_LIB(gpu graphics procedural render render-utils) setup_hifi_library(Widgets Network Script) -link_hifi_libraries(shared gpu procedural model model-networking script-engine render render-utils image ui pointers) +link_hifi_libraries(shared gpu procedural graphics model-networking script-engine render render-utils image ui pointers) include_hifi_library_headers(networking) include_hifi_library_headers(gl) include_hifi_library_headers(ktx) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 3a737e581b..c2a201a965 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -42,6 +42,7 @@ size_t std::hash::operator()(const EntityItemID& id) const { return qHash(id); } std::function EntityTreeRenderer::_entitiesShouldFadeFunction; +std::function EntityTreeRenderer::_renderDebugHullsOperator = [] { return false; }; EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState, AbstractScriptingServicesInterface* scriptingServices) : diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index dccd14aac6..f5cedfdd01 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -116,10 +116,14 @@ public: EntityItemPointer getEntity(const EntityItemID& id); void onEntityChanged(const EntityItemID& id); + static void setRenderDebugHullsOperator(std::function renderDebugHullsOperator) { _renderDebugHullsOperator = renderDebugHullsOperator; } + static bool shouldRenderDebugHulls() { return _renderDebugHullsOperator(); } + signals: void enterEntity(const EntityItemID& entityItemID); void leaveEntity(const EntityItemID& entityItemID); void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); + void setRenderDebugHulls(); public slots: void addingEntity(const EntityItemID& entityID); @@ -255,6 +259,8 @@ private: static int _entitiesScriptEngineCount; static CalculateEntityLoadingPriority _calculateEntityLoadingPriorityFunc; static std::function _entitiesShouldFadeFunction; + + static std::function _renderDebugHullsOperator; }; diff --git a/libraries/entities-renderer/src/RenderableEntityItem.cpp b/libraries/entities-renderer/src/RenderableEntityItem.cpp index 9e32977de1..fb9aba636b 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableEntityItem.cpp @@ -141,6 +141,10 @@ std::shared_ptr make_renderer(const EntityItemPointer& entity) { } EntityRenderer::EntityRenderer(const EntityItemPointer& entity) : _entity(entity) { + connect(entity.get(), &EntityItem::requestRenderUpdate, this, [&] { + _needsRenderUpdate = true; + emit requestRenderUpdate(); + }); } EntityRenderer::~EntityRenderer() { } @@ -311,6 +315,9 @@ void EntityRenderer::setSubRenderItemIDs(const render::ItemIDs& ids) { // Returns true if the item needs to have updateInscene called because of internal rendering // changes (animation, fading, etc) bool EntityRenderer::needsRenderUpdate() const { + if (_needsRenderUpdate) { + return true; + } if (_prevIsTransparent != isTransparent()) { return true; } @@ -331,10 +338,6 @@ bool EntityRenderer::needsRenderUpdateFromEntity(const EntityItemPointer& entity return true; } - if (_visible != entity->getVisible()) { - return true; - } - if (_moving != entity->isMovingRelativeToParent()) { return true; } @@ -363,6 +366,7 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa _moving = entity->isMovingRelativeToParent(); _visible = entity->getVisible(); + _needsRenderUpdate = false; }); } diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 5deb69711d..8eb82e2c6e 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -125,6 +125,7 @@ protected: bool _prevIsTransparent { false }; bool _visible { false }; bool _moving { false }; + bool _needsRenderUpdate { false }; // Only touched on the rendering thread bool _renderUpdateQueued{ false }; diff --git a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp index fc33ebc498..4ea293a3da 100644 --- a/libraries/entities-renderer/src/RenderableLightEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableLightEntityItem.cpp @@ -52,9 +52,9 @@ void LightEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint float exponent = entity->getExponent(); float cutoff = glm::radians(entity->getCutoff()); if (!entity->getIsSpotlight()) { - light->setType(model::Light::POINT); + light->setType(graphics::Light::POINT); } else { - light->setType(model::Light::SPOT); + light->setType(graphics::Light::SPOT); light->setSpotAngle(cutoff); light->setSpotExponent(exponent); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index c756070bc3..2e39e2e498 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -558,10 +558,10 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { if (type == SHAPE_TYPE_STATIC_MESH) { // copy into triangleIndices triangleIndices.reserve((int32_t)((gpu::Size)(triangleIndices.size()) + indices.getNumElements())); - gpu::BufferView::Iterator partItr = parts.cbegin(); - while (partItr != parts.cend()) { + gpu::BufferView::Iterator partItr = parts.cbegin(); + while (partItr != parts.cend()) { auto numIndices = partItr->_numIndices; - if (partItr->_topology == model::Mesh::TRIANGLES) { + if (partItr->_topology == graphics::Mesh::TRIANGLES) { // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up //assert(numIndices % TRIANGLE_STRIDE == 0); numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader @@ -572,7 +572,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { triangleIndices.push_back(*indexItr + meshIndexOffset); ++indexItr; } - } else if (partItr->_topology == model::Mesh::TRIANGLE_STRIP) { + } else if (partItr->_topology == graphics::Mesh::TRIANGLE_STRIP) { // TODO: resurrect assert after we start sanitizing FBXMesh higher up //assert(numIndices > 2); @@ -593,7 +593,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // the rest use previous and next index uint32_t triangleCount = 1; while (indexItr != indexEnd) { - if ((*indexItr) != model::Mesh::PRIMITIVE_RESTART_INDEX) { + if ((*indexItr) != graphics::Mesh::PRIMITIVE_RESTART_INDEX) { if (triangleCount % 2 == 0) { // even triangles use first two indices in order triangleIndices.push_back(*(indexItr - 2) + meshIndexOffset); @@ -613,12 +613,12 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { } } else if (type == SHAPE_TYPE_SIMPLE_COMPOUND) { // for each mesh copy unique part indices, separated by special bogus (flag) index values - gpu::BufferView::Iterator partItr = parts.cbegin(); - while (partItr != parts.cend()) { + gpu::BufferView::Iterator partItr = parts.cbegin(); + while (partItr != parts.cend()) { // collect unique list of indices for this part std::set uniqueIndices; auto numIndices = partItr->_numIndices; - if (partItr->_topology == model::Mesh::TRIANGLES) { + if (partItr->_topology == graphics::Mesh::TRIANGLES) { // TODO: assert rather than workaround after we start sanitizing FBXMesh higher up //assert(numIndices% TRIANGLE_STRIDE == 0); numIndices -= numIndices % TRIANGLE_STRIDE; // WORKAROUND lack of sanity checking in FBXReader @@ -629,7 +629,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { uniqueIndices.insert(*indexItr); ++indexItr; } - } else if (partItr->_topology == model::Mesh::TRIANGLE_STRIP) { + } else if (partItr->_topology == graphics::Mesh::TRIANGLE_STRIP) { // TODO: resurrect assert after we start sanitizing FBXMesh higher up //assert(numIndices > TRIANGLE_STRIDE - 1); @@ -644,7 +644,7 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& shapeInfo) { // the rest use previous and next index uint32_t triangleCount = 1; while (indexItr != indexEnd) { - if ((*indexItr) != model::Mesh::PRIMITIVE_RESTART_INDEX) { + if ((*indexItr) != graphics::Mesh::PRIMITIVE_RESTART_INDEX) { if (triangleCount % 2 == 0) { // EVEN triangles use first two indices in order uniqueIndices.insert(*(indexItr - 2)); @@ -708,6 +708,26 @@ void RenderableModelEntityItem::setCollisionShape(const btCollisionShape* shape) } } +void RenderableModelEntityItem::setJointMap(std::vector jointMap) { + if (jointMap.size() > 0) { + _jointMap = jointMap; + _jointMapCompleted = true; + return; + } + + _jointMapCompleted = false; +}; + +int RenderableModelEntityItem::avatarJointIndex(int modelJointIndex) { + int result = -1; + int mapSize = (int) _jointMap.size(); + if (modelJointIndex >=0 && modelJointIndex < mapSize) { + result = _jointMap[modelJointIndex]; + } + + return result; +} + bool RenderableModelEntityItem::contains(const glm::vec3& point) const { auto model = getModel(); if (EntityItem::contains(point) && model && _compoundShapeResource && _compoundShapeResource->isLoaded()) { @@ -813,6 +833,10 @@ bool RenderableModelEntityItem::setAbsoluteJointTranslationInObjectFrame(int ind return setLocalJointTranslation(index, jointRelativePose.trans()); } +bool RenderableModelEntityItem::getJointMapCompleted() { + return _jointMapCompleted; +} + glm::quat RenderableModelEntityItem::getLocalJointRotation(int index) const { auto model = getModel(); if (model) { @@ -835,6 +859,13 @@ glm::vec3 RenderableModelEntityItem::getLocalJointTranslation(int index) const { return glm::vec3(); } +void RenderableModelEntityItem::setOverrideTransform(const Transform& transform, const glm::vec3& offset) { + auto model = getModel(); + if (model) { + model->overrideModelTransformAndOffset(transform, offset); + } +} + bool RenderableModelEntityItem::setLocalJointRotation(int index, const glm::quat& rotation) { autoResizeJointArrays(); bool result = false; @@ -929,6 +960,26 @@ bool RenderableModelEntityItem::getMeshes(MeshProxyList& result) { return !result.isEmpty(); } +void RenderableModelEntityItem::simulateRelayedJoints() { + ModelPointer model = getModel(); + if (model && model->isLoaded()) { + copyAnimationJointDataToModel(); + model->simulate(0.0f); + model->updateRenderItems(); + } +} + +void RenderableModelEntityItem::stopModelOverrideIfNoParent() { + auto model = getModel(); + if (model) { + bool overriding = model->isOverridingModelTransformAndOffset(); + QUuid parentID = getParentID(); + if (overriding && (!_relayParentJoints || parentID.isNull())) { + model->stopTransformAndOffsetOverride(); + } + } +} + void RenderableModelEntityItem::copyAnimationJointDataToModel() { auto model = getModel(); if (!model || !model->isLoaded()) { @@ -954,8 +1005,23 @@ void RenderableModelEntityItem::copyAnimationJointDataToModel() { using namespace render; using namespace render::entities; +ModelEntityRenderer::ModelEntityRenderer(const EntityItemPointer& entity) : Parent(entity) { + connect(DependencyManager::get().data(), &EntityTreeRenderer::setRenderDebugHulls, this, [&] { + _needsCollisionGeometryUpdate = true; + emit requestRenderUpdate(); + }); +} + +void ModelEntityRenderer::setKey(bool didVisualGeometryRequestSucceed) { + if (didVisualGeometryRequestSucceed) { + _itemKey = ItemKey::Builder().withTypeMeta(); + } else { + _itemKey = ItemKey::Builder().withTypeMeta().withTypeShape(); + } +} + ItemKey ModelEntityRenderer::getKey() { - return ItemKey::Builder().withTypeMeta().withTypeShape(); + return _itemKey; } uint32_t ModelEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) { @@ -984,7 +1050,7 @@ void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { return; } - QVector jointsData; + QVector jointsData; const QVector& frames = _animation->getFramesReference(); // NOTE: getFrames() is too heavy int frameCount = frames.size(); @@ -1080,7 +1146,7 @@ bool ModelEntityRenderer::needsRenderUpdate() const { return true; } - if (!_texturesLoaded && model->getGeometry() && model->getGeometry()->areTexturesLoaded()) { + if (!_texturesLoaded) { return true; } @@ -1209,7 +1275,10 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce // Check for addition if (_hasModel && !(bool)_model) { model = std::make_shared(nullptr, entity.get()); - connect(model.get(), &Model::setURLFinished, this, &ModelEntityRenderer::requestRenderUpdate); + connect(model.get(), &Model::setURLFinished, this, [&](bool didVisualGeometryRequestSucceed) { + setKey(didVisualGeometryRequestSucceed); + emit requestRenderUpdate(); + }); connect(model.get(), &Model::requestRenderUpdate, this, &ModelEntityRenderer::requestRenderUpdate); connect(entity.get(), &RenderableModelEntityItem::requestCollisionGeometryUpdate, this, &ModelEntityRenderer::flagForCollisionGeometryUpdate); model->setLoadingPriority(EntityTreeRenderer::getEntityLoadingPriority(*entity)); @@ -1262,6 +1331,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce } entity->updateModelBounds(); + entity->stopModelOverrideIfNoParent(); if (model->isVisible() != _visible) { // FIXME: this seems like it could be optimized if we tracked our last known visible state in @@ -1275,9 +1345,9 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce setCollisionMeshKey(entity->getCollisionMeshKey()); _needsCollisionGeometryUpdate = false; ShapeType type = entity->getShapeType(); - if (_showCollisionGeometry && type != SHAPE_TYPE_STATIC_MESH && type != SHAPE_TYPE_NONE) { + if (DependencyManager::get()->shouldRenderDebugHulls() && type != SHAPE_TYPE_STATIC_MESH && type != SHAPE_TYPE_NONE) { // NOTE: it is OK if _collisionMeshKey is nullptr - model::MeshPointer mesh = collisionMeshCache.getMesh(_collisionMeshKey); + graphics::MeshPointer mesh = collisionMeshCache.getMesh(_collisionMeshKey); // NOTE: the model will render the collisionGeometry if it has one _model->setCollisionMesh(mesh); } else { @@ -1286,7 +1356,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce collisionMeshCache.releaseMesh(_collisionMeshKey); } // clear model's collision geometry - model::MeshPointer mesh = nullptr; + graphics::MeshPointer mesh = nullptr; _model->setCollisionMesh(mesh); } } @@ -1310,6 +1380,8 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce if (!_texturesLoaded && model->getGeometry() && model->getGeometry()->areTexturesLoaded()) { _texturesLoaded = true; model->updateRenderItems(); + } else if (!_texturesLoaded) { + emit requestRenderUpdate(); } // When the individual mesh parts of a model finish fading, they will mark their Model as needing updating @@ -1322,8 +1394,14 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce // That is where _currentFrame and _lastAnimated were updated. if (_animating) { DETAILED_PROFILE_RANGE(simulation_physics, "Animate"); + if (!jointsMapped()) { mapJoints(entity, model->getJointNames()); + //else the joint have been mapped before but we have a new animation to load + } else if (_animation && (_animation->getURL().toString() != entity->getAnimationURL())) { + _animation = DependencyManager::get()->getAnimation(entity->getAnimationURL()); + _jointMappingCompleted = false; + mapJoints(entity, model->getJointNames()); } if (!(entity->getAnimationFirstFrame() < 0) && !(entity->getAnimationFirstFrame() > entity->getAnimationLastFrame())) { animate(entity); @@ -1342,20 +1420,11 @@ void ModelEntityRenderer::doRender(RenderArgs* args) { DETAILED_PROFILE_RANGE(render_detail, "MetaModelRender"); DETAILED_PERFORMANCE_TIMER("RMEIrender"); - ModelPointer model; - withReadLock([&]{ - model = _model; - }); - - // If we don't have a model, or the model doesn't have visual geometry, render our bounding box as green wireframe - if (!model || (model && model->didVisualGeometryRequestFail())) { - static glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f); - gpu::Batch& batch = *args->_batch; - batch.setModelTransform(getModelTransform()); // we want to include the scale as well - DependencyManager::get()->renderWireCubeInstance(args, batch, greenColor); - return; - } - + // If the model doesn't have visual geometry, render our bounding box as green wireframe + static glm::vec4 greenColor(0.0f, 1.0f, 0.0f, 1.0f); + gpu::Batch& batch = *args->_batch; + batch.setModelTransform(getModelTransform()); // we want to include the scale as well + DependencyManager::get()->renderWireCubeInstance(args, batch, greenColor); // Enqueue updates for the next frame #if WANT_EXTRA_DEBUGGING @@ -1366,12 +1435,6 @@ void ModelEntityRenderer::doRender(RenderArgs* args) { // Remap textures for the next frame to avoid flicker // remapTextures(); - - bool showCollisionGeometry = (bool)(args->_debugFlags & (int)RenderArgs::RENDER_DEBUG_HULLS); - if (showCollisionGeometry != _showCollisionGeometry) { - _showCollisionGeometry = showCollisionGeometry; - flagForCollisionGeometryUpdate(); - } } void ModelEntityRenderer::mapJoints(const TypedEntityPointer& entity, const QStringList& modelJointNames) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index b7a339e1e1..b3988e0239 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -81,8 +81,14 @@ public: void setCollisionShape(const btCollisionShape* shape) override; virtual bool contains(const glm::vec3& point) const override; + void stopModelOverrideIfNoParent(); virtual bool shouldBePhysical() const override; + void simulateRelayedJoints(); + bool getJointMapCompleted(); + void setJointMap(std::vector jointMap); + int avatarJointIndex(int modelJointIndex); + void setOverrideTransform(const Transform& transform, const glm::vec3& offset); // these are in the frame of this object (model space) virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override; @@ -90,7 +96,6 @@ public: virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override; virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override; - virtual glm::quat getLocalJointRotation(int index) const override; virtual glm::vec3 getLocalJointTranslation(int index) const override; virtual bool setLocalJointRotation(int index, const glm::quat& rotation) override; @@ -119,7 +124,9 @@ private: void getCollisionGeometryResource(); GeometryResource::Pointer _compoundShapeResource; + bool _jointMapCompleted { false }; bool _originalTexturesRead { false }; + std::vector _jointMap; QVariantMap _originalTextures; bool _dimensionsInitialized { true }; bool _needsJointSimulation { false }; @@ -133,12 +140,13 @@ class ModelEntityRenderer : public TypedEntityRenderer _texturedPipeline; +// FIXME: This is interfering with the uniform buffers in DeferredLightingEffect.cpp, so use 11 to avoid collisions +static int32_t PARTICLE_UNIFORM_SLOT { 11 }; -static ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const ShapeKey& key) { +static ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const ShapeKey& key, gpu::Batch& batch) { auto texturedPipeline = _texturedPipeline.lock(); if (!texturedPipeline) { auto state = std::make_shared(); @@ -39,6 +41,12 @@ static ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, co auto program = gpu::Shader::createProgram(vertShader, fragShader); _texturedPipeline = texturedPipeline = gpu::Pipeline::create(program, state); + + batch.runLambda([program] { + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("particleBuffer"), PARTICLE_UNIFORM_SLOT)); + gpu::Shader::makeProgram(*program, slotBindings); + }); } return std::make_shared(texturedPipeline, nullptr, nullptr, nullptr); @@ -320,7 +328,7 @@ void ParticleEffectEntityRenderer::doRender(RenderArgs* args) { transform.setScale(vec3(1)); } batch.setModelTransform(transform); - batch.setUniformBuffer(0, _uniformBuffer); + batch.setUniformBuffer(PARTICLE_UNIFORM_SLOT, _uniformBuffer); batch.setInputFormat(_vertexFormat); batch.setInputBuffer(0, _particleBuffer, 0, sizeof(GpuParticle)); diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index 21764dff7f..4d223669b4 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -33,8 +33,9 @@ using namespace render; using namespace render::entities; static uint8_t CUSTOM_PIPELINE_NUMBER { 0 }; -static const int32_t PAINTSTROKE_TEXTURE_SLOT{ 0 }; -static const int32_t PAINTSTROKE_UNIFORM_SLOT{ 0 }; +static const int32_t PAINTSTROKE_TEXTURE_SLOT { 0 }; +// FIXME: This is interfering with the uniform buffers in DeferredLightingEffect.cpp, so use 11 to avoid collisions +static const int32_t PAINTSTROKE_UNIFORM_SLOT { 11 }; static gpu::Stream::FormatPointer polylineFormat; static gpu::PipelinePointer polylinePipeline; #ifdef POLYLINE_ENTITY_USE_FADE_EFFECT @@ -45,7 +46,7 @@ struct PolyLineUniforms { glm::vec3 color; }; -static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key) { +static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key, gpu::Batch& batch) { if (!polylinePipeline) { auto VS = gpu::Shader::createVertex(std::string(paintStroke_vert)); auto PS = gpu::Shader::createPixel(std::string(paintStroke_frag)); @@ -55,15 +56,21 @@ static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlum auto fadePS = gpu::Shader::createPixel(std::string(paintStroke_fade_frag)); gpu::ShaderPointer fadeProgram = gpu::Shader::createProgram(fadeVS, fadePS); #endif - gpu::Shader::BindingSet slotBindings; - slotBindings.insert(gpu::Shader::Binding(std::string("originalTexture"), PAINTSTROKE_TEXTURE_SLOT)); - slotBindings.insert(gpu::Shader::Binding(std::string("polyLineBuffer"), PAINTSTROKE_UNIFORM_SLOT)); - gpu::Shader::makeProgram(*program, slotBindings); + batch.runLambda([program #ifdef POLYLINE_ENTITY_USE_FADE_EFFECT - slotBindings.insert(gpu::Shader::Binding(std::string("fadeMaskMap"), PAINTSTROKE_TEXTURE_SLOT + 1)); - slotBindings.insert(gpu::Shader::Binding(std::string("fadeParametersBuffer"), PAINTSTROKE_UNIFORM_SLOT + 1)); - gpu::Shader::makeProgram(*fadeProgram, slotBindings); + , fadeProgram #endif + ] { + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("originalTexture"), PAINTSTROKE_TEXTURE_SLOT)); + slotBindings.insert(gpu::Shader::Binding(std::string("polyLineBuffer"), PAINTSTROKE_UNIFORM_SLOT)); + gpu::Shader::makeProgram(*program, slotBindings); +#ifdef POLYLINE_ENTITY_USE_FADE_EFFECT + slotBindings.insert(gpu::Shader::Binding(std::string("fadeMaskMap"), PAINTSTROKE_TEXTURE_SLOT + 1)); + slotBindings.insert(gpu::Shader::Binding(std::string("fadeParametersBuffer"), PAINTSTROKE_UNIFORM_SLOT + 1)); + gpu::Shader::makeProgram(*fadeProgram, slotBindings); +#endif + }); gpu::StatePointer state = gpu::StatePointer(new gpu::State()); state->setDepthTest(true, true, gpu::LESS_EQUAL); PrepareStencil::testMask(*state); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 496f8b7323..aadd49fde8 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -65,7 +65,7 @@ #pragma warning(pop) #endif -#include "model/Geometry.h" +#include "graphics/Geometry.h" #include "StencilMaskPass.h" @@ -1060,7 +1060,7 @@ void RenderablePolyVoxEntityItem::recomputeMesh() { auto entity = std::static_pointer_cast(getThisPointer()); QtConcurrent::run([entity, voxelSurfaceStyle] { - model::MeshPointer mesh(new model::Mesh()); + graphics::MeshPointer mesh(new graphics::Mesh()); // A mesh object to hold the result of surface extraction PolyVox::SurfaceMesh polyVoxMesh; @@ -1122,18 +1122,18 @@ void RenderablePolyVoxEntityItem::recomputeMesh() { sizeof(PolyVox::PositionMaterialNormal), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ))); - std::vector parts; - parts.emplace_back(model::Mesh::Part((model::Index)0, // startIndex - (model::Index)vecIndices.size(), // numIndices - (model::Index)0, // baseVertex - model::Mesh::TRIANGLES)); // topology - mesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), + std::vector parts; + parts.emplace_back(graphics::Mesh::Part((graphics::Index)0, // startIndex + (graphics::Index)vecIndices.size(), // numIndices + (graphics::Index)0, // baseVertex + graphics::Mesh::TRIANGLES)); // topology + mesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(graphics::Mesh::Part), (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); entity->setMesh(mesh); }); } -void RenderablePolyVoxEntityItem::setMesh(model::MeshPointer mesh) { +void RenderablePolyVoxEntityItem::setMesh(graphics::MeshPointer mesh) { // this catches the payload from recomputeMesh bool neighborsNeedUpdate; withWriteLock([&] { @@ -1164,7 +1164,7 @@ void RenderablePolyVoxEntityItem::computeShapeInfoWorker() { PolyVoxSurfaceStyle voxelSurfaceStyle; glm::vec3 voxelVolumeSize; - model::MeshPointer mesh; + graphics::MeshPointer mesh; withReadLock([&] { voxelSurfaceStyle = _voxelSurfaceStyle; @@ -1422,27 +1422,29 @@ bool RenderablePolyVoxEntityItem::getMeshes(MeshProxyList& result) { } bool success = false; - MeshProxy* meshProxy = nullptr; - glm::mat4 transform = voxelToLocalMatrix(); - withReadLock([&] { - gpu::BufferView::Index numVertices = (gpu::BufferView::Index)_mesh->getNumVertices(); - if (!_meshReady) { - // we aren't ready to return a mesh. the caller will have to try again later. - success = false; - } else if (numVertices == 0) { - // we are ready, but there are no triangles in the mesh. - success = true; - } else { - success = true; - // the mesh will be in voxel-space. transform it into object-space - meshProxy = new SimpleMeshProxy( - _mesh->map([=](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); }, - [=](glm::vec3 color){ return color; }, - [=](glm::vec3 normal){ return glm::normalize(glm::vec3(transform * glm::vec4(normal, 0.0f))); }, - [&](uint32_t index){ return index; })); - result << meshProxy; - } - }); + if (_mesh) { + MeshProxy* meshProxy = nullptr; + glm::mat4 transform = voxelToLocalMatrix(); + withReadLock([&] { + gpu::BufferView::Index numVertices = (gpu::BufferView::Index)_mesh->getNumVertices(); + if (!_meshReady) { + // we aren't ready to return a mesh. the caller will have to try again later. + success = false; + } else if (numVertices == 0) { + // we are ready, but there are no triangles in the mesh. + success = true; + } else { + success = true; + // the mesh will be in voxel-space. transform it into object-space + meshProxy = new SimpleMeshProxy( + _mesh->map([=](glm::vec3 position) { return glm::vec3(transform * glm::vec4(position, 1.0f)); }, + [=](glm::vec3 color) { return color; }, + [=](glm::vec3 normal) { return glm::normalize(glm::vec3(transform * glm::vec4(normal, 0.0f))); }, + [&](uint32_t index) { return index; })); + result << meshProxy; + } + }); + } return success; } @@ -1455,7 +1457,7 @@ static gpu::PipelinePointer _pipelines[2]; static gpu::PipelinePointer _wireframePipelines[2]; static gpu::Stream::FormatPointer _vertexFormat; -ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const ShapeKey& key) { +ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const ShapeKey& key, gpu::Batch& batch) { if (!_pipelines[0]) { gpu::ShaderPointer vertexShaders[2] = { gpu::Shader::createVertex(std::string(polyvox_vert)), gpu::Shader::createVertex(std::string(polyvox_fade_vert)) }; gpu::ShaderPointer pixelShaders[2] = { gpu::Shader::createPixel(std::string(polyvox_frag)), gpu::Shader::createPixel(std::string(polyvox_fade_frag)) }; @@ -1483,7 +1485,10 @@ ShapePipelinePointer shapePipelineFactory(const ShapePlumber& plumber, const Sha // Two sets of pipelines: normal and fading for (auto i = 0; i < 2; i++) { gpu::ShaderPointer program = gpu::Shader::createProgram(vertexShaders[i], pixelShaders[i]); - gpu::Shader::makeProgram(*program, slotBindings); + + batch.runLambda([program, slotBindings] { + gpu::Shader::makeProgram(*program, slotBindings); + }); _pipelines[i] = gpu::Pipeline::create(program, state); _wireframePipelines[i] = gpu::Pipeline::create(program, wireframeState); @@ -1580,7 +1585,7 @@ void PolyVoxEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& s void PolyVoxEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { _lastVoxelToWorldMatrix = entity->voxelToWorldMatrix(); - model::MeshPointer newMesh; + graphics::MeshPointer newMesh; entity->withReadLock([&] { newMesh = entity->_mesh; }); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 6ac518f79b..03c7351a59 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -21,8 +21,8 @@ #include #include -#include -#include +#include +#include #include #include @@ -104,7 +104,7 @@ public: void forEachVoxelValue(const ivec3& voxelSize, std::function thunk); QByteArray volDataToArray(quint16 voxelXSize, quint16 voxelYSize, quint16 voxelZSize) const; - void setMesh(model::MeshPointer mesh); + void setMesh(graphics::MeshPointer mesh); void setCollisionPoints(ShapeInfo::PointCollection points, AABox box); PolyVox::SimpleVolume* getVolData() { return _volData.get(); } @@ -134,7 +134,7 @@ private: // may not match _voxelVolumeSize. bool _meshDirty { true }; // does collision-shape need to be recomputed? bool _meshReady{ false }; - model::MeshPointer _mesh; + graphics::MeshPointer _mesh; ShapeInfo _shapeInfo; @@ -178,7 +178,7 @@ private: bool _hasTransitioned{ false }; #endif - model::MeshPointer _mesh; + graphics::MeshPointer _mesh; std::array _xyzTextures; glm::vec3 _lastVoxelVolumeSize; glm::mat4 _lastVoxelToWorldMatrix; diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 591bccaabe..b43944f26a 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -13,7 +13,7 @@ #include -#include +#include #include #include @@ -145,10 +145,8 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { updateSkyboxMap(); if (_needBackgroundUpdate) { - if (BackgroundStage::isIndexInvalid(_backgroundIndex)) { + if (_skyboxMode == COMPONENT_MODE_ENABLED && BackgroundStage::isIndexInvalid(_backgroundIndex)) { _backgroundIndex = _backgroundStage->addBackground(_background); - } else { - } _needBackgroundUpdate = false; } @@ -164,21 +162,31 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { } if (_visible) { - // FInally, push the light visible in the frame - // THe directional key light for sure - _stage->_currentFrame.pushSunLight(_sunIndex); - - // The ambient light only if it has a valid texture to render with - if (_validAmbientTexture || _validSkyboxTexture) { - _stage->_currentFrame.pushAmbientLight(_ambientIndex); + // Finally, push the lights visible in the frame + // + // If component is disabled then push component off state + // else if component is enabled then push current state + // (else mode is inherit, the value from the parent zone will be used + // + if (_keyLightMode == COMPONENT_MODE_DISABLED) { + _stage->_currentFrame.pushSunLight(_stage->getSunOffLight()); + } else if (_keyLightMode == COMPONENT_MODE_ENABLED) { + _stage->_currentFrame.pushSunLight(_sunIndex); } - // The background only if the mode is not inherit - if (_backgroundMode != BACKGROUND_MODE_INHERIT) { + if (_skyboxMode == COMPONENT_MODE_DISABLED) { + _backgroundStage->_currentFrame.pushBackground(INVALID_INDEX); + } else if (_skyboxMode == COMPONENT_MODE_ENABLED) { _backgroundStage->_currentFrame.pushBackground(_backgroundIndex); } - // Haze only if the mode is not inherit + if (_ambientLightMode == COMPONENT_MODE_DISABLED) { + _stage->_currentFrame.pushAmbientLight(_stage->getAmbientOffLight()); + } else if (_ambientLightMode == COMPONENT_MODE_ENABLED) { + _stage->_currentFrame.pushAmbientLight(_ambientIndex); + } + + // Haze only if the mode is not inherit, as the model deals with on/off if (_hazeMode != COMPONENT_MODE_INHERIT) { _hazeStage->_currentFrame.pushHaze(_hazeIndex); } @@ -194,14 +202,13 @@ void ZoneEntityRenderer::removeFromScene(const ScenePointer& scene, Transaction& Parent::removeFromScene(scene, transaction); } - void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) { DependencyManager::get()->updateZone(entity->getID()); // FIXME one of the bools here could become true between being fetched and being reset, // resulting in a lost update - bool sunChanged = entity->keyLightPropertiesChanged(); - bool backgroundChanged = entity->backgroundPropertiesChanged(); + bool keyLightChanged = entity->keyLightPropertiesChanged(); + bool ambientLightChanged = entity->ambientLightPropertiesChanged(); bool skyboxChanged = entity->skyboxPropertiesChanged(); bool hazeChanged = entity->hazePropertiesChanged(); @@ -211,9 +218,9 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen _lastDimensions = entity->getScaledDimensions(); _keyLightProperties = entity->getKeyLightProperties(); + _ambientLightProperties = entity->getAmbientLightProperties(); _skyboxProperties = entity->getSkyboxProperties(); _hazeProperties = entity->getHazeProperties(); - _stageProperties = entity->getStageProperties(); #if 0 if (_lastShapeURL != _typedEntity->getCompoundShapeURL()) { @@ -236,15 +243,15 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen updateKeyZoneItemFromEntity(); - if (sunChanged) { - updateKeySunFromEntity(); + if (keyLightChanged) { + updateKeySunFromEntity(entity); } - if (sunChanged || skyboxChanged) { - updateKeyAmbientFromEntity(); + if (ambientLightChanged) { + updateAmbientLightFromEntity(entity); } - if (backgroundChanged || skyboxChanged) { + if (skyboxChanged) { updateKeyBackgroundFromEntity(entity); } @@ -267,9 +274,10 @@ ItemKey ZoneEntityRenderer::getKey() { bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const { if (entity->keyLightPropertiesChanged() || - entity->backgroundPropertiesChanged() || + entity->ambientLightPropertiesChanged() || entity->hazePropertiesChanged() || entity->skyboxPropertiesChanged()) { + return true; } @@ -310,9 +318,11 @@ bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint return false; } -void ZoneEntityRenderer::updateKeySunFromEntity() { +void ZoneEntityRenderer::updateKeySunFromEntity(const TypedEntityPointer& entity) { + setKeyLightMode((ComponentMode)entity->getKeyLightMode()); + const auto& sunLight = editSunLight(); - sunLight->setType(model::Light::SUN); + sunLight->setType(graphics::Light::SUN); sunLight->setPosition(_lastPosition); sunLight->setOrientation(_lastRotation); @@ -322,20 +332,22 @@ void ZoneEntityRenderer::updateKeySunFromEntity() { sunLight->setDirection(_keyLightProperties.getDirection()); } -void ZoneEntityRenderer::updateKeyAmbientFromEntity() { +void ZoneEntityRenderer::updateAmbientLightFromEntity(const TypedEntityPointer& entity) { + setAmbientLightMode((ComponentMode)entity->getAmbientLightMode()); + const auto& ambientLight = editAmbientLight(); - ambientLight->setType(model::Light::AMBIENT); + ambientLight->setType(graphics::Light::AMBIENT); ambientLight->setPosition(_lastPosition); ambientLight->setOrientation(_lastRotation); - // Set the keylight - ambientLight->setAmbientIntensity(_keyLightProperties.getAmbientIntensity()); + // Set the ambient light + ambientLight->setAmbientIntensity(_ambientLightProperties.getAmbientIntensity()); - if (_keyLightProperties.getAmbientURL().isEmpty()) { + if (_ambientLightProperties.getAmbientURL().isEmpty()) { setAmbientURL(_skyboxProperties.getURL()); } else { - setAmbientURL(_keyLightProperties.getAmbientURL()); + setAmbientURL(_ambientLightProperties.getAmbientURL()); } } @@ -348,30 +360,31 @@ void ZoneEntityRenderer::updateHazeFromEntity(const TypedEntityPointer& entity) haze->setHazeActive(hazeMode == COMPONENT_MODE_ENABLED); haze->setAltitudeBased(_hazeProperties.getHazeAltitudeEffect()); - haze->setHazeRangeFactor(model::Haze::convertHazeRangeToHazeRangeFactor(_hazeProperties.getHazeRange())); + haze->setHazeRangeFactor(graphics::Haze::convertHazeRangeToHazeRangeFactor(_hazeProperties.getHazeRange())); xColor hazeColor = _hazeProperties.getHazeColor(); haze->setHazeColor(glm::vec3(hazeColor.red / 255.0, hazeColor.green / 255.0, hazeColor.blue / 255.0)); xColor hazeGlareColor = _hazeProperties.getHazeGlareColor(); haze->setHazeGlareColor(glm::vec3(hazeGlareColor.red / 255.0, hazeGlareColor.green / 255.0, hazeGlareColor.blue / 255.0)); haze->setHazeEnableGlare(_hazeProperties.getHazeEnableGlare()); - haze->setHazeGlareBlend(model::Haze::convertGlareAngleToPower(_hazeProperties.getHazeGlareAngle())); + haze->setHazeGlareBlend(graphics::Haze::convertGlareAngleToPower(_hazeProperties.getHazeGlareAngle())); float hazeAltitude = _hazeProperties.getHazeCeiling() - _hazeProperties.getHazeBaseRef(); - haze->setHazeAltitudeFactor(model::Haze::convertHazeAltitudeToHazeAltitudeFactor(hazeAltitude)); + haze->setHazeAltitudeFactor(graphics::Haze::convertHazeAltitudeToHazeAltitudeFactor(hazeAltitude)); haze->setHazeBaseReference(_hazeProperties.getHazeBaseRef()); haze->setHazeBackgroundBlend(_hazeProperties.getHazeBackgroundBlend()); haze->setHazeAttenuateKeyLight(_hazeProperties.getHazeAttenuateKeyLight()); - haze->setHazeKeyLightRangeFactor(model::Haze::convertHazeRangeToHazeRangeFactor(_hazeProperties.getHazeKeyLightRange())); - haze->setHazeKeyLightAltitudeFactor(model::Haze::convertHazeAltitudeToHazeAltitudeFactor(_hazeProperties.getHazeKeyLightAltitude())); + haze->setHazeKeyLightRangeFactor(graphics::Haze::convertHazeRangeToHazeRangeFactor(_hazeProperties.getHazeKeyLightRange())); + haze->setHazeKeyLightAltitudeFactor(graphics::Haze::convertHazeAltitudeToHazeAltitudeFactor(_hazeProperties.getHazeKeyLightAltitude())); haze->setZoneTransform(entity->getTransform().getMatrix()); } void ZoneEntityRenderer::updateKeyBackgroundFromEntity(const TypedEntityPointer& entity) { + setSkyboxMode((ComponentMode)entity->getSkyboxMode()); + editBackground(); - setBackgroundMode(entity->getBackgroundMode()); setSkyboxColor(_skyboxProperties.getColorVec3()); setProceduralUserData(entity->getUserData()); setSkyboxURL(_skyboxProperties.getURL()); @@ -403,7 +416,6 @@ void ZoneEntityRenderer::setAmbientURL(const QString& ambientUrl) { _ambientTextureURL = ambientUrl; if (_ambientTextureURL.isEmpty()) { - _validAmbientTexture = false; _pendingAmbientTexture = false; _ambientTexture.clear(); @@ -431,7 +443,6 @@ void ZoneEntityRenderer::updateAmbientMap() { _ambientLight->setAmbientSpherePreset(gpu::SphericalHarmonics::BREEZEWAY); } editAmbientLight()->setAmbientMap(texture); - _validAmbientTexture = true; } else { qCDebug(entitiesrenderer) << "Failed to load ambient texture:" << _ambientTexture->getURL(); } @@ -447,7 +458,6 @@ void ZoneEntityRenderer::setSkyboxURL(const QString& skyboxUrl) { _skyboxTextureURL = skyboxUrl; if (_skyboxTextureURL.isEmpty()) { - _validSkyboxTexture = false; _pendingSkyboxTexture = false; _skyboxTexture.clear(); @@ -467,7 +477,6 @@ void ZoneEntityRenderer::updateSkyboxMap() { auto texture = _skyboxTexture->getGPUTexture(); if (texture) { editSkybox()->setCubemap(texture); - _validSkyboxTexture = true; } else { qCDebug(entitiesrenderer) << "Failed to load Skybox texture:" << _skyboxTexture->getURL(); } @@ -475,14 +484,22 @@ void ZoneEntityRenderer::updateSkyboxMap() { } } -void ZoneEntityRenderer::setBackgroundMode(BackgroundMode mode) { - _backgroundMode = mode; -} - void ZoneEntityRenderer::setHazeMode(ComponentMode mode) { _hazeMode = mode; } +void ZoneEntityRenderer::setKeyLightMode(ComponentMode mode) { + _keyLightMode = mode; +} + +void ZoneEntityRenderer::setAmbientLightMode(ComponentMode mode) { + _ambientLightMode = mode; +} + +void ZoneEntityRenderer::setSkyboxMode(ComponentMode mode) { + _skyboxMode = mode; +} + void ZoneEntityRenderer::setSkyboxColor(const glm::vec3& color) { editSkybox()->setColor(color); } diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h index 050a8a4386..69f0663c06 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h @@ -13,9 +13,9 @@ #define hifi_RenderableZoneEntityItem_h #include -#include -#include -#include +#include +#include +#include #include #include #include @@ -46,24 +46,28 @@ protected: private: void updateKeyZoneItemFromEntity(); - void updateKeySunFromEntity(); - void updateKeyAmbientFromEntity(); + void updateKeySunFromEntity(const TypedEntityPointer& entity); + void updateAmbientLightFromEntity(const TypedEntityPointer& entity); void updateHazeFromEntity(const TypedEntityPointer& entity); void updateKeyBackgroundFromEntity(const TypedEntityPointer& entity); void updateAmbientMap(); void updateSkyboxMap(); void setAmbientURL(const QString& ambientUrl); void setSkyboxURL(const QString& skyboxUrl); - void setBackgroundMode(BackgroundMode mode); + void setHazeMode(ComponentMode mode); + void setKeyLightMode(ComponentMode mode); + void setAmbientLightMode(ComponentMode mode); + void setSkyboxMode(ComponentMode mode); + void setSkyboxColor(const glm::vec3& color); void setProceduralUserData(const QString& userData); - model::LightPointer editSunLight() { _needSunUpdate = true; return _sunLight; } - model::LightPointer editAmbientLight() { _needAmbientUpdate = true; return _ambientLight; } - model::SunSkyStagePointer editBackground() { _needBackgroundUpdate = true; return _background; } - model::SkyboxPointer editSkybox() { return editBackground()->getSkybox(); } - model::HazePointer editHaze() { _needHazeUpdate = true; return _haze; } + graphics::LightPointer editSunLight() { _needSunUpdate = true; return _sunLight; } + graphics::LightPointer editAmbientLight() { _needAmbientUpdate = true; return _ambientLight; } + graphics::SunSkyStagePointer editBackground() { _needBackgroundUpdate = true; return _background; } + graphics::SkyboxPointer editSkybox() { return editBackground()->getSkybox(); } + graphics::HazePointer editHaze() { _needHazeUpdate = true; return _haze; } bool _needsInitialSimulation{ true }; glm::vec3 _lastPosition; @@ -79,13 +83,15 @@ private: #endif LightStagePointer _stage; - const model::LightPointer _sunLight{ std::make_shared() }; - const model::LightPointer _ambientLight{ std::make_shared() }; - const model::SunSkyStagePointer _background{ std::make_shared() }; - const model::HazePointer _haze{ std::make_shared() }; + const graphics::LightPointer _sunLight{ std::make_shared() }; + const graphics::LightPointer _ambientLight{ std::make_shared() }; + const graphics::SunSkyStagePointer _background{ std::make_shared() }; + const graphics::HazePointer _haze{ std::make_shared() }; - BackgroundMode _backgroundMode{ BACKGROUND_MODE_INHERIT }; - ComponentMode _hazeMode{ COMPONENT_MODE_INHERIT }; + ComponentMode _keyLightMode { COMPONENT_MODE_INHERIT }; + ComponentMode _ambientLightMode { COMPONENT_MODE_INHERIT }; + ComponentMode _skyboxMode { COMPONENT_MODE_INHERIT }; + ComponentMode _hazeMode { COMPONENT_MODE_INHERIT }; indexed_container::Index _sunIndex{ LightStage::INVALID_INDEX }; indexed_container::Index _shadowIndex{ LightStage::INVALID_INDEX }; @@ -104,20 +110,18 @@ private: bool _needHazeUpdate{ true }; KeyLightPropertyGroup _keyLightProperties; + AmbientLightPropertyGroup _ambientLightProperties; HazePropertyGroup _hazeProperties; - StagePropertyGroup _stageProperties; SkyboxPropertyGroup _skyboxProperties; // More attributes used for rendering: QString _ambientTextureURL; NetworkTexturePointer _ambientTexture; bool _pendingAmbientTexture{ false }; - bool _validAmbientTexture{ false }; QString _skyboxTextureURL; NetworkTexturePointer _skyboxTexture; bool _pendingSkyboxTexture{ false }; - bool _validSkyboxTexture{ false }; QString _proceduralUserData; Transform _renderTransform; diff --git a/libraries/entities-renderer/src/polyvox.slf b/libraries/entities-renderer/src/polyvox.slf index 56f6f31d71..50034db48c 100644 --- a/libraries/entities-renderer/src/polyvox.slf +++ b/libraries/entities-renderer/src/polyvox.slf @@ -11,7 +11,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include DeferredBufferWrite.slh@> in vec3 _normal; diff --git a/libraries/entities-renderer/src/polyvox_fade.slf b/libraries/entities-renderer/src/polyvox_fade.slf index 7af43be53f..4c179a15b6 100644 --- a/libraries/entities-renderer/src/polyvox_fade.slf +++ b/libraries/entities-renderer/src/polyvox_fade.slf @@ -11,7 +11,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include DeferredBufferWrite.slh@> <@include Fade.slh@> diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index c23740654e..d6d9058e44 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -1,4 +1,4 @@ set(TARGET_NAME entities) setup_hifi_library(Network Script) include_directories(SYSTEM "${OPENSSL_INCLUDE_DIR}") -link_hifi_libraries(shared networking octree avatars model) +link_hifi_libraries(shared networking octree avatars graphics) diff --git a/libraries/entities/src/AmbientLightPropertyGroup.cpp b/libraries/entities/src/AmbientLightPropertyGroup.cpp new file mode 100644 index 0000000000..dbcb0eef75 --- /dev/null +++ b/libraries/entities/src/AmbientLightPropertyGroup.cpp @@ -0,0 +1,155 @@ +// +// AmbientLightPropertyGroup.cpp +// libraries/entities/src +// +// Created by Nissim Hadar on 2017/12/24. +// Copyright 2013 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "AmbientLightPropertyGroup.h" + +#include +#include + +#include "EntityItemProperties.h" +#include "EntityItemPropertiesMacros.h" + +const float AmbientLightPropertyGroup::DEFAULT_AMBIENT_LIGHT_INTENSITY = 0.5f; + +void AmbientLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, + QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { + + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_LIGHT_INTENSITY, AmbientLight, ambientLight, AmbientIntensity, ambientIntensity); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_AMBIENT_LIGHT_URL, AmbientLight, ambientLight, AmbientURL, ambientURL); +} + +void AmbientLightPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientLight, ambientIntensity, float, setAmbientIntensity); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(ambientLight, ambientURL, QString, setAmbientURL); + + // legacy property support + COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(ambientLightAmbientIntensity, float, setAmbientIntensity, getAmbientIntensity); +} + +void AmbientLightPropertyGroup::merge(const AmbientLightPropertyGroup& other) { + COPY_PROPERTY_IF_CHANGED(ambientIntensity); + COPY_PROPERTY_IF_CHANGED(ambientURL); +} + +void AmbientLightPropertyGroup::debugDump() const { + qCDebug(entities) << " AmbientLightPropertyGroup: ---------------------------------------------"; + qCDebug(entities) << " ambientIntensity:" << getAmbientIntensity(); + qCDebug(entities) << " ambientURL:" << getAmbientURL(); +} + +void AmbientLightPropertyGroup::listChangedProperties(QList& out) { + if (ambientIntensityChanged()) { + out << "ambientLight-ambientIntensity"; + } + if (ambientURLChanged()) { + out << "ambientLight-ambientURL"; + } +} + +bool AmbientLightPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_INTENSITY, getAmbientIntensity()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_URL, getAmbientURL()); + + return true; +} + +bool AmbientLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt, + int& processedBytes) { + + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + + READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_INTENSITY, float, setAmbientIntensity); + READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_URL, QString, setAmbientURL); + + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_LIGHT_INTENSITY, AmbientIntensity); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_AMBIENT_LIGHT_URL, AmbientURL); + + processedBytes += bytesRead; + + Q_UNUSED(somethingChanged); + + return true; +} + +void AmbientLightPropertyGroup::markAllChanged() { + _ambientIntensityChanged = true; + _ambientURLChanged = true; +} + +EntityPropertyFlags AmbientLightPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_INTENSITY, ambientIntensity); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_URL, ambientURL); + + return changedProperties; +} + +void AmbientLightPropertyGroup::getProperties(EntityItemProperties& properties) const { + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientLight, AmbientIntensity, getAmbientIntensity); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(AmbientLight, AmbientURL, getAmbientURL); +} + +bool AmbientLightPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientLight, AmbientIntensity, ambientIntensity, setAmbientIntensity); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(AmbientLight, AmbientURL, ambientURL, setAmbientURL); + + return somethingChanged; +} + +EntityPropertyFlags AmbientLightPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + + requestedProperties += PROP_AMBIENT_LIGHT_INTENSITY; + requestedProperties += PROP_AMBIENT_LIGHT_URL; + + return requestedProperties; +} + +void AmbientLightPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_INTENSITY, getAmbientIntensity()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_URL, getAmbientURL()); +} + +int AmbientLightPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_INTENSITY, float, setAmbientIntensity); + READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_URL, QString, setAmbientURL); + + return bytesRead; +} diff --git a/libraries/entities/src/StagePropertyGroup.h b/libraries/entities/src/AmbientLightPropertyGroup.h similarity index 71% rename from libraries/entities/src/StagePropertyGroup.h rename to libraries/entities/src/AmbientLightPropertyGroup.h index 3e5bad5443..fbbc7c9900 100644 --- a/libraries/entities/src/StagePropertyGroup.h +++ b/libraries/entities/src/AmbientLightPropertyGroup.h @@ -1,21 +1,25 @@ // -// StagePropertyGroup.h +// AmbientLightPropertyGroup.h // libraries/entities/src // -// Created by Brad Hefta-Gaub on 12/4/13. +// Created by Nissim Hadar on 2017/12/24. // Copyright 2013 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#ifndef hifi_StagePropertyGroup_h -#define hifi_StagePropertyGroup_h + +#ifndef hifi_AmbientLightPropertyGroup_h +#define hifi_AmbientLightPropertyGroup_h + +#include + +#include #include - -#include "PropertyGroup.h" #include "EntityItemPropertiesMacros.h" +#include "PropertyGroup.h" class EntityItemProperties; class EncodeBitstreamParams; @@ -23,11 +27,7 @@ class OctreePacketData; class EntityTreeElementExtraEncodeData; class ReadBitstreamToTreeParams; -#include -#include - - -class StagePropertyGroup : public PropertyGroup { +class AmbientLightPropertyGroup : public PropertyGroup { public: // EntityItemProperty related helpers virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, @@ -35,7 +35,7 @@ public: EntityItemProperties& defaultEntityProperties) const override; virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; - void merge(const StagePropertyGroup& other); + void merge(const AmbientLightPropertyGroup& other); virtual void debugDump() const override; virtual void listChangedProperties(QList& out) override; @@ -74,23 +74,10 @@ public: EntityPropertyFlags& propertyFlags, bool overwriteLocalData, bool& somethingChanged) override; - static const bool DEFAULT_STAGE_SUN_MODEL_ENABLED; - static const float DEFAULT_STAGE_LATITUDE; - static const float DEFAULT_STAGE_LONGITUDE; - static const float DEFAULT_STAGE_ALTITUDE; - static const quint16 DEFAULT_STAGE_DAY; - static const float DEFAULT_STAGE_HOUR; + static const float DEFAULT_AMBIENT_LIGHT_INTENSITY; - float calculateHour() const; - uint16_t calculateDay() const; - - DEFINE_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, SunModelEnabled, sunModelEnabled, bool, DEFAULT_STAGE_SUN_MODEL_ENABLED); - DEFINE_PROPERTY(PROP_STAGE_LATITUDE, Latitude, latitude, float, DEFAULT_STAGE_LATITUDE); - DEFINE_PROPERTY(PROP_STAGE_LONGITUDE, Longitude, longitude, float, DEFAULT_STAGE_LONGITUDE); - DEFINE_PROPERTY(PROP_STAGE_ALTITUDE, Altitude, altitude, float, DEFAULT_STAGE_ALTITUDE); - DEFINE_PROPERTY(PROP_STAGE_DAY, Day, day, uint16_t, DEFAULT_STAGE_DAY); - DEFINE_PROPERTY(PROP_STAGE_HOUR, Hour, hour, float, DEFAULT_STAGE_HOUR); - DEFINE_PROPERTY(PROP_STAGE_AUTOMATIC_HOURDAY, AutomaticHourDay, automaticHourDay, bool, false); + DEFINE_PROPERTY(PROP_AMBIENT_LIGHT_INTENSITY, AmbientIntensity, ambientIntensity, float, DEFAULT_AMBIENT_LIGHT_INTENSITY); + DEFINE_PROPERTY_REF(PROP_AMBIENT_LIGHT_URL, AmbientURL, ambientURL, QString, ""); }; -#endif // hifi_StagePropertyGroup_h +#endif // hifi_AmbientLightPropertyGroup_h diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index fc753713ba..fe5213baa8 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -2086,6 +2086,10 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi EntityDynamicPointer action = _objectActions[actionID]; auto removedActionType = action->getType(); + action->setOwnerEntity(nullptr); + action->setIsMine(false); + _objectActions.remove(actionID); + if ((removedActionType == DYNAMIC_TYPE_HOLD || removedActionType == DYNAMIC_TYPE_FAR_GRAB) && !stillHasGrabActions()) { _dirtyFlags &= ~Simulation::NO_BOOTSTRAPPING; _dirtyFlags |= Simulation::DIRTY_COLLISION_GROUP; // may need to not collide with own avatar @@ -2101,9 +2105,6 @@ bool EntityItem::removeActionInternal(const QUuid& actionID, EntitySimulationPoi // because they should have been set correctly when the action was added // and/or when children were linked } - action->setOwnerEntity(nullptr); - action->setIsMine(false); - _objectActions.remove(actionID); if (simulation) { action->removeFromSimulation(simulation); @@ -2715,9 +2716,17 @@ bool EntityItem::getVisible() const { } void EntityItem::setVisible(bool value) { + bool changed = false; withWriteLock([&] { - _visible = value; + if (_visible != value) { + changed = true; + _visible = value; + } }); + + if (changed) { + emit requestRenderUpdate(); + } } bool EntityItem::isChildOfMyAvatar() const { diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index d8fb3b3ab6..f9559a375b 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -368,6 +368,7 @@ public: void* getPhysicsInfo() const { return _physicsInfo; } void setPhysicsInfo(void* data) { _physicsInfo = data; } + EntityTreeElementPointer getElement() const { return _element; } EntityTreePointer getTree() const; virtual SpatialParentTree* getParentTree() const override; @@ -469,6 +470,9 @@ public: static QString _marketplacePublicKey; static void retrieveMarketplacePublicKey(); +signals: + void requestRenderUpdate(); + protected: QHash _changeHandlers; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 111b851962..e2a5ddf8b5 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -33,8 +33,8 @@ AnimationPropertyGroup EntityItemProperties::_staticAnimation; SkyboxPropertyGroup EntityItemProperties::_staticSkybox; HazePropertyGroup EntityItemProperties::_staticHaze; -StagePropertyGroup EntityItemProperties::_staticStage; KeyLightPropertyGroup EntityItemProperties::_staticKeyLight; +AmbientLightPropertyGroup EntityItemProperties::_staticAmbientLight; EntityPropertyList PROP_LAST_ITEM = (EntityPropertyList)(PROP_AFTER_LAST_ITEM - 1); @@ -79,6 +79,7 @@ void EntityItemProperties::debugDump() const { getSkybox().debugDump(); getHaze().debugDump(); getKeyLight().debugDump(); + getAmbientLight().debugDump(); qCDebug(entities) << " changed properties..."; EntityPropertyFlags props = getChangedProperties(); @@ -182,47 +183,7 @@ void EntityItemProperties::setShapeTypeFromString(const QString& shapeName) { } } -using BackgroundPair = std::pair; -const std::array BACKGROUND_MODES = { { - BackgroundPair { BACKGROUND_MODE_INHERIT, { "inherit" } }, - BackgroundPair { BACKGROUND_MODE_SKYBOX, { "skybox" } } -} }; - -QString EntityItemProperties::getBackgroundModeAsString() const { - return BACKGROUND_MODES[_backgroundMode].second; -} - -QString EntityItemProperties::getBackgroundModeString(BackgroundMode mode) { - return BACKGROUND_MODES[mode].second; -} - -void EntityItemProperties::setBackgroundModeFromString(const QString& backgroundMode) { - auto result = std::find_if(BACKGROUND_MODES.begin(), BACKGROUND_MODES.end(), [&](const BackgroundPair& pair) { - return (pair.second == backgroundMode); - }); - if (result != BACKGROUND_MODES.end()) { - _backgroundMode = result->first; - _backgroundModeChanged = true; - } -} - -using ComponentPair = std::pair; -const std::array COMPONENT_MODES = { { - ComponentPair{ COMPONENT_MODE_INHERIT,{ "inherit" } }, - ComponentPair{ COMPONENT_MODE_DISABLED,{ "disabled" } }, - ComponentPair{ COMPONENT_MODE_ENABLED,{ "enabled" } } -} }; - -QString EntityItemProperties::getHazeModeAsString() const { - // return "inherit" if _hazeMode is not valid - if (_hazeMode < COMPONENT_MODE_ITEM_COUNT) { - return COMPONENT_MODES[_hazeMode].second; - } else { - return COMPONENT_MODES[COMPONENT_MODE_INHERIT].second; - } -} - -QString EntityItemProperties::getHazeModeString(uint32_t mode) { +QString EntityItemProperties::getComponentModeAsString(uint32_t mode) { // return "inherit" if mode is not valid if (mode < COMPONENT_MODE_ITEM_COUNT) { return COMPONENT_MODES[mode].second; @@ -231,10 +192,27 @@ QString EntityItemProperties::getHazeModeString(uint32_t mode) { } } -void EntityItemProperties::setHazeModeFromString(const QString& hazeMode) { - auto result = std::find_if(COMPONENT_MODES.begin(), COMPONENT_MODES.end(), [&](const ComponentPair& pair) { - return (pair.second == hazeMode); +QString EntityItemProperties::getHazeModeAsString() const { + return getComponentModeAsString(_hazeMode); +} + +QString EntityItemProperties::getComponentModeString(uint32_t mode) { + // return "inherit" if mode is not valid + if (mode < COMPONENT_MODE_ITEM_COUNT) { + return COMPONENT_MODES[mode].second; + } else { + return COMPONENT_MODES[COMPONENT_MODE_INHERIT].second; + } +} + +std::array::const_iterator EntityItemProperties::findComponent(const QString& mode) { + return std::find_if(COMPONENT_MODES.begin(), COMPONENT_MODES.end(), [&](const ComponentPair& pair) { + return (pair.second == mode); }); +} + +void EntityItemProperties::setHazeModeFromString(const QString& hazeMode) { + auto result = findComponent(hazeMode); if (result != COMPONENT_MODES.end()) { _hazeMode = result->first; @@ -242,6 +220,45 @@ void EntityItemProperties::setHazeModeFromString(const QString& hazeMode) { } } +QString EntityItemProperties::getKeyLightModeAsString() const { + return getComponentModeAsString(_keyLightMode); +} + +void EntityItemProperties::setKeyLightModeFromString(const QString& keyLightMode) { + auto result = findComponent(keyLightMode); + + if (result != COMPONENT_MODES.end()) { + _keyLightMode = result->first; + _keyLightModeChanged = true; + } +} + +QString EntityItemProperties::getAmbientLightModeAsString() const { + return getComponentModeAsString(_ambientLightMode); +} + +void EntityItemProperties::setAmbientLightModeFromString(const QString& ambientLightMode) { + auto result = findComponent(ambientLightMode); + + if (result != COMPONENT_MODES.end()) { + _ambientLightMode = result->first; + _ambientLightModeChanged = true; + } +} + +QString EntityItemProperties::getSkyboxModeAsString() const { + return getComponentModeAsString(_skyboxMode); +} + +void EntityItemProperties::setSkyboxModeFromString(const QString& skyboxMode) { + auto result = findComponent(skyboxMode); + + if (result != COMPONENT_MODES.end()) { + _skyboxMode = result->first; + _skyboxModeChanged = true; + } +} + EntityPropertyFlags EntityItemProperties::getChangedProperties() const { EntityPropertyFlags changedProperties; @@ -327,9 +344,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_STATIC_CERTIFICATE_VERSION, staticCertificateVersion); CHECK_PROPERTY_CHANGE(PROP_NAME, name); - CHECK_PROPERTY_CHANGE(PROP_BACKGROUND_MODE, backgroundMode); CHECK_PROPERTY_CHANGE(PROP_HAZE_MODE, hazeMode); + CHECK_PROPERTY_CHANGE(PROP_KEY_LIGHT_MODE, keyLightMode); + CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_MODE, ambientLightMode); + CHECK_PROPERTY_CHANGE(PROP_SKYBOX_MODE, skyboxMode); CHECK_PROPERTY_CHANGE(PROP_SOURCE_URL, sourceUrl); CHECK_PROPERTY_CHANGE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize); @@ -376,11 +395,12 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_SHAPE, shape); CHECK_PROPERTY_CHANGE(PROP_DPI, dpi); + CHECK_PROPERTY_CHANGE(PROP_RELAY_PARENT_JOINTS, relayParentJoints); changedProperties += _animation.getChangedProperties(); changedProperties += _keyLight.getChangedProperties(); + changedProperties += _ambientLight.getChangedProperties(); changedProperties += _skybox.getChangedProperties(); - changedProperties += _stage.getChangedProperties(); changedProperties += _haze.getChangedProperties(); return changedProperties; @@ -508,6 +528,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_ROTATIONS, jointRotations); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_TRANSLATIONS_SET, jointTranslationsSet); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_JOINT_TRANSLATIONS, jointTranslations); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_RELAY_PARENT_JOINTS, relayParentJoints); } if (_type == EntityTypes::Model || _type == EntityTypes::Zone || _type == EntityTypes::ParticleEffect) { @@ -553,10 +574,8 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool // Zones only if (_type == EntityTypes::Zone) { _keyLight.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + _ambientLight.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); - COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BACKGROUND_MODE, backgroundMode, getBackgroundModeAsString()); - - _stage.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); _skybox.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FLYING_ALLOWED, flyingAllowed); @@ -565,6 +584,10 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_HAZE_MODE, hazeMode, getHazeModeAsString()); _haze.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_KEY_LIGHT_MODE, keyLightMode, getKeyLightModeAsString()); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AMBIENT_LIGHT_MODE, ambientLightMode, getAmbientLightModeAsString()); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SKYBOX_MODE, skyboxMode, getSkyboxModeAsString()); } // Web only @@ -699,7 +722,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionless, bool, setCollisionless); COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(ignoreForCollisions, bool, setCollisionless, getCollisionless); // legacy support COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionMask, uint8_t, setCollisionMask); - COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(collidesWith, CollisionMask); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(collidesWith, CollisionMask); COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(collisionsWillMove, bool, setDynamic, getDynamic); // legacy support COPY_PROPERTY_FROM_QSCRIPTVALUE(dynamic, bool, setDynamic); COPY_PROPERTY_FROM_QSCRIPTVALUE(isSpotlight, bool, setIsSpotlight); @@ -714,7 +737,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(lineHeight, float, setLineHeight); COPY_PROPERTY_FROM_QSCRIPTVALUE(textColor, xColor, setTextColor); COPY_PROPERTY_FROM_QSCRIPTVALUE(backgroundColor, xColor, setBackgroundColor); - COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(shapeType, ShapeType); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(shapeType, ShapeType); COPY_PROPERTY_FROM_QSCRIPTVALUE(maxParticles, quint32, setMaxParticles); COPY_PROPERTY_FROM_QSCRIPTVALUE(lifespan, float, setLifespan); COPY_PROPERTY_FROM_QSCRIPTVALUE(isEmitting, bool, setIsEmitting); @@ -734,6 +757,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusSpread, float, setRadiusSpread); COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusStart, float, setRadiusStart); COPY_PROPERTY_FROM_QSCRIPTVALUE(radiusFinish, float, setRadiusFinish); + COPY_PROPERTY_FROM_QSCRIPTVALUE(relayParentJoints, bool, setRelayParentJoints); // Certifiable Properties COPY_PROPERTY_FROM_QSCRIPTVALUE(itemName, QString, setItemName); @@ -751,9 +775,10 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(name, QString, setName); COPY_PROPERTY_FROM_QSCRIPTVALUE(collisionSoundURL, QString, setCollisionSoundURL); - COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(backgroundMode, BackgroundMode); - - COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(hazeMode, HazeMode); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(hazeMode, HazeMode); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(keyLightMode, KeyLightMode); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(ambientLightMode, AmbientLightMode); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(skyboxMode, SkyboxMode); COPY_PROPERTY_FROM_QSCRIPTVALUE(sourceUrl, QString, setSourceUrl); COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, glmVec3, setVoxelVolumeSize); @@ -783,8 +808,8 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool _animation.copyFromScriptValue(object, _defaultSettings); _keyLight.copyFromScriptValue(object, _defaultSettings); + _ambientLight.copyFromScriptValue(object, _defaultSettings); _skybox.copyFromScriptValue(object, _defaultSettings); - _stage.copyFromScriptValue(object, _defaultSettings); _haze.copyFromScriptValue(object, _defaultSettings); COPY_PROPERTY_FROM_QSCRIPTVALUE(xTextureURL, QString, setXTextureURL); @@ -910,9 +935,10 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(name); COPY_PROPERTY_IF_CHANGED(collisionSoundURL); - COPY_PROPERTY_IF_CHANGED(backgroundMode); - COPY_PROPERTY_IF_CHANGED(hazeMode); + COPY_PROPERTY_IF_CHANGED(keyLightMode); + COPY_PROPERTY_IF_CHANGED(ambientLightMode); + COPY_PROPERTY_IF_CHANGED(skyboxMode); COPY_PROPERTY_IF_CHANGED(sourceUrl); COPY_PROPERTY_IF_CHANGED(voxelVolumeSize); @@ -932,8 +958,8 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { _animation.merge(other._animation); _keyLight.merge(other._keyLight); + _ambientLight.merge(other._ambientLight); _skybox.merge(other._skybox); - _stage.merge(other._stage); _haze.merge(other._haze); COPY_PROPERTY_IF_CHANGED(xTextureURL); @@ -1101,13 +1127,11 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_COLOR, KeyLightColor, keyLightColor, xColor); ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_INTENSITY, KeyLightIntensity, keyLightIntensity, float); - ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_AMBIENT_INTENSITY, KeyLightAmbientIntensity, keyLightAmbientIntensity, float); ADD_PROPERTY_TO_MAP(PROP_KEYLIGHT_DIRECTION, KeyLightDirection, keyLightDirection, glm::vec3); ADD_PROPERTY_TO_MAP(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3); ADD_PROPERTY_TO_MAP(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray); ADD_PROPERTY_TO_MAP(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t); ADD_PROPERTY_TO_MAP(PROP_NAME, Name, name, QString); - ADD_PROPERTY_TO_MAP(PROP_BACKGROUND_MODE, BackgroundMode, backgroundMode, BackgroundMode); ADD_PROPERTY_TO_MAP(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString); ADD_PROPERTY_TO_MAP(PROP_LINE_WIDTH, LineWidth, lineWidth, float); ADD_PROPERTY_TO_MAP(PROP_LINE_POINTS, LinePoints, linePoints, QVector); @@ -1142,6 +1166,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_JOINT_ROTATIONS, JointRotations, jointRotations, QVector); ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS_SET, JointTranslationsSet, jointTranslationsSet, QVector); ADD_PROPERTY_TO_MAP(PROP_JOINT_TRANSLATIONS, JointTranslations, jointTranslations, QVector); + ADD_PROPERTY_TO_MAP(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool); ADD_PROPERTY_TO_MAP(PROP_SHAPE, Shape, shape, QString); @@ -1158,14 +1183,6 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_COLOR, Skybox, skybox, Color, color); ADD_GROUP_PROPERTY_TO_MAP(PROP_SKYBOX_URL, Skybox, skybox, URL, url); - ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_SUN_MODEL_ENABLED, Stage, stage, SunModelEnabled, sunModelEnabled); - ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_LATITUDE, Stage, stage, Latitude, latitude); - ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_LONGITUDE, Stage, stage, Longitude, longitude); - ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_ALTITUDE, Stage, stage, Altitude, altitude); - ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_DAY, Stage, stage, Day, day); - ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_HOUR, Stage, stage, Hour, hour); - ADD_GROUP_PROPERTY_TO_MAP(PROP_STAGE_AUTOMATIC_HOURDAY, Stage, stage, AutomaticHourDay, automaticHourDay); - ADD_PROPERTY_TO_MAP(PROP_FLYING_ALLOWED, FlyingAllowed, flyingAllowed, bool); ADD_PROPERTY_TO_MAP(PROP_GHOSTING_ALLOWED, GhostingAllowed, ghostingAllowed, bool); ADD_PROPERTY_TO_MAP(PROP_FILTER_URL, FilterURL, filterURL, QString); @@ -1188,6 +1205,10 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_KEYLIGHT_RANGE, Haze, haze, HazeKeyLightRange, hazeKeyLightRange); ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_KEYLIGHT_ALTITUDE, Haze, haze, HazeKeyLightAltitude, hazeKeyLightAltitude); + ADD_PROPERTY_TO_MAP(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t); + ADD_PROPERTY_TO_MAP(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t); + ADD_PROPERTY_TO_MAP(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t); + ADD_PROPERTY_TO_MAP(PROP_DPI, DPI, dpi, uint16_t); // FIXME - these are not yet handled @@ -1369,6 +1390,7 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_JOINT_ROTATIONS, properties.getJointRotations()); APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS_SET, properties.getJointTranslationsSet()); APPEND_ENTITY_PROPERTY(PROP_JOINT_TRANSLATIONS, properties.getJointTranslations()); + APPEND_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, properties.getRelayParentJoints()); } if (properties.getType() == EntityTypes::Light) { @@ -1414,14 +1436,12 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy _staticKeyLight.setProperties(properties); _staticKeyLight.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); - _staticStage.setProperties(properties); - _staticStage.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + _staticAmbientLight.setProperties(properties); + _staticAmbientLight.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)properties.getShapeType()); APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, properties.getCompoundShapeURL()); - APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_MODE, (uint32_t)properties.getBackgroundMode()); - _staticSkybox.setProperties(properties); _staticSkybox.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); @@ -1432,6 +1452,10 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)properties.getHazeMode()); _staticHaze.setProperties(properties); _staticHaze.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + + APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, (uint32_t)properties.getKeyLightMode()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, (uint32_t)properties.getAmbientLightMode()); + APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)properties.getSkyboxMode()); } if (properties.getType() == EntityTypes::PolyVox) { @@ -1726,6 +1750,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_ROTATIONS, QVector, setJointRotations); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_TRANSLATIONS_SET, QVector, setJointTranslationsSet); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_JOINT_TRANSLATIONS, QVector, setJointTranslations); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints); } if (properties.getType() == EntityTypes::Light) { @@ -1768,12 +1793,11 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int } if (properties.getType() == EntityTypes::Zone) { - properties.getKeyLight().decodeFromEditPacket(propertyFlags, dataAt , processedBytes); - properties.getStage().decodeFromEditPacket(propertyFlags, dataAt , processedBytes); + properties.getKeyLight().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + properties.getAmbientLight().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BACKGROUND_MODE, BackgroundMode, setBackgroundMode); properties.getSkybox().decodeFromEditPacket(propertyFlags, dataAt , processedBytes); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_FLYING_ALLOWED, bool, setFlyingAllowed); @@ -1782,7 +1806,11 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HAZE_MODE, uint32_t, setHazeMode); properties.getHaze().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); - } + + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEY_LIGHT_MODE, uint32_t, setKeyLightMode); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AMBIENT_LIGHT_MODE, uint32_t, setAmbientLightMode); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SKYBOX_MODE, uint32_t, setSkyboxMode); + } if (properties.getType() == EntityTypes::PolyVox) { READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_VOXEL_VOLUME_SIZE, glm::vec3, setVoxelVolumeSize); @@ -2010,13 +2038,13 @@ void EntityItemProperties::markAllChanged() { _staticCertificateVersionChanged = true; _keyLight.markAllChanged(); + _ambientLight.markAllChanged(); + _skybox.markAllChanged(); - _backgroundModeChanged = true; _hazeModeChanged = true; _animation.markAllChanged(); _skybox.markAllChanged(); - _stage.markAllChanged(); _haze.markAllChanged(); _sourceUrlChanged = true; @@ -2067,6 +2095,7 @@ void EntityItemProperties::markAllChanged() { _owningAvatarIDChanged = true; _dpiChanged = true; + _relayParentJointsChanged = true; } // The minimum bounding box for the entity. @@ -2354,14 +2383,22 @@ QList EntityItemProperties::listChangedProperties() { out += "staticCertificateVersion"; } - if (backgroundModeChanged()) { - out += "backgroundMode"; - } - if (hazeModeChanged()) { out += "hazeMode"; } + if (keyLightModeChanged()) { + out += "keyLightMode"; + } + + if (ambientLightModeChanged()) { + out += "ambientLightMode"; + } + + if (skyboxModeChanged()) { + out += "skyboxMode"; + } + if (voxelVolumeSizeChanged()) { out += "voxelVolumeSize"; } @@ -2425,6 +2462,9 @@ QList EntityItemProperties::listChangedProperties() { if (jointTranslationsChanged()) { out += "jointTranslations"; } + if (relayParentJointsChanged()) { + out += "relayParentJoints"; + } if (queryAACubeChanged()) { out += "queryAACube"; } @@ -2463,8 +2503,8 @@ QList EntityItemProperties::listChangedProperties() { getAnimation().listChangedProperties(out); getKeyLight().listChangedProperties(out); + getAmbientLight().listChangedProperties(out); getSkybox().listChangedProperties(out); - getStage().listChangedProperties(out); getHaze().listChangedProperties(out); return out; diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 4887b42138..3e0770f386 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -41,12 +41,18 @@ #include "SimulationOwner.h" #include "SkyboxPropertyGroup.h" #include "HazePropertyGroup.h" -#include "StagePropertyGroup.h" #include "TextEntityItem.h" #include "ZoneEntityItem.h" const quint64 UNKNOWN_CREATED_TIME = 0; +using ComponentPair = std::pair; +const std::array COMPONENT_MODES = { { + ComponentPair { COMPONENT_MODE_INHERIT, { "inherit" } }, + ComponentPair { COMPONENT_MODE_DISABLED, { "disabled" } }, + ComponentPair { COMPONENT_MODE_ENABLED, { "enabled" } } +} }; + /// A collection of properties of an entity item used in the scripting API. Translates between the actual properties of an /// entity and a JavaScript style hash/QScriptValue storing a set of properties. Used in scripting to set/get the complete /// set of entity item properties via JavaScript hashes/QScriptValues @@ -173,13 +179,15 @@ public: DEFINE_PROPERTY(PROP_RADIUS_FINISH, RadiusFinish, radiusFinish, float, particle::DEFAULT_RADIUS_FINISH); DEFINE_PROPERTY(PROP_EMITTER_SHOULD_TRAIL, EmitterShouldTrail, emitterShouldTrail, bool, particle::DEFAULT_EMITTER_SHOULD_TRAIL); DEFINE_PROPERTY_GROUP(KeyLight, keyLight, KeyLightPropertyGroup); + DEFINE_PROPERTY_GROUP(AmbientLight, ambientLight, AmbientLightPropertyGroup); DEFINE_PROPERTY_REF(PROP_VOXEL_VOLUME_SIZE, VoxelVolumeSize, voxelVolumeSize, glm::vec3, PolyVoxEntityItem::DEFAULT_VOXEL_VOLUME_SIZE); DEFINE_PROPERTY_REF(PROP_VOXEL_DATA, VoxelData, voxelData, QByteArray, PolyVoxEntityItem::DEFAULT_VOXEL_DATA); DEFINE_PROPERTY_REF(PROP_VOXEL_SURFACE_STYLE, VoxelSurfaceStyle, voxelSurfaceStyle, uint16_t, PolyVoxEntityItem::DEFAULT_VOXEL_SURFACE_STYLE); DEFINE_PROPERTY_REF(PROP_NAME, Name, name, QString, ENTITY_ITEM_DEFAULT_NAME); - DEFINE_PROPERTY_REF_ENUM(PROP_BACKGROUND_MODE, BackgroundMode, backgroundMode, BackgroundMode, BACKGROUND_MODE_INHERIT); - DEFINE_PROPERTY_GROUP(Stage, stage, StagePropertyGroup); + DEFINE_PROPERTY_REF_ENUM(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); + DEFINE_PROPERTY_REF_ENUM(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); + DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_GROUP(Skybox, skybox, SkyboxPropertyGroup); @@ -247,10 +255,12 @@ public: DEFINE_PROPERTY_REF(PROP_LAST_EDITED_BY, LastEditedBy, lastEditedBy, QUuid, ENTITY_ITEM_DEFAULT_LAST_EDITED_BY); DEFINE_PROPERTY_REF(PROP_SERVER_SCRIPTS, ServerScripts, serverScripts, QString, ENTITY_ITEM_DEFAULT_SERVER_SCRIPTS); + DEFINE_PROPERTY(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool, ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS); - static QString getBackgroundModeString(BackgroundMode mode); - static QString getHazeModeString(uint32_t mode); + static QString getComponentModeString(uint32_t mode); + static QString getComponentModeAsString(uint32_t mode); + std::array::const_iterator findComponent(const QString& mode); public: float getMaxDimension() const { return glm::compMax(_dimensions); } @@ -478,8 +488,11 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, CertificateID, certificateID, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, StaticCertificateVersion, staticCertificateVersion, ""); - DEBUG_PROPERTY_IF_CHANGED(debug, properties, BackgroundMode, backgroundMode, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, HazeMode, hazeMode, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, KeyLightMode, keyLightMode, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, AmbientLightMode, ambientLightMode, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, SkyboxMode, skyboxMode, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelVolumeSize, voxelVolumeSize, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelData, voxelData, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, VoxelSurfaceStyle, voxelSurfaceStyle, ""); diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index 49bce37fbd..eb09a64628 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -92,4 +92,6 @@ const uint16_t ENTITY_ITEM_DEFAULT_DPI = 30; const QUuid ENTITY_ITEM_DEFAULT_LAST_EDITED_BY = QUuid(); +const bool ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS = false; + #endif // hifi_EntityItemPropertiesDefaults_h diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 278d945ab0..f291973e52 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -367,7 +367,7 @@ inline xColor xColor_convertFromScriptValue(const QScriptValue& v, bool& isValid } \ } -#define COPY_PROPERTY_FROM_QSCRITPTVALUE_ENUM(P, S) \ +#define COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(P, S) \ QScriptValue P = object.property(#P); \ if (P.isValid()) { \ QString newValue = P.toVariant().toString(); \ diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index d515d60535..90438ab01c 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -40,6 +40,7 @@ enum EntityPropertyList { PROP_ANIMATION_FRAME_INDEX, PROP_ANIMATION_PLAYING, PROP_ANIMATION_ALLOW_TRANSLATION, + PROP_RELAY_PARENT_JOINTS, // these properties are supported by the EntityItem base class PROP_REGISTRATION_POINT, @@ -220,6 +221,10 @@ enum EntityPropertyList { PROP_HAZE_KEYLIGHT_RANGE, PROP_HAZE_KEYLIGHT_ALTITUDE, + PROP_KEY_LIGHT_MODE, + PROP_AMBIENT_LIGHT_MODE, + PROP_SKYBOX_MODE, + PROP_LOCAL_DIMENSIONS, // only used to convert values to and from scripts //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -243,21 +248,14 @@ enum EntityPropertyList { // the size of the properties bitflags mask PROP_KEYLIGHT_COLOR = PROP_COLOR, PROP_KEYLIGHT_INTENSITY = PROP_INTENSITY, - PROP_KEYLIGHT_AMBIENT_INTENSITY = PROP_CUTOFF, PROP_KEYLIGHT_DIRECTION = PROP_EXPONENT, - PROP_STAGE_SUN_MODEL_ENABLED = PROP_IS_SPOTLIGHT, - PROP_STAGE_LATITUDE = PROP_DIFFUSE_COLOR, - PROP_STAGE_LONGITUDE = PROP_AMBIENT_COLOR_UNUSED, - PROP_STAGE_ALTITUDE = PROP_SPECULAR_COLOR_UNUSED, - PROP_STAGE_DAY = PROP_LINEAR_ATTENUATION_UNUSED, - PROP_STAGE_HOUR = PROP_QUADRATIC_ATTENUATION_UNUSED, - PROP_STAGE_AUTOMATIC_HOURDAY = PROP_ANIMATION_FRAME_INDEX, - PROP_BACKGROUND_MODE = PROP_MODEL_URL, PROP_SKYBOX_COLOR = PROP_ANIMATION_URL, PROP_SKYBOX_URL = PROP_ANIMATION_FPS, - PROP_KEYLIGHT_AMBIENT_URL = PROP_ANIMATION_PLAYING, - + + PROP_AMBIENT_LIGHT_INTENSITY = PROP_CUTOFF, + PROP_AMBIENT_LIGHT_URL = PROP_ANIMATION_PLAYING, + // Aliases/Piggyback properties for Web. These properties intentionally reuse the enum values for // other properties which will never overlap with each other. PROP_SOURCE_URL = PROP_MODEL_URL, diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 794e56b262..15fc3256bd 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -39,11 +39,9 @@ #include "EntityEditFilters.h" #include "EntityDynamicFactoryInterface.h" - static const quint64 DELETED_ENTITIES_EXTRA_USECS_TO_CONSIDER = USECS_PER_MSEC * 50; const float EntityTree::DEFAULT_MAX_TMP_ENTITY_LIFETIME = 60 * 60; // 1 hour - // combines the ray cast arguments into a single object class RayArgs { public: @@ -2237,10 +2235,14 @@ bool EntityTree::writeToMap(QVariantMap& entityDescription, OctreeElementPointer } bool EntityTree::readFromMap(QVariantMap& map) { + // These are needed to deal with older content (before adding inheritance modes) + int contentVersion = map["Version"].toInt(); + bool needsConversion = (contentVersion < (int)EntityVersion::ZoneLightInheritModes); + // map will have a top-level list keyed as "Entities". This will be extracted // and iterated over. Each member of this list is converted to a QVariantMap, then // to a QScriptValue, and then to EntityItemProperties. These properties are used - // to add the new entity to the EnitytTree. + // to add the new entity to the EntityTree. QVariantList entitiesQList = map["Entities"].toList(); QScriptEngine scriptEngine; @@ -2281,12 +2283,44 @@ bool EntityTree::readFromMap(QVariantMap& map) { properties.setOwningAvatarID(myNodeID); } + // Fix for older content not containing mode fields in the zones + if (needsConversion && (properties.getType() == EntityTypes::EntityType::Zone)) { + // The legacy version had no keylight mode - this is set to on + properties.setKeyLightMode(COMPONENT_MODE_ENABLED); + + // The ambient URL has been moved from "keyLight" to "ambientLight" + if (entityMap.contains("keyLight")) { + QVariantMap keyLightObject = entityMap["keyLight"].toMap(); + properties.getAmbientLight().setAmbientURL(keyLightObject["ambientURL"].toString()); + } + + // Copy the skybox URL if the ambient URL is empty, as this is the legacy behaviour + // Use skybox value only if it is not empty, else set ambientMode to inherit (to use default URL) + properties.setAmbientLightMode(COMPONENT_MODE_ENABLED); + if (properties.getAmbientLight().getAmbientURL() == "") { + if (properties.getSkybox().getURL() != "") { + properties.getAmbientLight().setAmbientURL(properties.getSkybox().getURL()); + } else { + properties.setAmbientLightMode(COMPONENT_MODE_INHERIT); + } + } + + // The background should be enabled if the mode is skybox + // Note that if the values are default then they are not stored in the JSON file + if (entityMap.contains("backgroundMode") && (entityMap["backgroundMode"].toString() == "skybox")) { + properties.setSkyboxMode(COMPONENT_MODE_ENABLED); + } else { + properties.setSkyboxMode(COMPONENT_MODE_INHERIT); + } + } + EntityItemPointer entity = addEntity(entityItemID, properties); if (!entity) { qCDebug(entities) << "adding Entity failed:" << entityItemID << properties.getType(); success = false; } } + return success; } diff --git a/libraries/entities/src/KeyLightPropertyGroup.cpp b/libraries/entities/src/KeyLightPropertyGroup.cpp index 4246da309b..c476b4c23c 100644 --- a/libraries/entities/src/KeyLightPropertyGroup.cpp +++ b/libraries/entities/src/KeyLightPropertyGroup.cpp @@ -17,54 +17,41 @@ #include "EntityItemProperties.h" #include "EntityItemPropertiesMacros.h" - const xColor KeyLightPropertyGroup::DEFAULT_KEYLIGHT_COLOR = { 255, 255, 255 }; const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_INTENSITY = 1.0f; const float KeyLightPropertyGroup::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY = 0.5f; const glm::vec3 KeyLightPropertyGroup::DEFAULT_KEYLIGHT_DIRECTION = { 0.0f, -1.0f, 0.0f }; -void KeyLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { - +void KeyLightPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, + QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_COLOR, KeyLight, keyLight, Color, color); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_INTENSITY, KeyLight, keyLight, Intensity, intensity); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_AMBIENT_INTENSITY, KeyLight, keyLight, AmbientIntensity, ambientIntensity); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_DIRECTION, KeyLight, keyLight, Direction, direction); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_KEYLIGHT_AMBIENT_URL, KeyLight, keyLight, AmbientURL, ambientURL); - } void KeyLightPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, color, xColor, setColor); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, intensity, float, setIntensity); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, ambientIntensity, float, setAmbientIntensity); COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, direction, glmVec3, setDirection); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(keyLight, ambientURL, QString, setAmbientURL); // legacy property support COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightColor, xColor, setColor, getColor); COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightIntensity, float, setIntensity, getIntensity); - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightAmbientIntensity, float, setAmbientIntensity, getAmbientIntensity); COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(keyLightDirection, glmVec3, setDirection, getDirection); } void KeyLightPropertyGroup::merge(const KeyLightPropertyGroup& other) { COPY_PROPERTY_IF_CHANGED(color); COPY_PROPERTY_IF_CHANGED(intensity); - COPY_PROPERTY_IF_CHANGED(ambientIntensity); COPY_PROPERTY_IF_CHANGED(direction); - COPY_PROPERTY_IF_CHANGED(ambientURL); } - - void KeyLightPropertyGroup::debugDump() const { qCDebug(entities) << " KeyLightPropertyGroup: ---------------------------------------------"; qCDebug(entities) << " color:" << getColor(); // << "," << getColor()[1] << "," << getColor()[2]; qCDebug(entities) << " intensity:" << getIntensity(); qCDebug(entities) << " direction:" << getDirection(); - qCDebug(entities) << " ambientIntensity:" << getAmbientIntensity(); - qCDebug(entities) << " ambientURL:" << getAmbientURL(); } void KeyLightPropertyGroup::listChangedProperties(QList& out) { @@ -77,52 +64,38 @@ void KeyLightPropertyGroup::listChangedProperties(QList& out) { if (directionChanged()) { out << "keyLight-direction"; } - if (ambientIntensityChanged()) { - out << "keyLight-ambientIntensity"; - } - if (ambientURLChanged()) { - out << "keyLight-ambientURL"; - } } - bool KeyLightPropertyGroup::appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { bool successPropertyFits = true; APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, getColor()); APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, getIntensity()); - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, getAmbientIntensity()); APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, getDirection()); - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_URL, getAmbientURL()); return true; } - -bool KeyLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { - +bool KeyLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt, + int& processedBytes) { + int bytesRead = 0; bool overwriteLocalData = true; bool somethingChanged = false; READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, xColor, setColor); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, float, setIntensity); - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, float, setAmbientIntensity); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, glm::vec3, setDirection); - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_URL, QString, setAmbientURL); - - + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_COLOR, Color); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_INTENSITY, Intensity); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_AMBIENT_INTENSITY, AmbientIntensity); DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_DIRECTION, Direction); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_KEYLIGHT_AMBIENT_URL, AmbientURL); processedBytes += bytesRead; @@ -134,9 +107,7 @@ bool KeyLightPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFl void KeyLightPropertyGroup::markAllChanged() { _colorChanged = true; _intensityChanged = true; - _ambientIntensityChanged = true; _directionChanged = true; - _ambientURLChanged = true; } EntityPropertyFlags KeyLightPropertyGroup::getChangedProperties() const { @@ -144,20 +115,15 @@ EntityPropertyFlags KeyLightPropertyGroup::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_COLOR, color); CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_INTENSITY, intensity); - CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_AMBIENT_INTENSITY, ambientIntensity); CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_DIRECTION, direction); - CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_AMBIENT_URL, ambientURL); return changedProperties; } -void KeyLightPropertyGroup::getProperties(EntityItemProperties& properties) const { - +void KeyLightPropertyGroup::getProperties(EntityItemProperties& properties) const { COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, Color, getColor); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, Intensity, getIntensity); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, AmbientIntensity, getAmbientIntensity); COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, Direction, getDirection); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(KeyLight, AmbientURL, getAmbientURL); } bool KeyLightPropertyGroup::setProperties(const EntityItemProperties& properties) { @@ -165,9 +131,7 @@ bool KeyLightPropertyGroup::setProperties(const EntityItemProperties& properties SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, Color, color, setColor); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, Intensity, intensity, setIntensity); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, AmbientIntensity, ambientIntensity, setAmbientIntensity); SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, Direction, direction, setDirection); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(KeyLight, AmbientURL, ambientURL, setAmbientURL); return somethingChanged; } @@ -177,9 +141,7 @@ EntityPropertyFlags KeyLightPropertyGroup::getEntityProperties(EncodeBitstreamPa requestedProperties += PROP_KEYLIGHT_COLOR; requestedProperties += PROP_KEYLIGHT_INTENSITY; - requestedProperties += PROP_KEYLIGHT_AMBIENT_INTENSITY; requestedProperties += PROP_KEYLIGHT_DIRECTION; - requestedProperties += PROP_KEYLIGHT_AMBIENT_URL; return requestedProperties; } @@ -196,9 +158,7 @@ void KeyLightPropertyGroup::appendSubclassData(OctreePacketData* packetData, Enc APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, getColor()); APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, getIntensity()); - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, getAmbientIntensity()); APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, getDirection()); - APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_URL, getAmbientURL()); } int KeyLightPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, @@ -211,9 +171,7 @@ int KeyLightPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* READ_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, xColor, setColor); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, float, setIntensity); - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, float, setAmbientIntensity); READ_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, glm::vec3, setDirection); - READ_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_URL, QString, setAmbientURL); return bytesRead; } diff --git a/libraries/entities/src/KeyLightPropertyGroup.h b/libraries/entities/src/KeyLightPropertyGroup.h index 210d410bd9..f33ebb282d 100644 --- a/libraries/entities/src/KeyLightPropertyGroup.h +++ b/libraries/entities/src/KeyLightPropertyGroup.h @@ -81,9 +81,7 @@ public: DEFINE_PROPERTY_REF(PROP_KEYLIGHT_COLOR, Color, color, xColor, DEFAULT_KEYLIGHT_COLOR); DEFINE_PROPERTY(PROP_KEYLIGHT_INTENSITY, Intensity, intensity, float, DEFAULT_KEYLIGHT_INTENSITY); - DEFINE_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, AmbientIntensity, ambientIntensity, float, DEFAULT_KEYLIGHT_AMBIENT_INTENSITY); DEFINE_PROPERTY_REF(PROP_KEYLIGHT_DIRECTION, Direction, direction, glm::vec3, DEFAULT_KEYLIGHT_DIRECTION); - DEFINE_PROPERTY_REF(PROP_KEYLIGHT_AMBIENT_URL, AmbientURL, ambientURL, QString, ""); }; #endif // hifi_KeyLightPropertyGroup_h diff --git a/libraries/entities/src/ModelEntityItem.cpp b/libraries/entities/src/ModelEntityItem.cpp index 3e6d19f430..a615beefa9 100644 --- a/libraries/entities/src/ModelEntityItem.cpp +++ b/libraries/entities/src/ModelEntityItem.cpp @@ -62,7 +62,7 @@ EntityItemProperties ModelEntityItem::getProperties(EntityPropertyFlags desiredP COPY_ENTITY_PROPERTY_TO_PROPERTIES(jointRotations, getJointRotations); COPY_ENTITY_PROPERTY_TO_PROPERTIES(jointTranslationsSet, getJointTranslationsSet); COPY_ENTITY_PROPERTY_TO_PROPERTIES(jointTranslations, getJointTranslations); - + COPY_ENTITY_PROPERTY_TO_PROPERTIES(relayParentJoints, getRelayParentJoints); _animationProperties.getProperties(properties); return properties; } @@ -80,6 +80,7 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointRotations, setJointRotations); SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointTranslationsSet, setJointTranslationsSet); SET_ENTITY_PROPERTY_FROM_PROPERTIES(jointTranslations, setJointTranslations); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(relayParentJoints, setRelayParentJoints); bool somethingChangedInAnimations = _animationProperties.setProperties(properties); @@ -115,6 +116,7 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, READ_ENTITY_PROPERTY(PROP_MODEL_URL, QString, setModelURL); READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures); + READ_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, bool, setRelayParentJoints); int bytesFromAnimation; withWriteLock([&] { @@ -155,6 +157,7 @@ EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams& requestedProperties += PROP_JOINT_ROTATIONS; requestedProperties += PROP_JOINT_TRANSLATIONS_SET; requestedProperties += PROP_JOINT_TRANSLATIONS; + requestedProperties += PROP_RELAY_PARENT_JOINTS; return requestedProperties; } @@ -173,6 +176,7 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, getModelURL()); APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, getCompoundShapeURL()); APPEND_ENTITY_PROPERTY(PROP_TEXTURES, getTextures()); + APPEND_ENTITY_PROPERTY(PROP_RELAY_PARENT_JOINTS, getRelayParentJoints()); withReadLock([&] { _animationProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, @@ -267,9 +271,9 @@ void ModelEntityItem::updateFrameCount() { if (!getAnimationHold() && getAnimationIsPlaying()) { float deltaTime = (float)interval / (float)USECS_PER_SECOND; _currentFrame += (deltaTime * getAnimationFPS()); - if (_currentFrame > getAnimationLastFrame()) { - if (getAnimationLoop()) { - _currentFrame = getAnimationFirstFrame() + (int)(glm::floor(_currentFrame - getAnimationFirstFrame())) % (updatedFrameCount - 1); + if (_currentFrame > getAnimationLastFrame() + 1) { + if (getAnimationLoop() && getAnimationFirstFrame() != getAnimationLastFrame()) { + _currentFrame = getAnimationFirstFrame() + (int)(_currentFrame - getAnimationFirstFrame()) % updatedFrameCount; } else { _currentFrame = getAnimationLastFrame(); } @@ -448,7 +452,7 @@ void ModelEntityItem::resizeJointArrays(int newSize) { }); } -void ModelEntityItem::setAnimationJointsData(const QVector& jointsData) { +void ModelEntityItem::setAnimationJointsData(const QVector& jointsData) { resizeJointArrays(jointsData.size()); _jointDataLock.withWriteLock([&] { for (auto index = 0; index < jointsData.size(); ++index) { @@ -586,6 +590,18 @@ QString ModelEntityItem::getModelURL() const { }); } +void ModelEntityItem::setRelayParentJoints(bool relayJoints) { + withWriteLock([&] { + _relayParentJoints = relayJoints; + }); +} + +bool ModelEntityItem::getRelayParentJoints() const { + return resultWithReadLock([&] { + return _relayParentJoints; + }); +} + QString ModelEntityItem::getCompoundShapeURL() const { return _compoundShapeURL.get(); } diff --git a/libraries/entities/src/ModelEntityItem.h b/libraries/entities/src/ModelEntityItem.h index 7fee022011..c2109ba51f 100644 --- a/libraries/entities/src/ModelEntityItem.h +++ b/libraries/entities/src/ModelEntityItem.h @@ -99,6 +99,9 @@ public: void setAnimationHold(bool hold); bool getAnimationHold() const; + void setRelayParentJoints(bool relayJoints); + bool getRelayParentJoints() const; + void setAnimationFirstFrame(float firstFrame); float getAnimationFirstFrame() const; @@ -121,7 +124,7 @@ public: virtual void setJointTranslations(const QVector& translations); virtual void setJointTranslationsSet(const QVector& translationsSet); - virtual void setAnimationJointsData(const QVector& jointsData); + virtual void setAnimationJointsData(const QVector& jointsData); QVector getJointRotations() const; QVector getJointRotationsSet() const; @@ -147,7 +150,7 @@ protected: bool _jointTranslationsExplicitlySet{ false }; // were the joints set as a property or just side effect of animations struct ModelJointData { - JointData joint; + EntityJointData joint; bool rotationDirty { false }; bool translationDirty { false }; }; @@ -157,6 +160,7 @@ protected: rgbColor _color; QString _modelURL; + bool _relayParentJoints; ThreadSafeValueCache _compoundShapeURL; diff --git a/libraries/entities/src/StagePropertyGroup.cpp b/libraries/entities/src/StagePropertyGroup.cpp deleted file mode 100644 index cd486c96ac..0000000000 --- a/libraries/entities/src/StagePropertyGroup.cpp +++ /dev/null @@ -1,283 +0,0 @@ -// -// StagePropertyGroup.cpp -// libraries/entities/src -// -// Created by Brad Hefta-Gaub on 12/4/13. -// Copyright 2013 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include -#include -#include - -#include - -#include "StagePropertyGroup.h" -#include "EntityItemProperties.h" -#include "EntityItemPropertiesMacros.h" - -const bool StagePropertyGroup::DEFAULT_STAGE_SUN_MODEL_ENABLED = false; -const float StagePropertyGroup::DEFAULT_STAGE_LATITUDE = 37.777f; -const float StagePropertyGroup::DEFAULT_STAGE_LONGITUDE = 122.407f; -const float StagePropertyGroup::DEFAULT_STAGE_ALTITUDE = 0.03f; -const quint16 StagePropertyGroup::DEFAULT_STAGE_DAY = 60; -const float StagePropertyGroup::DEFAULT_STAGE_HOUR = 12.0f; - -void StagePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_STAGE_SUN_MODEL_ENABLED, Stage, stage, SunModelEnabled, sunModelEnabled); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_STAGE_LATITUDE, Stage, stage, Latitude, latitude); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_STAGE_LONGITUDE, Stage, stage, Longitude, longitude); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_STAGE_ALTITUDE, Stage, stage, Altitude, altitude); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_STAGE_DAY, Stage, stage, Day, day); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_STAGE_HOUR, Stage, stage, Hour, hour); - COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_STAGE_AUTOMATIC_HOURDAY, Stage, stage, AutomaticHourDay, automaticHourDay); -} - -void StagePropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { - - // Backward compatibility support for the old way of doing stage properties - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(stageSunModelEnabled, bool, setSunModelEnabled, getSunModelEnabled); - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(stageLatitude, float, setLatitude, getLatitude); - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(stageLongitude, float, setLongitude, getLongitude); - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(stageAltitude, float, setAltitude, getAltitude); - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(stageDay, uint16_t, setDay, getDay); - COPY_PROPERTY_FROM_QSCRIPTVALUE_GETTER(stageHour, float, setHour, getHour); - - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(stage, sunModelEnabled, bool, setSunModelEnabled); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(stage, latitude, float, setLatitude); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(stage, longitude, float, setLongitude); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(stage, altitude, float, setAltitude); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(stage, day, uint16_t, setDay); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(stage, hour, float, setHour); - COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(stage, automaticHourDay, bool, setAutomaticHourDay); -} - -void StagePropertyGroup::merge(const StagePropertyGroup& other) { - COPY_PROPERTY_IF_CHANGED(sunModelEnabled); - COPY_PROPERTY_IF_CHANGED(latitude); - COPY_PROPERTY_IF_CHANGED(longitude); - COPY_PROPERTY_IF_CHANGED(altitude); - COPY_PROPERTY_IF_CHANGED(day); - COPY_PROPERTY_IF_CHANGED(hour); - COPY_PROPERTY_IF_CHANGED(automaticHourDay); -} - - -void StagePropertyGroup::debugDump() const { - qCDebug(entities) << " StagePropertyGroup: ---------------------------------------------"; - qCDebug(entities) << " _sunModelEnabled:" << _sunModelEnabled; - qCDebug(entities) << " _latitude:" << _latitude; - qCDebug(entities) << " _longitude:" << _longitude; - qCDebug(entities) << " _altitude:" << _altitude; - qCDebug(entities) << " _day:" << _day; - qCDebug(entities) << " _hour:" << _hour; - qCDebug(entities) << " _automaticHourDay:" << _automaticHourDay; -} - -void StagePropertyGroup::listChangedProperties(QList& out) { - if (sunModelEnabledChanged()) { - out << "stage-sunModelEnabled"; - } - if (latitudeChanged()) { - out << "stage-latitude"; - } - if (altitudeChanged()) { - out << "stage-altitude"; - } - if (dayChanged()) { - out << "stage-day"; - } - if (hourChanged()) { - out << "stage-hour"; - } - if (automaticHourDayChanged()) { - out << "stage-automaticHourDay"; - } -} - -bool StagePropertyGroup::appendToEditPacket(OctreePacketData* packetData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, getSunModelEnabled()); - APPEND_ENTITY_PROPERTY(PROP_STAGE_LATITUDE, getLatitude()); - APPEND_ENTITY_PROPERTY(PROP_STAGE_LONGITUDE, getLongitude()); - APPEND_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, getAltitude()); - APPEND_ENTITY_PROPERTY(PROP_STAGE_DAY, getDay()); - APPEND_ENTITY_PROPERTY(PROP_STAGE_HOUR, getHour()); - APPEND_ENTITY_PROPERTY(PROP_STAGE_AUTOMATIC_HOURDAY, getAutomaticHourDay()); - - return true; -} - - -bool StagePropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { - - int bytesRead = 0; - bool overwriteLocalData = true; - bool somethingChanged = false; - - READ_ENTITY_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, bool, setSunModelEnabled); - READ_ENTITY_PROPERTY(PROP_STAGE_LATITUDE, float, setLatitude); - READ_ENTITY_PROPERTY(PROP_STAGE_LONGITUDE, float, setLongitude); - READ_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, float, setAltitude); - READ_ENTITY_PROPERTY(PROP_STAGE_DAY, quint16, setDay); - READ_ENTITY_PROPERTY(PROP_STAGE_HOUR, float, setHour); - READ_ENTITY_PROPERTY(PROP_STAGE_AUTOMATIC_HOURDAY, bool, setAutomaticHourDay); - - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_STAGE_SUN_MODEL_ENABLED, SunModelEnabled); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_STAGE_LATITUDE, Latitude); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_STAGE_LONGITUDE, Longitude); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_STAGE_ALTITUDE, Altitude); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_STAGE_DAY, Day); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_STAGE_HOUR, Hour); - DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_STAGE_AUTOMATIC_HOURDAY, AutomaticHourDay); - - processedBytes += bytesRead; - - Q_UNUSED(somethingChanged); - - return true; -} - -void StagePropertyGroup::markAllChanged() { - _sunModelEnabledChanged = true; - _latitudeChanged = true; - _longitudeChanged = true; - _altitudeChanged = true; - _dayChanged = true; - _hourChanged = true; - _automaticHourDayChanged = true; -} - -EntityPropertyFlags StagePropertyGroup::getChangedProperties() const { - EntityPropertyFlags changedProperties; - - CHECK_PROPERTY_CHANGE(PROP_STAGE_SUN_MODEL_ENABLED, sunModelEnabled); - CHECK_PROPERTY_CHANGE(PROP_STAGE_LATITUDE, latitude); - CHECK_PROPERTY_CHANGE(PROP_STAGE_LONGITUDE, longitude); - CHECK_PROPERTY_CHANGE(PROP_STAGE_ALTITUDE, altitude); - CHECK_PROPERTY_CHANGE(PROP_STAGE_DAY, day); - CHECK_PROPERTY_CHANGE(PROP_STAGE_HOUR, hour); - CHECK_PROPERTY_CHANGE(PROP_STAGE_AUTOMATIC_HOURDAY, automaticHourDay); - - return changedProperties; -} - -void StagePropertyGroup::getProperties(EntityItemProperties& properties) const { - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Stage, SunModelEnabled, getSunModelEnabled); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Stage, Latitude, getLatitude); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Stage, Longitude, getLongitude); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Stage, Altitude, getAltitude); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Stage, Day, getDay); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Stage, Hour, getHour); - COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Stage, AutomaticHourDay, getAutomaticHourDay); -} - -bool StagePropertyGroup::setProperties(const EntityItemProperties& properties) { - bool somethingChanged = false; - - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Stage, SunModelEnabled, sunModelEnabled, setSunModelEnabled); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Stage, Latitude, latitude, setLatitude); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Stage, Longitude, longitude, setLongitude); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Stage, Altitude, altitude, setAltitude); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Stage, Day, day, setDay); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Stage, Hour, hour, setHour); - SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Stage, AutomaticHourDay, automaticHourDay, setAutomaticHourDay); - - return somethingChanged; -} - -EntityPropertyFlags StagePropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { - EntityPropertyFlags requestedProperties; - - requestedProperties += PROP_STAGE_SUN_MODEL_ENABLED; - requestedProperties += PROP_STAGE_LATITUDE; - requestedProperties += PROP_STAGE_LONGITUDE; - requestedProperties += PROP_STAGE_ALTITUDE; - requestedProperties += PROP_STAGE_DAY; - requestedProperties += PROP_STAGE_HOUR; - requestedProperties += PROP_STAGE_AUTOMATIC_HOURDAY; - - return requestedProperties; -} - -void StagePropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, - EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, - EntityPropertyFlags& requestedProperties, - EntityPropertyFlags& propertyFlags, - EntityPropertyFlags& propertiesDidntFit, - int& propertyCount, - OctreeElement::AppendState& appendState) const { - - bool successPropertyFits = true; - - APPEND_ENTITY_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, getSunModelEnabled()); - APPEND_ENTITY_PROPERTY(PROP_STAGE_LATITUDE, getLatitude()); - APPEND_ENTITY_PROPERTY(PROP_STAGE_LONGITUDE, getLongitude()); - APPEND_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, getAltitude()); - APPEND_ENTITY_PROPERTY(PROP_STAGE_DAY, getDay()); - APPEND_ENTITY_PROPERTY(PROP_STAGE_HOUR, getHour()); - APPEND_ENTITY_PROPERTY(PROP_STAGE_AUTOMATIC_HOURDAY, getAutomaticHourDay()); -} - -int StagePropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, - ReadBitstreamToTreeParams& args, - EntityPropertyFlags& propertyFlags, bool overwriteLocalData, - bool& somethingChanged) { - - int bytesRead = 0; - const unsigned char* dataAt = data; - - READ_ENTITY_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, bool, setSunModelEnabled); - READ_ENTITY_PROPERTY(PROP_STAGE_LATITUDE, float, setLatitude); - READ_ENTITY_PROPERTY(PROP_STAGE_LONGITUDE, float, setLongitude); - READ_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, float, setAltitude); - READ_ENTITY_PROPERTY(PROP_STAGE_DAY, quint16, setDay); - READ_ENTITY_PROPERTY(PROP_STAGE_HOUR, float, setHour); - READ_ENTITY_PROPERTY(PROP_STAGE_AUTOMATIC_HOURDAY, bool, setAutomaticHourDay); - - return bytesRead; -} - -static const float TOTAL_LONGITUDES = 360.0f; -static const float HOURS_PER_DAY = 24; -static const float SECONDS_PER_DAY = 60 * 60 * HOURS_PER_DAY; -static const float MSECS_PER_DAY = SECONDS_PER_DAY * MSECS_PER_SECOND; - -float StagePropertyGroup::calculateHour() const { - if (!_automaticHourDay) { - return _hour; - } - - QDateTime utc(QDateTime::currentDateTimeUtc()); - float adjustFromUTC = (_longitude / TOTAL_LONGITUDES); - float offsetFromUTCinMsecs = adjustFromUTC * MSECS_PER_DAY; - int msecsSinceStartOfDay = utc.time().msecsSinceStartOfDay(); - float calutatedHour = ((msecsSinceStartOfDay + offsetFromUTCinMsecs) / MSECS_PER_DAY) * HOURS_PER_DAY; - - // calculate hour based on longitude and time from GMT - return calutatedHour; -} - -uint16_t StagePropertyGroup::calculateDay() const { - - if (!_automaticHourDay) { - return _day; - } - - QDateTime utc(QDateTime::currentDateTimeUtc()); - int calutatedDay = utc.date().dayOfYear(); - - // calculate day based on longitude and time from GMT - return calutatedDay; -} - diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index fbfbc71a85..107e7cf561 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -42,8 +42,6 @@ ZoneEntityItem::ZoneEntityItem(const EntityItemID& entityItemID) : EntityItem(en _shapeType = DEFAULT_SHAPE_TYPE; _compoundShapeURL = DEFAULT_COMPOUND_SHAPE_URL; - - _backgroundMode = BACKGROUND_MODE_INHERIT; } EntityItemProperties ZoneEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { @@ -53,12 +51,13 @@ EntityItemProperties ZoneEntityItem::getProperties(EntityPropertyFlags desiredPr withReadLock([&] { _keyLightProperties.getProperties(properties); }); - - _stageProperties.getProperties(properties); + + withReadLock([&] { + _ambientLightProperties.getProperties(properties); + }); COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType); COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(backgroundMode, getBackgroundMode); // Contains a QString property, must be synchronized withReadLock([&] { @@ -72,6 +71,10 @@ EntityItemProperties ZoneEntityItem::getProperties(EntityPropertyFlags desiredPr COPY_ENTITY_PROPERTY_TO_PROPERTIES(hazeMode, getHazeMode); _hazeProperties.getProperties(properties); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightMode, getKeyLightMode); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(ambientLightMode, getAmbientLightMode); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(skyboxMode, getSkyboxMode); + return properties; } @@ -100,12 +103,12 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie withWriteLock([&] { _keyLightPropertiesChanged = _keyLightProperties.setProperties(properties); }); - - _stagePropertiesChanged = _stageProperties.setProperties(properties); + withWriteLock([&] { + _ambientLightPropertiesChanged = _ambientLightProperties.setProperties(properties); + }); SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType); SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(backgroundMode, setBackgroundMode); // Contains a QString property, must be synchronized withWriteLock([&] { @@ -115,11 +118,16 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie SET_ENTITY_PROPERTY_FROM_PROPERTIES(flyingAllowed, setFlyingAllowed); SET_ENTITY_PROPERTY_FROM_PROPERTIES(ghostingAllowed, setGhostingAllowed); SET_ENTITY_PROPERTY_FROM_PROPERTIES(filterURL, setFilterURL); - + SET_ENTITY_PROPERTY_FROM_PROPERTIES(hazeMode, setHazeMode); _hazePropertiesChanged = _hazeProperties.setProperties(properties); - somethingChanged = somethingChanged || _keyLightPropertiesChanged || _stagePropertiesChanged || _skyboxPropertiesChanged || _hazePropertiesChanged; + SET_ENTITY_PROPERTY_FROM_PROPERTIES(keyLightMode, setKeyLightMode); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(ambientLightMode, setAmbientLightMode); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(skyboxMode, setSkyboxMode); + + somethingChanged = somethingChanged || _keyLightPropertiesChanged || _ambientLightPropertiesChanged || + _stagePropertiesChanged || _skyboxPropertiesChanged || _hazePropertiesChanged; return somethingChanged; } @@ -141,15 +149,18 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, bytesRead += bytesFromKeylight; dataAt += bytesFromKeylight; - int bytesFromStage = _stageProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _stagePropertiesChanged); - somethingChanged = somethingChanged || _stagePropertiesChanged; - bytesRead += bytesFromStage; - dataAt += bytesFromStage; + int bytesFromAmbientlight; + withWriteLock([&] { + bytesFromAmbientlight = _ambientLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, _ambientLightPropertiesChanged); + }); + + somethingChanged = somethingChanged || _ambientLightPropertiesChanged; + bytesRead += bytesFromAmbientlight; + dataAt += bytesFromAmbientlight; READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType); READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); - READ_ENTITY_PROPERTY(PROP_BACKGROUND_MODE, BackgroundMode, setBackgroundMode); int bytesFromSkybox; withWriteLock([&] { @@ -173,6 +184,10 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, bytesRead += bytesFromHaze; dataAt += bytesFromHaze; + READ_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, uint32_t, setKeyLightMode); + READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, uint32_t, setAmbientLightMode); + READ_ENTITY_PROPERTY(PROP_SKYBOX_MODE, uint32_t, setSkyboxMode); + return bytesRead; } @@ -185,11 +200,12 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p requestedProperties += _keyLightProperties.getEntityProperties(params); }); - requestedProperties += _stageProperties.getEntityProperties(params); + withReadLock([&] { + requestedProperties += _ambientLightProperties.getEntityProperties(params); + }); requestedProperties += PROP_SHAPE_TYPE; requestedProperties += PROP_COMPOUND_SHAPE_URL; - requestedProperties += PROP_BACKGROUND_MODE; withReadLock([&] { requestedProperties += _skyboxProperties.getEntityProperties(params); @@ -202,6 +218,10 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p requestedProperties += PROP_HAZE_MODE; requestedProperties += _hazeProperties.getEntityProperties(params); + requestedProperties += PROP_KEY_LIGHT_MODE; + requestedProperties += PROP_AMBIENT_LIGHT_MODE; + requestedProperties += PROP_SKYBOX_MODE; + return requestedProperties; } @@ -216,15 +236,13 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits bool successPropertyFits = true; _keyLightProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - - _stageProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); + propertyFlags, propertiesDidntFit, propertyCount, appendState); + _ambientLightProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)getShapeType()); APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, getCompoundShapeURL()); - APPEND_ENTITY_PROPERTY(PROP_BACKGROUND_MODE, (uint32_t)getBackgroundMode()); // could this be a uint16?? _skyboxProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); @@ -236,6 +254,10 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)getHazeMode()); _hazeProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + + APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, (uint32_t)getKeyLightMode()); + APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, (uint32_t)getAmbientLightMode()); + APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)getSkyboxMode()); } void ZoneEntityItem::debugDump() const { @@ -244,13 +266,15 @@ void ZoneEntityItem::debugDump() const { qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); - qCDebug(entities) << " _backgroundMode:" << EntityItemProperties::getBackgroundModeString(_backgroundMode); - qCDebug(entities) << " _hazeMode:" << EntityItemProperties::getHazeModeString(_hazeMode); + qCDebug(entities) << " _hazeMode:" << EntityItemProperties::getComponentModeString(_hazeMode); + qCDebug(entities) << " _keyLightMode:" << EntityItemProperties::getComponentModeString(_keyLightMode); + qCDebug(entities) << " _ambientLightMode:" << EntityItemProperties::getComponentModeString(_ambientLightMode); + qCDebug(entities) << " _skyboxMode:" << EntityItemProperties::getComponentModeString(_skyboxMode); _keyLightProperties.debugDump(); + _ambientLightProperties.debugDump(); _skyboxProperties.debugDump(); _hazeProperties.debugDump(); - _stageProperties.debugDump(); } ShapeType ZoneEntityItem::getShapeType() const { @@ -313,7 +337,7 @@ QString ZoneEntityItem::getCompoundShapeURL() const { void ZoneEntityItem::resetRenderingPropertiesChanged() { withWriteLock([&] { _keyLightPropertiesChanged = false; - _backgroundPropertiesChanged = false; + _ambientLightPropertiesChanged = false; _skyboxPropertiesChanged = false; _hazePropertiesChanged = false; _stagePropertiesChanged = false; @@ -321,7 +345,7 @@ void ZoneEntityItem::resetRenderingPropertiesChanged() { } void ZoneEntityItem::setHazeMode(const uint32_t value) { - if (value < COMPONENT_MODE_ITEM_COUNT) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _hazeMode) { _hazeMode = value; _hazePropertiesChanged = true; } @@ -330,3 +354,36 @@ void ZoneEntityItem::setHazeMode(const uint32_t value) { uint32_t ZoneEntityItem::getHazeMode() const { return _hazeMode; } + +void ZoneEntityItem::setKeyLightMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _keyLightMode) { + _keyLightMode = value; + _keyLightPropertiesChanged = true; + } +} + +uint32_t ZoneEntityItem::getKeyLightMode() const { + return _keyLightMode; +} + +void ZoneEntityItem::setAmbientLightMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _ambientLightMode) { + _ambientLightMode = value; + _ambientLightPropertiesChanged = true; + } +} + +uint32_t ZoneEntityItem::getAmbientLightMode() const { + return _ambientLightMode; +} + +void ZoneEntityItem::setSkyboxMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _skyboxMode) { + _skyboxMode = value; + _skyboxPropertiesChanged = true; + } +} + +uint32_t ZoneEntityItem::getSkyboxMode() const { + return _skyboxMode; +} diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index 46e8a00c24..05dcc05416 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -13,11 +13,11 @@ #define hifi_ZoneEntityItem_h #include "KeyLightPropertyGroup.h" +#include "AmbientLightPropertyGroup.h" #include "EntityItem.h" #include "EntityTree.h" #include "SkyboxPropertyGroup.h" #include "HazePropertyGroup.h" -#include "StagePropertyGroup.h" #include class ZoneEntityItem : public EntityItem { @@ -66,19 +66,24 @@ public: virtual void setCompoundShapeURL(const QString& url); KeyLightPropertyGroup getKeyLightProperties() const { return resultWithReadLock([&] { return _keyLightProperties; }); } - - void setBackgroundMode(BackgroundMode value) { _backgroundMode = value; _backgroundPropertiesChanged = true; } - BackgroundMode getBackgroundMode() const { return _backgroundMode; } + AmbientLightPropertyGroup getAmbientLightProperties() const { return resultWithReadLock([&] { return _ambientLightProperties; }); } void setHazeMode(const uint32_t value); uint32_t getHazeMode() const; + void setKeyLightMode(uint32_t value); + uint32_t getKeyLightMode() const; + + void setAmbientLightMode(uint32_t value); + uint32_t getAmbientLightMode() const; + + void setSkyboxMode(uint32_t value); + uint32_t getSkyboxMode() const; + SkyboxPropertyGroup getSkyboxProperties() const { return resultWithReadLock([&] { return _skyboxProperties; }); } const HazePropertyGroup& getHazeProperties() const { return _hazeProperties; } - const StagePropertyGroup& getStageProperties() const { return _stageProperties; } - bool getFlyingAllowed() const { return _flyingAllowed; } void setFlyingAllowed(bool value) { _flyingAllowed = value; } bool getGhostingAllowed() const { return _ghostingAllowed; } @@ -87,7 +92,7 @@ public: void setFilterURL(const QString url); bool keyLightPropertiesChanged() const { return _keyLightPropertiesChanged; } - bool backgroundPropertiesChanged() const { return _backgroundPropertiesChanged; } + bool ambientLightPropertiesChanged() const { return _ambientLightPropertiesChanged; } bool skyboxPropertiesChanged() const { return _skyboxPropertiesChanged; } bool hazePropertiesChanged() const { @@ -112,21 +117,22 @@ public: static const bool DEFAULT_GHOSTING_ALLOWED; static const QString DEFAULT_FILTER_URL; - static const uint32_t DEFAULT_HAZE_MODE{ (uint32_t)COMPONENT_MODE_INHERIT }; - protected: KeyLightPropertyGroup _keyLightProperties; + AmbientLightPropertyGroup _ambientLightProperties; ShapeType _shapeType = DEFAULT_SHAPE_TYPE; QString _compoundShapeURL; - BackgroundMode _backgroundMode = BACKGROUND_MODE_INHERIT; + // The following 3 values are the defaults for zone creation + uint32_t _keyLightMode { COMPONENT_MODE_INHERIT }; + uint32_t _skyboxMode { COMPONENT_MODE_INHERIT }; + uint32_t _ambientLightMode { COMPONENT_MODE_INHERIT }; - uint32_t _hazeMode{ DEFAULT_HAZE_MODE }; + uint32_t _hazeMode { COMPONENT_MODE_INHERIT }; SkyboxPropertyGroup _skyboxProperties; HazePropertyGroup _hazeProperties; - StagePropertyGroup _stageProperties; bool _flyingAllowed { DEFAULT_FLYING_ALLOWED }; bool _ghostingAllowed { DEFAULT_GHOSTING_ALLOWED }; @@ -134,7 +140,7 @@ protected: // Dirty flags turn true when either keylight properties is changing values. bool _keyLightPropertiesChanged { false }; - bool _backgroundPropertiesChanged{ false }; + bool _ambientLightPropertiesChanged { false }; bool _skyboxPropertiesChanged { false }; bool _hazePropertiesChanged{ false }; bool _stagePropertiesChanged { false }; diff --git a/libraries/fbx/CMakeLists.txt b/libraries/fbx/CMakeLists.txt index 683ddb52f7..e422af3629 100644 --- a/libraries/fbx/CMakeLists.txt +++ b/libraries/fbx/CMakeLists.txt @@ -1,7 +1,7 @@ set(TARGET_NAME fbx) setup_hifi_library() -link_hifi_libraries(shared model networking image) +link_hifi_libraries(shared graphics networking image) include_hifi_library_headers(gpu image) target_draco() diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 7d3328a2dd..e40b218344 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -25,8 +25,8 @@ #include #include -#include -#include +#include +#include static const QByteArray FBX_BINARY_PROLOG = "Kaydara FBX Binary "; static const int FBX_HEADER_BYTES_BEFORE_VERSION = 23; @@ -60,6 +60,7 @@ public: QVector indices; QVector vertices; QVector normals; + QVector tangents; }; struct FBXJointShapeInfo { @@ -115,6 +116,7 @@ public: int jointIndex; glm::mat4 inverseBindMatrix; + Transform inverseBindTransform; }; const int MAX_NUM_PIXELS_FOR_FBX_TEXTURE = 2048 * 2048; @@ -181,7 +183,7 @@ public: QString materialID; QString name; QString shadingModel; - model::MaterialPointer _material; + graphics::MaterialPointer _material; FBXTexture normalTexture; FBXTexture albedoTexture; @@ -224,7 +226,7 @@ public: QVector texCoords; QVector texCoords1; QVector clusterIndices; - QVector clusterWeights; + QVector clusterWeights; QVector originalIndices; QVector clusters; @@ -236,7 +238,7 @@ public: unsigned int meshIndex; // the order the meshes appeared in the object file - model::MeshPointer _mesh; + graphics::MeshPointer _mesh; bool wasCompressed { false }; }; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index e4fea00a34..171fc88443 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -72,7 +72,7 @@ Extents FBXGeometry::getUnscaledMeshExtents() const { return scaledExtents; } -// TODO: Move to model::Mesh when Sam's ready +// TODO: Move to graphics::Mesh when Sam's ready bool FBXGeometry::convexHullContains(const glm::vec3& point) const { if (!getUnscaledMeshExtents().containsPoint(point)) { return false; @@ -148,6 +148,59 @@ glm::vec3 parseVec3(const QString& string) { return value; } +enum RotationOrder { + OrderXYZ = 0, + OrderXZY, + OrderYZX, + OrderYXZ, + OrderZXY, + OrderZYX, + OrderSphericXYZ +}; + +bool haveReportedUnhandledRotationOrder = false; // Report error only once per FBX file. + +glm::vec3 convertRotationToXYZ(int rotationOrder, const glm::vec3& rotation) { + // Convert rotation with given rotation order to have order XYZ. + if (rotationOrder == OrderXYZ) { + return rotation; + } + + glm::quat xyzRotation; + + switch (rotationOrder) { + case OrderXZY: + xyzRotation = glm::quat(glm::radians(glm::vec3(0, rotation.y, 0))) + * (glm::quat(glm::radians(glm::vec3(0, 0, rotation.z))) * glm::quat(glm::radians(glm::vec3(rotation.x, 0, 0)))); + break; + case OrderYZX: + xyzRotation = glm::quat(glm::radians(glm::vec3(rotation.x, 0, 0))) + * (glm::quat(glm::radians(glm::vec3(0, 0, rotation.z))) * glm::quat(glm::radians(glm::vec3(0, rotation.y, 0)))); + break; + case OrderYXZ: + xyzRotation = glm::quat(glm::radians(glm::vec3(0, 0, rotation.z))) + * (glm::quat(glm::radians(glm::vec3(rotation.x, 0, 0))) * glm::quat(glm::radians(glm::vec3(0, rotation.y, 0)))); + break; + case OrderZXY: + xyzRotation = glm::quat(glm::radians(glm::vec3(0, rotation.y, 0))) + * (glm::quat(glm::radians(glm::vec3(rotation.x, 0, 0))) * glm::quat(glm::radians(glm::vec3(0, 0, rotation.z)))); + break; + case OrderZYX: + xyzRotation = glm::quat(glm::radians(glm::vec3(rotation.x, 0, 0))) + * (glm::quat(glm::radians(glm::vec3(0, rotation.y, 0))) * glm::quat(glm::radians(glm::vec3(0, 0, rotation.z)))); + break; + default: + // FIXME: Handle OrderSphericXYZ. + if (!haveReportedUnhandledRotationOrder) { + qCDebug(modelformat) << "ERROR: Unhandled rotation order in FBX file:" << rotationOrder; + haveReportedUnhandledRotationOrder = true; + } + return rotation; + } + + return glm::degrees(safeEulerAngles(xyzRotation)); +} + QString processID(const QString& id) { // Blender (at least) prepends a type to the ID, so strip it out return id.mid(id.lastIndexOf(':') + 1); @@ -303,17 +356,97 @@ FBXBlendshape extractBlendshape(const FBXNode& object) { return blendshape; } +using IndexAccessor = std::function; -void setTangents(FBXMesh& mesh, int firstIndex, int secondIndex) { - const glm::vec3& normal = mesh.normals.at(firstIndex); - glm::vec3 bitangent = glm::cross(normal, mesh.vertices.at(secondIndex) - mesh.vertices.at(firstIndex)); - if (glm::length(bitangent) < EPSILON) { - return; +static void setTangents(const FBXMesh& mesh, const IndexAccessor& vertexAccessor, int firstIndex, int secondIndex, + const QVector& vertices, const QVector& normals, QVector& tangents) { + glm::vec3 vertex[2]; + glm::vec3 normal; + glm::vec3* tangent = vertexAccessor(mesh, firstIndex, secondIndex, vertex, normal); + if (tangent) { + glm::vec3 bitangent = glm::cross(normal, vertex[1] - vertex[0]); + if (glm::length(bitangent) < EPSILON) { + return; + } + glm::vec2 texCoordDelta = mesh.texCoords.at(secondIndex) - mesh.texCoords.at(firstIndex); + glm::vec3 normalizedNormal = glm::normalize(normal); + *tangent += glm::cross(glm::angleAxis(-atan2f(-texCoordDelta.t, texCoordDelta.s), normalizedNormal) * + glm::normalize(bitangent), normalizedNormal); } - glm::vec2 texCoordDelta = mesh.texCoords.at(secondIndex) - mesh.texCoords.at(firstIndex); - glm::vec3 normalizedNormal = glm::normalize(normal); - mesh.tangents[firstIndex] += glm::cross(glm::angleAxis(-atan2f(-texCoordDelta.t, texCoordDelta.s), normalizedNormal) * - glm::normalize(bitangent), normalizedNormal); +} + +static void createTangents(const FBXMesh& mesh, bool generateFromTexCoords, + const QVector& vertices, const QVector& normals, QVector& tangents, + IndexAccessor accessor) { + // if we have a normal map (and texture coordinates), we must compute tangents + if (generateFromTexCoords && !mesh.texCoords.isEmpty()) { + tangents.resize(vertices.size()); + + foreach(const FBXMeshPart& part, mesh.parts) { + for (int i = 0; i < part.quadIndices.size(); i += 4) { + setTangents(mesh, accessor, part.quadIndices.at(i), part.quadIndices.at(i + 1), vertices, normals, tangents); + setTangents(mesh, accessor, part.quadIndices.at(i + 1), part.quadIndices.at(i + 2), vertices, normals, tangents); + setTangents(mesh, accessor, part.quadIndices.at(i + 2), part.quadIndices.at(i + 3), vertices, normals, tangents); + setTangents(mesh, accessor, part.quadIndices.at(i + 3), part.quadIndices.at(i), vertices, normals, tangents); + } + // <= size - 3 in order to prevent overflowing triangleIndices when (i % 3) != 0 + // This is most likely evidence of a further problem in extractMesh() + for (int i = 0; i <= part.triangleIndices.size() - 3; i += 3) { + setTangents(mesh, accessor, part.triangleIndices.at(i), part.triangleIndices.at(i + 1), vertices, normals, tangents); + setTangents(mesh, accessor, part.triangleIndices.at(i + 1), part.triangleIndices.at(i + 2), vertices, normals, tangents); + setTangents(mesh, accessor, part.triangleIndices.at(i + 2), part.triangleIndices.at(i), vertices, normals, tangents); + } + if ((part.triangleIndices.size() % 3) != 0) { + qCDebug(modelformat) << "Error in extractFBXGeometry part.triangleIndices.size() is not divisible by three "; + } + } + } +} + +static void createMeshTangents(FBXMesh& mesh, bool generateFromTexCoords) { + // This is the only workaround I've found to trick the compiler into understanding that mesh.tangents isn't + // const in the lambda function. + auto& tangents = mesh.tangents; + createTangents(mesh, generateFromTexCoords, mesh.vertices, mesh.normals, mesh.tangents, + [&](const FBXMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) { + outVertices[0] = mesh.vertices[firstIndex]; + outVertices[1] = mesh.vertices[secondIndex]; + outNormal = mesh.normals[firstIndex]; + return &(tangents[firstIndex]); + }); +} + +static void createBlendShapeTangents(FBXMesh& mesh, bool generateFromTexCoords, FBXBlendshape& blendShape) { + // Create lookup to get index in blend shape from vertex index in mesh + std::vector reverseIndices; + reverseIndices.resize(mesh.vertices.size()); + std::iota(reverseIndices.begin(), reverseIndices.end(), 0); + + for (int indexInBlendShape = 0; indexInBlendShape < blendShape.indices.size(); ++indexInBlendShape) { + auto indexInMesh = blendShape.indices[indexInBlendShape]; + reverseIndices[indexInMesh] = indexInBlendShape; + } + + createTangents(mesh, generateFromTexCoords, blendShape.vertices, blendShape.normals, blendShape.tangents, + [&](const FBXMesh& mesh, int firstIndex, int secondIndex, glm::vec3* outVertices, glm::vec3& outNormal) { + const auto index1 = reverseIndices[firstIndex]; + const auto index2 = reverseIndices[secondIndex]; + + if (index1 < blendShape.vertices.size()) { + outVertices[0] = blendShape.vertices[index1]; + if (index2 < blendShape.vertices.size()) { + outVertices[1] = blendShape.vertices[index2]; + } else { + // Index isn't in the blend shape so return vertex from mesh + outVertices[1] = mesh.vertices[secondIndex]; + } + outNormal = blendShape.normals[index1]; + return &blendShape.tangents[index1]; + } else { + // Index isn't in blend shape so return nullptr + return (glm::vec3*)nullptr; + } + }); } QVector getIndices(const QVector ids, QVector modelIDs) { @@ -550,6 +683,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS glm::vec3 ambientColor; QString hifiGlobalNodeID; unsigned int meshIndex = 0; + haveReportedUnhandledRotationOrder = false; foreach (const FBXNode& child, node.children) { if (child.name == "FBXHeaderExtension") { @@ -651,6 +785,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS glm::vec3 translation; // NOTE: the euler angles as supplied by the FBX file are in degrees glm::vec3 rotationOffset; + int rotationOrder = OrderXYZ; // Default rotation order set in "Definitions" node is assumed to be XYZ. glm::vec3 preRotation, rotation, postRotation; glm::vec3 scale = glm::vec3(1.0f, 1.0f, 1.0f); glm::vec3 scalePivot, rotationPivot, scaleOffset; @@ -684,6 +819,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS index = 4; } if (properties) { + static const QVariant ROTATION_ORDER = QByteArray("RotationOrder"); static const QVariant GEOMETRIC_TRANSLATION = QByteArray("GeometricTranslation"); static const QVariant GEOMETRIC_ROTATION = QByteArray("GeometricRotation"); static const QVariant GEOMETRIC_SCALING = QByteArray("GeometricScaling"); @@ -710,6 +846,9 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS if (childProperty == LCL_TRANSLATION) { translation = getVec3(property.properties, index); + } else if (childProperty == ROTATION_ORDER) { + rotationOrder = property.properties.at(index).toInt(); + } else if (childProperty == ROTATION_OFFSET) { rotationOffset = getVec3(property.properties, index); @@ -717,13 +856,13 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS rotationPivot = getVec3(property.properties, index); } else if (childProperty == PRE_ROTATION) { - preRotation = getVec3(property.properties, index); + preRotation = convertRotationToXYZ(rotationOrder, getVec3(property.properties, index)); } else if (childProperty == LCL_ROTATION) { - rotation = getVec3(property.properties, index); + rotation = convertRotationToXYZ(rotationOrder, getVec3(property.properties, index)); } else if (childProperty == POST_ROTATION) { - postRotation = getVec3(property.properties, index); + postRotation = convertRotationToXYZ(rotationOrder, getVec3(property.properties, index)); } else if (childProperty == SCALING_PIVOT) { scalePivot = getVec3(property.properties, index); @@ -1570,27 +1709,9 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } } - // if we have a normal map (and texture coordinates), we must compute tangents - if (generateTangents && !extracted.mesh.texCoords.isEmpty()) { - extracted.mesh.tangents.resize(extracted.mesh.vertices.size()); - foreach (const FBXMeshPart& part, extracted.mesh.parts) { - for (int i = 0; i < part.quadIndices.size(); i += 4) { - setTangents(extracted.mesh, part.quadIndices.at(i), part.quadIndices.at(i + 1)); - setTangents(extracted.mesh, part.quadIndices.at(i + 1), part.quadIndices.at(i + 2)); - setTangents(extracted.mesh, part.quadIndices.at(i + 2), part.quadIndices.at(i + 3)); - setTangents(extracted.mesh, part.quadIndices.at(i + 3), part.quadIndices.at(i)); - } - // <= size - 3 in order to prevent overflowing triangleIndices when (i % 3) != 0 - // This is most likely evidence of a further problem in extractMesh() - for (int i = 0; i <= part.triangleIndices.size() - 3; i += 3) { - setTangents(extracted.mesh, part.triangleIndices.at(i), part.triangleIndices.at(i + 1)); - setTangents(extracted.mesh, part.triangleIndices.at(i + 1), part.triangleIndices.at(i + 2)); - setTangents(extracted.mesh, part.triangleIndices.at(i + 2), part.triangleIndices.at(i)); - } - if ((part.triangleIndices.size() % 3) != 0){ - qCDebug(modelformat) << "Error in extractFBXGeometry part.triangleIndices.size() is not divisible by three "; - } - } + createMeshTangents(extracted.mesh, generateTangents); + for (auto& blendShape : extracted.mesh.blendshapes) { + createBlendShapeTangents(extracted.mesh, generateTangents, blendShape); } // find the clusters with which the mesh is associated @@ -1612,7 +1733,18 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS qCDebug(modelformat) << "Joint not in model list: " << jointID; fbxCluster.jointIndex = 0; } + fbxCluster.inverseBindMatrix = glm::inverse(cluster.transformLink) * modelTransform; + + // slam bottom row to (0, 0, 0, 1), we KNOW this is not a perspective matrix and + // sometimes floating point fuzz can be introduced after the inverse. + fbxCluster.inverseBindMatrix[0][3] = 0.0f; + fbxCluster.inverseBindMatrix[1][3] = 0.0f; + fbxCluster.inverseBindMatrix[2][3] = 0.0f; + fbxCluster.inverseBindMatrix[3][3] = 1.0f; + + fbxCluster.inverseBindTransform = Transform(fbxCluster.inverseBindMatrix); + extracted.mesh.clusters.append(fbxCluster); // override the bind rotation with the transform link @@ -1714,22 +1846,22 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } // now that we've accumulated the most relevant weights for each vertex - // normalize and compress to 8-bits + // normalize and compress to 16-bits extracted.mesh.clusterWeights.fill(0, numClusterIndices); int numVertices = extracted.mesh.vertices.size(); for (int i = 0; i < numVertices; ++i) { int j = i * WEIGHTS_PER_VERTEX; - // normalize weights into uint8_t + // normalize weights into uint16_t float totalWeight = weightAccumulators[j]; for (int k = j + 1; k < j + WEIGHTS_PER_VERTEX; ++k) { totalWeight += weightAccumulators[k]; } if (totalWeight > 0.0f) { const float ALMOST_HALF = 0.499f; - float weightScalingFactor = (float)(UINT8_MAX) / totalWeight; + float weightScalingFactor = (float)(UINT16_MAX) / totalWeight; for (int k = j; k < j + WEIGHTS_PER_VERTEX; ++k) { - extracted.mesh.clusterWeights[k] = (uint8_t)(weightScalingFactor * weightAccumulators[k] + ALMOST_HALF); + extracted.mesh.clusterWeights[k] = (uint16_t)(weightScalingFactor * weightAccumulators[k] + ALMOST_HALF); } } } diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index 843a874a62..ad002b237c 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -14,6 +14,7 @@ #include "FBX.h" +#include #include #include #include @@ -27,12 +28,25 @@ #include #include -#include -#include +#include +#include class QIODevice; class FBXNode; +#if defined(Q_OS_ANDROID) +#define FBX_PACK_NORMALS 0 +#else +#define FBX_PACK_NORMALS 1 +#endif + +#if FBX_PACK_NORMALS +using NormalType = glm::uint32; +#define FBX_NORMAL_ELEMENT gpu::Element::VEC4F_NORMALIZED_XYZ10W2 +#else +using NormalType = glm::vec3; +#define FBX_NORMAL_ELEMENT gpu::Element::VEC3F_XYZ +#endif /// Reads FBX geometry from the supplied model and mapping data. /// \exception QString if an error occurs in parsing @@ -114,6 +128,8 @@ public: QHash meshes; static void buildModelMesh(FBXMesh& extractedMesh, const QString& url); + static glm::vec3 normalizeDirForPacking(const glm::vec3& dir); + FBXTexture getTexture(const QString& textureID); QHash _textureNames; diff --git a/libraries/fbx/src/FBXReader_Material.cpp b/libraries/fbx/src/FBXReader_Material.cpp index ef6496cd10..4aa3044934 100644 --- a/libraries/fbx/src/FBXReader_Material.cpp +++ b/libraries/fbx/src/FBXReader_Material.cpp @@ -279,7 +279,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { } // Finally create the true material representation - material._material = std::make_shared(); + material._material = std::make_shared(); // Emissive color is the mix of emissiveColor with emissiveFactor auto emissive = material.emissiveColor * (isMaterialLambert ? 1.0f : material.emissiveFactor); // In lambert there is not emissiveFactor @@ -293,7 +293,7 @@ void FBXReader::consolidateFBXMaterials(const QVariantHash& mapping) { material._material->setRoughness(material.roughness); material._material->setMetallic(material.metallic); } else { - material._material->setRoughness(model::Material::shininessToRoughness(material.shininess)); + material._material->setRoughness(graphics::Material::shininessToRoughness(material.shininess)); float metallic = std::max(material.specularColor.x, std::max(material.specularColor.y, material.specularColor.z)); material._material->setMetallic(metallic); diff --git a/libraries/fbx/src/FBXReader_Mesh.cpp b/libraries/fbx/src/FBXReader_Mesh.cpp index c64cbcc90d..b0e2faa600 100644 --- a/libraries/fbx/src/FBXReader_Mesh.cpp +++ b/libraries/fbx/src/FBXReader_Mesh.cpp @@ -37,6 +37,20 @@ #include +#include +#include + +using vec2h = glm::tvec2; + +#define FBX_PACK_COLORS 1 + +#if FBX_PACK_COLORS +using ColorType = glm::uint32; +#define FBX_COLOR_ELEMENT gpu::Element::COLOR_RGBA_32 +#else +using ColorType = glm::vec3; +#define FBX_COLOR_ELEMENT gpu::Element::VEC3F_XYZ +#endif class Vertex { public: @@ -225,7 +239,7 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn foreach (const FBXNode& subdata, child.children) { if (subdata.name == "Colors") { data.colors = createVec4VectorRGBA(getDoubleVector(subdata), data.averageColor); - } else if (subdata.name == "ColorsIndex") { + } else if (subdata.name == "ColorsIndex" || subdata.name == "ColorIndex") { data.colorIndices = getIntVector(subdata); } else if (subdata.name == "MappingInformationType" && subdata.properties.at(0) == BY_VERTICE) { @@ -543,6 +557,14 @@ ExtractedMesh FBXReader::extractMesh(const FBXNode& object, unsigned int& meshIn return data.extracted; } +glm::vec3 FBXReader::normalizeDirForPacking(const glm::vec3& dir) { + auto maxCoord = glm::max(fabsf(dir.x), glm::max(fabsf(dir.y), fabsf(dir.z))); + if (maxCoord > 1e-6f) { + return dir / maxCoord; + } + return dir; +} + void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { static QString repeatedMessage = LogHandler::getInstance().addRepeatedMessageRegex("buildModelMesh failed -- .*"); @@ -562,7 +584,7 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { } FBXMesh& fbxMesh = extractedMesh; - model::MeshPointer mesh(new model::Mesh()); + graphics::MeshPointer mesh(new graphics::Mesh()); // Grab the vertices in a buffer auto vb = std::make_shared(); @@ -571,37 +593,120 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { gpu::BufferView vbv(vb, gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); mesh->setVertexBuffer(vbv); + if (!fbxMesh.normals.empty() && fbxMesh.tangents.empty()) { + // Fill with a dummy value to force tangents to be present if there are normals + fbxMesh.tangents.reserve(fbxMesh.normals.size()); + std::fill_n(std::back_inserter(fbxMesh.tangents), fbxMesh.normals.size(), Vectors::UNIT_X); + } + // Same thing with blend shapes + for (auto& blendShape : fbxMesh.blendshapes) { + if (!blendShape.normals.empty() && blendShape.tangents.empty()) { + // Fill with a dummy value to force tangents to be present if there are normals + blendShape.tangents.reserve(blendShape.normals.size()); + std::fill_n(std::back_inserter(blendShape.tangents), blendShape.normals.size(), Vectors::UNIT_X); + } + } + // evaluate all attribute channels sizes - int normalsSize = fbxMesh.normals.size() * sizeof(glm::vec3); - int tangentsSize = fbxMesh.tangents.size() * sizeof(glm::vec3); - int colorsSize = fbxMesh.colors.size() * sizeof(glm::vec3); - int texCoordsSize = fbxMesh.texCoords.size() * sizeof(glm::vec2); - int texCoords1Size = fbxMesh.texCoords1.size() * sizeof(glm::vec2); + const int normalsSize = fbxMesh.normals.size() * sizeof(NormalType); + const int tangentsSize = fbxMesh.tangents.size() * sizeof(NormalType); + // If there are normals then there should be tangents + + assert(normalsSize <= tangentsSize); + if (tangentsSize > normalsSize) { + qWarning() << "Unexpected tangents in " << url; + } + const auto normalsAndTangentsSize = normalsSize + tangentsSize; + const int normalsAndTangentsStride = 2 * sizeof(NormalType); + const int colorsSize = fbxMesh.colors.size() * sizeof(ColorType); + // Texture coordinates are stored in 2 half floats + const int texCoordsSize = fbxMesh.texCoords.size() * sizeof(vec2h); + const int texCoords1Size = fbxMesh.texCoords1.size() * sizeof(vec2h); int clusterIndicesSize = fbxMesh.clusterIndices.size() * sizeof(uint8_t); if (fbxMesh.clusters.size() > UINT8_MAX) { // we need 16 bits instead of just 8 for clusterIndices clusterIndicesSize *= 2; } - int clusterWeightsSize = fbxMesh.clusterWeights.size() * sizeof(uint8_t); - int normalsOffset = 0; - int tangentsOffset = normalsOffset + normalsSize; - int colorsOffset = tangentsOffset + tangentsSize; - int texCoordsOffset = colorsOffset + colorsSize; - int texCoords1Offset = texCoordsOffset + texCoordsSize; - int clusterIndicesOffset = texCoords1Offset + texCoords1Size; - int clusterWeightsOffset = clusterIndicesOffset + clusterIndicesSize; - int totalAttributeSize = clusterWeightsOffset + clusterWeightsSize; + const int clusterWeightsSize = fbxMesh.clusterWeights.size() * sizeof(uint16_t); + + // Normals and tangents are interleaved + const int normalsOffset = 0; + const int tangentsOffset = normalsOffset + sizeof(NormalType); + const int colorsOffset = normalsOffset + normalsSize + tangentsSize; + const int texCoordsOffset = colorsOffset + colorsSize; + const int texCoords1Offset = texCoordsOffset + texCoordsSize; + const int clusterIndicesOffset = texCoords1Offset + texCoords1Size; + const int clusterWeightsOffset = clusterIndicesOffset + clusterIndicesSize; + const int totalAttributeSize = clusterWeightsOffset + clusterWeightsSize; // Copy all attribute data in a single attribute buffer auto attribBuffer = std::make_shared(); attribBuffer->resize(totalAttributeSize); - attribBuffer->setSubData(normalsOffset, normalsSize, (gpu::Byte*) fbxMesh.normals.constData()); - attribBuffer->setSubData(tangentsOffset, tangentsSize, (gpu::Byte*) fbxMesh.tangents.constData()); - attribBuffer->setSubData(colorsOffset, colorsSize, (gpu::Byte*) fbxMesh.colors.constData()); - attribBuffer->setSubData(texCoordsOffset, texCoordsSize, (gpu::Byte*) fbxMesh.texCoords.constData()); - attribBuffer->setSubData(texCoords1Offset, texCoords1Size, (gpu::Byte*) fbxMesh.texCoords1.constData()); + + // Interleave normals and tangents + if (normalsSize > 0) { + std::vector normalsAndTangents; + + normalsAndTangents.reserve(fbxMesh.normals.size() + fbxMesh.tangents.size()); + for (auto normalIt = fbxMesh.normals.constBegin(), tangentIt = fbxMesh.tangents.constBegin(); + normalIt != fbxMesh.normals.constEnd(); + ++normalIt, ++tangentIt) { +#if FBX_PACK_NORMALS + const auto normal = normalizeDirForPacking(*normalIt); + const auto tangent = normalizeDirForPacking(*tangentIt); + const auto packedNormal = glm::packSnorm3x10_1x2(glm::vec4(normal, 0.0f)); + const auto packedTangent = glm::packSnorm3x10_1x2(glm::vec4(tangent, 0.0f)); +#else + const auto packedNormal = *normalIt; + const auto packedTangent = *tangentIt; +#endif + normalsAndTangents.push_back(packedNormal); + normalsAndTangents.push_back(packedTangent); + } + attribBuffer->setSubData(normalsOffset, normalsAndTangentsSize, (const gpu::Byte*) normalsAndTangents.data()); + } + + if (colorsSize > 0) { +#if FBX_PACK_COLORS + std::vector colors; + + colors.reserve(fbxMesh.colors.size()); + for (const auto& color : fbxMesh.colors) { + colors.push_back(glm::packUnorm4x8(glm::vec4(color, 1.0f))); + } + attribBuffer->setSubData(colorsOffset, colorsSize, (const gpu::Byte*) colors.data()); +#else + attribBuffer->setSubData(colorsOffset, colorsSize, (const gpu::Byte*) fbxMesh.colors.constData()); +#endif + } + + if (texCoordsSize > 0) { + QVector texCoordData; + texCoordData.reserve(fbxMesh.texCoords.size()); + for (auto& texCoordVec2f : fbxMesh.texCoords) { + vec2h texCoordVec2h; + + texCoordVec2h.x = glm::detail::toFloat16(texCoordVec2f.x); + texCoordVec2h.y = glm::detail::toFloat16(texCoordVec2f.y); + texCoordData.push_back(texCoordVec2h); + } + attribBuffer->setSubData(texCoordsOffset, texCoordsSize, (const gpu::Byte*) texCoordData.constData()); + } + + if (texCoords1Size > 0) { + QVector texCoordData; + texCoordData.reserve(fbxMesh.texCoords1.size()); + for (auto& texCoordVec2f : fbxMesh.texCoords1) { + vec2h texCoordVec2h; + + texCoordVec2h.x = glm::detail::toFloat16(texCoordVec2f.x); + texCoordVec2h.y = glm::detail::toFloat16(texCoordVec2f.y); + texCoordData.push_back(texCoordVec2h); + } + attribBuffer->setSubData(texCoords1Offset, texCoords1Size, (const gpu::Byte*) texCoordData.constData()); + } if (fbxMesh.clusters.size() < UINT8_MAX) { // yay! we can fit the clusterIndices within 8-bits @@ -612,57 +717,54 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { assert(fbxMesh.clusterIndices[i] <= UINT8_MAX); clusterIndices[i] = (uint8_t)(fbxMesh.clusterIndices[i]); } - attribBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (gpu::Byte*) clusterIndices.constData()); + attribBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) clusterIndices.constData()); } else { - attribBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (gpu::Byte*) fbxMesh.clusterIndices.constData()); + attribBuffer->setSubData(clusterIndicesOffset, clusterIndicesSize, (const gpu::Byte*) fbxMesh.clusterIndices.constData()); } - attribBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (gpu::Byte*) fbxMesh.clusterWeights.constData()); + attribBuffer->setSubData(clusterWeightsOffset, clusterWeightsSize, (const gpu::Byte*) fbxMesh.clusterWeights.constData()); if (normalsSize) { mesh->addAttribute(gpu::Stream::NORMAL, - model::BufferView(attribBuffer, normalsOffset, normalsSize, - gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ))); - } - if (tangentsSize) { + graphics::BufferView(attribBuffer, normalsOffset, normalsAndTangentsSize, + normalsAndTangentsStride, FBX_NORMAL_ELEMENT)); mesh->addAttribute(gpu::Stream::TANGENT, - model::BufferView(attribBuffer, tangentsOffset, tangentsSize, - gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ))); + graphics::BufferView(attribBuffer, tangentsOffset, normalsAndTangentsSize, + normalsAndTangentsStride, FBX_NORMAL_ELEMENT)); } if (colorsSize) { mesh->addAttribute(gpu::Stream::COLOR, - model::BufferView(attribBuffer, colorsOffset, colorsSize, - gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RGB))); + graphics::BufferView(attribBuffer, colorsOffset, colorsSize, FBX_COLOR_ELEMENT)); } if (texCoordsSize) { mesh->addAttribute(gpu::Stream::TEXCOORD, - model::BufferView( attribBuffer, texCoordsOffset, texCoordsSize, - gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV))); + graphics::BufferView( attribBuffer, texCoordsOffset, texCoordsSize, + gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV))); } if (texCoords1Size) { mesh->addAttribute( gpu::Stream::TEXCOORD1, - model::BufferView(attribBuffer, texCoords1Offset, texCoords1Size, - gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV))); + graphics::BufferView(attribBuffer, texCoords1Offset, texCoords1Size, + gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV))); } else if (texCoordsSize) { mesh->addAttribute(gpu::Stream::TEXCOORD1, - model::BufferView(attribBuffer, texCoordsOffset, texCoordsSize, - gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV))); + graphics::BufferView(attribBuffer, texCoordsOffset, texCoordsSize, + gpu::Element(gpu::VEC2, gpu::HALF, gpu::UV))); } if (clusterIndicesSize) { if (fbxMesh.clusters.size() < UINT8_MAX) { mesh->addAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, - model::BufferView(attribBuffer, clusterIndicesOffset, clusterIndicesSize, + graphics::BufferView(attribBuffer, clusterIndicesOffset, clusterIndicesSize, gpu::Element(gpu::VEC4, gpu::UINT8, gpu::XYZW))); } else { mesh->addAttribute(gpu::Stream::SKIN_CLUSTER_INDEX, - model::BufferView(attribBuffer, clusterIndicesOffset, clusterIndicesSize, + graphics::BufferView(attribBuffer, clusterIndicesOffset, clusterIndicesSize, gpu::Element(gpu::VEC4, gpu::UINT16, gpu::XYZW))); } } if (clusterWeightsSize) { mesh->addAttribute(gpu::Stream::SKIN_CLUSTER_WEIGHT, - model::BufferView(attribBuffer, clusterWeightsOffset, clusterWeightsSize, - gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::XYZW))); + graphics::BufferView(attribBuffer, clusterWeightsOffset, clusterWeightsSize, + gpu::Element(gpu::VEC4, gpu::NUINT16, gpu::XYZW))); } @@ -682,12 +784,12 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { int indexNum = 0; int offset = 0; - std::vector< model::Mesh::Part > parts; + std::vector< graphics::Mesh::Part > parts; if (extractedMesh.parts.size() > 1) { indexNum = 0; } foreach(const FBXMeshPart& part, extractedMesh.parts) { - model::Mesh::Part modelPart(indexNum, 0, 0, model::Mesh::TRIANGLES); + graphics::Mesh::Part modelPart(indexNum, 0, 0, graphics::Mesh::TRIANGLES); if (part.quadTrianglesIndices.size()) { indexBuffer->setSubData(offset, @@ -715,7 +817,7 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { if (parts.size()) { auto pb = std::make_shared(); - pb->setData(parts.size() * sizeof(model::Mesh::Part), (const gpu::Byte*) parts.data()); + pb->setData(parts.size() * sizeof(graphics::Mesh::Part), (const gpu::Byte*) parts.data()); gpu::BufferView pbv(pb, gpu::Element(gpu::VEC4, gpu::UINT32, gpu::XYZW)); mesh->setPartBuffer(pbv); } else { @@ -723,7 +825,7 @@ void FBXReader::buildModelMesh(FBXMesh& extractedMesh, const QString& url) { return; } - // model::Box box = + // graphics::Box box = mesh->evalPartBound(0); extractedMesh._mesh = mesh; diff --git a/libraries/fbx/src/GLTFReader.cpp b/libraries/fbx/src/GLTFReader.cpp index c8501688ac..0c04b3d733 100644 --- a/libraries/fbx/src/GLTFReader.cpp +++ b/libraries/fbx/src/GLTFReader.cpp @@ -748,7 +748,7 @@ bool GLTFReader::buildGeometry(FBXGeometry& geometry, const QUrl& url) { QString& matid = materialIDs[i]; geometry.materials[matid] = FBXMaterial(); FBXMaterial& fbxMaterial = geometry.materials[matid]; - fbxMaterial._material = std::make_shared(); + fbxMaterial._material = std::make_shared(); setFBXMaterial(fbxMaterial, _file.materials[i]); } diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index 7fb916efde..ba93a49cb9 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -750,8 +750,8 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, objMaterial.opacity); FBXMaterial& fbxMaterial = geometry.materials[materialID]; fbxMaterial.materialID = materialID; - fbxMaterial._material = std::make_shared(); - model::MaterialPointer modelMaterial = fbxMaterial._material; + fbxMaterial._material = std::make_shared(); + graphics::MaterialPointer modelMaterial = fbxMaterial._material; if (!objMaterial.diffuseTextureFilename.isEmpty()) { fbxMaterial.albedoTexture.filename = objMaterial.diffuseTextureFilename; @@ -763,7 +763,7 @@ FBXGeometry* OBJReader::readOBJ(QByteArray& model, const QVariantHash& mapping, modelMaterial->setEmissive(fbxMaterial.emissiveColor); modelMaterial->setAlbedo(fbxMaterial.diffuseColor); modelMaterial->setMetallic(glm::length(fbxMaterial.specularColor)); - modelMaterial->setRoughness(model::Material::shininessToRoughness(fbxMaterial.shininess)); + modelMaterial->setRoughness(graphics::Material::shininessToRoughness(fbxMaterial.shininess)); if (fbxMaterial.opacity <= 0.0f) { modelMaterial->setOpacity(1.0f); diff --git a/libraries/fbx/src/OBJWriter.cpp b/libraries/fbx/src/OBJWriter.cpp index e0d3d36b06..4441ae6649 100644 --- a/libraries/fbx/src/OBJWriter.cpp +++ b/libraries/fbx/src/OBJWriter.cpp @@ -11,7 +11,7 @@ #include #include -#include "model/Geometry.h" +#include "graphics/Geometry.h" #include "OBJWriter.h" #include "ModelFormatLogging.h" @@ -105,13 +105,13 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { const gpu::BufferView& partBuffer = mesh->getPartBuffer(); const gpu::BufferView& indexBuffer = mesh->getIndexBuffer(); - model::Index partCount = (model::Index)mesh->getNumParts(); + graphics::Index partCount = (graphics::Index)mesh->getNumParts(); for (int partIndex = 0; partIndex < partCount; partIndex++) { - const model::Mesh::Part& part = partBuffer.get(partIndex); + const graphics::Mesh::Part& part = partBuffer.get(partIndex); out << "g part-" << nth++ << "\n"; - // model::Mesh::TRIANGLES + // graphics::Mesh::TRIANGLES // TODO -- handle other formats gpu::BufferView::Iterator indexItr = indexBuffer.cbegin(); indexItr += part._startIndex; diff --git a/libraries/fbx/src/OBJWriter.h b/libraries/fbx/src/OBJWriter.h index b6e20e1ae6..536e8cf837 100644 --- a/libraries/fbx/src/OBJWriter.h +++ b/libraries/fbx/src/OBJWriter.h @@ -15,9 +15,9 @@ #include #include -#include +#include -using MeshPointer = std::shared_ptr; +using MeshPointer = std::shared_ptr; bool writeOBJToTextStream(QTextStream& out, QList meshes); bool writeOBJToFile(QString path, QList meshes); diff --git a/libraries/gl/src/gl/Config.cpp b/libraries/gl/src/gl/Config.cpp index 1f29fe21b1..0e3a174e91 100644 --- a/libraries/gl/src/gl/Config.cpp +++ b/libraries/gl/src/gl/Config.cpp @@ -13,23 +13,112 @@ #include -#if defined(Q_OS_ANDROID) -PFNGLQUERYCOUNTEREXTPROC glQueryCounterEXT = NULL; -PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64vEXT = NULL; -PFNGLFRAMEBUFFERTEXTUREEXTPROC glFramebufferTextureEXT = NULL; +#if defined(Q_OS_WIN) +#elif defined(Q_OS_ANDROID) +#elif defined(Q_OS_MAC) +#include +#include +#include +#include +#else +#include +#include #endif + + +#if defined(Q_OS_WIN) + +static void* getGlProcessAddress(const char *namez) { + auto result = wglGetProcAddress(namez); + if (!result) { + static HMODULE glModule = nullptr; + if (!glModule) { + glModule = LoadLibraryW(L"opengl32.dll"); + } + result = GetProcAddress(glModule, namez); + } + if (!result) { + OutputDebugStringA(namez); + OutputDebugStringA("\n"); + } + return (void*)result; +} + +typedef BOOL(APIENTRYP PFNWGLSWAPINTERVALEXTPROC)(int interval); +typedef int (APIENTRYP PFNWGLGETSWAPINTERVALEXTPROC) (void); +typedef BOOL(APIENTRYP PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +typedef HGLRC(APIENTRYP PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hDC, HGLRC hShareContext, const int *attribList); + +PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT; +PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT; +PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB; +PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB; + +#elif defined(Q_OS_ANDROID) + +static void* getGlProcessAddress(const char *namez) { + auto result = eglGetProcAddress(namez); + return (void*)result; +} + +#elif defined(Q_OS_MAC) + +static void* getGlProcessAddress(const char *namez) { + static void* GL_LIB = nullptr; + if (nullptr == GL_LIB) { + GL_LIB = dlopen("/System/Library/Frameworks/OpenGL.framework/Versions/Current/OpenGL", RTLD_NOW | RTLD_GLOBAL); + } + return dlsym(GL_LIB, namez); +} + +#else + +static void* getGlProcessAddress(const char *namez) { + return (void*)glXGetProcAddressARB((const GLubyte*)namez); +} + +#endif + + + void gl::initModuleGl() { static std::once_flag once; std::call_once(once, [] { -#if defined(Q_OS_ANDROID) - glQueryCounterEXT = (PFNGLQUERYCOUNTEREXTPROC)eglGetProcAddress("glQueryCounterEXT"); - glGetQueryObjectui64vEXT = (PFNGLGETQUERYOBJECTUI64VEXTPROC)eglGetProcAddress("glGetQueryObjectui64vEXT"); - glFramebufferTextureEXT = (PFNGLFRAMEBUFFERTEXTUREEXTPROC)eglGetProcAddress("glFramebufferTextureEXT"); +#if defined(Q_OS_WIN) + wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)getGlProcessAddress("wglSwapIntervalEXT"); + wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)getGlProcessAddress("wglGetSwapIntervalEXT"); + wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)getGlProcessAddress("wglChoosePixelFormatARB"); + wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)getGlProcessAddress("wglCreateContextAttribsARB"); +#endif + +#if defined(USE_GLES) + gladLoadGLES2Loader(getGlProcessAddress); #else - glewExperimental = true; - glewInit(); + gladLoadGLLoader(getGlProcessAddress); #endif }); } +int gl::getSwapInterval() { +#if defined(Q_OS_WIN) + return wglGetSwapIntervalEXT(); +#elif defined(Q_OS_MAC) + GLint interval; + CGLGetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval); + return interval; +#else + // TODO: Fill in for linux + return 1; +#endif +} + +void gl::setSwapInterval(int interval) { +#if defined(Q_OS_WIN) + wglSwapIntervalEXT(interval); +#elif defined(Q_OS_MAC) + CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval); +#else + Q_UNUSED(interval); +#endif +} diff --git a/libraries/gl/src/gl/Config.h b/libraries/gl/src/gl/Config.h index cdf86675c6..aad000a242 100644 --- a/libraries/gl/src/gl/Config.h +++ b/libraries/gl/src/gl/Config.h @@ -14,12 +14,7 @@ #include -#if defined(Q_OS_ANDROID) -#define HIFI_GLES -#define HIFI_EGL -#endif - -#if defined(HIFI_GLES) +#if defined(USE_GLES) // Minimum GL ES version required is 3.2 #define GL_MIN_VERSION_MAJOR 0x03 #define GL_MIN_VERSION_MINOR 0x02 @@ -35,51 +30,28 @@ #define MINIMUM_GL_VERSION ((GL_MIN_VERSION_MAJOR << 8) | GL_MIN_VERSION_MINOR) -#if defined(HIFI_GLES) +#include + +#if defined(Q_OS_ANDROID) #include +#else + +#ifndef GL_SLUMINANCE8_EXT +#define GL_SLUMINANCE8_EXT 0x8C47 #endif -#if defined(HIFI_GLES) -#include +// Prevent inclusion of System GL headers +#define __glext_h_ +#define __gl_h_ +#define __gl3_h_ -#define GL_DEPTH_COMPONENT32_OES 0x81A7 -#define GL_TIME_ELAPSED_EXT 0x88BF -#define GL_TIMESTAMP_EXT 0x8E28 -#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 -#define GL_TEXTURE_BORDER_COLOR_EXT 0x1004 -#define GL_CLAMP_TO_BORDER_EXT 0x812D -#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE -#define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF - - -// Add some additional extensions missing from GLES 3.1 -extern "C" { - typedef void (GL_APIENTRYP PFNGLQUERYCOUNTEREXTPROC) (GLuint id, GLenum target); - typedef void (GL_APIENTRYP PFNGLGETQUERYOBJECTUI64VEXTPROC) (GLuint id, GLenum pname, GLuint64 *params); - typedef void (GL_APIENTRYP PFNGLFRAMEBUFFERTEXTUREEXTPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); - extern PFNGLQUERYCOUNTEREXTPROC glQueryCounterEXT; - extern PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64vEXT; - extern PFNGLFRAMEBUFFERTEXTUREEXTPROC glFramebufferTextureEXT; -} - -#else // !defined(HIFI_GLES) - -#define GL_GLEXT_PROTOTYPES 1 -#include - -#if defined(Q_OS_DARWIN) -#include -#include -#include -#elif defined(Q_OS_WIN64) -#include #endif -#endif // !defined(Q_OS_ANDROID) - // Platform specific code to load the GL functions namespace gl { void initModuleGl(); + int getSwapInterval(); + void setSwapInterval(int swapInterval); } #endif // hifi_gpu_GPUConfig_h diff --git a/libraries/gl/src/gl/Context.cpp b/libraries/gl/src/gl/Context.cpp index f7e08e607b..8acbada5aa 100644 --- a/libraries/gl/src/gl/Context.cpp +++ b/libraries/gl/src/gl/Context.cpp @@ -23,10 +23,11 @@ #include #include #include "GLLogging.h" +#include "Config.h" #ifdef Q_OS_WIN -#ifdef DEBUG +#if defined(DEBUG) || defined(USE_GLES) static bool enableDebugLogger = true; #else static const QString DEBUG_FLAG("HIFI_DEBUG_OPENGL"); @@ -69,15 +70,9 @@ Context::Context(QWindow* window) { setWindow(window); } -#ifdef Q_OS_WIN -void Context::destroyWin32Context(HGLRC hglrc) { - wglDeleteContext(hglrc); -} -#endif - void Context::release() { doneCurrent(); -#ifdef Q_OS_WIN +#ifdef GL_CUSTOM_CONTEXT if (_wrappedContext) { destroyContext(_wrappedContext); _wrappedContext = nullptr; @@ -123,14 +118,68 @@ void Context::setWindow(QWindow* window) { release(); _window = window; -#ifdef Q_OS_WIN +#ifdef GL_CUSTOM_CONTEXT _hwnd = (HWND)window->winId(); #endif updateSwapchainMemoryCounter(); } -#ifdef Q_OS_WIN +void Context::clear() { + glClearColor(0, 0, 0, 1); + QSize windowSize = _window->size() * _window->devicePixelRatio(); + glViewport(0, 0, windowSize.width(), windowSize.height()); + glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + swapBuffers(); +} + +#if defined(GL_CUSTOM_CONTEXT) + +static void debugMessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) { + if (GL_DEBUG_SEVERITY_NOTIFICATION == severity) { + return; + } + // FIXME For high severity errors, force a sync to the log, since we might crash + // before the log file was flushed otherwise. Performance hit here + qCDebug(glLogging) << "OpenGL: " << message; +} + +static void setupPixelFormatSimple(HDC hdc) { + // FIXME build the PFD based on the + static const PIXELFORMATDESCRIPTOR pfd = // pfd Tells Windows How We Want Things To Be + { + sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor + 1, // Version Number + PFD_DRAW_TO_WINDOW | // Format Must Support Window + PFD_SUPPORT_OPENGL | // Format Must Support OpenGL + PFD_DOUBLEBUFFER, // Must Support Double Buffering + PFD_TYPE_RGBA, // Request An RGBA Format + 24, // Select Our Color Depth + 0, 0, 0, 0, 0, 0, // Color Bits Ignored + 1, // Alpha Buffer + 0, // Shift Bit Ignored + 0, // No Accumulation Buffer + 0, 0, 0, 0, // Accumulation Bits Ignored + 24, // 24 Bit Z-Buffer (Depth Buffer) + 8, // 8 Bit Stencil Buffer + 0, // No Auxiliary Buffer + PFD_MAIN_PLANE, // Main Drawing Layer + 0, // Reserved + 0, 0, 0 // Layer Masks Ignored + }; + auto pixelFormat = ChoosePixelFormat(hdc, &pfd); + if (pixelFormat == 0) { + throw std::runtime_error("Unable to create initial context"); + } + + if (SetPixelFormat(hdc, pixelFormat, &pfd) == FALSE) { + throw std::runtime_error("Unable to create initial context"); + } +} + +void Context::destroyWin32Context(HGLRC hglrc) { + wglDeleteContext(hglrc); +} bool Context::makeCurrent() { BOOL result = wglMakeCurrent(_hdc, _hglrc); @@ -148,56 +197,38 @@ void Context::doneCurrent() { wglMakeCurrent(0, 0); } -void GLAPIENTRY debugMessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) { - if (GL_DEBUG_SEVERITY_NOTIFICATION == severity) { - return; - } - qCDebug(glLogging) << "OpenGL: " << message; - - // For high severity errors, force a sync to the log, since we might crash - // before the log file was flushed otherwise. Performance hit here - if (GL_DEBUG_SEVERITY_HIGH == severity) { - AbstractLoggerInterface* logger = AbstractLoggerInterface::get(); - if (logger) { - // FIXME find a way to force the log file to sync that doesn't lead to a deadlock - // logger->sync(); - } - } -} +// Pixel format arguments +#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20A9 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_COLOR_BITS_ARB 0x2014 +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_TYPE_RGBA_ARB 0x202B +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 -// FIXME build the PFD based on the -static const PIXELFORMATDESCRIPTOR pfd = // pfd Tells Windows How We Want Things To Be -{ - sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor - 1, // Version Number - PFD_DRAW_TO_WINDOW | // Format Must Support Window - PFD_SUPPORT_OPENGL | // Format Must Support OpenGL - PFD_DOUBLEBUFFER, // Must Support Double Buffering - PFD_TYPE_RGBA, // Request An RGBA Format - 24, // Select Our Color Depth - 0, 0, 0, 0, 0, 0, // Color Bits Ignored - 1, // Alpha Buffer - 0, // Shift Bit Ignored - 0, // No Accumulation Buffer - 0, 0, 0, 0, // Accumulation Bits Ignored - 24, // 24 Bit Z-Buffer (Depth Buffer) - 8, // 8 Bit Stencil Buffer - 0, // No Auxiliary Buffer - PFD_MAIN_PLANE, // Main Drawing Layer - 0, // Reserved - 0, 0, 0 // Layer Masks Ignored -}; +// Context create arguments +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 -void setupPixelFormatSimple(HDC hdc) { - auto pixelFormat = ChoosePixelFormat(hdc, &pfd); - if (pixelFormat == 0) { - throw std::runtime_error("Unable to create initial context"); - } +// Context create flag bits +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 + +#if !defined(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB) +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 +#endif + +typedef BOOL(APIENTRYP PFNWGLCHOOSEPIXELFORMATARBPROC)(HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats); +typedef HGLRC(APIENTRYP PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC hDC, HGLRC hShareContext, const int *attribList); + +GLAPI PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB; +GLAPI PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB; - if (SetPixelFormat(hdc, pixelFormat, &pfd) == FALSE) { - throw std::runtime_error("Unable to create initial context"); - } -} void Context::create() { if (!PRIMARY) { @@ -218,7 +249,6 @@ void Context::create() { if (qApp->property(hifi::properties::CRASHED).toBool()) { enableDebugLogger = true; } - auto hdc = GetDC(hwnd); setupPixelFormatSimple(hdc); auto glrc = wglCreateContext(hdc); @@ -227,20 +257,25 @@ void Context::create() { if (!makeCurrentResult) { throw std::runtime_error("Unable to create initial context"); } - glewExperimental = true; - glewInit(); - if (glewIsSupported("GL_VERSION_4_5")) { - _version = 0x0405; - } else if (glewIsSupported("GL_VERSION_4_3")) { - _version = 0x0403; - } - glGetError(); + gl::initModuleGl(); wglMakeCurrent(0, 0); wglDeleteContext(glrc); ReleaseDC(hwnd, hdc); }); _hdc = GetDC(_hwnd); +#if defined(USE_GLES) + _version = 0x0200; +#else + if (GLAD_GL_VERSION_4_5) { + _version = 0x0405; + } else if (GLAD_GL_VERSION_4_3) { + _version = 0x0403; + } else { + _version = 0x0401; + } +#endif + static int pixelFormat = 0; static PIXELFORMATDESCRIPTOR pfd; if (!pixelFormat) { @@ -261,6 +296,7 @@ void Context::create() { formatAttribs.push_back(24); formatAttribs.push_back(WGL_STENCIL_BITS_ARB); formatAttribs.push_back(8); + #ifdef NATIVE_SRGB_FRAMEBUFFER // formatAttribs.push_back(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB); // formatAttribs.push_back(GL_TRUE); @@ -286,7 +322,11 @@ void Context::create() { contextAttribs.push_back(WGL_CONTEXT_MINOR_VERSION_ARB); contextAttribs.push_back(minorVersion); contextAttribs.push_back(WGL_CONTEXT_PROFILE_MASK_ARB); +#if defined(USE_GLES) + contextAttribs.push_back(WGL_CONTEXT_ES2_PROFILE_BIT_EXT); +#else contextAttribs.push_back(WGL_CONTEXT_CORE_PROFILE_BIT_ARB); +#endif contextAttribs.push_back(WGL_CONTEXT_FLAGS_ARB); if (enableDebugLogger) { contextAttribs.push_back(WGL_CONTEXT_DEBUG_BIT_ARB); @@ -307,22 +347,17 @@ void Context::create() { qApp->setProperty(hifi::properties::gl::PRIMARY_CONTEXT, QVariant::fromValue((void*)PRIMARY)); } + if (!makeCurrent()) { + throw std::runtime_error("Could not make context current"); + } if (enableDebugLogger) { - makeCurrent(); glDebugMessageCallback(debugMessageCallback, NULL); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB); - doneCurrent(); } + doneCurrent(); } -#endif -void Context::clear() { - glClearColor(0, 0, 0, 1); - QSize windowSize = _window->size() * _window->devicePixelRatio(); - glViewport(0, 0, windowSize.width(), windowSize.height()); - glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - swapBuffers(); -} +#endif OffscreenContext::~OffscreenContext() { diff --git a/libraries/gl/src/gl/Context.h b/libraries/gl/src/gl/Context.h index d1b0d53631..4f0747a86f 100644 --- a/libraries/gl/src/gl/Context.h +++ b/libraries/gl/src/gl/Context.h @@ -22,6 +22,9 @@ class QWindow; class QOpenGLContext; class QThread; +#if defined(Q_OS_WIN) +#define GL_CUSTOM_CONTEXT +#endif namespace gl { class Context { @@ -29,7 +32,7 @@ namespace gl { QWindow* _window { nullptr }; static Context* PRIMARY; static void destroyContext(QOpenGLContext* context); -#if defined(Q_OS_WIN) +#if defined(GL_CUSTOM_CONTEXT) uint32_t _version { 0x0401 }; HWND _hwnd { 0 }; HDC _hdc { 0 }; diff --git a/libraries/gl/src/gl/ContextQt.cpp b/libraries/gl/src/gl/ContextQt.cpp index 4d5d2c4e9f..dd65c3076c 100644 --- a/libraries/gl/src/gl/ContextQt.cpp +++ b/libraries/gl/src/gl/ContextQt.cpp @@ -8,6 +8,8 @@ #include "Context.h" +#include "Config.h" + #include #include @@ -28,7 +30,7 @@ void Context::makeCurrent(QOpenGLContext* context, QSurface* surface) { } QOpenGLContext* Context::qglContext() { -#ifdef Q_OS_WIN +#ifdef GL_CUSTOM_CONTEXT if (!_wrappedContext) { _wrappedContext = new QOpenGLContext(); _wrappedContext->setNativeHandle(QVariant::fromValue(QWGLNativeContext(_hglrc, _hwnd))); @@ -45,10 +47,12 @@ void Context::moveToThread(QThread* thread) { qglContext()->moveToThread(thread); } -#ifndef Q_OS_WIN +#ifndef GL_CUSTOM_CONTEXT bool Context::makeCurrent() { updateSwapchainMemoryCounter(); - return _context->makeCurrent(_window); + bool result = _context->makeCurrent(_window); + gl::initModuleGl(); + return result; } void Context::swapBuffers() { diff --git a/libraries/gl/src/gl/GLHelpers.cpp b/libraries/gl/src/gl/GLHelpers.cpp index 722253c26f..d5e3bb5a67 100644 --- a/libraries/gl/src/gl/GLHelpers.cpp +++ b/libraries/gl/src/gl/GLHelpers.cpp @@ -2,6 +2,8 @@ #include +#include "Config.h" + #include #include #include @@ -11,8 +13,6 @@ #include #include -#include - size_t evalGLFormatSwapchainPixelSize(const QSurfaceFormat& format) { size_t pixelSize = format.redBufferSize() + format.greenBufferSize() + format.blueBufferSize() + format.alphaBufferSize(); // We don't apply the length of the swap chain into this pixelSize since it is not vsible for the Process (on windows). @@ -28,18 +28,19 @@ const QSurfaceFormat& getDefaultOpenGLSurfaceFormat() { static QSurfaceFormat format; static std::once_flag once; std::call_once(once, [] { -#if defined(HIFI_GLES) +#if defined(USE_GLES) format.setRenderableType(QSurfaceFormat::OpenGLES); format.setRedBufferSize(8); format.setGreenBufferSize(8); format.setBlueBufferSize(8); format.setAlphaBufferSize(8); +#else + format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); #endif // Qt Quick may need a depth and stencil buffer. Always make sure these are available. format.setDepthBufferSize(DEFAULT_GL_DEPTH_BUFFER_BITS); format.setStencilBufferSize(DEFAULT_GL_STENCIL_BUFFER_BITS); setGLFormatVersion(format); - format.setProfile(QSurfaceFormat::OpenGLContextProfile::CoreProfile); QSurfaceFormat::setDefaultFormat(format); }); return format; diff --git a/libraries/gl/src/gl/GLHelpers.h b/libraries/gl/src/gl/GLHelpers.h index 73319e2e6d..581bb09360 100644 --- a/libraries/gl/src/gl/GLHelpers.h +++ b/libraries/gl/src/gl/GLHelpers.h @@ -26,7 +26,7 @@ class QGLFormat; template // https://bugreports.qt.io/browse/QTBUG-64703 prevents us from using "defined(QT_OPENGL_ES_3_1)" -#if defined(Q_OS_ANDROID) +#if defined(USE_GLES) void setGLFormatVersion(F& format, int major = 3, int minor = 2) #else void setGLFormatVersion(F& format, int major = 4, int minor = 5) diff --git a/libraries/gl/src/gl/GLShaders.cpp b/libraries/gl/src/gl/GLShaders.cpp index 8ef0198676..017c92b71c 100644 --- a/libraries/gl/src/gl/GLShaders.cpp +++ b/libraries/gl/src/gl/GLShaders.cpp @@ -63,12 +63,17 @@ namespace gl { } */ - qCWarning(glLogging) << "GLShader::compileShader - failed to compile the gl shader object:"; + qCCritical(glLogging) << "GLShader::compileShader - failed to compile the gl shader object:"; + int lineNumber = 0; for (auto s : srcstr) { - qCWarning(glLogging) << s; + QString str(s); + QStringList lines = str.split("\n"); + for (auto& line : lines) { + qCCritical(glLogging).noquote() << QString("%1: %2").arg(lineNumber++, 5, 10, QChar('0')).arg(line); + } } - qCWarning(glLogging) << "GLShader::compileShader - errors:"; - qCWarning(glLogging) << temp; + qCCritical(glLogging) << "GLShader::compileShader - errors:"; + qCCritical(glLogging) << temp; error = std::string(temp); delete[] temp; diff --git a/libraries/gl/src/gl/GLWidget.cpp b/libraries/gl/src/gl/GLWidget.cpp index 23bdcff640..c1d049f7f3 100644 --- a/libraries/gl/src/gl/GLWidget.cpp +++ b/libraries/gl/src/gl/GLWidget.cpp @@ -69,6 +69,7 @@ void GLWidget::createContext() { _context->create(); _context->makeCurrent(); _context->clear(); + _context->doneCurrent(); } bool GLWidget::makeCurrent() { diff --git a/libraries/gl/src/gl/GLWindow.cpp b/libraries/gl/src/gl/GLWindow.cpp index c40301deb0..e1e6279b1c 100644 --- a/libraries/gl/src/gl/GLWindow.cpp +++ b/libraries/gl/src/gl/GLWindow.cpp @@ -8,6 +8,7 @@ #include "GLWindow.h" +#include "Config.h" #include #include @@ -19,26 +20,18 @@ void GLWindow::createContext(QOpenGLContext* shareContext) { } void GLWindow::createContext(const QSurfaceFormat& format, QOpenGLContext* shareContext) { - setSurfaceType(QSurface::OpenGLSurface); - setFormat(format); - _context = new QOpenGLContext; - _context->setFormat(format); - if (shareContext) { - _context->setShareContext(shareContext); - } + _context = new gl::Context(); + _context->setWindow(this); _context->create(); + _context->makeCurrent(); + _context->clear(); } GLWindow::~GLWindow() { - if (_context) { - _context->doneCurrent(); - _context->deleteLater(); - _context = nullptr; - } } bool GLWindow::makeCurrent() { - bool makeCurrentResult = _context->makeCurrent(this); + bool makeCurrentResult = _context->makeCurrent(); Q_ASSERT(makeCurrentResult); std::call_once(_reportOnce, []{ @@ -48,8 +41,6 @@ bool GLWindow::makeCurrent() { qCDebug(glLogging) << "GL Renderer: " << QString((const char*) glGetString(GL_RENDERER)); }); - Q_ASSERT(_context == QOpenGLContext::currentContext()); - return makeCurrentResult; } @@ -58,11 +49,11 @@ void GLWindow::doneCurrent() { } void GLWindow::swapBuffers() { - _context->swapBuffers(this); + _context->swapBuffers(); } QOpenGLContext* GLWindow::context() const { - return _context; + return _context->qglContext(); } diff --git a/libraries/gl/src/gl/GLWindow.h b/libraries/gl/src/gl/GLWindow.h index bccfcd7320..e3df21cb49 100644 --- a/libraries/gl/src/gl/GLWindow.h +++ b/libraries/gl/src/gl/GLWindow.h @@ -12,9 +12,9 @@ #include #include +#include "Context.h" class QOpenGLContext; -class QOpenGLDebugLogger; class GLWindow : public QWindow { public: @@ -26,8 +26,8 @@ public: void swapBuffers(); QOpenGLContext* context() const; private: + gl::Context* _context{ nullptr }; std::once_flag _reportOnce; - QOpenGLContext* _context{ nullptr }; }; #endif diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp index 67eb3520c9..8a476c45ad 100644 --- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp +++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp @@ -12,6 +12,8 @@ #include "OffscreenGLCanvas.h" +#include "Config.h" + #include #include #include diff --git a/libraries/gl/src/gl/OpenGLVersionChecker.cpp b/libraries/gl/src/gl/OpenGLVersionChecker.cpp deleted file mode 100644 index 771a8b9a75..0000000000 --- a/libraries/gl/src/gl/OpenGLVersionChecker.cpp +++ /dev/null @@ -1,112 +0,0 @@ -// -// OpenGLVersionChecker.cpp -// libraries/gl/src/gl -// -// Created by David Rowe on 28 Jan 2016. -// Copyright 2016 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 "OpenGLVersionChecker.h" -#include "Config.h" - -#include - -#include -#include -#include -#include - -#include "GLHelpers.h" - -OpenGLVersionChecker::OpenGLVersionChecker(int& argc, char** argv) : - QApplication(argc, argv) -{ -} - -const QGLFormat& getDefaultGLFormat() { - // Specify an OpenGL 3.3 format using the Core profile. - // That is, no old-school fixed pipeline functionality - static QGLFormat glFormat; - static std::once_flag once; - std::call_once(once, [] { - setGLFormatVersion(glFormat); - glFormat.setProfile(QGLFormat::CoreProfile); // Requires >=Qt-4.8.0 - glFormat.setSampleBuffers(false); - glFormat.setDepth(false); - glFormat.setStencil(false); - QGLFormat::setDefaultFormat(glFormat); - }); - return glFormat; -} - - -QJsonObject OpenGLVersionChecker::checkVersion(bool& valid, bool& override) { - valid = true; - override = false; - - QGLWidget* glWidget = new QGLWidget(getDefaultGLFormat()); - - valid = glWidget->isValid(); - // Inform user if no OpenGL support - if (!valid) { - QMessageBox messageBox; - messageBox.setWindowTitle("Missing OpenGL Support"); - messageBox.setIcon(QMessageBox::Warning); - messageBox.setText(QString().sprintf("Your system does not support OpenGL, Interface cannot run.")); - messageBox.setInformativeText("Press OK to exit."); - messageBox.setStandardButtons(QMessageBox::Ok); - messageBox.setDefaultButton(QMessageBox::Ok); - messageBox.exec(); - return QJsonObject(); - } - - // Retrieve OpenGL version - // glWidget->initializeGL(); - glWidget->makeCurrent(); - QJsonObject glData = getGLContextData(); - delete glWidget; - - // Compare against minimum - // The GL_VERSION string begins with a version number in one of these forms: - // - major_number.minor_number - // - major_number.minor_number.release_number - // Reference: https://www.opengl.org/sdk/docs/man/docbook4/xhtml/glGetString.xml - - int minimumMajorNumber = (MINIMUM_GL_VERSION >> 8) & 0xFF; - int minimumMinorNumber = (MINIMUM_GL_VERSION & 0xFF); - int majorNumber = 0; - int minorNumber = 0; - const QString version { "version" }; - if (glData.contains(version)) { - QString glVersion = glData[version].toString(); - QStringList versionParts = glVersion.split(QRegularExpression("[\\.\\s]")); - if (versionParts.size() >= 2) { - majorNumber = versionParts[0].toInt(); - minorNumber = versionParts[1].toInt(); - valid = (majorNumber > minimumMajorNumber - || (majorNumber == minimumMajorNumber && minorNumber >= minimumMinorNumber)); - } else { - valid = false; - } - } else { - valid = false; - } - - // Prompt user if below minimum - if (!valid) { - QMessageBox messageBox; - messageBox.setWindowTitle("OpenGL Version Too Low"); - messageBox.setIcon(QMessageBox::Warning); - messageBox.setText(QString().sprintf("Your OpenGL version of %i.%i is lower than the minimum of %i.%i.", - majorNumber, minorNumber, minimumMajorNumber, minimumMinorNumber)); - messageBox.setInformativeText("Press OK to exit; Ignore to continue."); - messageBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Ignore); - messageBox.setDefaultButton(QMessageBox::Ok); - override = messageBox.exec() == QMessageBox::Ignore; - } - - return glData; -} diff --git a/libraries/gl/src/gl/OpenGLVersionChecker.h b/libraries/gl/src/gl/OpenGLVersionChecker.h deleted file mode 100644 index 913675af24..0000000000 --- a/libraries/gl/src/gl/OpenGLVersionChecker.h +++ /dev/null @@ -1,25 +0,0 @@ -// -// OpenGLVersionChecker.h -// libraries/gl/src/gl -// -// Created by David Rowe on 28 Jan 2016. -// Copyright 2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_OpenGLVersionChecker_h -#define hifi_OpenGLVersionChecker_h - -#include - -class OpenGLVersionChecker : public QApplication { - -public: - OpenGLVersionChecker(int& argc, char** argv); - - static QJsonObject checkVersion(bool& valid, bool& override); -}; - -#endif // hifi_OpenGLVersionChecker_h diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index 8abcb841a6..5e45749f02 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -153,30 +153,7 @@ void GLBackend::init() { qCDebug(gpugllogging) << "\tcard:" << gpu->getName(); qCDebug(gpugllogging) << "\tdriver:" << gpu->getDriver(); qCDebug(gpugllogging) << "\tdedicated memory:" << gpu->getMemory() << "MB"; - - glewExperimental = true; - GLenum err = glewInit(); - glGetError(); // clear the potential error from glewExperimental - if (GLEW_OK != err) { - // glewInit failed, something is seriously wrong. - qCDebug(gpugllogging, "Error: %s\n", glewGetErrorString(err)); - } - qCDebug(gpugllogging, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION)); - -#if defined(Q_OS_WIN) - if (wglewGetExtension("WGL_EXT_swap_control")) { - int swapInterval = wglGetSwapIntervalEXT(); - qCDebug(gpugllogging, "V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF")); - } -#endif - -#if defined(Q_OS_LINUX) - // TODO: Write the correct code for Linux... - /* if (wglewGetExtension("WGL_EXT_swap_control")) { - int swapInterval = wglGetSwapIntervalEXT(); - qCDebug(gpugllogging, "V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF")); - }*/ -#endif + qCDebug(gpugllogging, "V-Sync is %s\n", (::gl::getSwapInterval() > 0 ? "ON" : "OFF")); #if THREADED_TEXTURE_BUFFERING // This has to happen on the main thread in order to give the thread // pool a reasonable parent object diff --git a/libraries/gpu-gl/src/gpu/gl/GLShared.h b/libraries/gpu-gl/src/gpu/gl/GLShared.h index d3ad7c028b..6c2948a736 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLShared.h +++ b/libraries/gpu-gl/src/gpu/gl/GLShared.h @@ -110,7 +110,8 @@ static const GLenum ELEMENT_TYPE_TO_GL[gpu::NUM_TYPES] = { GL_UNSIGNED_SHORT, GL_BYTE, GL_UNSIGNED_BYTE, - GL_UNSIGNED_BYTE + GL_UNSIGNED_BYTE, + GL_INT_2_10_10_10_REV, }; bool checkGLError(const char* name = nullptr); diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp index cde9d28b67..c67f8d7de8 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp @@ -75,7 +75,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { break; case gpu::NUINT8: if ((dstFormat.getSemantic() == gpu::SRGB || dstFormat.getSemantic() == gpu::SRGBA)) { - result = GL_SLUMINANCE8; + result = GL_SLUMINANCE8_EXT; } else { result = GL_R8; } @@ -260,6 +260,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { case gpu::NINT8: result = GL_RGBA8_SNORM; break; + case gpu::NINT2_10_10_10: case gpu::NUINT32: case gpu::NINT32: case gpu::COMPRESSED: @@ -532,7 +533,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E } case gpu::NUINT8: { if ((dstFormat.getSemantic() == gpu::SRGB || dstFormat.getSemantic() == gpu::SRGBA)) { - texel.internalFormat = GL_SLUMINANCE8; + texel.internalFormat = GL_SLUMINANCE8_EXT; } else { texel.internalFormat = GL_R8; } @@ -544,6 +545,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E } case gpu::COMPRESSED: case gpu::NUINT2: + case gpu::NINT2_10_10_10: case gpu::NUM_TYPES: { // quiet compiler Q_UNREACHABLE(); } @@ -595,6 +597,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E } case gpu::COMPRESSED: case gpu::NUINT2: + case gpu::NINT2_10_10_10: case gpu::NUM_TYPES: { // quiet compiler Q_UNREACHABLE(); } @@ -756,6 +759,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E break; case gpu::NUINT32: case gpu::NINT32: + case gpu::NINT2_10_10_10: case gpu::COMPRESSED: case gpu::NUM_TYPES: // quiet compiler Q_UNREACHABLE(); diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp index 32bfb777a8..88cf89ad99 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.cpp @@ -102,7 +102,7 @@ void GL41Backend::do_drawInstanced(const Batch& batch, size_t paramOffset) { _stats._DSNumTriangles += (trueNumInstances * numVertices) / 3; _stats._DSNumDrawcalls += trueNumInstances; } else { - glDrawArraysInstancedARB(mode, startVertex, numVertices, numInstances); + glDrawArraysInstanced(mode, startVertex, numVertices, numInstances); _stats._DSNumTriangles += (numInstances * numVertices) / 3; _stats._DSNumDrawcalls += numInstances; } diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp index cde4ff5f75..925e83bb77 100644 --- a/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl41/GL41BackendTexture.cpp @@ -156,7 +156,7 @@ void GL41Texture::syncSampler() const { glTexParameteri(_target, GL_TEXTURE_MAG_FILTER, fm.magFilter); if (sampler.doComparison()) { - glTexParameteri(_target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); + glTexParameteri(_target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE_ARB); glTexParameteri(_target, GL_TEXTURE_COMPARE_FUNC, COMPARISON_TO_GL[sampler.getComparisonFunction()]); } else { glTexParameteri(_target, GL_TEXTURE_COMPARE_MODE, GL_NONE); diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp index 795a630ccd..1d415c7ca3 100644 --- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp +++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp @@ -197,7 +197,7 @@ void GL45Texture::syncSampler() const { glTextureParameteri(_id, GL_TEXTURE_MAG_FILTER, fm.magFilter); if (sampler.doComparison()) { - glTextureParameteri(_id, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); + glTextureParameteri(_id, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE_ARB); glTextureParameteri(_id, GL_TEXTURE_COMPARE_FUNC, COMPARISON_TO_GL[sampler.getComparisonFunction()]); } else { glTextureParameteri(_id, GL_TEXTURE_COMPARE_MODE, GL_NONE); diff --git a/libraries/gpu-gles/CMakeLists.txt b/libraries/gpu-gles/CMakeLists.txt index 55ec53b184..16575a7018 100644 --- a/libraries/gpu-gles/CMakeLists.txt +++ b/libraries/gpu-gles/CMakeLists.txt @@ -1,11 +1,5 @@ set(TARGET_NAME gpu-gles) -setup_hifi_library(OpenGL) +setup_hifi_library(Concurrent) link_hifi_libraries(shared gl gpu) GroupSources("src") - target_opengl() -target_nsight() - -if (NOT ANDROID) - target_glew() -endif () diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp index cb00d00b3e..6fd5df6f81 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp @@ -22,17 +22,18 @@ #include "nvToolsExt.h" #endif +#include #include #include #include #include "GLTexture.h" #include "GLShader.h" + using namespace gpu; using namespace gpu::gl; static GLBackend* INSTANCE{ nullptr }; -static const char* GL_BACKEND_PROPERTY_NAME = "com.highfidelity.gl.backend"; BackendPointer GLBackend::createBackend() { // FIXME provide a mechanism to override the backend for testing @@ -45,18 +46,17 @@ BackendPointer GLBackend::createBackend() { result->initInput(); result->initTransform(); + result->initTextureManagementStage(); INSTANCE = result.get(); void* voidInstance = &(*result); - qApp->setProperty(GL_BACKEND_PROPERTY_NAME, QVariant::fromValue(voidInstance)); - - gl::GLTexture::initTextureTransferHelper(); + qApp->setProperty(hifi::properties::gl::BACKEND, QVariant::fromValue(voidInstance)); return result; } GLBackend& getBackend() { if (!INSTANCE) { - INSTANCE = static_cast(qApp->property(GL_BACKEND_PROPERTY_NAME).value()); + INSTANCE = static_cast(qApp->property(hifi::properties::gl::BACKEND).value()); } return *INSTANCE; } @@ -65,9 +65,6 @@ bool GLBackend::makeProgram(Shader& shader, const Shader::BindingSet& slotBindin return GLShader::makeProgram(getBackend(), shader, slotBindings); } -std::array commandNames = { - {QString("draw"),QString("drawIndexed"),QString("drawInstanced"),QString("drawIndexedInstanced"),QString("multiDrawIndirect"),QString("multiDrawIndexedIndirect"),QString("setInputFormat"),QString("setInputBuffer"),QString("setIndexBuffer"),QString("setIndirectBuffer"),QString("setModelTransform"),QString("setViewTransform"),QString("setProjectionTransform"),QString("setViewportTransform"),QString("setDepthRangeTransform"),QString("setPipeline"),QString("setStateBlendFactor"),QString("setStateScissorRect"),QString("setUniformBuffer"),QString("setResourceTexture"),QString("setFramebuffer"),QString("clearFramebuffer"),QString("blit"),QString("generateTextureMips"),QString("beginQuery"),QString("endQuery"),QString("getQuery"),QString("resetStages"),QString("runLambda"),QString("startNamedCall"),QString("stopNamedCall"),QString("glUniform1i"),QString("glUniform1f"),QString("glUniform2f"),QString("glUniform3f"),QString("glUniform4f"),QString("glUniform3fv"),QString("glUniform4fv"),QString("glUniform4iv"),QString("glUniformMatrix3fv"),QString("glUniformMatrix4fv"),QString("glColor4f"),QString("pushProfileRange"),QString("popProfileRange"),QString("NUM_COMMANDS")} -}; GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = { @@ -94,6 +91,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::gl::GLBackend::do_setStateScissorRect), (&::gpu::gl::GLBackend::do_setUniformBuffer), + (&::gpu::gl::GLBackend::do_setResourceBuffer), (&::gpu::gl::GLBackend::do_setResourceTexture), (&::gpu::gl::GLBackend::do_setFramebuffer), @@ -107,6 +105,11 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::gl::GLBackend::do_resetStages), + (&::gpu::gl::GLBackend::do_disableContextViewCorrection), + (&::gpu::gl::GLBackend::do_restoreContextViewCorrection), + (&::gpu::gl::GLBackend::do_disableContextStereo), + (&::gpu::gl::GLBackend::do_restoreContextStereo), + (&::gpu::gl::GLBackend::do_runLambda), (&::gpu::gl::GLBackend::do_startNamedCall), @@ -144,16 +147,11 @@ void GLBackend::init() { qCDebug(gpugllogging) << "\tcard:" << gpu->getName(); qCDebug(gpugllogging) << "\tdriver:" << gpu->getDriver(); qCDebug(gpugllogging) << "\tdedicated memory:" << gpu->getMemory() << "MB"; - - /*glewExperimental = true; - GLenum err = glewInit(); - glGetError(); // clear the potential error from glewExperimental - if (GLEW_OK != err) { - // glewInit failed, something is seriously wrong. - qCDebug(gpugllogging, "Error: %s\n", glewGetErrorString(err)); - } - qCDebug(gpugllogging, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION)); - */ +#if THREADED_TEXTURE_BUFFERING + // This has to happen on the main thread in order to give the thread + // pool a reasonable parent object + GLVariableAllocationSupport::TransferJob::startBufferingThread(); +#endif }); } @@ -165,8 +163,6 @@ GLBackend::GLBackend() { GLBackend::~GLBackend() { - resetStages(); - killInput(); killTransform(); } @@ -178,7 +174,7 @@ void GLBackend::renderPassTransfer(const Batch& batch) { _inRenderTransferPass = true; { // Sync all the buffers - ANDROID_PROFILE(render, "syncGPUBuffer", 0xffaaffaa, 1) + PROFILE_RANGE(render_gpu_gl_detail, "syncGPUBuffer"); for (auto& cached : batch._buffers._items) { if (cached._data) { @@ -187,8 +183,8 @@ void GLBackend::renderPassTransfer(const Batch& batch) { } } - { // Sync all the buffers - ANDROID_PROFILE(render, "syncCPUTransform", 0xffaaaaff, 1) + { // Sync all the transform states + PROFILE_RANGE(render_gpu_gl_detail, "syncCPUTransform"); _transform._cameras.clear(); _transform._cameraOffsets.clear(); @@ -203,10 +199,17 @@ void GLBackend::renderPassTransfer(const Batch& batch) { _transform.preUpdate(_commandIndex, _stereo); break; + case Batch::COMMAND_disableContextStereo: + _stereo._contextDisable = true; + break; + + case Batch::COMMAND_restoreContextStereo: + _stereo._contextDisable = false; + break; + case Batch::COMMAND_setViewportTransform: case Batch::COMMAND_setViewTransform: case Batch::COMMAND_setProjectionTransform: { - ANDROID_PROFILE_COMMAND(render, (int)(*command), 0xffeeaaff, 1) CommandCall call = _commandCalls[(*command)]; (this->*(call))(batch, *offset); break; @@ -221,8 +224,7 @@ void GLBackend::renderPassTransfer(const Batch& batch) { } { // Sync the transform buffers - //PROFILE_RANGE(render_gpu_gl, "transferTransformState"); - ANDROID_PROFILE(render, "transferTransformState", 0xff0000ff, 1) + PROFILE_RANGE(render_gpu_gl_detail, "transferGPUTransform"); transferTransformState(batch); } @@ -257,14 +259,12 @@ void GLBackend::renderPassDraw(const Batch& batch) { updateInput(); updateTransform(batch); updatePipeline(); - {ANDROID_PROFILE_COMMAND(render, (int)(*command), 0xff0000ff, 1) + CommandCall call = _commandCalls[(*command)]; (this->*(call))(batch, *offset); - } break; } default: { - ANDROID_PROFILE_COMMAND(render, (int)(*command), 0xffff00ff, 1) CommandCall call = _commandCalls[(*command)]; (this->*(call))(batch, *offset); break; @@ -277,7 +277,6 @@ void GLBackend::renderPassDraw(const Batch& batch) { } void GLBackend::render(const Batch& batch) { - ANDROID_PROFILE(render, "GLBackendRender", 0xffff00ff, 1) _transform._skybox = _stereo._skybox = batch.isSkyboxEnabled(); // Allow the batch to override the rendering stereo settings // for things like full framebuffer copy operations (deferred lighting passes) @@ -287,16 +286,24 @@ void GLBackend::render(const Batch& batch) { } { - //PROFILE_RANGE(render_gpu_gl, "Transfer"); - ANDROID_PROFILE(render, "Transfer", 0xff0000ff, 1) + PROFILE_RANGE(render_gpu_gl_detail, "Transfer"); renderPassTransfer(batch); } +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + if (_stereo.isStereo()) { + glEnable(GL_CLIP_DISTANCE0_EXT); + } +#endif { - //PROFILE_RANGE(render_gpu_gl, _stereo._enable ? "Render Stereo" : "Render"); - ANDROID_PROFILE(render, "RenderPassDraw", 0xff00ddff, 1) + PROFILE_RANGE(render_gpu_gl_detail, _stereo.isStereo() ? "Render Stereo" : "Render"); renderPassDraw(batch); } +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + if (_stereo.isStereo()) { + glDisable(GL_CLIP_DISTANCE0_EXT); + } +#endif // Restore the saved stereo state for the next batch _stereo._enable = savedStereo; @@ -304,15 +311,15 @@ void GLBackend::render(const Batch& batch) { void GLBackend::syncCache() { + PROFILE_RANGE(render_gpu_gl_detail, __FUNCTION__); + syncTransformStateCache(); syncPipelineStateCache(); syncInputStateCache(); syncOutputStateCache(); - - //glEnable(GL_LINE_SMOOTH); - qDebug() << "TODO: GLBackend.cpp:syncCache GL_LINE_SMOOTH"; } +#ifdef GPU_STEREO_DRAWCALL_DOUBLED void GLBackend::setupStereoSide(int side) { ivec4 vp = _transform._viewport; vp.z /= 2; @@ -320,19 +327,35 @@ void GLBackend::setupStereoSide(int side) { #ifdef GPU_STEREO_CAMERA_BUFFER #ifdef GPU_STEREO_DRAWCALL_DOUBLED - //glVertexAttribI1i(14, side); - glVertexAttribI4i(14, side, 0, 0, 0); - + glVertexAttribI1i(14, side); #endif #else _transform.bindCurrentCamera(side); #endif } +#else +#endif void GLBackend::do_resetStages(const Batch& batch, size_t paramOffset) { resetStages(); } +void GLBackend::do_disableContextViewCorrection(const Batch& batch, size_t paramOffset) { + _transform._viewCorrectionEnabled = false; +} + +void GLBackend::do_restoreContextViewCorrection(const Batch& batch, size_t paramOffset) { + _transform._viewCorrectionEnabled = true; +} + +void GLBackend::do_disableContextStereo(const Batch& batch, size_t paramOffset) { + +} + +void GLBackend::do_restoreContextStereo(const Batch& batch, size_t paramOffset) { + +} + void GLBackend::do_runLambda(const Batch& batch, size_t paramOffset) { std::function f = batch._lambdas.get(batch._params[paramOffset]._uint); f(); @@ -361,18 +384,22 @@ void GLBackend::resetStages() { void GLBackend::do_pushProfileRange(const Batch& batch, size_t paramOffset) { - auto name = batch._profileRanges.get(batch._params[paramOffset]._uint); - profileRanges.push_back(name); + if (trace_render_gpu_gl_detail().isDebugEnabled()) { + auto name = batch._profileRanges.get(batch._params[paramOffset]._uint); + profileRanges.push_back(name); #if defined(NSIGHT_FOUND) - nvtxRangePush(name.c_str()); + nvtxRangePush(name.c_str()); #endif + } } void GLBackend::do_popProfileRange(const Batch& batch, size_t paramOffset) { - profileRanges.pop_back(); + if (trace_render_gpu_gl_detail().isDebugEnabled()) { + profileRanges.pop_back(); #if defined(NSIGHT_FOUND) - nvtxRangePop(); + nvtxRangePop(); #endif + } } // TODO: As long as we have gl calls explicitely issued from interface @@ -380,8 +407,11 @@ void GLBackend::do_popProfileRange(const Batch& batch, size_t paramOffset) { // term strategy is to get rid of any GL calls in favor of the HIFI GPU API // As long as we don;t use several versions of shaders we can avoid this more complex code path -// #define GET_UNIFORM_LOCATION(shaderUniformLoc) _pipeline._programShader->getUniformLocation(shaderUniformLoc, isStereo()); +#ifdef GPU_STEREO_CAMERA_BUFFER +#define GET_UNIFORM_LOCATION(shaderUniformLoc) ((_pipeline._programShader) ? _pipeline._programShader->getUniformLocation(shaderUniformLoc, (GLShader::Version) isStereo()) : -1) +#else #define GET_UNIFORM_LOCATION(shaderUniformLoc) shaderUniformLoc +#endif void GLBackend::do_glUniform1i(const Batch& batch, size_t paramOffset) { if (_pipeline._program == 0) { @@ -391,7 +421,7 @@ void GLBackend::do_glUniform1i(const Batch& batch, size_t paramOffset) { } updatePipeline(); - glUniform1f( + glUniform1i( GET_UNIFORM_LOCATION(batch._params[paramOffset + 1]._int), batch._params[paramOffset + 0]._int); (void)CHECK_GL_ERROR(); @@ -545,6 +575,10 @@ void GLBackend::do_glColor4f(const Batch& batch, size_t paramOffset) { if (_input._colorAttribute != newColor) { _input._colorAttribute = newColor; glVertexAttrib4fv(gpu::Stream::COLOR, &_input._colorAttribute.r); + // Color has been changed and is not white. To prevent colors from bleeding + // between different objects, we need to set the _hadColorAttribute flag + // as if a previous render call had potential colors + _input._hadColorAttribute = (newColor != glm::vec4(1.0f, 1.0f, 1.0f, 1.0f)); } (void)CHECK_GL_ERROR(); } @@ -590,6 +624,7 @@ void GLBackend::queueLambda(const std::function lambda) const { } void GLBackend::recycle() const { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__) { std::list> lamdbasTrash; { @@ -709,9 +744,9 @@ void GLBackend::recycle() const { } } -#ifndef THREADED_TEXTURE_TRANSFER - gl::GLTexture::_textureTransferHelper->process(); -#endif + GLVariableAllocationSupport::manageMemory(); + GLVariableAllocationSupport::_frameTexturesCreated = 0; + Texture::KtxStorage::releaseOpenKtxFiles(); } void GLBackend::setCameraCorrection(const Mat4& correction) { diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackend.h b/libraries/gpu-gles/src/gpu/gl/GLBackend.h index f8f307bc17..ea06b6b672 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gles/src/gpu/gl/GLBackend.h @@ -29,9 +29,16 @@ // Different versions for the stereo drawcall // Current preferred is "instanced" which draw the shape twice but instanced and rely on clipping plane to draw left/right side only +#define GPU_STEREO_TECHNIQUE_DOUBLED_SIMPLE //#define GPU_STEREO_TECHNIQUE_DOUBLED_SMARTER //#define GPU_STEREO_TECHNIQUE_INSTANCED + +// Let these be configured by the one define picked above +#ifdef GPU_STEREO_TECHNIQUE_DOUBLED_SIMPLE +#define GPU_STEREO_DRAWCALL_DOUBLED +#endif + #ifdef GPU_STEREO_TECHNIQUE_DOUBLED_SMARTER #define GPU_STEREO_DRAWCALL_DOUBLED #define GPU_STEREO_CAMERA_BUFFER @@ -42,259 +49,265 @@ #define GPU_STEREO_CAMERA_BUFFER #endif -//#define ANDROID_INTENSIVE_INSTRUMENTATION 1 - -#ifdef ANDROID_INTENSIVE_INSTRUMENTATION -#define ANDROID_PROFILE_COMMAND(category, commandIndex, argbColor, payload, ...) PROFILE_RANGE_EX(category, commandNames[commandIndex], argbColor, payload, ##__VA_ARGS__); -#define ANDROID_PROFILE(category, name, argbColor, payload, ...) PROFILE_RANGE_EX(category, name, argbColor, payload, ##__VA_ARGS__); -#else -#define ANDROID_PROFILE_COMMAND(category, commandIndex, argbColor, payload, ...) -#define ANDROID_PROFILE(category, name, argbColor, payload, ...) -#endif namespace gpu { namespace gl { - class GLBackend : public Backend, public std::enable_shared_from_this { - // Context Backend static interface required - friend class gpu::Context; - static void init(); - static BackendPointer createBackend(); +class GLBackend : public Backend, public std::enable_shared_from_this { + // Context Backend static interface required + friend class gpu::Context; + static void init(); + static BackendPointer createBackend(); - protected: - explicit GLBackend(bool syncCache); - GLBackend(); - public: - static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet()); +protected: + explicit GLBackend(bool syncCache); + GLBackend(); +public: + static bool makeProgram(Shader& shader, const Shader::BindingSet& slotBindings = Shader::BindingSet()); - ~GLBackend(); + virtual ~GLBackend(); - void setCameraCorrection(const Mat4& correction); - void render(const Batch& batch) final override; + void setCameraCorrection(const Mat4& correction); + void render(const Batch& batch) final override; - // This call synchronize the Full Backend cache with the current GLState - // THis is only intended to be used when mixing raw gl calls with the gpu api usage in order to sync - // the gpu::Backend state with the true gl state which has probably been messed up by these ugly naked gl calls - // Let's try to avoid to do that as much as possible! - void syncCache() final override; + // This call synchronize the Full Backend cache with the current GLState + // THis is only intended to be used when mixing raw gl calls with the gpu api usage in order to sync + // the gpu::Backend state with the true gl state which has probably been messed up by these ugly naked gl calls + // Let's try to avoid to do that as much as possible! + void syncCache() final override; - // This is the ugly "download the pixels to sysmem for taking a snapshot" - // Just avoid using it, it's ugly and will break performances - virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, - const Vec4i& region, QImage& destImage) final override; + // This is the ugly "download the pixels to sysmem for taking a snapshot" + // Just avoid using it, it's ugly and will break performances + virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, + const Vec4i& region, QImage& destImage) final override; - // this is the maximum numeber of available input buffers - size_t getNumInputBuffers() const { return _input._invalidBuffers.size(); } + // this is the maximum numeber of available input buffers + size_t getNumInputBuffers() const { return _input._invalidBuffers.size(); } - // this is the maximum per shader stage on the low end apple - // TODO make it platform dependant at init time - static const int MAX_NUM_UNIFORM_BUFFERS = 12; - size_t getMaxNumUniformBuffers() const { return MAX_NUM_UNIFORM_BUFFERS; } + // this is the maximum per shader stage on the low end apple + // TODO make it platform dependant at init time + static const int MAX_NUM_UNIFORM_BUFFERS = 12; + size_t getMaxNumUniformBuffers() const { return MAX_NUM_UNIFORM_BUFFERS; } - // this is the maximum per shader stage on the low end apple - // TODO make it platform dependant at init time - static const int MAX_NUM_RESOURCE_TEXTURES = 16; - size_t getMaxNumResourceTextures() const { return MAX_NUM_RESOURCE_TEXTURES; } + // this is the maximum per shader stage on the low end apple + // TODO make it platform dependant at init time + static const int MAX_NUM_RESOURCE_BUFFERS = 16; + size_t getMaxNumResourceBuffers() const { return MAX_NUM_RESOURCE_BUFFERS; } + static const int MAX_NUM_RESOURCE_TEXTURES = 16; + size_t getMaxNumResourceTextures() const { return MAX_NUM_RESOURCE_TEXTURES; } - // Draw Stage - virtual void do_draw(const Batch& batch, size_t paramOffset) = 0; - virtual void do_drawIndexed(const Batch& batch, size_t paramOffset) = 0; - virtual void do_drawInstanced(const Batch& batch, size_t paramOffset) = 0; - virtual void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) = 0; - virtual void do_multiDrawIndirect(const Batch& batch, size_t paramOffset) = 0; - virtual void do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) = 0; + // Draw Stage + virtual void do_draw(const Batch& batch, size_t paramOffset) = 0; + virtual void do_drawIndexed(const Batch& batch, size_t paramOffset) = 0; + virtual void do_drawInstanced(const Batch& batch, size_t paramOffset) = 0; + virtual void do_drawIndexedInstanced(const Batch& batch, size_t paramOffset) = 0; + virtual void do_multiDrawIndirect(const Batch& batch, size_t paramOffset) = 0; + virtual void do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) = 0; - // Input Stage - virtual void do_setInputFormat(const Batch& batch, size_t paramOffset) final; - virtual void do_setInputBuffer(const Batch& batch, size_t paramOffset) final; - virtual void do_setIndexBuffer(const Batch& batch, size_t paramOffset) final; - virtual void do_setIndirectBuffer(const Batch& batch, size_t paramOffset) final; - virtual void do_generateTextureMips(const Batch& batch, size_t paramOffset) final; + // Input Stage + virtual void do_setInputFormat(const Batch& batch, size_t paramOffset) final; + virtual void do_setInputBuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_setIndexBuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_setIndirectBuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_generateTextureMips(const Batch& batch, size_t paramOffset) final; - // Transform Stage - virtual void do_setModelTransform(const Batch& batch, size_t paramOffset) final; - virtual void do_setViewTransform(const Batch& batch, size_t paramOffset) final; - virtual void do_setProjectionTransform(const Batch& batch, size_t paramOffset) final; - virtual void do_setViewportTransform(const Batch& batch, size_t paramOffset) final; - virtual void do_setDepthRangeTransform(const Batch& batch, size_t paramOffset) final; + // Transform Stage + virtual void do_setModelTransform(const Batch& batch, size_t paramOffset) final; + virtual void do_setViewTransform(const Batch& batch, size_t paramOffset) final; + virtual void do_setProjectionTransform(const Batch& batch, size_t paramOffset) final; + virtual void do_setViewportTransform(const Batch& batch, size_t paramOffset) final; + virtual void do_setDepthRangeTransform(const Batch& batch, size_t paramOffset) final; - // Uniform Stage - virtual void do_setUniformBuffer(const Batch& batch, size_t paramOffset) final; + // Uniform Stage + virtual void do_setUniformBuffer(const Batch& batch, size_t paramOffset) final; - // Resource Stage - virtual void do_setResourceTexture(const Batch& batch, size_t paramOffset) final; + // Resource Stage + virtual void do_setResourceBuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_setResourceTexture(const Batch& batch, size_t paramOffset) final; - // Pipeline Stage - virtual void do_setPipeline(const Batch& batch, size_t paramOffset) final; + // Pipeline Stage + virtual void do_setPipeline(const Batch& batch, size_t paramOffset) final; - // Output stage - virtual void do_setFramebuffer(const Batch& batch, size_t paramOffset) final; - virtual void do_clearFramebuffer(const Batch& batch, size_t paramOffset) final; - virtual void do_blit(const Batch& batch, size_t paramOffset) = 0; + // Output stage + virtual void do_setFramebuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_clearFramebuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_blit(const Batch& batch, size_t paramOffset) = 0; - // Query section - virtual void do_beginQuery(const Batch& batch, size_t paramOffset) final; - virtual void do_endQuery(const Batch& batch, size_t paramOffset) final; - virtual void do_getQuery(const Batch& batch, size_t paramOffset) final; + // Query section + virtual void do_beginQuery(const Batch& batch, size_t paramOffset) final; + virtual void do_endQuery(const Batch& batch, size_t paramOffset) final; + virtual void do_getQuery(const Batch& batch, size_t paramOffset) final; - // Reset stages - virtual void do_resetStages(const Batch& batch, size_t paramOffset) final; + // Reset stages + virtual void do_resetStages(const Batch& batch, size_t paramOffset) final; - virtual void do_runLambda(const Batch& batch, size_t paramOffset) final; + + virtual void do_disableContextViewCorrection(const Batch& batch, size_t paramOffset) final; + virtual void do_restoreContextViewCorrection(const Batch& batch, size_t paramOffset) final; - virtual void do_startNamedCall(const Batch& batch, size_t paramOffset) final; - virtual void do_stopNamedCall(const Batch& batch, size_t paramOffset) final; + virtual void do_disableContextStereo(const Batch& batch, size_t paramOffset) final; + virtual void do_restoreContextStereo(const Batch& batch, size_t paramOffset) final; - static const int MAX_NUM_ATTRIBUTES = Stream::NUM_INPUT_SLOTS; - // The drawcall Info attribute channel is reserved and is the upper bound for the number of availables Input buffers - static const int MAX_NUM_INPUT_BUFFERS = Stream::DRAW_CALL_INFO; + virtual void do_runLambda(const Batch& batch, size_t paramOffset) final; - virtual void do_pushProfileRange(const Batch& batch, size_t paramOffset) final; - virtual void do_popProfileRange(const Batch& batch, size_t paramOffset) final; + virtual void do_startNamedCall(const Batch& batch, size_t paramOffset) final; + virtual void do_stopNamedCall(const Batch& batch, size_t paramOffset) final; - // TODO: As long as we have gl calls explicitely issued from interface - // code, we need to be able to record and batch these calls. THe long - // term strategy is to get rid of any GL calls in favor of the HIFI GPU API - virtual void do_glUniform1i(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform1f(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform2f(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform3f(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform4f(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform3fv(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform4fv(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniform4iv(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniformMatrix3fv(const Batch& batch, size_t paramOffset) final; - virtual void do_glUniformMatrix4fv(const Batch& batch, size_t paramOffset) final; + static const int MAX_NUM_ATTRIBUTES = Stream::NUM_INPUT_SLOTS; + // The drawcall Info attribute channel is reserved and is the upper bound for the number of availables Input buffers + static const int MAX_NUM_INPUT_BUFFERS = Stream::DRAW_CALL_INFO; - virtual void do_glColor4f(const Batch& batch, size_t paramOffset) final; + virtual void do_pushProfileRange(const Batch& batch, size_t paramOffset) final; + virtual void do_popProfileRange(const Batch& batch, size_t paramOffset) final; - // The State setters called by the GLState::Commands when a new state is assigned - virtual void do_setStateFillMode(int32 mode) final; - virtual void do_setStateCullMode(int32 mode) final; - virtual void do_setStateFrontFaceClockwise(bool isClockwise) final; - virtual void do_setStateDepthClampEnable(bool enable) final; - virtual void do_setStateScissorEnable(bool enable) final; - virtual void do_setStateMultisampleEnable(bool enable) final; - virtual void do_setStateAntialiasedLineEnable(bool enable) final; - virtual void do_setStateDepthBias(Vec2 bias) final; - virtual void do_setStateDepthTest(State::DepthTest test) final; - virtual void do_setStateStencil(State::StencilActivation activation, State::StencilTest frontTest, State::StencilTest backTest) final; - virtual void do_setStateAlphaToCoverageEnable(bool enable) final; - virtual void do_setStateSampleMask(uint32 mask) final; - virtual void do_setStateBlend(State::BlendFunction blendFunction) final; - virtual void do_setStateColorWriteMask(uint32 mask) final; - virtual void do_setStateBlendFactor(const Batch& batch, size_t paramOffset) final; - virtual void do_setStateScissorRect(const Batch& batch, size_t paramOffset) final; + // TODO: As long as we have gl calls explicitely issued from interface + // code, we need to be able to record and batch these calls. THe long + // term strategy is to get rid of any GL calls in favor of the HIFI GPU API + virtual void do_glUniform1i(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform1f(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform2f(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform3f(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform4f(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform3fv(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform4fv(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniform4iv(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniformMatrix3fv(const Batch& batch, size_t paramOffset) final; + virtual void do_glUniformMatrix4fv(const Batch& batch, size_t paramOffset) final; - virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0; - virtual GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) = 0; - virtual GLuint getBufferID(const Buffer& buffer) = 0; - virtual GLuint getQueryID(const QueryPointer& query) = 0; - virtual bool isTextureReady(const TexturePointer& texture); + virtual void do_glColor4f(const Batch& batch, size_t paramOffset) final; - virtual void releaseBuffer(GLuint id, Size size) const; - virtual void releaseExternalTexture(GLuint id, const Texture::ExternalRecycler& recycler) const; - virtual void releaseTexture(GLuint id, Size size) const; - virtual void releaseFramebuffer(GLuint id) const; - virtual void releaseShader(GLuint id) const; - virtual void releaseProgram(GLuint id) const; - virtual void releaseQuery(GLuint id) const; - virtual void queueLambda(const std::function lambda) const; + // The State setters called by the GLState::Commands when a new state is assigned + virtual void do_setStateFillMode(int32 mode) final; + virtual void do_setStateCullMode(int32 mode) final; + virtual void do_setStateFrontFaceClockwise(bool isClockwise) final; + virtual void do_setStateDepthClampEnable(bool enable) final; + virtual void do_setStateScissorEnable(bool enable) final; + virtual void do_setStateMultisampleEnable(bool enable) final; + virtual void do_setStateAntialiasedLineEnable(bool enable) final; + virtual void do_setStateDepthBias(Vec2 bias) final; + virtual void do_setStateDepthTest(State::DepthTest test) final; + virtual void do_setStateStencil(State::StencilActivation activation, State::StencilTest frontTest, State::StencilTest backTest) final; + virtual void do_setStateAlphaToCoverageEnable(bool enable) final; + virtual void do_setStateSampleMask(uint32 mask) final; + virtual void do_setStateBlend(State::BlendFunction blendFunction) final; + virtual void do_setStateColorWriteMask(uint32 mask) final; + virtual void do_setStateBlendFactor(const Batch& batch, size_t paramOffset) final; + virtual void do_setStateScissorRect(const Batch& batch, size_t paramOffset) final; - bool isTextureManagementSparseEnabled() const override { return (_textureManagement._sparseCapable && Texture::getEnableSparseTextures()); } + virtual GLuint getFramebufferID(const FramebufferPointer& framebuffer) = 0; + virtual GLuint getTextureID(const TexturePointer& texture) final; + virtual GLuint getBufferID(const Buffer& buffer) = 0; + virtual GLuint getQueryID(const QueryPointer& query) = 0; - protected: + virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0; + virtual GLBuffer* syncGPUObject(const Buffer& buffer) = 0; + virtual GLTexture* syncGPUObject(const TexturePointer& texture); + virtual GLQuery* syncGPUObject(const Query& query) = 0; + //virtual bool isTextureReady(const TexturePointer& texture); - void recycle() const override; - virtual GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) = 0; - virtual GLBuffer* syncGPUObject(const Buffer& buffer) = 0; - virtual GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) = 0; - virtual GLQuery* syncGPUObject(const Query& query) = 0; + virtual void releaseBuffer(GLuint id, Size size) const; + virtual void releaseExternalTexture(GLuint id, const Texture::ExternalRecycler& recycler) const; + virtual void releaseTexture(GLuint id, Size size) const; + virtual void releaseFramebuffer(GLuint id) const; + virtual void releaseShader(GLuint id) const; + virtual void releaseProgram(GLuint id) const; + virtual void releaseQuery(GLuint id) const; + virtual void queueLambda(const std::function lambda) const; - static const size_t INVALID_OFFSET = (size_t)-1; - bool _inRenderTransferPass { false }; - int32_t _uboAlignment { 0 }; - int _currentDraw { -1 }; + bool isTextureManagementSparseEnabled() const override { return (_textureManagement._sparseCapable && Texture::getEnableSparseTextures()); } - std::list profileRanges; - mutable Mutex _trashMutex; - mutable std::list> _buffersTrash; - mutable std::list> _texturesTrash; - mutable std::list> _externalTexturesTrash; - mutable std::list _framebuffersTrash; - mutable std::list _shadersTrash; - mutable std::list _programsTrash; - mutable std::list _queriesTrash; - mutable std::list> _lambdaQueue; +protected: - void renderPassTransfer(const Batch& batch); - void renderPassDraw(const Batch& batch); - void setupStereoSide(int side); + void recycle() const override; - virtual void initInput() final; - virtual void killInput() final; - virtual void syncInputStateCache() final; - virtual void resetInputStage(); - virtual void updateInput(); + static const size_t INVALID_OFFSET = (size_t)-1; + bool _inRenderTransferPass { false }; + int32_t _uboAlignment { 0 }; + int _currentDraw { -1 }; - struct InputStageState { - bool _invalidFormat { true }; - Stream::FormatPointer _format; - std::string _formatKey; + std::list profileRanges; + mutable Mutex _trashMutex; + mutable std::list> _buffersTrash; + mutable std::list> _texturesTrash; + mutable std::list> _externalTexturesTrash; + mutable std::list _framebuffersTrash; + mutable std::list _shadersTrash; + mutable std::list _programsTrash; + mutable std::list _queriesTrash; + mutable std::list> _lambdaQueue; - typedef std::bitset ActivationCache; - ActivationCache _attributeActivation { 0 }; + void renderPassTransfer(const Batch& batch); + void renderPassDraw(const Batch& batch); - typedef std::bitset BuffersState; +#ifdef GPU_STEREO_DRAWCALL_DOUBLED + void setupStereoSide(int side); +#endif - BuffersState _invalidBuffers{ 0 }; - BuffersState _attribBindingBuffers{ 0 }; + virtual void initInput() final; + virtual void killInput() final; + virtual void syncInputStateCache() final; + virtual void resetInputStage(); + virtual void updateInput() = 0; - Buffers _buffers; - Offsets _bufferOffsets; - Offsets _bufferStrides; - std::vector _bufferVBOs; + struct InputStageState { + bool _invalidFormat { true }; + bool _hadColorAttribute{ true }; + Stream::FormatPointer _format; + std::string _formatKey; - glm::vec4 _colorAttribute{ 0.0f }; + typedef std::bitset ActivationCache; + ActivationCache _attributeActivation { 0 }; - BufferPointer _indexBuffer; - Offset _indexBufferOffset { 0 }; - Type _indexBufferType { UINT32 }; + typedef std::bitset BuffersState; - BufferPointer _indirectBuffer; - Offset _indirectBufferOffset{ 0 }; - Offset _indirectBufferStride{ 0 }; + BuffersState _invalidBuffers{ 0 }; + BuffersState _attribBindingBuffers{ 0 }; - GLuint _defaultVAO { 0 }; + Buffers _buffers; + Offsets _bufferOffsets; + Offsets _bufferStrides; + std::vector _bufferVBOs; - InputStageState() : - _invalidFormat(true), - _format(0), - _formatKey(), - _attributeActivation(0), - _buffers(_invalidBuffers.size(), BufferPointer(0)), - _bufferOffsets(_invalidBuffers.size(), 0), - _bufferStrides(_invalidBuffers.size(), 0), - _bufferVBOs(_invalidBuffers.size(), 0) {} - } _input; + glm::vec4 _colorAttribute{ 0.0f }; - virtual void initTransform() = 0; - void killTransform(); - // Synchronize the state cache of this Backend with the actual real state of the GL Context - void syncTransformStateCache(); - void updateTransform(const Batch& batch); - void resetTransformStage(); + BufferPointer _indexBuffer; + Offset _indexBufferOffset { 0 }; + Type _indexBufferType { UINT32 }; + + BufferPointer _indirectBuffer; + Offset _indirectBufferOffset{ 0 }; + Offset _indirectBufferStride{ 0 }; - // Allows for correction of the camera pose to account for changes - // between the time when a was recorded and the time(s) when it is - // executed - struct CameraCorrection { - Mat4 correction; - Mat4 correctionInverse; - }; + GLuint _defaultVAO { 0 }; - struct TransformStageState { + InputStageState() : + _invalidFormat(true), + _format(0), + _formatKey(), + _attributeActivation(0), + _buffers(_invalidBuffers.size(), BufferPointer(0)), + _bufferOffsets(_invalidBuffers.size(), 0), + _bufferStrides(_invalidBuffers.size(), 0), + _bufferVBOs(_invalidBuffers.size(), 0) {} + } _input; + + virtual void initTransform() = 0; + void killTransform(); + // Synchronize the state cache of this Backend with the actual real state of the GL Context + void syncTransformStateCache(); + virtual void updateTransform(const Batch& batch) = 0; + virtual void resetTransformStage(); + + // Allows for correction of the camera pose to account for changes + // between the time when a was recorded and the time(s) when it is + // executed + struct CameraCorrection { + Mat4 correction; + Mat4 correctionInverse; + }; + + struct TransformStageState { #ifdef GPU_STEREO_CAMERA_BUFFER - struct Cameras { + struct Cameras { TransformCamera _cams[2]; Cameras() {}; @@ -304,124 +317,158 @@ namespace gpu { namespace gl { using CameraBufferElement = Cameras; #else - using CameraBufferElement = TransformCamera; + using CameraBufferElement = TransformCamera; #endif - using TransformCameras = std::vector; + using TransformCameras = std::vector; - TransformCamera _camera; - TransformCameras _cameras; + TransformCamera _camera; + TransformCameras _cameras; - mutable std::map _drawCallInfoOffsets; + mutable std::map _drawCallInfoOffsets; - GLuint _objectBuffer { 0 }; - GLuint _cameraBuffer { 0 }; - GLuint _drawCallInfoBuffer { 0 }; - GLuint _objectBufferTexture { 0 }; - size_t _cameraUboSize { 0 }; - bool _viewIsCamera{ false }; - bool _skybox { false }; - Transform _view; - CameraCorrection _correction; + GLuint _objectBuffer { 0 }; + GLuint _cameraBuffer { 0 }; + GLuint _drawCallInfoBuffer { 0 }; + GLuint _objectBufferTexture { 0 }; + size_t _cameraUboSize { 0 }; + bool _viewIsCamera{ false }; + bool _skybox { false }; + Transform _view; + CameraCorrection _correction; + bool _viewCorrectionEnabled{ true }; - Mat4 _projection; - Vec4i _viewport { 0, 0, 1, 1 }; - Vec2 _depthRange { 0.0f, 1.0f }; - bool _invalidView { false }; - bool _invalidProj { false }; - bool _invalidViewport { false }; - bool _enabledDrawcallInfoBuffer{ false }; + Mat4 _projection; + Vec4i _viewport { 0, 0, 1, 1 }; + Vec2 _depthRange { 0.0f, 1.0f }; + bool _invalidView { false }; + bool _invalidProj { false }; + bool _invalidViewport { false }; - using Pair = std::pair; - using List = std::list; - List _cameraOffsets; - mutable List::const_iterator _camerasItr; - mutable size_t _currentCameraOffset{ INVALID_OFFSET }; + bool _enabledDrawcallInfoBuffer{ false }; - void preUpdate(size_t commandIndex, const StereoState& stereo); - void update(size_t commandIndex, const StereoState& stereo) const; - void bindCurrentCamera(int stereoSide) const; - } _transform; + using Pair = std::pair; + using List = std::list; + List _cameraOffsets; + mutable List::const_iterator _camerasItr; + mutable size_t _currentCameraOffset{ INVALID_OFFSET }; - virtual void transferTransformState(const Batch& batch) const = 0; + void preUpdate(size_t commandIndex, const StereoState& stereo); + void update(size_t commandIndex, const StereoState& stereo) const; + void bindCurrentCamera(int stereoSide) const; + } _transform; - struct UniformStageState { - std::array _buffers; - //Buffers _buffers { }; - } _uniform; + virtual void transferTransformState(const Batch& batch) const = 0; - void releaseUniformBuffer(uint32_t slot); - void resetUniformStage(); + struct UniformStageState { + std::array _buffers; + //Buffers _buffers { }; + } _uniform; - // update resource cache and do the gl unbind call with the current gpu::Texture cached at slot s - void releaseResourceTexture(uint32_t slot); + void releaseUniformBuffer(uint32_t slot); + void resetUniformStage(); - void resetResourceStage(); + // update resource cache and do the gl bind/unbind call with the current gpu::Buffer cached at slot s + // This is using different gl object depending on the gl version + virtual bool bindResourceBuffer(uint32_t slot, BufferPointer& buffer) = 0; + virtual void releaseResourceBuffer(uint32_t slot) = 0; - struct ResourceStageState { - std::array _textures; - //Textures _textures { { MAX_NUM_RESOURCE_TEXTURES } }; - int findEmptyTextureSlot() const; - } _resource; + // update resource cache and do the gl unbind call with the current gpu::Texture cached at slot s + void releaseResourceTexture(uint32_t slot); - size_t _commandIndex{ 0 }; + void resetResourceStage(); - // Standard update pipeline check that the current Program and current State or good to go for a - void updatePipeline(); - // Force to reset all the state fields indicated by the 'toBeReset" signature - void resetPipelineState(State::Signature toBeReset); - // Synchronize the state cache of this Backend with the actual real state of the GL Context - void syncPipelineStateCache(); - void resetPipelineStage(); + struct ResourceStageState { + std::array _buffers; + std::array _textures; + //Textures _textures { { MAX_NUM_RESOURCE_TEXTURES } }; + int findEmptyTextureSlot() const; + } _resource; - struct PipelineStageState { - PipelinePointer _pipeline; + size_t _commandIndex{ 0 }; - GLuint _program { 0 }; - GLint _cameraCorrectionLocation { -1 }; - GLShader* _programShader { nullptr }; - bool _invalidProgram { false }; + // Standard update pipeline check that the current Program and current State or good to go for a + void updatePipeline(); + // Force to reset all the state fields indicated by the 'toBeReset" signature + void resetPipelineState(State::Signature toBeReset); + // Synchronize the state cache of this Backend with the actual real state of the GL Context + void syncPipelineStateCache(); + void resetPipelineStage(); - BufferView _cameraCorrectionBuffer { gpu::BufferView(std::make_shared(sizeof(CameraCorrection), nullptr )) }; + struct PipelineStageState { + PipelinePointer _pipeline; - State::Data _stateCache{ State::DEFAULT }; - State::Signature _stateSignatureCache { 0 }; + GLuint _program { 0 }; + GLint _cameraCorrectionLocation { -1 }; + GLShader* _programShader { nullptr }; + bool _invalidProgram { false }; - GLState* _state { nullptr }; - bool _invalidState { false }; + BufferView _cameraCorrectionBuffer { gpu::BufferView(std::make_shared(sizeof(CameraCorrection), nullptr )) }; + BufferView _cameraCorrectionBufferIdentity { gpu::BufferView(std::make_shared(sizeof(CameraCorrection), nullptr )) }; - PipelineStageState() { - _cameraCorrectionBuffer.edit() = CameraCorrection(); - } - } _pipeline; + State::Data _stateCache{ State::DEFAULT }; + State::Signature _stateSignatureCache { 0 }; - // Synchronize the state cache of this Backend with the actual real state of the GL Context - void syncOutputStateCache(); - void resetOutputStage(); + GLState* _state { nullptr }; + bool _invalidState { false }; - struct OutputStageState { - FramebufferPointer _framebuffer { nullptr }; - GLuint _drawFBO { 0 }; - } _output; + PipelineStageState() { + _cameraCorrectionBuffer.edit() = CameraCorrection(); + _cameraCorrectionBufferIdentity.edit() = CameraCorrection(); + _cameraCorrectionBufferIdentity._buffer->flush(); + } + } _pipeline; - void resetQueryStage(); - struct QueryStageState { - uint32_t _rangeQueryDepth { 0 }; - } _queryStage; + // Backend dependant compilation of the shader + virtual GLShader* compileBackendProgram(const Shader& program); + virtual GLShader* compileBackendShader(const Shader& shader); + virtual std::string getBackendShaderHeader() const; + virtual void makeProgramBindings(ShaderObject& shaderObject); + class ElementResource { + public: + gpu::Element _element; + uint16 _resource; + ElementResource(Element&& elem, uint16 resource) : _element(elem), _resource(resource) {} + }; + ElementResource getFormatFromGLUniform(GLenum gltype); + static const GLint UNUSED_SLOT {-1}; + static bool isUnusedSlot(GLint binding) { return (binding == UNUSED_SLOT); } + virtual int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, + Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers); + virtual int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers); + virtual int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& resourceBuffers) = 0; + virtual int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs); + virtual int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs); - void resetStages(); - struct TextureManagementStageState { - bool _sparseCapable { false }; - } _textureManagement; - virtual void initTextureManagementStage() {} + // Synchronize the state cache of this Backend with the actual real state of the GL Context + void syncOutputStateCache(); + void resetOutputStage(); + + struct OutputStageState { + FramebufferPointer _framebuffer { nullptr }; + GLuint _drawFBO { 0 }; + } _output; - typedef void (GLBackend::*CommandCall)(const Batch&, size_t); - static CommandCall _commandCalls[Batch::NUM_COMMANDS]; - friend class GLState; - friend class GLTexture; - }; + void resetQueryStage(); + struct QueryStageState { + uint32_t _rangeQueryDepth { 0 }; + } _queryStage; - } } + void resetStages(); + + struct TextureManagementStageState { + bool _sparseCapable { false }; + } _textureManagement; + virtual void initTextureManagementStage() {} + + typedef void (GLBackend::*CommandCall)(const Batch&, size_t); + static CommandCall _commandCalls[Batch::NUM_COMMANDS]; + friend class GLState; + friend class GLTexture; + friend class GLShader; +}; + +} } #endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendInput.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendInput.cpp index 057682584d..4145eb6061 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendInput.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendInput.cpp @@ -73,13 +73,11 @@ void GLBackend::initInput() { if(!_input._defaultVAO) { glGenVertexArrays(1, &_input._defaultVAO); } - qDebug() << "glBindVertexArray(" << _input._defaultVAO << ")"; glBindVertexArray(_input._defaultVAO); (void) CHECK_GL_ERROR(); } void GLBackend::killInput() { - qDebug() << "glBindVertexArray(0)"; glBindVertexArray(0); if(_input._defaultVAO) { glDeleteVertexArrays(1, &_input._defaultVAO); @@ -94,7 +92,6 @@ void GLBackend::syncInputStateCache() { _input._attributeActivation[i] = active; } //_input._defaultVAO - qDebug() << "glBindVertexArray("<<_input._defaultVAO<< ")"; glBindVertexArray(_input._defaultVAO); } @@ -103,7 +100,6 @@ void GLBackend::resetInputStage() { _input._indexBufferType = UINT32; _input._indexBufferOffset = 0; _input._indexBuffer.reset(); - //qDebug() << "GLBackend::resetInputStage glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);"; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); (void) CHECK_GL_ERROR(); @@ -159,49 +155,83 @@ void GLBackend::do_setIndirectBuffer(const Batch& batch, size_t paramOffset) { (void)CHECK_GL_ERROR(); } - -// Core 41 doesn't expose the features to really separate the vertex format from the vertex buffers binding -// Core 43 does :) -// FIXME crashing problem with glVertexBindingDivisor / glVertexAttribFormat -// Once resolved, break this up into the GL 4.1 and 4.5 backends -#if 1 || (GPU_INPUT_PROFILE == GPU_CORE_41) -#define NO_SUPPORT_VERTEX_ATTRIB_FORMAT -#else -#define SUPPORT_VERTEX_ATTRIB_FORMAT -#endif - void GLBackend::updateInput() { -#if defined(SUPPORT_VERTEX_ATTRIB_FORMAT) if (_input._invalidFormat) { - InputStageState::ActivationCache newActivation; // Assign the vertex format required if (_input._format) { - for (auto& it : _input._format->getAttributes()) { - const Stream::Attribute& attrib = (it).second; + bool hasColorAttribute{ false }; - GLuint slot = attrib._slot; - GLuint count = attrib._element.getLocationScalarCount(); - uint8_t locationCount = attrib._element.getLocationCount(); - GLenum type = _elementTypeToGL41Type[attrib._element.getType()]; - GLuint offset = attrib._offset;; - GLboolean isNormalized = attrib._element.isNormalized(); + _input._attribBindingBuffers.reset(); - GLenum perLocationSize = attrib._element.getLocationSize(); + const Stream::Format::AttributeMap& attributes = _input._format->getAttributes(); + auto& inputChannels = _input._format->getChannels(); + for (auto& channelIt : inputChannels) { + auto bufferChannelNum = (channelIt).first; + const Stream::Format::ChannelMap::value_type::second_type& channel = (channelIt).second; + _input._attribBindingBuffers.set(bufferChannelNum); - for (size_t locNum = 0; locNum < locationCount; ++locNum) { - newActivation.set(slot + locNum); - glVertexAttribFormat(slot + locNum, count, type, isNormalized, offset + locNum * perLocationSize); - glVertexAttribBinding(slot + locNum, attrib._channel); + GLuint frequency = 0; + for (unsigned int i = 0; i < channel._slots.size(); i++) { + const Stream::Attribute& attrib = attributes.at(channel._slots[i]); + + GLuint slot = attrib._slot; + GLuint count = attrib._element.getLocationScalarCount(); + uint8_t locationCount = attrib._element.getLocationCount(); + GLenum type = gl::ELEMENT_TYPE_TO_GL[attrib._element.getType()]; + + GLuint offset = (GLuint)attrib._offset;; + GLboolean isNormalized = attrib._element.isNormalized(); + + GLenum perLocationSize = attrib._element.getLocationSize(); + + hasColorAttribute = hasColorAttribute || (slot == Stream::COLOR); + + for (GLuint locNum = 0; locNum < locationCount; ++locNum) { + GLuint attriNum = (GLuint)(slot + locNum); + newActivation.set(attriNum); + if (!_input._attributeActivation[attriNum]) { + _input._attributeActivation.set(attriNum); + glEnableVertexAttribArray(attriNum); + } + if (attrib._element.isInteger()) { + glVertexAttribIFormat(attriNum, count, type, offset + locNum * perLocationSize); + } else { + glVertexAttribFormat(attriNum, count, type, isNormalized, offset + locNum * perLocationSize); + } + glVertexAttribBinding(attriNum, attrib._channel); + } + + if (i == 0) { + frequency = attrib._frequency; + } else { + assert(frequency == attrib._frequency); + } + + + (void)CHECK_GL_ERROR(); } - glVertexBindingDivisor(attrib._channel, attrib._frequency); +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glVertexBindingDivisor(bufferChannelNum, frequency * (isStereo() ? 2 : 1)); +#else + glVertexBindingDivisor(bufferChannelNum, frequency); +#endif } - (void)CHECK_GL_ERROR(); + + if (_input._hadColorAttribute && !hasColorAttribute) { + // The previous input stage had a color attribute but this one doesn't so reset + // color to pure white. + const auto white = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); + glVertexAttrib4fv(Stream::COLOR, &white.r); + _input._colorAttribute = white; + } + _input._hadColorAttribute = hasColorAttribute; } // Manage Activation what was and what is expected now - for (size_t i = 0; i < newActivation.size(); i++) { + // This should only disable VertexAttribs since the one needed by the vertex format (if it exists) have been enabled above + for (GLuint i = 0; i < (GLuint)newActivation.size(); i++) { bool newState = newActivation[i]; if (newState != _input._attributeActivation[i]) { if (newState) { @@ -219,120 +249,18 @@ void GLBackend::updateInput() { } if (_input._invalidBuffers.any()) { - int numBuffers = _input._buffers.size(); - auto buffer = _input._buffers.data(); auto vbo = _input._bufferVBOs.data(); auto offset = _input._bufferOffsets.data(); auto stride = _input._bufferStrides.data(); - for (int bufferNum = 0; bufferNum < numBuffers; bufferNum++) { - if (_input._invalidBuffers.test(bufferNum)) { - glBindVertexBuffer(bufferNum, (*vbo), (*offset), (*stride)); + for (GLuint buffer = 0; buffer < _input._buffers.size(); buffer++, vbo++, offset++, stride++) { + if (_input._invalidBuffers.test(buffer)) { + glBindVertexBuffer(buffer, (*vbo), (*offset), (GLsizei)(*stride)); } - buffer++; - vbo++; - offset++; - stride++; } + _input._invalidBuffers.reset(); (void)CHECK_GL_ERROR(); } -#else - if (_input._invalidFormat || _input._invalidBuffers.any()) { - - if (_input._invalidFormat) { - InputStageState::ActivationCache newActivation; - - _stats._ISNumFormatChanges++; - - // Check expected activation - if (_input._format) { - for (auto& it : _input._format->getAttributes()) { - const Stream::Attribute& attrib = (it).second; - uint8_t locationCount = attrib._element.getLocationCount(); - for (int i = 0; i < locationCount; ++i) { - newActivation.set(attrib._slot + i); - } - } - } - - // Manage Activation what was and what is expected now - for (unsigned int i = 0; i < newActivation.size(); i++) { - bool newState = newActivation[i]; - if (newState != _input._attributeActivation[i]) { - - if (newState) { - glEnableVertexAttribArray(i); - } else { - glDisableVertexAttribArray(i); - } - (void)CHECK_GL_ERROR(); - - _input._attributeActivation.flip(i); - } - } - } - - // now we need to bind the buffers and assign the attrib pointers - if (_input._format) { - const Buffers& buffers = _input._buffers; - const Offsets& offsets = _input._bufferOffsets; - const Offsets& strides = _input._bufferStrides; - - const Stream::Format::AttributeMap& attributes = _input._format->getAttributes(); - auto& inputChannels = _input._format->getChannels(); - _stats._ISNumInputBufferChanges++; - - GLuint boundVBO = 0; - for (auto& channelIt : inputChannels) { - const Stream::Format::ChannelMap::value_type::second_type& channel = (channelIt).second; - if ((channelIt).first < buffers.size()) { - int bufferNum = (channelIt).first; - - if (_input._invalidBuffers.test(bufferNum) || _input._invalidFormat) { - // GLuint vbo = gpu::GL41Backend::getBufferID((*buffers[bufferNum])); - GLuint vbo = _input._bufferVBOs[bufferNum]; - if (boundVBO != vbo) { - //qDebug() << "GLBackend::updateInput glBindBuffer(GL_ARRAY_BUFFER, " << vbo <<")"; - glBindBuffer(GL_ARRAY_BUFFER, vbo); - (void)CHECK_GL_ERROR(); - boundVBO = vbo; - } - _input._invalidBuffers[bufferNum] = false; - - for (unsigned int i = 0; i < channel._slots.size(); i++) { - const Stream::Attribute& attrib = attributes.at(channel._slots[i]); - GLuint slot = attrib._slot; - GLuint count = attrib._element.getLocationScalarCount(); - uint8_t locationCount = attrib._element.getLocationCount(); - GLenum type = gl::ELEMENT_TYPE_TO_GL[attrib._element.getType()]; - // GLenum perLocationStride = strides[bufferNum]; - GLenum perLocationStride = attrib._element.getLocationSize(); - GLuint stride = (GLuint)strides[bufferNum]; - GLuint pointer = (GLuint)(attrib._offset + offsets[bufferNum]); - GLboolean isNormalized = attrib._element.isNormalized(); - - for (size_t locNum = 0; locNum < locationCount; ++locNum) { - glVertexAttribPointer(slot + (GLuint)locNum, count, type, isNormalized, stride, - reinterpret_cast(pointer + perLocationStride * (GLuint)locNum)); -#ifdef GPU_STEREO_DRAWCALL_INSTANCED - glVertexAttribDivisor(slot + (GLuint)locNum, attrib._frequency * (isStereo() ? 2 : 1)); -#else - glVertexAttribDivisor(slot + (GLuint)locNum, attrib._frequency); -#endif - } - - // TODO: Support properly the IAttrib version - - (void)CHECK_GL_ERROR(); - } - } - } - } - } - // everything format related should be in sync now - _input._invalidFormat = false; - } -#endif } diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendOutput.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendOutput.cpp index 6fddb810ee..45c0de8ed7 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendOutput.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendOutput.cpp @@ -48,8 +48,8 @@ void GLBackend::do_setFramebuffer(const Batch& batch, size_t paramOffset) { } void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) { - if (_stereo._enable && !_pipeline._stateCache.scissorEnable) { - //qWarning("Clear without scissor in stereo mode"); + if (_stereo.isStereo() && !_pipeline._stateCache.scissorEnable) { + qWarning("Clear without scissor in stereo mode"); } uint32 masks = batch._params[paramOffset + 7]._uint; @@ -63,17 +63,21 @@ void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) { int useScissor = batch._params[paramOffset + 0]._int; GLuint glmask = 0; + bool restoreStencilMask = false; + uint8_t cacheStencilMask = 0xFF; if (masks & Framebuffer::BUFFER_STENCIL) { glClearStencil(stencil); glmask |= GL_STENCIL_BUFFER_BIT; - // TODO: we will probably need to also check the write mask of stencil like we do - // for depth buffer, but as would say a famous Fez owner "We'll cross that bridge when we come to it" + cacheStencilMask = _pipeline._stateCache.stencilActivation.getWriteMaskFront(); + if (cacheStencilMask != 0xFF) { + restoreStencilMask = true; + glStencilMask(0xFF); + } } bool restoreDepthMask = false; if (masks & Framebuffer::BUFFER_DEPTH) { glClearDepthf(depth); - glmask |= GL_DEPTH_BUFFER_BIT; bool cacheDepthMask = _pipeline._stateCache.depthTest.getWriteMask(); @@ -122,6 +126,11 @@ void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) { glDisable(GL_SCISSOR_TEST); } + // Restore Stencil write mask + if (restoreStencilMask) { + glStencilMask(cacheStencilMask); + } + // Restore write mask meaning turn back off if (restoreDepthMask) { glDepthMask(GL_FALSE); @@ -142,22 +151,19 @@ void GLBackend::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, co auto readFBO = getFramebufferID(srcFramebuffer); if (srcFramebuffer && readFBO) { if ((srcFramebuffer->getWidth() < (region.x + region.z)) || (srcFramebuffer->getHeight() < (region.y + region.w))) { - qCDebug(gpugllogging) << "GLBackend::downloadFramebuffer : srcFramebuffer is too small to provide the region queried"; + qCWarning(gpugllogging) << "GLBackend::downloadFramebuffer : srcFramebuffer is too small to provide the region queried"; return; } } if ((destImage.width() < region.z) || (destImage.height() < region.w)) { - qCDebug(gpugllogging) << "GLBackend::downloadFramebuffer : destImage is too small to receive the region of the framebuffer"; + qCWarning(gpugllogging) << "GLBackend::downloadFramebuffer : destImage is too small to receive the region of the framebuffer"; return; } GLenum format = GL_RGBA; - //GLenum format = GL_BGRA; - qDebug() << "TODO: GLBackendOutput.cpp:do_clearFramebuffer GL_BGRA"; - if (destImage.format() != QImage::Format_ARGB32) { - qCDebug(gpugllogging) << "GLBackend::downloadFramebuffer : destImage format must be FORMAT_ARGB32 to receive the region of the framebuffer"; + qCWarning(gpugllogging) << "GLBackend::downloadFramebuffer : destImage format must be FORMAT_ARGB32 to receive the region of the framebuffer"; return; } diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp index c35966d440..b80be8492f 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendPipeline.cpp @@ -31,7 +31,6 @@ void GLBackend::do_setPipeline(const Batch& batch, size_t paramOffset) { // null pipeline == reset if (!pipeline) { - qDebug() << " null pipeline"; _pipeline._pipeline.reset(); _pipeline._program = 0; @@ -78,7 +77,12 @@ void GLBackend::do_setPipeline(const Batch& batch, size_t paramOffset) { if (_pipeline._invalidProgram) { glUseProgram(_pipeline._program); if (_pipeline._cameraCorrectionLocation != -1) { - auto cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBuffer._buffer); + gl::GLBuffer* cameraCorrectionBuffer = nullptr; + if (_transform._viewCorrectionEnabled) { + cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBuffer._buffer); + } else { + cameraCorrectionBuffer = syncGPUObject(*_pipeline._cameraCorrectionBufferIdentity._buffer); + } glBindBufferRange(GL_UNIFORM_BUFFER, _pipeline._cameraCorrectionLocation, cameraCorrectionBuffer->_id, 0, sizeof(CameraCorrection)); } (void) CHECK_GL_ERROR(); @@ -150,6 +154,10 @@ void GLBackend::resetUniformStage() { void GLBackend::do_setUniformBuffer(const Batch& batch, size_t paramOffset) { GLuint slot = batch._params[paramOffset + 3]._uint; + if (slot >(GLuint)MAX_NUM_UNIFORM_BUFFERS) { + qCDebug(gpugllogging) << "GLBackend::do_setUniformBuffer: Trying to set a uniform Buffer at slot #" << slot << " which doesn't exist. MaxNumUniformBuffers = " << getMaxNumUniformBuffers(); + return; + } BufferPointer uniformBuffer = batch._buffers.get(batch._params[paramOffset + 2]._uint); GLintptr rangeStart = batch._params[paramOffset + 1]._uint; GLsizeiptr rangeSize = batch._params[paramOffset + 0]._uint; @@ -192,15 +200,48 @@ void GLBackend::releaseResourceTexture(uint32_t slot) { } void GLBackend::resetResourceStage() { + for (uint32_t i = 0; i < _resource._buffers.size(); i++) { + releaseResourceBuffer(i); + } for (uint32_t i = 0; i < _resource._textures.size(); i++) { releaseResourceTexture(i); } } +void GLBackend::do_setResourceBuffer(const Batch& batch, size_t paramOffset) { + GLuint slot = batch._params[paramOffset + 1]._uint; + if (slot >= (GLuint)MAX_NUM_RESOURCE_BUFFERS) { + qCDebug(gpugllogging) << "GLBackend::do_setResourceBuffer: Trying to set a resource Buffer at slot #" << slot << " which doesn't exist. MaxNumResourceBuffers = " << getMaxNumResourceBuffers(); + return; + } + + auto resourceBuffer = batch._buffers.get(batch._params[paramOffset + 0]._uint); + + if (!resourceBuffer) { + releaseResourceBuffer(slot); + return; + } + // check cache before thinking + if (_resource._buffers[slot] == resourceBuffer) { + return; + } + + // One more True Buffer bound + _stats._RSNumResourceBufferBounded++; + + // If successful bind then cache it + if (bindResourceBuffer(slot, resourceBuffer)) { + _resource._buffers[slot] = resourceBuffer; + } else { // else clear slot and cache + releaseResourceBuffer(slot); + return; + } +} + void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { GLuint slot = batch._params[paramOffset + 1]._uint; if (slot >= (GLuint) MAX_NUM_RESOURCE_TEXTURES) { - // "GLBackend::do_setResourceTexture: Trying to set a resource Texture at slot #" + slot + " which doesn't exist. MaxNumResourceTextures = " + getMaxNumResourceTextures()); + qCDebug(gpugllogging) << "GLBackend::do_setResourceTexture: Trying to set a resource Texture at slot #" << slot << " which doesn't exist. MaxNumResourceTextures = " << getMaxNumResourceTextures(); return; } @@ -230,7 +271,7 @@ void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { _resource._textures[slot] = resourceTexture; - _stats._RSAmountTextureMemoryBounded += object->size(); + _stats._RSAmountTextureMemoryBounded += (int) object->size(); } else { releaseResourceTexture(slot); diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendQuery.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendQuery.cpp index e5126bb3df..43c8f8f465 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendQuery.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendQuery.cpp @@ -25,6 +25,7 @@ static bool timeElapsed = false; #endif void GLBackend::do_beginQuery(const Batch& batch, size_t paramOffset) { +#if !defined(USE_GLES) auto query = batch._queries.get(batch._params[paramOffset]._uint); GLQuery* glquery = syncGPUObject(*query); if (glquery) { @@ -40,9 +41,11 @@ void GLBackend::do_beginQuery(const Batch& batch, size_t paramOffset) { glquery->_rangeQueryDepth = _queryStage._rangeQueryDepth; (void)CHECK_GL_ERROR(); } +#endif } void GLBackend::do_endQuery(const Batch& batch, size_t paramOffset) { +#if !defined(USE_GLES) auto query = batch._queries.get(batch._params[paramOffset]._uint); GLQuery* glquery = syncGPUObject(*query); if (glquery) { @@ -65,9 +68,11 @@ void GLBackend::do_endQuery(const Batch& batch, size_t paramOffset) { (void)CHECK_GL_ERROR(); } +#endif } void GLBackend::do_getQuery(const Batch& batch, size_t paramOffset) { +#if !defined(USE_GLES) auto query = batch._queries.get(batch._params[paramOffset]._uint); if (glGetQueryObjectui64vEXT == NULL) return; @@ -87,6 +92,7 @@ void GLBackend::do_getQuery(const Batch& batch, size_t paramOffset) { } (void)CHECK_GL_ERROR(); } +#endif } void GLBackend::resetQueryStage() { diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp new file mode 100644 index 0000000000..fd44ad462f --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendShader.cpp @@ -0,0 +1,560 @@ +// +// Created by Gabriel Calero & Cristian Duarte on 2017/12/28 +// Copyright 2013-2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLBackend.h" +#include "GLShader.h" +#include + +using namespace gpu; +using namespace gpu::gl; + +// GLSL version +std::string GLBackend::getBackendShaderHeader() const { + return std::string("#version 310 es"); +} + +// Shader domain +static const size_t NUM_SHADER_DOMAINS = 2; + +// GL Shader type enums +// Must match the order of type specified in gpu::Shader::Type +static const std::array SHADER_DOMAINS{ { + GL_VERTEX_SHADER, + GL_FRAGMENT_SHADER, + // GL_GEOMETRY_SHADER, +} }; + +// Domain specific defines +// Must match the order of type specified in gpu::Shader::Type +static const std::array DOMAIN_DEFINES{ { + "#define GPU_VERTEX_SHADER", + "#define GPU_PIXEL_SHADER", + // "#define GPU_GEOMETRY_SHADER", +} }; + +// Stereo specific defines +static const std::string stereoVersion{ +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + "#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED\n#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN" +#endif +#ifdef GPU_STEREO_DRAWCALL_DOUBLED +#ifdef GPU_STEREO_CAMERA_BUFFER + "#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED" +#else + "#define GPU_TRANSFORM_IS_STEREO" +#endif +#endif +}; + +// Versions specific of the shader +static const std::array VERSION_DEFINES { { + "", + stereoVersion +} }; + +GLShader* GLBackend::compileBackendShader(const Shader& shader) { + // Any GLSLprogram ? normally yes... + const std::string& shaderSource = shader.getSource().getCode(); + GLenum shaderDomain = SHADER_DOMAINS[shader.getType()]; + GLShader::ShaderObjects shaderObjects; + + for (int version = 0; version < GLShader::NumVersions; version++) { + auto& shaderObject = shaderObjects[version]; + + std::string shaderDefines = getBackendShaderHeader() + "\n" + DOMAIN_DEFINES[shader.getType()] + "\n" + VERSION_DEFINES[version] + + "\n#extension GL_EXT_texture_buffer : enable" + + "\nprecision lowp float; // check precision 2" + + "\nprecision lowp samplerBuffer;" + + "\nprecision lowp sampler2DShadow;"; + std::string error; + +#ifdef SEPARATE_PROGRAM + bool result = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, shaderObject.glprogram, error); +#else + bool result = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, error); +#endif + if (!result) { + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << error.c_str(); + return nullptr; + } + } + + // So far so good, the shader is created successfully + GLShader* object = new GLShader(this->shared_from_this()); + object->_shaderObjects = shaderObjects; + + return object; +} + +GLShader* GLBackend::compileBackendProgram(const Shader& program) { + if (!program.isProgram()) { + return nullptr; + } + + GLShader::ShaderObjects programObjects; + + for (int version = 0; version < GLShader::NumVersions; version++) { + auto& programObject = programObjects[version]; + + // Let's go through every shaders and make sure they are ready to go + std::vector shaderGLObjects; + for (auto subShader : program.getShaders()) { + auto object = GLShader::sync((*this), *subShader); + if (object) { + shaderGLObjects.push_back(object->_shaderObjects[version].glshader); + } else { + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - One of the shaders of the program is not compiled?"; + return nullptr; + } + } + + std::string error; + GLuint glprogram = ::gl::compileProgram(shaderGLObjects, error); + if (glprogram == 0) { + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << error.c_str(); + return nullptr; + } + + programObject.glprogram = glprogram; + + makeProgramBindings(programObject); + } + + // So far so good, the program versions have all been created successfully + GLShader* object = new GLShader(this->shared_from_this()); + object->_shaderObjects = programObjects; + + return object; +} + +GLBackend::ElementResource GLBackend::getFormatFromGLUniform(GLenum gltype) { + switch (gltype) { + case GL_FLOAT: + return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_VEC2: + return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_VEC3: + return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_VEC4: + return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); + + case GL_INT: + return ElementResource(Element(SCALAR, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC2: + return ElementResource(Element(VEC2, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC3: + return ElementResource(Element(VEC3, gpu::INT32, UNIFORM), Resource::BUFFER); + case GL_INT_VEC4: + return ElementResource(Element(VEC4, gpu::INT32, UNIFORM), Resource::BUFFER); + + case GL_UNSIGNED_INT: + return ElementResource(Element(SCALAR, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_UNSIGNED_INT_VEC2: + return ElementResource(Element(VEC2, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_UNSIGNED_INT_VEC3: + return ElementResource(Element(VEC3, gpu::UINT32, UNIFORM), Resource::BUFFER); + case GL_UNSIGNED_INT_VEC4: + return ElementResource(Element(VEC4, gpu::UINT32, UNIFORM), Resource::BUFFER); + + case GL_BOOL: + return ElementResource(Element(SCALAR, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC2: + return ElementResource(Element(VEC2, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC3: + return ElementResource(Element(VEC3, gpu::BOOL, UNIFORM), Resource::BUFFER); + case GL_BOOL_VEC4: + return ElementResource(Element(VEC4, gpu::BOOL, UNIFORM), Resource::BUFFER); + + case GL_FLOAT_MAT2: + return ElementResource(Element(gpu::MAT2, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_MAT3: + return ElementResource(Element(MAT3, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_FLOAT_MAT4: + return ElementResource(Element(MAT4, gpu::FLOAT, UNIFORM), Resource::BUFFER); + + //{GL_FLOAT_MAT2x3 mat2x3}, + //{GL_FLOAT_MAT2x4 mat2x4}, + //{GL_FLOAT_MAT3x2 mat3x2}, + //{GL_FLOAT_MAT3x4 mat3x4}, + //{GL_FLOAT_MAT4x2 mat4x2}, + //{GL_FLOAT_MAT4x3 mat4x3}, + //{GL_DOUBLE_MAT2 dmat2}, + //{GL_DOUBLE_MAT3 dmat3}, + //{GL_DOUBLE_MAT4 dmat4}, + //{GL_DOUBLE_MAT2x3 dmat2x3}, + //{GL_DOUBLE_MAT2x4 dmat2x4}, + //{GL_DOUBLE_MAT3x2 dmat3x2}, + //{GL_DOUBLE_MAT3x4 dmat3x4}, + //{GL_DOUBLE_MAT4x2 dmat4x2}, + //{GL_DOUBLE_MAT4x3 dmat4x3}, + + case GL_SAMPLER_2D: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D); + + case GL_SAMPLER_3D: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_3D); + case GL_SAMPLER_CUBE: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_CUBE); + + case GL_SAMPLER_2D_MULTISAMPLE: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_SAMPLER_2D_ARRAY: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); + + case GL_SAMPLER_2D_SHADOW: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D); + case GL_SAMPLER_CUBE_SHADOW: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_CUBE); + + case GL_SAMPLER_2D_ARRAY_SHADOW: + return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D_ARRAY); + + // {GL_SAMPLER_1D_SHADOW sampler1DShadow}, + // {GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow}, + + case GL_SAMPLER_BUFFER: + return ElementResource(Element(SCALAR, gpu::FLOAT, RESOURCE_BUFFER), Resource::BUFFER); + + // {GL_SAMPLER_2D_RECT sampler2DRect}, + // {GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow}, + + case GL_INT_SAMPLER_2D: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D); + case GL_INT_SAMPLER_2D_MULTISAMPLE: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_INT_SAMPLER_3D: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_3D); + case GL_INT_SAMPLER_CUBE: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_CUBE); + + case GL_INT_SAMPLER_2D_ARRAY: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); + + // {GL_INT_SAMPLER_BUFFER isamplerBuffer}, + // {GL_INT_SAMPLER_2D_RECT isampler2DRect}, + + case GL_UNSIGNED_INT_SAMPLER_2D: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D); + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); + case GL_UNSIGNED_INT_SAMPLER_3D: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_3D); + case GL_UNSIGNED_INT_SAMPLER_CUBE: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_CUBE); + + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); + //{GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer}, + //{GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect}, + + //{GL_IMAGE_1D image1D}, + //{GL_IMAGE_2D image2D}, + //{GL_IMAGE_3D image3D}, + //{GL_IMAGE_2D_RECT image2DRect}, + //{GL_IMAGE_CUBE imageCube}, + //{GL_IMAGE_BUFFER imageBuffer}, + //{GL_IMAGE_1D_ARRAY image1DArray}, + //{GL_IMAGE_2D_ARRAY image2DArray}, + //{GL_IMAGE_2D_MULTISAMPLE image2DMS}, + //{GL_IMAGE_2D_MULTISAMPLE_ARRAY image2DMSArray}, + //{GL_INT_IMAGE_1D iimage1D}, + //{GL_INT_IMAGE_2D iimage2D}, + //{GL_INT_IMAGE_3D iimage3D}, + //{GL_INT_IMAGE_2D_RECT iimage2DRect}, + //{GL_INT_IMAGE_CUBE iimageCube}, + //{GL_INT_IMAGE_BUFFER iimageBuffer}, + //{GL_INT_IMAGE_1D_ARRAY iimage1DArray}, + //{GL_INT_IMAGE_2D_ARRAY iimage2DArray}, + //{GL_INT_IMAGE_2D_MULTISAMPLE iimage2DMS}, + //{GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY iimage2DMSArray}, + //{GL_UNSIGNED_INT_IMAGE_1D uimage1D}, + //{GL_UNSIGNED_INT_IMAGE_2D uimage2D}, + //{GL_UNSIGNED_INT_IMAGE_3D uimage3D}, + //{GL_UNSIGNED_INT_IMAGE_2D_RECT uimage2DRect}, + //{GL_UNSIGNED_INT_IMAGE_CUBE uimageCube},+ [0] {_name="fInnerRadius" _location=0 _element={_semantic=15 '\xf' _dimension=0 '\0' _type=0 '\0' } } gpu::Shader::Slot + + //{GL_UNSIGNED_INT_IMAGE_BUFFER uimageBuffer}, + //{GL_UNSIGNED_INT_IMAGE_1D_ARRAY uimage1DArray}, + //{GL_UNSIGNED_INT_IMAGE_2D_ARRAY uimage2DArray}, + //{GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE uimage2DMS}, + //{GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY uimage2DMSArray}, + //{GL_UNSIGNED_INT_ATOMIC_COUNTER atomic_uint} +#if 0 + case GL_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D); + case GL_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D); + case GL_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_UNSIGNED_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D); + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D_ARRAY); + case GL_DOUBLE: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_DOUBLE_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_DOUBLE_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); + case GL_DOUBLE_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); +#endif + + default: + return ElementResource(Element(), Resource::BUFFER); + } +}; + +int GLBackend::makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, + Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) { + GLint uniformsCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount); + + for (int i = 0; i < uniformsCount; i++) { + const GLint NAME_LENGTH = 256; + GLchar name[NAME_LENGTH]; + GLint length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name); + GLint location = glGetUniformLocation(glprogram, name); + const GLint INVALID_UNIFORM_LOCATION = -1; + + // Try to make sense of the gltype + auto elementResource = getFormatFromGLUniform(type); + + // The uniform as a standard var type + if (location != INVALID_UNIFORM_LOCATION) { + // Let's make sure the name doesn't contains an array element + std::string sname(name); + auto foundBracket = sname.find_first_of('['); + if (foundBracket != std::string::npos) { + // std::string arrayname = sname.substr(0, foundBracket); + + if (sname[foundBracket + 1] == '0') { + sname = sname.substr(0, foundBracket); + } else { + // skip this uniform since it's not the first element of an array + continue; + } + } + + if (elementResource._resource == Resource::BUFFER) { + uniforms.insert(Shader::Slot(sname, location, elementResource._element, elementResource._resource)); + } else { + // For texture/Sampler, the location is the actual binding value + GLint binding = -1; + glGetUniformiv(glprogram, location, &binding); + + auto requestedBinding = slotBindings.find(std::string(sname)); + if (requestedBinding != slotBindings.end()) { + if (binding != (*requestedBinding)._location) { + binding = (*requestedBinding)._location; + for (auto i = 0; i < size; i++) { + // If we are working with an array of textures, reserve for each elemet + glProgramUniform1i(glprogram, location + i, binding + i); + } + } + } + + textures.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); + samplers.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); + } + } + } + + return uniformsCount; +} + +int GLBackend::makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) { + GLint buffersCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_UNIFORM_BLOCKS, &buffersCount); + + // fast exit + if (buffersCount == 0) { + return 0; + } + + GLint maxNumUniformBufferSlots = 0; + glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxNumUniformBufferSlots); + std::vector uniformBufferSlotMap(maxNumUniformBufferSlots, -1); + + struct UniformBlockInfo { + using Vector = std::vector; + const GLuint index{ 0 }; + const std::string name; + GLint binding{ -1 }; + GLint size{ 0 }; + + static std::string getName(GLuint glprogram, GLuint i) { + static const GLint NAME_LENGTH = 256; + GLint length = 0; + GLchar nameBuffer[NAME_LENGTH]; + glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length); + glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, nameBuffer); + return std::string(nameBuffer); + } + + UniformBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) { + glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_BINDING, &binding); + glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_DATA_SIZE, &size); + } + }; + + UniformBlockInfo::Vector uniformBlocks; + uniformBlocks.reserve(buffersCount); + for (int i = 0; i < buffersCount; i++) { + uniformBlocks.push_back(UniformBlockInfo(glprogram, i)); + } + + for (auto& info : uniformBlocks) { + auto requestedBinding = slotBindings.find(info.name); + if (requestedBinding != slotBindings.end()) { + info.binding = (*requestedBinding)._location; + glUniformBlockBinding(glprogram, info.index, info.binding); + uniformBufferSlotMap[info.binding] = info.index; + } + } + + for (auto& info : uniformBlocks) { + if (slotBindings.count(info.name)) { + continue; + } + + // If the binding is 0, or the binding maps to an already used binding + if (info.binding == 0 || !isUnusedSlot(uniformBufferSlotMap[info.binding])) { + // If no binding was assigned then just do it finding a free slot + auto slotIt = std::find_if(uniformBufferSlotMap.begin(), uniformBufferSlotMap.end(), GLBackend::isUnusedSlot); + if (slotIt != uniformBufferSlotMap.end()) { + info.binding = slotIt - uniformBufferSlotMap.begin(); + glUniformBlockBinding(glprogram, info.index, info.binding); + } else { + // This should neve happen, an active ubo cannot find an available slot among the max available?! + info.binding = -1; + } + } + + uniformBufferSlotMap[info.binding] = info.index; + } + + for (auto& info : uniformBlocks) { + static const Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER); + buffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size)); + } + return buffersCount; +} + +int GLBackend::makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs) { + GLint inputsCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_ATTRIBUTES, &inputsCount); + + for (int i = 0; i < inputsCount; i++) { + const GLint NAME_LENGTH = 256; + GLchar name[NAME_LENGTH]; + GLint length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); + + GLint binding = glGetAttribLocation(glprogram, name); + + auto elementResource = getFormatFromGLUniform(type); + inputs.insert(Shader::Slot(name, binding, elementResource._element, -1)); + } + + return inputsCount; +} + +int GLBackend::makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs) { + /* GLint outputsCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_, &outputsCount); + + for (int i = 0; i < inputsCount; i++) { + const GLint NAME_LENGTH = 256; + GLchar name[NAME_LENGTH]; + GLint length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); + + auto element = getFormatFromGLUniform(type); + outputs.insert(Shader::Slot(name, i, element)); + } + */ + return 0; //inputsCount; +} + +void GLBackend::makeProgramBindings(ShaderObject& shaderObject) { + if (!shaderObject.glprogram) { + return; + } + GLuint glprogram = shaderObject.glprogram; + GLint loc = -1; + + //Check for gpu specific attribute slotBindings + loc = glGetAttribLocation(glprogram, "inPosition"); + if (loc >= 0 && loc != gpu::Stream::POSITION) { + glBindAttribLocation(glprogram, gpu::Stream::POSITION, "inPosition"); + } + + loc = glGetAttribLocation(glprogram, "inNormal"); + if (loc >= 0 && loc != gpu::Stream::NORMAL) { + glBindAttribLocation(glprogram, gpu::Stream::NORMAL, "inNormal"); + } + + loc = glGetAttribLocation(glprogram, "inColor"); + if (loc >= 0 && loc != gpu::Stream::COLOR) { + glBindAttribLocation(glprogram, gpu::Stream::COLOR, "inColor"); + } + + loc = glGetAttribLocation(glprogram, "inTexCoord0"); + if (loc >= 0 && loc != gpu::Stream::TEXCOORD) { + glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "inTexCoord0"); + } + + loc = glGetAttribLocation(glprogram, "inTangent"); + if (loc >= 0 && loc != gpu::Stream::TANGENT) { + glBindAttribLocation(glprogram, gpu::Stream::TANGENT, "inTangent"); + } + + char attribName[] = "inTexCoordn"; + for (auto i = 0; i < 4; i++) { + auto streamId = gpu::Stream::TEXCOORD1 + i; + + attribName[strlen(attribName) - 1] = '1' + i; + loc = glGetAttribLocation(glprogram, attribName); + if (loc >= 0 && loc != streamId) { + glBindAttribLocation(glprogram, streamId, attribName); + } + } + + loc = glGetAttribLocation(glprogram, "inSkinClusterIndex"); + if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_INDEX) { + glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_INDEX, "inSkinClusterIndex"); + } + + loc = glGetAttribLocation(glprogram, "inSkinClusterWeight"); + if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_WEIGHT) { + glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_WEIGHT, "inSkinClusterWeight"); + } + + loc = glGetAttribLocation(glprogram, "_drawCallInfo"); + if (loc >= 0 && loc != gpu::Stream::DRAW_CALL_INFO) { + glBindAttribLocation(glprogram, gpu::Stream::DRAW_CALL_INFO, "_drawCallInfo"); + } + + // Link again to take into account the assigned attrib location + glLinkProgram(glprogram); + + GLint linked = 0; + glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); + if (!linked) { + qCWarning(gpugllogging) << "GLShader::makeBindings - failed to link after assigning slotBindings?"; + } +} diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendState.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendState.cpp index 0a7db78b11..4a5c772b8b 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendState.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendState.cpp @@ -11,6 +11,8 @@ #include "GLBackend.h" #include "GLState.h" +#include + using namespace gpu; using namespace gpu::gl; @@ -96,13 +98,11 @@ void GLBackend::do_setStateFrontFaceClockwise(bool isClockwise) { void GLBackend::do_setStateDepthClampEnable(bool enable) { if (_pipeline._stateCache.depthClampEnable != enable) { - if (enable) { - qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthClampEnable GL_DEPTH_CLAMP"; - //glEnable(GL_DEPTH_CLAMP); - } else { - //glDisable(GL_DEPTH_CLAMP); - qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthClampEnable GL_DEPTH_CLAMP"; - } + //if (enable) { + // glEnable(GL_DEPTH_CLAMP); + //} else { + // glDisable(GL_DEPTH_CLAMP); + //} (void)CHECK_GL_ERROR(); _pipeline._stateCache.depthClampEnable = enable; @@ -124,14 +124,14 @@ void GLBackend::do_setStateScissorEnable(bool enable) { void GLBackend::do_setStateMultisampleEnable(bool enable) { if (_pipeline._stateCache.multisampleEnable != enable) { +#if !defined(USE_GLES) if (enable) { - //glEnable(GL_MULTISAMPLE); - qDebug() << "TODO: GLBackendState.cpp:do_setStateMultisampleEnable GL_MULTISAMPLE"; + glEnable(GL_MULTISAMPLE); } else { - //glDisable(GL_MULTISAMPLE); - qDebug() << "TODO: GLBackendState.cpp:do_setStateMultisampleEnable GL_MULTISAMPLE"; + glDisable(GL_MULTISAMPLE); } (void)CHECK_GL_ERROR(); +#endif _pipeline._stateCache.multisampleEnable = enable; } @@ -139,14 +139,14 @@ void GLBackend::do_setStateMultisampleEnable(bool enable) { void GLBackend::do_setStateAntialiasedLineEnable(bool enable) { if (_pipeline._stateCache.antialisedLineEnable != enable) { +#if !defined(USE_GLES) if (enable) { - //glEnable(GL_LINE_SMOOTH); - qDebug() << "TODO: GLBackendState.cpp:do_setStateAntialiasedLineEnable GL_LINE_SMOOTH"; + glEnable(GL_LINE_SMOOTH); } else { - //glDisable(GL_LINE_SMOOTH); - qDebug() << "TODO: GLBackendState.cpp:do_setStateAntialiasedLineEnable GL_LINE_SMOOTH"; + glDisable(GL_LINE_SMOOTH); } (void)CHECK_GL_ERROR(); +#endif _pipeline._stateCache.antialisedLineEnable = enable; } @@ -156,17 +156,17 @@ void GLBackend::do_setStateDepthBias(Vec2 bias) { if ((bias.x != _pipeline._stateCache.depthBias) || (bias.y != _pipeline._stateCache.depthBiasSlopeScale)) { if ((bias.x != 0.0f) || (bias.y != 0.0f)) { glEnable(GL_POLYGON_OFFSET_FILL); - //glEnable(GL_POLYGON_OFFSET_LINE); - qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthBias GL_POLYGON_OFFSET_LINE"; - //glEnable(GL_POLYGON_OFFSET_POINT); - qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthBias GL_POLYGON_OFFSET_POINT"; +#if !defined(USE_GLES) + glEnable(GL_POLYGON_OFFSET_LINE); + glEnable(GL_POLYGON_OFFSET_POINT); +#endif glPolygonOffset(bias.x, bias.y); } else { glDisable(GL_POLYGON_OFFSET_FILL); - //glDisable(GL_POLYGON_OFFSET_LINE); - qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthBias GL_POLYGON_OFFSET_LINE"; - //glDisable(GL_POLYGON_OFFSET_POINT); - qDebug() << "TODO: GLBackendState.cpp:do_setStateDepthBias GL_POLYGON_OFFSET_POINT"; +#if !defined(USE_GLES) + glDisable(GL_POLYGON_OFFSET_LINE); + glDisable(GL_POLYGON_OFFSET_POINT); +#endif } (void)CHECK_GL_ERROR(); @@ -190,7 +190,7 @@ void GLBackend::do_setStateDepthTest(State::DepthTest test) { glDepthFunc(COMPARISON_TO_GL[test.getFunction()]); } if (CHECK_GL_ERROR()) { - qDebug() << "DepthTest" << (test.isEnabled() ? "Enabled" : "Disabled") + qCDebug(gpulogging) << "DepthTest" << (test.isEnabled() ? "Enabled" : "Disabled") << "Mask=" << (test.getWriteMask() ? "Write" : "no Write") << "Func=" << test.getFunction() << "Raw=" << test.getRaw(); diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendTexture.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendTexture.cpp index 4be7682a4f..ace33766ce 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendTexture.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendTexture.cpp @@ -15,13 +15,56 @@ using namespace gpu; using namespace gpu::gl; -bool GLBackend::isTextureReady(const TexturePointer& texture) { - // DO not transfer the texture, this call is expected for rendering texture - GLTexture* object = syncGPUObject(texture, true); - qDebug() << "GLBackendTexture isTextureReady syncGPUObject"; - return object && object->isReady(); + +GLuint GLBackend::getTextureID(const TexturePointer& texture) { + GLTexture* object = syncGPUObject(texture); + + if (!object) { + return 0; + } + + return object->_id; } +GLTexture* GLBackend::syncGPUObject(const TexturePointer& texturePointer) { + const Texture& texture = *texturePointer; + // Special case external textures + if (TextureUsageType::EXTERNAL == texture.getUsageType()) { + Texture::ExternalUpdates updates = texture.getUpdates(); + if (!updates.empty()) { + Texture::ExternalRecycler recycler = texture.getExternalRecycler(); + Q_ASSERT(recycler); + // Discard any superfluous updates + while (updates.size() > 1) { + const auto& update = updates.front(); + // Superfluous updates will never have been read, but we want to ensure the previous + // writes to them are complete before they're written again, so return them with the + // same fences they arrived with. This can happen on any thread because no GL context + // work is involved + recycler(update.first, update.second); + updates.pop_front(); + } + + // The last texture remaining is the one we'll use to create the GLTexture + const auto& update = updates.front(); + // Check for a fence, and if it exists, inject a wait into the command stream, then destroy the fence + if (update.second) { + GLsync fence = static_cast(update.second); + glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); + glDeleteSync(fence); + } + + // Create the new texture object (replaces any previous texture object) + new GLExternalTexture(shared_from_this(), texture, update.first); + } + + // Return the texture object (if any) associated with the texture, without extensive logic + // (external textures are + return Backend::getGPUObject(texture); + } + + return nullptr; +} void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) { TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint); @@ -30,8 +73,7 @@ void GLBackend::do_generateTextureMips(const Batch& batch, size_t paramOffset) { } // DO not transfer the texture, this call is expected for rendering texture - GLTexture* object = syncGPUObject(resourceTexture, false); - qDebug() << "GLBackendTexture do_generateTextureMips syncGPUObject"; + GLTexture* object = syncGPUObject(resourceTexture); if (!object) { return; } diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackendTransform.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackendTransform.cpp index 3068e24dac..f286a5cca9 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackendTransform.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLBackendTransform.cpp @@ -15,7 +15,6 @@ using namespace gpu::gl; // Transform Stage void GLBackend::do_setModelTransform(const Batch& batch, size_t paramOffset) { - qDebug() << "do_setModelTransform"; } void GLBackend::do_setViewTransform(const Batch& batch, size_t paramOffset) { @@ -38,7 +37,7 @@ void GLBackend::do_setViewportTransform(const Batch& batch, size_t paramOffset) glViewport(vp.x, vp.y, vp.z, vp.w); // Where we assign the GL viewport - if (_stereo._enable) { + if (_stereo.isStereo()) { vp.z /= 2; if (_stereo._pass) { vp.x += vp.z; @@ -103,7 +102,7 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo if (_invalidView) { // Apply the correction - if (_viewIsCamera && _correction.correction != glm::mat4()) { + if (_viewIsCamera && (_viewCorrectionEnabled && _correction.correction != glm::mat4())) { // FIXME should I switch to using the camera correction buffer in Transform.slf and leave this out? Transform result; _view.mult(result, _view, _correction.correction); @@ -120,7 +119,7 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo size_t offset = _cameraUboSize * _cameras.size(); _cameraOffsets.push_back(TransformStageState::Pair(commandIndex, offset)); - if (stereo._enable) { + if (stereo.isStereo()) { #ifdef GPU_STEREO_CAMERA_BUFFER _cameras.push_back(CameraBufferElement(_camera.getEyeCamera(0, stereo, _view), _camera.getEyeCamera(1, stereo, _view))); #else @@ -152,7 +151,7 @@ void GLBackend::TransformStageState::update(size_t commandIndex, const StereoSta #ifdef GPU_STEREO_CAMERA_BUFFER bindCurrentCamera(0); #else - if (!stereo._enable) { + if (!stereo.isStereo()) { bindCurrentCamera(0); } #endif @@ -162,51 +161,11 @@ void GLBackend::TransformStageState::update(size_t commandIndex, const StereoSta void GLBackend::TransformStageState::bindCurrentCamera(int eye) const { if (_currentCameraOffset != INVALID_OFFSET) { - //qDebug() << "GLBackend::TransformStageState::bindCurrentCamera"; glBindBufferRange(GL_UNIFORM_BUFFER, TRANSFORM_CAMERA_SLOT, _cameraBuffer, _currentCameraOffset + eye * _cameraUboSize, sizeof(CameraBufferElement)); } } -void GLBackend::updateTransform(const Batch& batch) { - _transform.update(_commandIndex, _stereo); - - auto& drawCallInfoBuffer = batch.getDrawCallInfoBuffer(); - if (batch._currentNamedCall.empty()) { - (void)CHECK_GL_ERROR(); - auto& drawCallInfo = drawCallInfoBuffer[_currentDraw]; - glDisableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); // Make sure attrib array is disabled - (void)CHECK_GL_ERROR(); - GLint current_vao, current_vbo, maxVertexAtribs; - glGetIntegerv(GL_VERTEX_ARRAY_BINDING, ¤t_vao); - glGetIntegerv(GL_ARRAY_BUFFER_BINDING, ¤t_vbo); - glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAtribs); - glVertexAttribI4i(gpu::Stream::DRAW_CALL_INFO, drawCallInfo.index, drawCallInfo.unused, 0, 0); - - //int values[] = {drawCallInfo.index, drawCallInfo.unused}; - //glVertexAttribIPointer(gpu::Stream::DRAW_CALL_INFO, 2, GL_INT, 0, (const GLvoid *) values); - - /* - //glDisableVertexAttribArray currentvao 1 current vbo 0 - GL_INVALID_OPERATION is generated - a non-zero vertex array object is bound, - zero is bound to the GL_ARRAY_BUFFER buffer object binding point and - the pointer argument is not NULL. TRUE - */ - //qDebug() << "GLBackend::updateTransform glVertexAttribIPointer done"; - (void)CHECK_GL_ERROR(); - - } else { - //qDebug() << "GLBackend::updateTransform else"; - glEnableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); // Make sure attrib array is enabled - glBindBuffer(GL_ARRAY_BUFFER, _transform._drawCallInfoBuffer); - glVertexAttribIPointer(gpu::Stream::DRAW_CALL_INFO, 2, GL_UNSIGNED_SHORT, 0, - _transform._drawCallInfoOffsets[batch._currentNamedCall]); - glVertexAttribDivisor(gpu::Stream::DRAW_CALL_INFO, 1); - } - - (void)CHECK_GL_ERROR(); -} - void GLBackend::resetTransformStage() { - + glDisableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); + _transform._enabledDrawcallInfoBuffer = false; } diff --git a/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp b/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp index 150bb2be70..0bca9e86d9 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.cpp @@ -21,28 +21,27 @@ GLFramebuffer::~GLFramebuffer() { } } -bool GLFramebuffer::checkStatus(GLenum target) const { - bool result = false; +bool GLFramebuffer::checkStatus() const { switch (_status) { case GL_FRAMEBUFFER_COMPLETE: // Success ! - result = true; - break; + return true; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: - qCDebug(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT."; + qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT."; break; case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: - qCDebug(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT."; + qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT."; break; -/* TODO: case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: - qCDebug(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER."; - break; - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: - qCDebug(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER."; - break; */ + //case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: + // qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER."; + // break; + //case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: + // qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER."; + // break; case GL_FRAMEBUFFER_UNSUPPORTED: - qCDebug(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_UNSUPPORTED."; + qCWarning(gpugllogging) << "GLFramebuffer::syncGPUObject : Framebuffer not valid, GL_FRAMEBUFFER_UNSUPPORTED."; break; } - return result; + return false; } diff --git a/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h b/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h index a2fd0999f3..5a388e1965 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h +++ b/libraries/gpu-gles/src/gpu/gl/GLFramebuffer.h @@ -64,7 +64,7 @@ public: protected: GLenum _status { GL_FRAMEBUFFER_COMPLETE }; virtual void update() = 0; - bool checkStatus(GLenum target) const; + bool checkStatus() const; GLFramebuffer(const std::weak_ptr& backend, const Framebuffer& framebuffer, GLuint id) : GLObject(backend, framebuffer, id) {} ~GLFramebuffer(); diff --git a/libraries/gpu-gles/src/gpu/gl/GLPipeline.cpp b/libraries/gpu-gles/src/gpu/gl/GLPipeline.cpp index 09c09de353..ebf1a55232 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLPipeline.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLPipeline.cpp @@ -51,10 +51,7 @@ GLPipeline* GLPipeline::sync(GLBackend& backend, const Pipeline& pipeline) { // Special case for view correction matrices, any pipeline that declares the correction buffer // uniform will automatically have it provided without any client code necessary. // Required for stable lighting in the HMD. - //CLIMAX_MERGE_START - //getbuffers() doesnt exist anymore.. use get uniformbuffers()? object->_cameraCorrection = shader->getUniformBuffers().findLocation("cameraCorrectionBuffer"); - //CLIMAX_MERGE_END object->_program = programObject; object->_state = stateObject; diff --git a/libraries/gpu-gles/src/gpu/gl/GLShader.cpp b/libraries/gpu-gles/src/gpu/gl/GLShader.cpp index b728010470..7ed9121978 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLShader.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLShader.cpp @@ -30,126 +30,6 @@ GLShader::~GLShader() { } } -// GLSL version -static const std::string glslVersion { - "#version 310 es" -}; - -// Shader domain -static const size_t NUM_SHADER_DOMAINS = 3; - -// GL Shader type enums -// Must match the order of type specified in gpu::Shader::Type -static const std::array SHADER_DOMAINS { { - GL_VERTEX_SHADER, - GL_FRAGMENT_SHADER, - //GL_GEOMETRY_SHADER, -} }; - -// Domain specific defines -// Must match the order of type specified in gpu::Shader::Type -static const std::array DOMAIN_DEFINES { { - "#define GPU_VERTEX_SHADER", - "#define GPU_PIXEL_SHADER", - "#define GPU_GEOMETRY_SHADER", -} }; - -// Stereo specific defines -static const std::string stereoVersion { -#ifdef GPU_STEREO_DRAWCALL_INSTANCED - "#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_INSTANCED\n#define GPU_TRANSFORM_STEREO_SPLIT_SCREEN" -#endif -#ifdef GPU_STEREO_DRAWCALL_DOUBLED -#ifdef GPU_STEREO_CAMERA_BUFFER - "#define GPU_TRANSFORM_IS_STEREO\n#define GPU_TRANSFORM_STEREO_CAMERA\n#define GPU_TRANSFORM_STEREO_CAMERA_ATTRIBUTED" -#else - "#define GPU_TRANSFORM_IS_STEREO" -#endif -#endif -}; - -// Versions specific of the shader -static const std::array VERSION_DEFINES { { - "", - stereoVersion -} }; - -GLShader* compileBackendShader(GLBackend& backend, const Shader& shader) { - // Any GLSLprogram ? normally yes... - const std::string& shaderSource = shader.getSource().getCode(); - GLenum shaderDomain = SHADER_DOMAINS[shader.getType()]; - GLShader::ShaderObjects shaderObjects; - - for (int version = 0; version < GLShader::NumVersions; version++) { - auto& shaderObject = shaderObjects[version]; - std::string shaderDefines = glslVersion + "\n" + DOMAIN_DEFINES[shader.getType()] + "\n" + VERSION_DEFINES[version] - + "\n" + "#extension GL_EXT_texture_buffer : enable" - + "\nprecision lowp float; // check precision 2" - + "\nprecision lowp samplerBuffer;" - + "\nprecision lowp sampler2DShadow;"; - // TODO Delete bool result = compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, shaderObject.glprogram); - std::string error; - - -#ifdef SEPARATE_PROGRAM - bool result = ::gl::compileShader(shaderDomain, shaderSource.c_str(), shaderDefines.c_str(), shaderObject.glshader, shaderObject.glprogram, error); -#else - bool result = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, error); -#endif - if (!result) { - qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Shader didn't compile:\n" << error.c_str(); - return nullptr; - } - } - - // So far so good, the shader is created successfully - GLShader* object = new GLShader(backend.shared_from_this()); - object->_shaderObjects = shaderObjects; - - return object; -} - -GLShader* compileBackendProgram(GLBackend& backend, const Shader& program) { - if (!program.isProgram()) { - return nullptr; - } - - GLShader::ShaderObjects programObjects; - - for (int version = 0; version < GLShader::NumVersions; version++) { - auto& programObject = programObjects[version]; - - // Let's go through every shaders and make sure they are ready to go - std::vector< GLuint > shaderGLObjects; - for (auto subShader : program.getShaders()) { - auto object = GLShader::sync(backend, *subShader); - if (object) { - shaderGLObjects.push_back(object->_shaderObjects[version].glshader); - } else { - qCDebug(gpugllogging) << "GLShader::compileBackendProgram - One of the shaders of the program is not compiled?"; - return nullptr; - } - } - - std::string error; - GLuint glprogram = ::gl::compileProgram(shaderGLObjects, error); - if (glprogram == 0) { - qCWarning(gpugllogging) << error.c_str(); - return nullptr; - } - - programObject.glprogram = glprogram; - - makeProgramBindings(programObject); - } - - // So far so good, the program versions have all been created successfully - GLShader* object = new GLShader(backend.shared_from_this()); - object->_shaderObjects = programObjects; - - return object; -} - GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) { GLShader* object = Backend::getGPUObject(shader); @@ -159,13 +39,13 @@ GLShader* GLShader::sync(GLBackend& backend, const Shader& shader) { } // need to have a gpu object? if (shader.isProgram()) { - GLShader* tempObject = compileBackendProgram(backend, shader); + GLShader* tempObject = backend.compileBackendProgram(shader); if (tempObject) { object = tempObject; Backend::setGPUObject(shader, object); } } else if (shader.isDomain()) { - GLShader* tempObject = compileBackendShader(backend, shader); + GLShader* tempObject = backend.compileBackendShader(shader); if (tempObject) { object = tempObject; Backend::setGPUObject(shader, object); @@ -189,21 +69,21 @@ bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::Bin auto& shaderObject = object->_shaderObjects[version]; if (shaderObject.glprogram) { Shader::SlotSet buffers; - makeUniformBlockSlots(shaderObject.glprogram, slotBindings, buffers); + backend.makeUniformBlockSlots(shaderObject.glprogram, slotBindings, buffers); Shader::SlotSet uniforms; Shader::SlotSet textures; Shader::SlotSet samplers; - makeUniformSlots(shaderObject.glprogram, slotBindings, uniforms, textures, samplers); + backend.makeUniformSlots(shaderObject.glprogram, slotBindings, uniforms, textures, samplers); Shader::SlotSet resourceBuffers; - makeResourceBufferSlots(shaderObject.glprogram, slotBindings, resourceBuffers); + backend.makeResourceBufferSlots(shaderObject.glprogram, slotBindings, resourceBuffers); Shader::SlotSet inputs; - makeInputSlots(shaderObject.glprogram, slotBindings, inputs); + backend.makeInputSlots(shaderObject.glprogram, slotBindings, inputs); Shader::SlotSet outputs; - makeOutputSlots(shaderObject.glprogram, slotBindings, outputs); + backend.makeOutputSlots(shaderObject.glprogram, slotBindings, outputs); // Define the public slots only from the default version if (version == 0) { @@ -222,3 +102,5 @@ bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::Bin return true; } + + diff --git a/libraries/gpu-gles/src/gpu/gl/GLShader.h b/libraries/gpu-gles/src/gpu/gl/GLShader.h index e03b487a60..dcf2dc330d 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLShader.h +++ b/libraries/gpu-gles/src/gpu/gl/GLShader.h @@ -12,6 +12,13 @@ namespace gpu { namespace gl { +struct ShaderObject { + GLuint glshader { 0 }; + GLuint glprogram { 0 }; + GLint transformCameraSlot { -1 }; + GLint transformObjectSlot { -1 }; +}; + class GLShader : public GPUObject { public: static GLShader* sync(GLBackend& backend, const Shader& shader); diff --git a/libraries/gpu-gles/src/gpu/gl/GLShared.cpp b/libraries/gpu-gles/src/gpu/gl/GLShared.cpp index 5d340889a6..f818a221b2 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLShared.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLShared.cpp @@ -17,6 +17,7 @@ Q_LOGGING_CATEGORY(gpugllogging, "hifi.gpu.gl") Q_LOGGING_CATEGORY(trace_render_gpu_gl, "trace.render.gpu.gl") +Q_LOGGING_CATEGORY(trace_render_gpu_gl_detail, "trace.render.gpu.gl.detail") namespace gpu { namespace gl { @@ -27,22 +28,25 @@ bool checkGLError(const char* name) { } else { switch (error) { case GL_INVALID_ENUM: - qCDebug(gpugllogging) << "GLBackend::" << name << ": An unacceptable value is specified for an enumerated argument.The offending command is ignored and has no other side effect than to set the error flag."; + qCWarning(gpugllogging) << "GLBackend::" << name << ": An unacceptable value is specified for an enumerated argument.The offending command is ignored and has no other side effect than to set the error flag."; break; case GL_INVALID_VALUE: - qCDebug(gpugllogging) << "GLBackend" << name << ": A numeric argument is out of range.The offending command is ignored and has no other side effect than to set the error flag"; + qCWarning(gpugllogging) << "GLBackend" << name << ": A numeric argument is out of range.The offending command is ignored and has no other side effect than to set the error flag"; break; case GL_INVALID_OPERATION: - qCDebug(gpugllogging) << "GLBackend" << name << ": The specified operation is not allowed in the current state.The offending command is ignored and has no other side effect than to set the error flag.."; + qCWarning(gpugllogging) << "GLBackend" << name << ": The specified operation is not allowed in the current state.The offending command is ignored and has no other side effect than to set the error flag.."; break; case GL_INVALID_FRAMEBUFFER_OPERATION: - qCDebug(gpugllogging) << "GLBackend" << name << ": The framebuffer object is not complete.The offending command is ignored and has no other side effect than to set the error flag."; + qCWarning(gpugllogging) << "GLBackend" << name << ": The framebuffer object is not complete.The offending command is ignored and has no other side effect than to set the error flag."; break; case GL_OUT_OF_MEMORY: - qCDebug(gpugllogging) << "GLBackend" << name << ": There is not enough memory left to execute the command.The state of the GL is undefined, except for the state of the error flags, after this error is recorded."; + qCWarning(gpugllogging) << "GLBackend" << name << ": There is not enough memory left to execute the command.The state of the GL is undefined, except for the state of the error flags, after this error is recorded."; break; - default: - qCDebug(gpugllogging) << "GLBackend" << name << ": Unknown error: " << error; + case GL_STACK_UNDERFLOW: + qCWarning(gpugllogging) << "GLBackend" << name << ": An attempt has been made to perform an operation that would cause an internal stack to underflow."; + break; + case GL_STACK_OVERFLOW: + qCWarning(gpugllogging) << "GLBackend" << name << ": An attempt has been made to perform an operation that would cause an internal stack to overflow."; break; } return true; @@ -50,12 +54,8 @@ bool checkGLError(const char* name) { } bool checkGLErrorDebug(const char* name) { -#ifdef DEBUG + // FIXME, disable in debug mode when near release return checkGLError(name); -#else - Q_UNUSED(name); - return false; -#endif } gpu::Size getFreeDedicatedMemory() { @@ -86,43 +86,6 @@ gpu::Size getFreeDedicatedMemory() { return result; } -gpu::Size getDedicatedMemory() { - static Size dedicatedMemory { 0 }; - static std::once_flag once; - std::call_once(once, [&] { - if (!dedicatedMemory) { - GLint atiGpuMemory[4]; - // not really total memory, but close enough if called early enough in the application lifecycle - //glGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, atiGpuMemory); - qDebug() << "TODO: GLShared.cpp.cpp:initInput GL_TEXTURE_FREE_MEMORY_ATI"; - if (GL_NO_ERROR == glGetError()) { - dedicatedMemory = KB_TO_BYTES(atiGpuMemory[0]); - } - } - - if (!dedicatedMemory) { - GLint nvGpuMemory { 0 }; - qDebug() << "TODO: GLShared.cpp.cpp:initInput GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX"; - //glGetIntegerv(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &nvGpuMemory); - if (GL_NO_ERROR == glGetError()) { - dedicatedMemory = KB_TO_BYTES(nvGpuMemory); - } - } - - if (!dedicatedMemory) { - auto gpuIdent = GPUIdent::getInstance(); - if (gpuIdent && gpuIdent->isValid()) { - dedicatedMemory = MB_TO_BYTES(gpuIdent->getMemory()); - } - } - }); - - return dedicatedMemory; -} - - - - ComparisonFunction comparisonFuncFromGL(GLenum func) { if (func == GL_NEVER) { return NEVER; @@ -219,21 +182,17 @@ State::BlendArg blendArgFromGL(GLenum blendArg) { void getCurrentGLState(State::Data& state) { { - GLint modes[2]; + //GLint modes[2]; //glGetIntegerv(GL_POLYGON_MODE, modes); - qDebug() << "TODO: GLShared.cpp:getCurrentGLState GL_POLYGON_MODE"; - qDebug() << "TODO: GLShared.cpp:getCurrentGLState GL_FILL"; - qDebug() << "TODO: GLShared.cpp:getCurrentGLState GL_LINE"; - - if (modes[0] == 0 /*GL_FILL*/) { - state.fillMode = State::FILL_FACE; - } else { - if (modes[0] == 0 /*GL_LINE*/) { - state.fillMode = State::FILL_LINE; - } else { - state.fillMode = State::FILL_POINT; - } - } + //if (modes[0] == GL_FILL) { + // state.fillMode = State::FILL_FACE; + //} else { + // if (modes[0] == GL_LINE) { + // state.fillMode = State::FILL_LINE; + // } else { + // state.fillMode = State::FILL_POINT; + // } + //} } { if (glIsEnabled(GL_CULL_FACE)) { @@ -248,15 +207,10 @@ void getCurrentGLState(State::Data& state) { GLint winding; glGetIntegerv(GL_FRONT_FACE, &winding); state.frontFaceClockwise = (winding == GL_CW); - //state.depthClampEnable = glIsEnabled(GL_DEPTH_CLAMP); - qDebug() << "TODO: GLShared.cpp.cpp:getCurrentGLState GL_DEPTH_CLAMP"; + state.depthClampEnable = false; //glIsEnabled(GL_DEPTH_CLAMP_EXT); state.scissorEnable = glIsEnabled(GL_SCISSOR_TEST); - //state.multisampleEnable = glIsEnabled(GL_MULTISAMPLE); - qDebug() << "TODO: GLShared.cpp.cpp:getCurrentGLState GL_MULTISAMPLE"; - - //state.antialisedLineEnable = glIsEnabled(GL_LINE_SMOOTH); - qDebug() << "TODO: GLShared.cpp.cpp:getCurrentGLState GL_LINE_SMOOTH"; - + state.multisampleEnable = false; //glIsEnabled(GL_MULTISAMPLE_EXT); + state.antialisedLineEnable = false; //glIsEnabled(GL_LINE_SMOOTH); } { if (glIsEnabled(GL_POLYGON_OFFSET_FILL)) { @@ -354,504 +308,6 @@ void getCurrentGLState(State::Data& state) { } -class ElementResource { -public: - gpu::Element _element; - uint16 _resource; - - ElementResource(Element&& elem, uint16 resource) : _element(elem), _resource(resource) {} -}; - -ElementResource getFormatFromGLUniform(GLenum gltype) { - switch (gltype) { - case GL_FLOAT: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - /* - case GL_DOUBLE: return ElementResource(Element(SCALAR, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE_VEC2: return ElementResource(Element(VEC2, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE_VEC3: return ElementResource(Element(VEC3, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_DOUBLE_VEC4: return ElementResource(Element(VEC4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - */ - case GL_INT: return ElementResource(Element(SCALAR, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_INT_VEC2: return ElementResource(Element(VEC2, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_INT_VEC3: return ElementResource(Element(VEC3, gpu::INT32, UNIFORM), Resource::BUFFER); - case GL_INT_VEC4: return ElementResource(Element(VEC4, gpu::INT32, UNIFORM), Resource::BUFFER); - - case GL_UNSIGNED_INT: return ElementResource(Element(SCALAR, gpu::UINT32, UNIFORM), Resource::BUFFER); -#if defined(Q_OS_WIN) - case GL_UNSIGNED_INT_VEC2: return ElementResource(Element(VEC2, gpu::UINT32, UNIFORM), Resource::BUFFER); - case GL_UNSIGNED_INT_VEC3: return ElementResource(Element(VEC3, gpu::UINT32, UNIFORM), Resource::BUFFER); - case GL_UNSIGNED_INT_VEC4: return ElementResource(Element(VEC4, gpu::UINT32, UNIFORM), Resource::BUFFER); -#endif - - case GL_BOOL: return ElementResource(Element(SCALAR, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_BOOL_VEC2: return ElementResource(Element(VEC2, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_BOOL_VEC3: return ElementResource(Element(VEC3, gpu::BOOL, UNIFORM), Resource::BUFFER); - case GL_BOOL_VEC4: return ElementResource(Element(VEC4, gpu::BOOL, UNIFORM), Resource::BUFFER); - - - case GL_FLOAT_MAT2: return ElementResource(Element(gpu::MAT2, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_MAT3: return ElementResource(Element(MAT3, gpu::FLOAT, UNIFORM), Resource::BUFFER); - case GL_FLOAT_MAT4: return ElementResource(Element(MAT4, gpu::FLOAT, UNIFORM), Resource::BUFFER); - - /* {GL_FLOAT_MAT2x3 mat2x3}, - {GL_FLOAT_MAT2x4 mat2x4}, - {GL_FLOAT_MAT3x2 mat3x2}, - {GL_FLOAT_MAT3x4 mat3x4}, - {GL_FLOAT_MAT4x2 mat4x2}, - {GL_FLOAT_MAT4x3 mat4x3}, - {GL_DOUBLE_MAT2 dmat2}, - {GL_DOUBLE_MAT3 dmat3}, - {GL_DOUBLE_MAT4 dmat4}, - {GL_DOUBLE_MAT2x3 dmat2x3}, - {GL_DOUBLE_MAT2x4 dmat2x4}, - {GL_DOUBLE_MAT3x2 dmat3x2}, - {GL_DOUBLE_MAT3x4 dmat3x4}, - {GL_DOUBLE_MAT4x2 dmat4x2}, - {GL_DOUBLE_MAT4x3 dmat4x3}, - */ - - //qDebug() << "TODO: GLShared.cpp.cpp:ElementResource GL_SAMPLER_1D"; - //case GL_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D); - case GL_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D); - - case GL_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_3D); - case GL_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_CUBE); - -#if defined(Q_OS_WIN) - case GL_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); - case GL_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_1D_ARRAY); - case GL_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER), Resource::TEXTURE_2D_ARRAY); - case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); -#endif - - case GL_SAMPLER_2D_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D); -#if defined(Q_OS_WIN) - case GL_SAMPLER_CUBE_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_CUBE); - - case GL_SAMPLER_2D_ARRAY_SHADOW: return ElementResource(Element(SCALAR, gpu::FLOAT, SAMPLER_SHADOW), Resource::TEXTURE_2D_ARRAY); -#endif - - // {GL_SAMPLER_1D_SHADOW sampler1DShadow}, - // {GL_SAMPLER_1D_ARRAY_SHADOW sampler1DArrayShadow}, - - // {GL_SAMPLER_BUFFER samplerBuffer}, - // {GL_SAMPLER_2D_RECT sampler2DRect}, - // {GL_SAMPLER_2D_RECT_SHADOW sampler2DRectShadow}, - -#if defined(Q_OS_WIN) - case GL_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D); - case GL_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D); - case GL_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); - case GL_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_3D); - case GL_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_CUBE); - - case GL_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); - case GL_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); - case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::INT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); - - // {GL_INT_SAMPLER_BUFFER isamplerBuffer}, - // {GL_INT_SAMPLER_2D_RECT isampler2DRect}, - - case GL_UNSIGNED_INT_SAMPLER_1D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D); - case GL_UNSIGNED_INT_SAMPLER_2D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D); - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D); - case GL_UNSIGNED_INT_SAMPLER_3D: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_3D); - case GL_UNSIGNED_INT_SAMPLER_CUBE: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_CUBE); - - case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_1D_ARRAY); - case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER), Resource::TEXTURE_2D_ARRAY); - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: return ElementResource(Element(SCALAR, gpu::UINT32, SAMPLER_MULTISAMPLE), Resource::TEXTURE_2D_ARRAY); -#endif - // {GL_UNSIGNED_INT_SAMPLER_BUFFER usamplerBuffer}, - // {GL_UNSIGNED_INT_SAMPLER_2D_RECT usampler2DRect}, - /* - {GL_IMAGE_1D image1D}, - {GL_IMAGE_2D image2D}, - {GL_IMAGE_3D image3D}, - {GL_IMAGE_2D_RECT image2DRect}, - {GL_IMAGE_CUBE imageCube}, - {GL_IMAGE_BUFFER imageBuffer}, - {GL_IMAGE_1D_ARRAY image1DArray}, - {GL_IMAGE_2D_ARRAY image2DArray}, - {GL_IMAGE_2D_MULTISAMPLE image2DMS}, - {GL_IMAGE_2D_MULTISAMPLE_ARRAY image2DMSArray}, - {GL_INT_IMAGE_1D iimage1D}, - {GL_INT_IMAGE_2D iimage2D}, - {GL_INT_IMAGE_3D iimage3D}, - {GL_INT_IMAGE_2D_RECT iimage2DRect}, - {GL_INT_IMAGE_CUBE iimageCube}, - {GL_INT_IMAGE_BUFFER iimageBuffer}, - {GL_INT_IMAGE_1D_ARRAY iimage1DArray}, - {GL_INT_IMAGE_2D_ARRAY iimage2DArray}, - {GL_INT_IMAGE_2D_MULTISAMPLE iimage2DMS}, - {GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY iimage2DMSArray}, - {GL_UNSIGNED_INT_IMAGE_1D uimage1D}, - {GL_UNSIGNED_INT_IMAGE_2D uimage2D}, - {GL_UNSIGNED_INT_IMAGE_3D uimage3D}, - {GL_UNSIGNED_INT_IMAGE_2D_RECT uimage2DRect}, - {GL_UNSIGNED_INT_IMAGE_CUBE uimageCube},+ [0] {_name="fInnerRadius" _location=0 _element={_semantic=15 '\xf' _dimension=0 '\0' _type=0 '\0' } } gpu::Shader::Slot - - {GL_UNSIGNED_INT_IMAGE_BUFFER uimageBuffer}, - {GL_UNSIGNED_INT_IMAGE_1D_ARRAY uimage1DArray}, - {GL_UNSIGNED_INT_IMAGE_2D_ARRAY uimage2DArray}, - {GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE uimage2DMS}, - {GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY uimage2DMSArray}, - {GL_UNSIGNED_INT_ATOMIC_COUNTER atomic_uint} - */ - default: - return ElementResource(Element(), Resource::BUFFER); - } - -}; - -int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, - Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) { - GLint uniformsCount = 0; - - glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount); - - for (int i = 0; i < uniformsCount; i++) { - const GLint NAME_LENGTH = 256; - GLchar name[NAME_LENGTH]; - GLint length = 0; - GLint size = 0; - GLenum type = 0; - glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name); - GLint location = glGetUniformLocation(glprogram, name); - const GLint INVALID_UNIFORM_LOCATION = -1; - - // Try to make sense of the gltype - auto elementResource = getFormatFromGLUniform(type); - - // The uniform as a standard var type - if (location != INVALID_UNIFORM_LOCATION) { - // Let's make sure the name doesn't contains an array element - std::string sname(name); - auto foundBracket = sname.find_first_of('['); - if (foundBracket != std::string::npos) { - // std::string arrayname = sname.substr(0, foundBracket); - - if (sname[foundBracket + 1] == '0') { - sname = sname.substr(0, foundBracket); - } else { - // skip this uniform since it's not the first element of an array - continue; - } - } - - if (elementResource._resource == Resource::BUFFER) { - uniforms.insert(Shader::Slot(sname, location, elementResource._element, elementResource._resource)); - } else { - // For texture/Sampler, the location is the actual binding value - GLint binding = -1; - glGetUniformiv(glprogram, location, &binding); - - auto requestedBinding = slotBindings.find(std::string(sname)); - if (requestedBinding != slotBindings.end()) { - if (binding != (*requestedBinding)._location) { - binding = (*requestedBinding)._location; - glProgramUniform1i(glprogram, location, binding); - } - } - - textures.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); - samplers.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); - } - } - } - - return uniformsCount; -} - -const GLint UNUSED_SLOT = -1; -bool isUnusedSlot(GLint binding) { - return (binding == UNUSED_SLOT); -} - -int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) { - GLint buffersCount = 0; - - glGetProgramiv(glprogram, GL_ACTIVE_UNIFORM_BLOCKS, &buffersCount); - - // fast exit - if (buffersCount == 0) { - return 0; - } - - GLint maxNumUniformBufferSlots = 0; - glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxNumUniformBufferSlots); - std::vector uniformBufferSlotMap(maxNumUniformBufferSlots, -1); - - struct UniformBlockInfo { - using Vector = std::vector; - const GLuint index{ 0 }; - const std::string name; - GLint binding{ -1 }; - GLint size{ 0 }; - - static std::string getName(GLuint glprogram, GLuint i) { - static const GLint NAME_LENGTH = 256; - GLint length = 0; - GLchar nameBuffer[NAME_LENGTH]; - glGetActiveUniformBlockiv(glprogram, i, GL_UNIFORM_BLOCK_NAME_LENGTH, &length); - glGetActiveUniformBlockName(glprogram, i, NAME_LENGTH, &length, nameBuffer); - return std::string(nameBuffer); - } - - UniformBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) { - glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_BINDING, &binding); - glGetActiveUniformBlockiv(glprogram, index, GL_UNIFORM_BLOCK_DATA_SIZE, &size); - } - }; - - UniformBlockInfo::Vector uniformBlocks; - uniformBlocks.reserve(buffersCount); - for (int i = 0; i < buffersCount; i++) { - uniformBlocks.push_back(UniformBlockInfo(glprogram, i)); - } - - for (auto& info : uniformBlocks) { - auto requestedBinding = slotBindings.find(info.name); - if (requestedBinding != slotBindings.end()) { - info.binding = (*requestedBinding)._location; - glUniformBlockBinding(glprogram, info.index, info.binding); - uniformBufferSlotMap[info.binding] = info.index; - } - } - - for (auto& info : uniformBlocks) { - if (slotBindings.count(info.name)) { - continue; - } - - // If the binding is 0, or the binding maps to an already used binding - if (info.binding == 0 || uniformBufferSlotMap[info.binding] != UNUSED_SLOT) { - // If no binding was assigned then just do it finding a free slot - auto slotIt = std::find_if(uniformBufferSlotMap.begin(), uniformBufferSlotMap.end(), isUnusedSlot); - if (slotIt != uniformBufferSlotMap.end()) { - info.binding = slotIt - uniformBufferSlotMap.begin(); - glUniformBlockBinding(glprogram, info.index, info.binding); - } else { - // This should neve happen, an active ubo cannot find an available slot among the max available?! - info.binding = -1; - } - } - - uniformBufferSlotMap[info.binding] = info.index; - } - - for (auto& info : uniformBlocks) { - static const Element element(SCALAR, gpu::UINT32, gpu::UNIFORM_BUFFER); - buffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size)); - } - return buffersCount; -} -//CLIMAX_MERGE_START -//This has been copied over from gl45backendshader.cpp -int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) { - GLint buffersCount = 0; - glGetProgramInterfaceiv(glprogram, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &buffersCount); - - // fast exit - if (buffersCount == 0) { - return 0; - } - - GLint maxNumResourceBufferSlots = 0; - glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &maxNumResourceBufferSlots); - std::vector resourceBufferSlotMap(maxNumResourceBufferSlots, -1); - - struct ResourceBlockInfo { - using Vector = std::vector; - const GLuint index{ 0 }; - const std::string name; - GLint binding{ -1 }; - GLint size{ 0 }; - - static std::string getName(GLuint glprogram, GLuint i) { - static const GLint NAME_LENGTH = 256; - GLint length = 0; - GLchar nameBuffer[NAME_LENGTH]; - glGetProgramResourceName(glprogram, GL_SHADER_STORAGE_BLOCK, i, NAME_LENGTH, &length, nameBuffer); - return std::string(nameBuffer); - } - - ResourceBlockInfo(GLuint glprogram, GLuint i) : index(i), name(getName(glprogram, i)) { - GLenum props[2] = { GL_BUFFER_BINDING, GL_BUFFER_DATA_SIZE}; - glGetProgramResourceiv(glprogram, GL_SHADER_STORAGE_BLOCK, i, 2, props, 2, nullptr, &binding); - } - }; - - ResourceBlockInfo::Vector resourceBlocks; - resourceBlocks.reserve(buffersCount); - for (int i = 0; i < buffersCount; i++) { - resourceBlocks.push_back(ResourceBlockInfo(glprogram, i)); - } - - for (auto& info : resourceBlocks) { - auto requestedBinding = slotBindings.find(info.name); - if (requestedBinding != slotBindings.end()) { - info.binding = (*requestedBinding)._location; - glUniformBlockBinding(glprogram, info.index, info.binding); - resourceBufferSlotMap[info.binding] = info.index; - } - } - - for (auto& info : resourceBlocks) { - if (slotBindings.count(info.name)) { - continue; - } - - // If the binding is -1, or the binding maps to an already used binding - if (info.binding == -1 || !isUnusedSlot(resourceBufferSlotMap[info.binding])) { - // If no binding was assigned then just do it finding a free slot - auto slotIt = std::find_if(resourceBufferSlotMap.begin(), resourceBufferSlotMap.end(), isUnusedSlot); - if (slotIt != resourceBufferSlotMap.end()) { - info.binding = slotIt - resourceBufferSlotMap.begin(); - glUniformBlockBinding(glprogram, info.index, info.binding); - } else { - // This should never happen, an active ssbo cannot find an available slot among the max available?! - info.binding = -1; - } - } - - resourceBufferSlotMap[info.binding] = info.index; - } - - for (auto& info : resourceBlocks) { - static const Element element(SCALAR, gpu::UINT32, gpu::RESOURCE_BUFFER); - resourceBuffers.insert(Shader::Slot(info.name, info.binding, element, Resource::BUFFER, info.size)); - } - return buffersCount; -} -//CLIMAX_MERGE_END - -int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs) { - GLint inputsCount = 0; - - glGetProgramiv(glprogram, GL_ACTIVE_ATTRIBUTES, &inputsCount); - - for (int i = 0; i < inputsCount; i++) { - const GLint NAME_LENGTH = 256; - GLchar name[NAME_LENGTH]; - GLint length = 0; - GLint size = 0; - GLenum type = 0; - glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); - - GLint binding = glGetAttribLocation(glprogram, name); - - auto elementResource = getFormatFromGLUniform(type); - inputs.insert(Shader::Slot(name, binding, elementResource._element, -1)); - } - - return inputsCount; -} - -int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs) { - /* GLint outputsCount = 0; - - glGetProgramiv(glprogram, GL_ACTIVE_, &outputsCount); - - for (int i = 0; i < inputsCount; i++) { - const GLint NAME_LENGTH = 256; - GLchar name[NAME_LENGTH]; - GLint length = 0; - GLint size = 0; - GLenum type = 0; - glGetActiveAttrib(glprogram, i, NAME_LENGTH, &length, &size, &type, name); - - auto element = getFormatFromGLUniform(type); - outputs.insert(Shader::Slot(name, i, element)); - } - */ - return 0; //inputsCount; -} - -void makeProgramBindings(ShaderObject& shaderObject) { - if (!shaderObject.glprogram) { - return; - } - GLuint glprogram = shaderObject.glprogram; - GLint loc = -1; - - //Check for gpu specific attribute slotBindings - loc = glGetAttribLocation(glprogram, "inPosition"); - if (loc >= 0 && loc != gpu::Stream::POSITION) { - glBindAttribLocation(glprogram, gpu::Stream::POSITION, "inPosition"); - } - - loc = glGetAttribLocation(glprogram, "inNormal"); - if (loc >= 0 && loc != gpu::Stream::NORMAL) { - glBindAttribLocation(glprogram, gpu::Stream::NORMAL, "inNormal"); - } - - loc = glGetAttribLocation(glprogram, "inColor"); - if (loc >= 0 && loc != gpu::Stream::COLOR) { - glBindAttribLocation(glprogram, gpu::Stream::COLOR, "inColor"); - } - - loc = glGetAttribLocation(glprogram, "inTexCoord0"); - if (loc >= 0 && loc != gpu::Stream::TEXCOORD) { - glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "inTexCoord0"); - } - - loc = glGetAttribLocation(glprogram, "inTangent"); - if (loc >= 0 && loc != gpu::Stream::TANGENT) { - glBindAttribLocation(glprogram, gpu::Stream::TANGENT, "inTangent"); - } - - loc = glGetAttribLocation(glprogram, "inTexCoord1"); - if (loc >= 0 && loc != gpu::Stream::TEXCOORD1) { - glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD1, "inTexCoord1"); - } - - loc = glGetAttribLocation(glprogram, "inSkinClusterIndex"); - if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_INDEX) { - glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_INDEX, "inSkinClusterIndex"); - } - - loc = glGetAttribLocation(glprogram, "inSkinClusterWeight"); - if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_WEIGHT) { - glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_WEIGHT, "inSkinClusterWeight"); - } - - loc = glGetAttribLocation(glprogram, "_drawCallInfo"); - if (loc >= 0 && loc != gpu::Stream::DRAW_CALL_INFO) { - glBindAttribLocation(glprogram, gpu::Stream::DRAW_CALL_INFO, "_drawCallInfo"); - } - - // Link again to take into account the assigned attrib location - glLinkProgram(glprogram); - - GLint linked = 0; - glGetProgramiv(glprogram, GL_LINK_STATUS, &linked); - if (!linked) { - qCWarning(gpugllogging) << "GLShader::makeBindings - failed to link after assigning slotBindings?"; - } - - // now assign the ubo binding, then DON't relink! - - //Check for gpu specific uniform slotBindings - loc = glGetProgramResourceIndex(glprogram, GL_SHADER_STORAGE_BLOCK, "transformObjectBuffer"); - if (loc >= 0) { - // FIXME GLES - // glShaderStorageBlockBinding(glprogram, loc, TRANSFORM_OBJECT_SLOT); - shaderObject.transformObjectSlot = TRANSFORM_OBJECT_SLOT; - } - - loc = glGetUniformBlockIndex(glprogram, "transformCameraBuffer"); - if (loc >= 0) { - glUniformBlockBinding(glprogram, loc, TRANSFORM_CAMERA_SLOT); - shaderObject.transformCameraSlot = TRANSFORM_CAMERA_SLOT; - } - - (void)CHECK_GL_ERROR(); -} - void serverWait() { auto fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); assert(fence); diff --git a/libraries/gpu-gles/src/gpu/gl/GLShared.h b/libraries/gpu-gles/src/gpu/gl/GLShared.h index 54209b106d..dcce896a37 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLShared.h +++ b/libraries/gpu-gles/src/gpu/gl/GLShared.h @@ -16,18 +16,18 @@ Q_DECLARE_LOGGING_CATEGORY(gpugllogging) Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl) +Q_DECLARE_LOGGING_CATEGORY(trace_render_gpu_gl_detail) + +#define BUFFER_OFFSET(bytes) ((GLubyte*) nullptr + (bytes)) namespace gpu { namespace gl { - static const GLint TRANSFORM_OBJECT_SLOT { 14 }; // SSBO binding slot - // Create a fence and inject a GPU wait on the fence void serverWait(); // Create a fence and synchronously wait on the fence void clientWait(); -gpu::Size getDedicatedMemory(); gpu::Size getFreeDedicatedMemory(); ComparisonFunction comparisonFuncFromGL(GLenum func); State::StencilOp stencilOpFromGL(GLenum stencilOp); @@ -35,25 +35,6 @@ State::BlendOp blendOpFromGL(GLenum blendOp); State::BlendArg blendArgFromGL(GLenum blendArg); void getCurrentGLState(State::Data& state); -struct ShaderObject { - GLuint glshader { 0 }; - GLuint glprogram { 0 }; - GLint transformCameraSlot { -1 }; - GLint transformObjectSlot { -1 }; -}; - -int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, - Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers); -int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers); -int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs); -int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs); -//CLIMAX_MERGE_START -//makeResourceBufferSlots has been added to glbacked as a virtual function and is being used in gl42 and gl45 overrides. -//Since these files dont exist in the andoid version create a stub here. -int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& resourceBuffers); -//CLIMAX_MERGE_END -void makeProgramBindings(ShaderObject& shaderObject); - enum GLSyncState { // The object is currently undergoing no processing, although it's content // may be out of date, or it's storage may be invalid relative to the @@ -128,7 +109,9 @@ static const GLenum ELEMENT_TYPE_TO_GL[gpu::NUM_TYPES] = { GL_SHORT, GL_UNSIGNED_SHORT, GL_BYTE, - GL_UNSIGNED_BYTE + GL_UNSIGNED_BYTE, + GL_UNSIGNED_BYTE, + GL_INT_2_10_10_10_REV, }; bool checkGLError(const char* name = nullptr); @@ -156,6 +139,7 @@ class GLQuery; class GLState; class GLShader; class GLTexture; +struct ShaderObject; } } // namespace gpu::gl diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.cpp index 9808d389f1..2a39901ee7 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.cpp @@ -11,13 +11,58 @@ using namespace gpu; using namespace gpu::gl; +bool GLTexelFormat::isCompressed() const { + switch (internalFormat) { + case GL_COMPRESSED_R11_EAC: + case GL_COMPRESSED_SIGNED_R11_EAC: + case GL_COMPRESSED_RG11_EAC: + case GL_COMPRESSED_SIGNED_RG11_EAC: + case GL_COMPRESSED_RGB8_ETC2: + case GL_COMPRESSED_SRGB8_ETC2: + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: + case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: + case GL_COMPRESSED_RGBA8_ETC2_EAC: + case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: + case GL_COMPRESSED_RGBA_ASTC_4x4: + case GL_COMPRESSED_RGBA_ASTC_5x4: + case GL_COMPRESSED_RGBA_ASTC_5x5: + case GL_COMPRESSED_RGBA_ASTC_6x5: + case GL_COMPRESSED_RGBA_ASTC_6x6: + case GL_COMPRESSED_RGBA_ASTC_8x5: + case GL_COMPRESSED_RGBA_ASTC_8x6: + case GL_COMPRESSED_RGBA_ASTC_8x8: + case GL_COMPRESSED_RGBA_ASTC_10x5: + case GL_COMPRESSED_RGBA_ASTC_10x6: + case GL_COMPRESSED_RGBA_ASTC_10x8: + case GL_COMPRESSED_RGBA_ASTC_10x10: + case GL_COMPRESSED_RGBA_ASTC_12x10: + case GL_COMPRESSED_RGBA_ASTC_12x12: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12: + return true; + default: + return false; + } +} GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { -// qDebug() << "GLTexelFormat::evalGLTexelFormatInternal " << dstFormat.getDimension() << ", " << dstFormat.getSemantic() << ", " << dstFormat.getType(); GLenum result = GL_RGBA8; switch (dstFormat.getDimension()) { case gpu::SCALAR: { switch (dstFormat.getSemantic()) { + case gpu::RED: case gpu::RGB: case gpu::RGBA: case gpu::SRGB: @@ -44,6 +89,12 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { case gpu::INT16: result = GL_R16I; break; + case gpu::NUINT16: + //result = GL_R16_EXT; + break; + case gpu::NINT16: + //result = GL_R16_SNORM_EXT; + break; case gpu::HALF: result = GL_R16F; break; @@ -55,8 +106,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { break; case gpu::NUINT8: if ((dstFormat.getSemantic() == gpu::SRGB || dstFormat.getSemantic() == gpu::SRGBA)) { - //result = GL_SLUMINANCE8; - qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormatInternal GL_SLUMINANCE8"; + result = GL_SLUMINANCE8_NV; } else { result = GL_R8; } @@ -65,7 +115,6 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { result = GL_R8_SNORM; break; default: - qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormatInternal " << dstFormat.getType(); Q_UNREACHABLE(); break; } @@ -74,10 +123,19 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { // the type should be float result = GL_R11F_G11F_B10F; break; - + case gpu::RGB9E5: + // the type should be float + result = GL_RGB9_E5; + break; case gpu::DEPTH: result = GL_DEPTH_COMPONENT16; switch (dstFormat.getType()) { + case gpu::UINT32: + case gpu::INT32: + case gpu::NUINT32: + case gpu::NINT32: + result = GL_DEPTH_COMPONENT32_OES; + break; case gpu::FLOAT: result = GL_DEPTH_COMPONENT32F; break; @@ -105,7 +163,7 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { break; default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; } @@ -114,10 +172,11 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { switch (dstFormat.getSemantic()) { case gpu::RGB: case gpu::RGBA: + case gpu::XY: result = GL_RG8; break; default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; @@ -131,11 +190,10 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { break; case gpu::SRGB: case gpu::SRGBA: - //result = GL_SRGB8; // standard 2.2 gamma correction color - result = GL_RGB8; // standard 2.2 gamma correction color + result = GL_SRGB8; // standard 2.2 gamma correction color break; default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; @@ -163,6 +221,12 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { case gpu::INT16: result = GL_RGBA16I; break; + case gpu::NUINT16: + //result = GL_RGBA16_EXT; + break; + case gpu::NINT16: + //result = GL_RGBA16_SNORM_EXT; + break; case gpu::HALF: result = GL_RGBA16F; break; @@ -175,45 +239,40 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { case gpu::NUINT8: result = GL_RGBA8; break; + case gpu::NUINT2: + //result = GL_RGBA2; + break; case gpu::NINT8: result = GL_RGBA8_SNORM; break; - case gpu::COMPRESSED: - case gpu::NUINT2: - case gpu::NINT16: - case gpu::NUINT16: + case gpu::NINT2_10_10_10: case gpu::NUINT32: case gpu::NINT32: + case gpu::COMPRESSED: case gpu::NUM_TYPES: // quiet compiler Q_UNREACHABLE(); } break; case gpu::SRGB: - //result = GL_SRGB8; - result = GL_RGB8; - qDebug() << "SRGBA Here 2"; + result = GL_SRGB8; break; case gpu::SRGBA: result = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color break; default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; } - default: qCDebug(gpugllogging) << "Unknown combination of texel format"; } //qDebug() << "GLTexelFormat::evalGLTexelFormatInternal result " << result; - return result; } GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const Element& srcFormat) { -// qDebug() << "GLTexelFormat::evalGLTexelFormat dst.getDimension=" << dstFormat.getDimension() << " dst.getType=" << dstFormat.getType() << " dst.getSemantic=" << dstFormat.getSemantic(); -// qDebug() << "GLTexelFormat::evalGLTexelFormat src.getDimension=" << srcFormat.getDimension() << " src.getType=" << srcFormat.getType() << " src.getSemantic=" << srcFormat.getSemantic(); if (dstFormat != srcFormat) { GLTexelFormat texel = { GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE }; @@ -224,20 +283,15 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; switch (dstFormat.getSemantic()) { + case gpu::RED: case gpu::RGB: case gpu::RGBA: texel.internalFormat = GL_R8; break; - //CLIMAX_MERGE_START - // case gpu::COMPRESSED_R: - // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RED_RGTC1"; - // //texel.internalFormat = GL_COMPRESSED_RED_RGTC1; - // break; - //CLIMAX_MERGE_END - case gpu::DEPTH: - texel.internalFormat = GL_DEPTH_COMPONENT32_OES; + texel.format = GL_DEPTH_COMPONENT; + texel.internalFormat = GL_DEPTH_COMPONENT32F; break; case gpu::DEPTH_STENCIL: texel.type = GL_UNSIGNED_INT_24_8; @@ -245,7 +299,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.internalFormat = GL_DEPTH24_STENCIL8; break; default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; } @@ -257,10 +311,11 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E switch (dstFormat.getSemantic()) { case gpu::RGB: case gpu::RGBA: + case gpu::XY: texel.internalFormat = GL_RG8; break; default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; @@ -276,19 +331,8 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E case gpu::RGBA: texel.internalFormat = GL_RGB8; break; - //CLIMAX_MERGE_START - //not needed? - // case gpu::COMPRESSED_RGB: - // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RGB"; - // //texel.internalFormat = GL_COMPRESSED_RGB; - // break; - // case gpu::COMPRESSED_SRGB: - // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_SRGB"; - // //texel.internalFormat = GL_COMPRESSED_SRGB; - // break; - //CLIMAX_MERGE_END default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; @@ -301,8 +345,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E switch (srcFormat.getSemantic()) { case gpu::BGRA: case gpu::SBGRA: - qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_BGRA"; - //texel.format = GL_BGRA; + texel.format = GL_RGBA; // GL_BGRA_EXT; break; case gpu::RGB: case gpu::RGBA: @@ -320,49 +363,19 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.internalFormat = GL_RGBA8; break; case gpu::SRGB: - //texel.internalFormat = GL_SRGB8; - texel.internalFormat = GL_RGB8; - qDebug() << "SRGBA Here 3"; + texel.internalFormat = GL_SRGB8; break; case gpu::SRGBA: texel.internalFormat = GL_SRGB8_ALPHA8; break; - - //CLIMAX_MERGE_START - // case gpu::COMPRESSED_RGBA: - // //texel.internalFormat = GL_COMPRESSED_RGBA; - // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RGBA"; - // break; - // case gpu::COMPRESSED_SRGBA: - // //texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA; - // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_SRGB_ALPHA"; - // break; - //CLIMAX_MERGE_END - // FIXME: WE will want to support this later - /* - case gpu::COMPRESSED_BC3_RGBA: - texel.internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - break; - case gpu::COMPRESSED_BC3_SRGBA: - texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; - break; - - case gpu::COMPRESSED_BC7_RGBA: - texel.internalFormat = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; - break; - case gpu::COMPRESSED_BC7_SRGBA: - texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM; - break; - */ - default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; } default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } return texel; } else { @@ -374,12 +387,8 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.type = ELEMENT_TYPE_TO_GL[dstFormat.getType()]; switch (dstFormat.getSemantic()) { - //CLIMAX_MERGE_START - // case gpu::COMPRESSED_R: { - // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RED_RGTC1"; - // //texel.internalFormat = GL_COMPRESSED_RED_RGTC1; - // break; - // } + + case gpu::RED: case gpu::RGB: case gpu::RGBA: case gpu::SRGB: @@ -415,13 +424,11 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E break; } case gpu::NUINT16: { - //texel.internalFormat = GL_R16; - qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_R16"; + texel.internalFormat = GL_R16_EXT; break; } case gpu::NINT16: { - //texel.internalFormat = GL_R16_SNORM; - qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_R16_SNORM"; + texel.internalFormat = GL_R16_SNORM_EXT; break; } case gpu::HALF: { @@ -438,9 +445,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E } case gpu::NUINT8: { if ((dstFormat.getSemantic() == gpu::SRGB || dstFormat.getSemantic() == gpu::SRGBA)) { -// texel.internalFormat = GL_SLUMINANCE8; - qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_SLUMINANCE8"; - + texel.internalFormat = GL_SLUMINANCE8_NV; } else { texel.internalFormat = GL_R8; } @@ -461,10 +466,16 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E case gpu::R11G11B10: texel.format = GL_RGB; - // the type should be float + texel.type = GL_UNSIGNED_INT_10F_11F_11F_REV; texel.internalFormat = GL_R11F_G11F_B10F; break; + case gpu::RGB9E5: + texel.format = GL_RGB; + texel.type = GL_UNSIGNED_INT_5_9_9_9_REV; + texel.internalFormat = GL_RGB9_E5; + break; + case gpu::DEPTH: texel.format = GL_DEPTH_COMPONENT; // It's depth component to load it texel.internalFormat = GL_DEPTH_COMPONENT32_OES; @@ -497,6 +508,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E } case gpu::COMPRESSED: case gpu::NUINT2: + case gpu::NINT2_10_10_10: case gpu::NUM_TYPES: { // quiet compiler Q_UNREACHABLE(); } @@ -508,7 +520,7 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.internalFormat = GL_DEPTH24_STENCIL8; break; default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; @@ -521,10 +533,11 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E switch (dstFormat.getSemantic()) { case gpu::RGB: case gpu::RGBA: + case gpu::XY: texel.internalFormat = GL_RG8; break; default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; @@ -542,20 +555,10 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E break; case gpu::SRGB: case gpu::SRGBA: - //texel.internalFormat = GL_SRGB8; // standard 2.2 gamma correction color - texel.internalFormat = GL_RGB8; // standard 2.2 gamma correction color + texel.internalFormat = GL_SRGB8; // standard 2.2 gamma correction color break; - //CLIMAX_MERGE_START - // case gpu::COMPRESSED_RGB: - // //texel.internalFormat = GL_COMPRESSED_RGB; - // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RGB"; - // break; - // case gpu::COMPRESSED_SRGB: - // //texel.internalFormat = GL_COMPRESSED_SRGB; - // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_SRGB"; - // break; default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; } @@ -592,13 +595,11 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E break; case gpu::NUINT16: texel.format = GL_RGBA; - //texel.internalFormat = GL_RGBA16; - qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_RGBA16"; + //texel.internalFormat = GL_RGBA16_EXT; break; case gpu::NINT16: texel.format = GL_RGBA; - qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_RGBA16_SNORM"; - //texel.internalFormat = GL_RGBA16_SNORM; + //texel.internalFormat = GL_RGBA16_SNORM_EXT; break; case gpu::HALF: texel.format = GL_RGBA; @@ -620,39 +621,32 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E texel.format = GL_RGBA; texel.internalFormat = GL_RGBA8_SNORM; break; + case gpu::NUINT2: + texel.format = GL_RGBA; + texel.internalFormat = GL_RGBA8; + break; case gpu::NUINT32: case gpu::NINT32: + case gpu::NINT2_10_10_10: case gpu::COMPRESSED: - case gpu::NUINT2: case gpu::NUM_TYPES: // quiet compiler Q_UNREACHABLE(); } break; case gpu::SRGB: - //texel.internalFormat = GL_SRGB8; - texel.internalFormat = GL_RGB8; // standard 2.2 gamma correction color + texel.internalFormat = GL_SRGB8; break; case gpu::SRGBA: texel.internalFormat = GL_SRGB8_ALPHA8; // standard 2.2 gamma correction color break; - //CLIMAX_MERGE_START - // case gpu::COMPRESSED_RGBA: - // //texel.internalFormat = GL_COMPRESSED_RGBA; - // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_RGBA"; - // break; - // case gpu::COMPRESSED_SRGBA: - // qDebug() << "TODO: GLTexelFormat.cpp:evalGLTexelFormat GL_COMPRESSED_SRGB_ALPHA"; - // //texel.internalFormat = GL_COMPRESSED_SRGB_ALPHA; - // break; default: - qCDebug(gpugllogging) << "Unknown combination of texel format"; + qCWarning(gpugllogging) << "Unknown combination of texel format"; } break; } default: qCDebug(gpugllogging) << "Unknown combination of texel format"; } - //qDebug() << "GLTexelFormat::evalGLTexelFormat Texel.type " << texel.type << " - texel.format=" << texel.format << " texel.internalFormat=" << texel.internalFormat; return texel; } } diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h b/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h index 94ded3dc23..8f37f6b604 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h +++ b/libraries/gpu-gles/src/gpu/gl/GLTexelFormat.h @@ -18,6 +18,11 @@ public: GLenum format; GLenum type; + GLTexelFormat(GLenum glinternalFormat, GLenum glformat, GLenum gltype) : internalFormat(glinternalFormat), format(glformat), type(gltype) {} + GLTexelFormat(GLenum glinternalFormat) : internalFormat(glinternalFormat) {} + + bool isCompressed() const; + static GLTexelFormat evalGLTexelFormat(const Element& dstFormat) { return evalGLTexelFormat(dstFormat, dstFormat); } diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexture.cpp b/libraries/gpu-gles/src/gpu/gl/GLTexture.cpp index 5f5e3a9be1..08b3f87094 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLTexture.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLTexture.cpp @@ -8,35 +8,28 @@ #include "GLTexture.h" +#include #include -#include "GLTextureTransfer.h" #include "GLBackend.h" using namespace gpu; using namespace gpu::gl; -std::shared_ptr GLTexture::_textureTransferHelper; -// FIXME placeholder for texture memory over-use -#define DEFAULT_MAX_MEMORY_MB 256 -#define MIN_FREE_GPU_MEMORY_PERCENTAGE 0.25f -#define OVER_MEMORY_PRESSURE 2.0f - -const GLenum GLTexture::CUBE_FACE_LAYOUT[6] = { +const GLenum GLTexture::CUBE_FACE_LAYOUT[GLTexture::TEXTURE_CUBE_NUM_FACES] = { GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z }; + const GLenum GLTexture::WRAP_MODES[Sampler::NUM_WRAP_MODES] = { GL_REPEAT, // WRAP_REPEAT, GL_MIRRORED_REPEAT, // WRAP_MIRROR, GL_CLAMP_TO_EDGE, // WRAP_CLAMP, - GL_CLAMP_TO_BORDER_EXT, // WRAP_BORDER, - - //GL_MIRROR_CLAMP_TO_EDGE_EXT // WRAP_MIRROR_ONCE, -// qDebug() << "TODO: GLTexture.cpp:WRAP_MODES GL_MIRROR_CLAMP_TO_EDGE_EXT"; + GL_CLAMP_TO_BORDER, // WRAP_BORDER, + GL_MIRRORED_REPEAT //GL_MIRROR_CLAMP_TO_EDGE_EXT // WRAP_MIRROR_ONCE, }; const GLFilterMode GLTexture::FILTER_MODES[Sampler::NUM_FILTERS] = { @@ -73,6 +66,17 @@ GLenum GLTexture::getGLTextureType(const Texture& texture) { return GL_TEXTURE_2D; } +uint8_t GLTexture::getFaceCount(GLenum target) { + switch (target) { + case GL_TEXTURE_2D: + return TEXTURE_2D_NUM_FACES; + case GL_TEXTURE_CUBE_MAP: + return TEXTURE_CUBE_NUM_FACES; + default: + Q_UNREACHABLE(); + break; + } +} const std::vector& GLTexture::getFaceTargets(GLenum target) { static std::vector cubeFaceTargets { @@ -96,228 +100,602 @@ const std::vector& GLTexture::getFaceTargets(GLenum target) { return faceTargets; } -// Default texture memory = GPU total memory - 2GB -#define GPU_MEMORY_RESERVE_BYTES MB_TO_BYTES(2048) -// Minimum texture memory = 1GB -#define TEXTURE_MEMORY_MIN_BYTES MB_TO_BYTES(1024) - - -float GLTexture::getMemoryPressure() { - // Check for an explicit memory limit - auto availableTextureMemory = Texture::getAllowedGPUMemoryUsage(); - - - // If no memory limit has been set, use a percentage of the total dedicated memory - if (!availableTextureMemory) { -#if 0 - auto totalMemory = getDedicatedMemory(); - if ((GPU_MEMORY_RESERVE_BYTES + TEXTURE_MEMORY_MIN_BYTES) > totalMemory) { - availableTextureMemory = TEXTURE_MEMORY_MIN_BYTES; - } else { - availableTextureMemory = totalMemory - GPU_MEMORY_RESERVE_BYTES; - } -#else - // Hardcode texture limit for sparse textures at 1 GB for now - availableTextureMemory = TEXTURE_MEMORY_MIN_BYTES; -#endif - } - - // Return the consumed texture memory divided by the available texture memory. - //CLIMAX_MERGE_START - //auto consumedGpuMemory = Context::getTextureGPUMemoryUsage() - Context::getTextureGPUFramebufferMemoryUsage(); - //float memoryPressure = (float)consumedGpuMemory / (float)availableTextureMemory; - //static Context::Size lastConsumedGpuMemory = 0; - //if (memoryPressure > 1.0f && lastConsumedGpuMemory != consumedGpuMemory) { - // lastConsumedGpuMemory = consumedGpuMemory; - // qCDebug(gpugllogging) << "Exceeded max allowed texture memory: " << consumedGpuMemory << " / " << availableTextureMemory; - //} - //return memoryPressure; - return 0; - -} - - -// Create the texture and allocate storage -GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, bool transferrable) : - GLObject(backend, texture, id), - _external(false), - _source(texture.source()), - _storageStamp(texture.getStamp()), - _target(getGLTextureType(texture)), - _internalFormat(gl::GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat())), - _maxMip(texture.getMaxMip()), - _minMip(texture.getMinMip()), - _virtualSize(texture.evalTotalSize()), - _transferrable(transferrable) -{ - //qDebug() << "GLTexture::GLTexture building GLTexture with _internalFormat" << _internalFormat; - auto strongBackend = _backend.lock(); - strongBackend->recycle(); - //CLIMAX_MERGE_START - //Backend::incrementTextureGPUCount(); - //Backend::updateTextureGPUVirtualMemoryUsage(0, _virtualSize); - //CLIMAX_MERGE_END - Backend::setGPUObject(texture, this); -} - GLTexture::GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id) : - GLObject(backend, texture, id), - _external(true), - _source(texture.source()), - _storageStamp(0), - _target(getGLTextureType(texture)), - _internalFormat(GL_RGBA8), - // FIXME force mips to 0? - _maxMip(texture.getMaxMip()), - _minMip(texture.getMinMip()), - _virtualSize(0), - _transferrable(false) + GLObject(backend, texture, id), + _source(texture.source()), + _target(getGLTextureType(texture)), + _texelFormat(GLTexelFormat::evalGLTexelFormatInternal(texture.getTexelFormat())) { Backend::setGPUObject(texture, this); - - // FIXME Is this necessary? - //withPreservedTexture([this] { - // syncSampler(); - // if (_gpuObject.isAutogenerateMips()) { - // generateMips(); - // } - //}); } GLTexture::~GLTexture() { auto backend = _backend.lock(); - if (backend) { - if (_external) { - auto recycler = _gpuObject.getExternalRecycler(); - if (recycler) { - backend->releaseExternalTexture(_id, recycler); - } else { - qWarning() << "No recycler available for texture " << _id << " possible leak"; - } - } else if (_id) { - // WARNING! Sparse textures do not use this code path. See GL45BackendTexture for - // the GL45Texture destructor for doing any required work tracking GPU stats - backend->releaseTexture(_id, _size); - } - - ////CLIMAX_MERGE_START - //if (!_external && !_transferrable) { - // Backend::updateTextureGPUFramebufferMemoryUsage(_size, 0); - //} + if (backend && _id) { + backend->releaseTexture(_id, 0); } - //Backend::updateTextureGPUVirtualMemoryUsage(_virtualSize, 0); - //CLIMAX_MERGE_END } -void GLTexture::createTexture() { - withPreservedTexture([&] { - allocateStorage(); - (void)CHECK_GL_ERROR(); - syncSampler(); - (void)CHECK_GL_ERROR(); +Size GLTexture::copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const { + if (!_gpuObject.isStoredMipFaceAvailable(sourceMip)) { + return 0; + } + auto dim = _gpuObject.evalMipDimensions(sourceMip); + auto mipData = _gpuObject.accessStoredMipFace(sourceMip, face); + auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face); + if (mipData) { + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); + return copyMipFaceLinesFromTexture(targetMip, face, dim, 0, texelFormat.internalFormat, texelFormat.format, texelFormat.type, mipSize, mipData->readData()); + } else { + qCDebug(gpugllogging) << "Missing mipData level=" << sourceMip << " face=" << (int)face << " for texture " << _gpuObject.source().c_str(); + } + return 0; +} + + +GLExternalTexture::GLExternalTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id) + : Parent(backend, texture, id) { + Backend::textureExternalCount.increment(); +} + +GLExternalTexture::~GLExternalTexture() { + auto backend = _backend.lock(); + if (backend) { + auto recycler = _gpuObject.getExternalRecycler(); + if (recycler) { + backend->releaseExternalTexture(_id, recycler); + } else { + qCWarning(gpugllogging) << "No recycler available for texture " << _id << " possible leak"; + } + const_cast(_id) = 0; + } + Backend::textureExternalCount.decrement(); +} + + +// Variable sized textures +using MemoryPressureState = GLVariableAllocationSupport::MemoryPressureState; +using WorkQueue = GLVariableAllocationSupport::WorkQueue; +using TransferJobPointer = GLVariableAllocationSupport::TransferJobPointer; + +std::list GLVariableAllocationSupport::_memoryManagedTextures; +MemoryPressureState GLVariableAllocationSupport::_memoryPressureState { MemoryPressureState::Idle }; +std::atomic GLVariableAllocationSupport::_memoryPressureStateStale { false }; +const uvec3 GLVariableAllocationSupport::INITIAL_MIP_TRANSFER_DIMENSIONS { 64, 64, 1 }; +WorkQueue GLVariableAllocationSupport::_transferQueue; +WorkQueue GLVariableAllocationSupport::_promoteQueue; +WorkQueue GLVariableAllocationSupport::_demoteQueue; +size_t GLVariableAllocationSupport::_frameTexturesCreated { 0 }; + +#define OVERSUBSCRIBED_PRESSURE_VALUE 0.95f +#define UNDERSUBSCRIBED_PRESSURE_VALUE 0.85f +#define DEFAULT_ALLOWED_TEXTURE_MEMORY_MB ((size_t)1024) + +static const size_t DEFAULT_ALLOWED_TEXTURE_MEMORY = MB_TO_BYTES(DEFAULT_ALLOWED_TEXTURE_MEMORY_MB); + +using TransferJob = GLVariableAllocationSupport::TransferJob; + +const uvec3 GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS { 1024, 1024, 1 }; +const size_t GLVariableAllocationSupport::MAX_TRANSFER_SIZE = GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS.x * GLVariableAllocationSupport::MAX_TRANSFER_DIMENSIONS.y * 4; + +#if THREADED_TEXTURE_BUFFERING + +TexturePointer GLVariableAllocationSupport::_currentTransferTexture; +TransferJobPointer GLVariableAllocationSupport::_currentTransferJob; +QThreadPool* TransferJob::_bufferThreadPool { nullptr }; + +void TransferJob::startBufferingThread() { + static std::once_flag once; + std::call_once(once, [&] { + _bufferThreadPool = new QThreadPool(qApp); + _bufferThreadPool->setMaxThreadCount(1); }); } -void GLTexture::withPreservedTexture(std::function f) const { - GLint boundTex = -1; - switch (_target) { - case GL_TEXTURE_2D: - glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex); - break; +#endif - case GL_TEXTURE_CUBE_MAP: - glGetIntegerv(GL_TEXTURE_BINDING_CUBE_MAP, &boundTex); - break; +TransferJob::TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines, uint32_t lineOffset) + : _parent(parent) { - default: - qFatal("Unsupported texture type"); + auto transferDimensions = _parent._gpuObject.evalMipDimensions(sourceMip); + GLenum format; + GLenum internalFormat; + GLenum type; + GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_parent._gpuObject.getTexelFormat(), _parent._gpuObject.getStoredMipFormat()); + format = texelFormat.format; + internalFormat = texelFormat.internalFormat; + type = texelFormat.type; + _transferSize = _parent._gpuObject.getStoredMipFaceSize(sourceMip, face); + + // If we're copying a subsection of the mip, do additional calculations to find the size and offset of the segment + if (0 != lines) { + transferDimensions.y = lines; + auto dimensions = _parent._gpuObject.evalMipDimensions(sourceMip); + auto bytesPerLine = (uint32_t)_transferSize / dimensions.y; + _transferOffset = bytesPerLine * lineOffset; + _transferSize = bytesPerLine * lines; } - (void)CHECK_GL_ERROR(); - glBindTexture(_target, _texture); - f(); - glBindTexture(_target, boundTex); - (void)CHECK_GL_ERROR(); + Backend::texturePendingGPUTransferMemSize.update(0, _transferSize); + + if (_transferSize > GLVariableAllocationSupport::MAX_TRANSFER_SIZE) { + qCWarning(gpugllogging) << "Transfer size of " << _transferSize << " exceeds theoretical maximum transfer size"; + } + + // Buffering can invoke disk IO, so it should be off of the main and render threads + _bufferingLambda = [=] { + auto mipStorage = _parent._gpuObject.accessStoredMipFace(sourceMip, face); + if (mipStorage) { + _mipData = mipStorage->createView(_transferSize, _transferOffset); + } else { + qCWarning(gpugllogging) << "Buffering failed because mip could not be retrieved from texture " << _parent._source.c_str() ; + } + }; + + _transferLambda = [=] { + if (_mipData) { + _parent.copyMipFaceLinesFromTexture(targetMip, face, transferDimensions, lineOffset, internalFormat, format, type, _mipData->size(), _mipData->readData()); + _mipData.reset(); + } else { + qCWarning(gpugllogging) << "Transfer failed because mip could not be retrieved from texture " << _parent._source.c_str(); + } + }; } -void GLTexture::setSize(GLuint size) const { - ////CLIMAX_MERGE_START - //if (!_external && !_transferrable) { - // Backend::updateTextureGPUFramebufferMemoryUsage(_size, 0); - //} - //Backend::updateTextureGPUMemoryUsage(_size, size); - const_cast(_size) = size; +TransferJob::TransferJob(const GLTexture& parent, std::function transferLambda) + : _parent(parent), _bufferingRequired(false), _transferLambda(transferLambda) { } -bool GLTexture::isInvalid() const { - return _storageStamp < _gpuObject.getStamp(); +TransferJob::~TransferJob() { + Backend::texturePendingGPUTransferMemSize.update(_transferSize, 0); } -bool GLTexture::isOutdated() const { - return GLSyncState::Idle == _syncState && _contentStamp < _gpuObject.getDataStamp(); -} - -bool GLTexture::isReady() const { - // If we have an invalid texture, we're never ready - if (isInvalid()) { +bool TransferJob::tryTransfer() { +#if THREADED_TEXTURE_BUFFERING + // Are we ready to transfer + if (!bufferingCompleted()) { + startBuffering(); return false; } - - auto syncState = _syncState.load(); - if (isOutdated() || Idle != syncState) { - return false; +#else + if (_bufferingRequired) { + _bufferingLambda(); } - +#endif + _transferLambda(); return true; } +#if THREADED_TEXTURE_BUFFERING +bool TransferJob::bufferingRequired() const { + if (!_bufferingRequired) { + return false; + } -// Do any post-transfer operations that might be required on the main context / rendering thread -void GLTexture::postTransfer() { - //CLIMAX_MERGE_START - - // setSyncState(GLSyncState::Idle); - // ++_transferCount; + // The default state of a QFuture is with status Canceled | Started | Finished, + // so we have to check isCancelled before we check the actual state + if (_bufferingStatus.isCanceled()) { + return true; + } - // // At this point the mip pixels have been loaded, we can notify the gpu texture to abandon it's memory - // switch (_gpuObject.getType()) { - // case Texture::TEX_2D: - // for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { - // if (_gpuObject.isStoredMipFaceAvailable(i)) { - // _gpuObject.notifyMipFaceGPULoaded(i); - // } - // } - // break; - - // case Texture::TEX_CUBE: - // // transfer pixels from each faces - // for (uint8_t f = 0; f < CUBE_NUM_FACES; f++) { - // for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { - // if (_gpuObject.isStoredMipFaceAvailable(i, f)) { - // _gpuObject.notifyMipFaceGPULoaded(i, f); - // } - // } - // } - // break; - - // default: - // qCWarning(gpugllogging) << __FUNCTION__ << " case for Texture Type " << _gpuObject.getType() << " not supported"; - // break; - // } - //CLIMAX_MERGE_END + return !_bufferingStatus.isStarted(); } -void GLTexture::initTextureTransferHelper() { - _textureTransferHelper = std::make_shared(); +bool TransferJob::bufferingCompleted() const { + if (!_bufferingRequired) { + return true; + } + + // The default state of a QFuture is with status Canceled | Started | Finished, + // so we have to check isCancelled before we check the actual state + if (_bufferingStatus.isCanceled()) { + return false; + } + + return _bufferingStatus.isFinished(); } -void GLTexture::startTransfer() { - createTexture(); +void TransferJob::startBuffering() { + if (bufferingRequired()) { + assert(_bufferingStatus.isCanceled()); + _bufferingStatus = QtConcurrent::run(_bufferThreadPool, [=] { + _bufferingLambda(); + }); + assert(!_bufferingStatus.isCanceled()); + assert(_bufferingStatus.isStarted()); + } +} +#endif + +GLVariableAllocationSupport::GLVariableAllocationSupport() { + _memoryPressureStateStale = true; } -void GLTexture::finishTransfer() { - if (_gpuObject.isAutogenerateMips()) { - generateMips(); +GLVariableAllocationSupport::~GLVariableAllocationSupport() { + _memoryPressureStateStale = true; +} + +void GLVariableAllocationSupport::addMemoryManagedTexture(const TexturePointer& texturePointer) { + _memoryManagedTextures.push_back(texturePointer); + if (MemoryPressureState::Idle != _memoryPressureState) { + addToWorkQueue(texturePointer); } } +void GLVariableAllocationSupport::addToWorkQueue(const TexturePointer& texturePointer) { + GLTexture* gltexture = Backend::getGPUObject(*texturePointer); + GLVariableAllocationSupport* vargltexture = dynamic_cast(gltexture); + switch (_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + if (vargltexture->canDemote()) { + // Demote largest first + _demoteQueue.push({ texturePointer, (float)gltexture->size() }); + } + break; + + case MemoryPressureState::Undersubscribed: + if (vargltexture->canPromote()) { + // Promote smallest first + _promoteQueue.push({ texturePointer, 1.0f / (float)gltexture->size() }); + } + break; + + case MemoryPressureState::Transfer: + if (vargltexture->hasPendingTransfers()) { + // Transfer priority given to smaller mips first + _transferQueue.push({ texturePointer, 1.0f / (float)gltexture->_gpuObject.evalMipSize(vargltexture->_populatedMip) }); + } + break; + + case MemoryPressureState::Idle: + Q_UNREACHABLE(); + break; + } +} + +WorkQueue& GLVariableAllocationSupport::getActiveWorkQueue() { + static WorkQueue empty; + switch (_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + return _demoteQueue; + + case MemoryPressureState::Undersubscribed: + return _promoteQueue; + + case MemoryPressureState::Transfer: + return _transferQueue; + + case MemoryPressureState::Idle: + Q_UNREACHABLE(); + break; + } + return empty; +} + +// FIXME hack for stats display +QString getTextureMemoryPressureModeString() { + switch (GLVariableAllocationSupport::_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + return "Oversubscribed"; + + case MemoryPressureState::Undersubscribed: + return "Undersubscribed"; + + case MemoryPressureState::Transfer: + return "Transfer"; + + case MemoryPressureState::Idle: + return "Idle"; + } + Q_UNREACHABLE(); + return "Unknown"; +} + +void GLVariableAllocationSupport::updateMemoryPressure() { + static size_t lastAllowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); + + size_t allowedMemoryAllocation = gpu::Texture::getAllowedGPUMemoryUsage(); + if (0 == allowedMemoryAllocation) { + allowedMemoryAllocation = DEFAULT_ALLOWED_TEXTURE_MEMORY; + } + + // If the user explicitly changed the allowed memory usage, we need to mark ourselves stale + // so that we react + if (allowedMemoryAllocation != lastAllowedMemoryAllocation) { + _memoryPressureStateStale = true; + lastAllowedMemoryAllocation = allowedMemoryAllocation; + } + + if (!_memoryPressureStateStale.exchange(false)) { + return; + } + + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + + // Clear any defunct textures (weak pointers that no longer have a valid texture) + _memoryManagedTextures.remove_if([&](const TextureWeakPointer& weakPointer) { + return weakPointer.expired(); + }); + + // Convert weak pointers to strong. This new list may still contain nulls if a texture was + // deleted on another thread between the previous line and this one + std::vector strongTextures; { + strongTextures.reserve(_memoryManagedTextures.size()); + std::transform( + _memoryManagedTextures.begin(), _memoryManagedTextures.end(), + std::back_inserter(strongTextures), + [](const TextureWeakPointer& p) { return p.lock(); }); + } + + size_t totalVariableMemoryAllocation = 0; + size_t idealMemoryAllocation = 0; + bool canDemote = false; + bool canPromote = false; + bool hasTransfers = false; + for (const auto& texture : strongTextures) { + // Race conditions can still leave nulls in the list, so we need to check + if (!texture) { + continue; + } + GLTexture* gltexture = Backend::getGPUObject(*texture); + GLVariableAllocationSupport* vartexture = dynamic_cast(gltexture); + // Track how much the texture thinks it should be using + idealMemoryAllocation += texture->evalTotalSize(); + // Track how much we're actually using + totalVariableMemoryAllocation += gltexture->size(); + canDemote |= vartexture->canDemote(); + canPromote |= vartexture->canPromote(); + hasTransfers |= vartexture->hasPendingTransfers(); + } + + size_t unallocated = idealMemoryAllocation - totalVariableMemoryAllocation; + float pressure = (float)totalVariableMemoryAllocation / (float)allowedMemoryAllocation; + + auto newState = MemoryPressureState::Idle; + if (pressure < UNDERSUBSCRIBED_PRESSURE_VALUE && (unallocated != 0 && canPromote)) { + newState = MemoryPressureState::Undersubscribed; + } else if (pressure > OVERSUBSCRIBED_PRESSURE_VALUE && canDemote) { + newState = MemoryPressureState::Oversubscribed; + } else if (hasTransfers) { + newState = MemoryPressureState::Transfer; + } + + if (newState != _memoryPressureState) { + _memoryPressureState = newState; + // Clear the existing queue + _transferQueue = WorkQueue(); + _promoteQueue = WorkQueue(); + _demoteQueue = WorkQueue(); + + // Populate the existing textures into the queue + if (_memoryPressureState != MemoryPressureState::Idle) { + for (const auto& texture : strongTextures) { + // Race conditions can still leave nulls in the list, so we need to check + if (!texture) { + continue; + } + addToWorkQueue(texture); + } + } + } +} + +TexturePointer GLVariableAllocationSupport::getNextWorkQueueItem(WorkQueue& workQueue) { + while (!workQueue.empty()) { + auto workTarget = workQueue.top(); + + auto texture = workTarget.first.lock(); + if (!texture) { + workQueue.pop(); + continue; + } + + // Check whether the resulting texture can actually have work performed + GLTexture* gltexture = Backend::getGPUObject(*texture); + GLVariableAllocationSupport* vartexture = dynamic_cast(gltexture); + switch (_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + if (vartexture->canDemote()) { + return texture; + } + break; + + case MemoryPressureState::Undersubscribed: + if (vartexture->canPromote()) { + return texture; + } + break; + + case MemoryPressureState::Transfer: + if (vartexture->hasPendingTransfers()) { + return texture; + } + break; + + case MemoryPressureState::Idle: + Q_UNREACHABLE(); + break; + } + + // If we got here, then the texture has no work to do in the current state, + // so pop it off the queue and continue + workQueue.pop(); + } + + return TexturePointer(); +} + +void GLVariableAllocationSupport::processWorkQueue(WorkQueue& workQueue) { + if (workQueue.empty()) { + return; + } + + // Get the front of the work queue to perform work + auto texture = getNextWorkQueueItem(workQueue); + if (!texture) { + return; + } + + // Grab the first item off the demote queue + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + + GLTexture* gltexture = Backend::getGPUObject(*texture); + GLVariableAllocationSupport* vartexture = dynamic_cast(gltexture); + switch (_memoryPressureState) { + case MemoryPressureState::Oversubscribed: + vartexture->demote(); + workQueue.pop(); + addToWorkQueue(texture); + _memoryPressureStateStale = true; + break; + + case MemoryPressureState::Undersubscribed: + vartexture->promote(); + workQueue.pop(); + addToWorkQueue(texture); + _memoryPressureStateStale = true; + break; + + case MemoryPressureState::Transfer: + if (vartexture->executeNextTransfer(texture)) { + workQueue.pop(); + addToWorkQueue(texture); + +#if THREADED_TEXTURE_BUFFERING + // Eagerly start the next buffering job if possible + texture = getNextWorkQueueItem(workQueue); + if (texture) { + gltexture = Backend::getGPUObject(*texture); + vartexture = dynamic_cast(gltexture); + vartexture->executeNextBuffer(texture); + } +#endif + } + break; + + case MemoryPressureState::Idle: + Q_UNREACHABLE(); + break; + } +} + +void GLVariableAllocationSupport::processWorkQueues() { + if (MemoryPressureState::Idle == _memoryPressureState) { + return; + } + + auto& workQueue = getActiveWorkQueue(); + // Do work on the front of the queue + processWorkQueue(workQueue); + + if (workQueue.empty()) { + _memoryPressureState = MemoryPressureState::Idle; + _memoryPressureStateStale = true; + } +} + +void GLVariableAllocationSupport::manageMemory() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + updateMemoryPressure(); + processWorkQueues(); +} + +bool GLVariableAllocationSupport::executeNextTransfer(const TexturePointer& currentTexture) { +#if THREADED_TEXTURE_BUFFERING + // If a transfer job is active on the buffering thread, but has not completed it's buffering lambda, + // then we need to exit early, since we don't want to have the transfer job leave scope while it's + // being used in another thread -- See https://highfidelity.fogbugz.com/f/cases/4626 + if (_currentTransferJob && !_currentTransferJob->bufferingCompleted()) { + return false; + } +#endif + + if (_populatedMip <= _allocatedMip) { +#if THREADED_TEXTURE_BUFFERING + _currentTransferJob.reset(); + _currentTransferTexture.reset(); +#endif + return true; + } + + // If the transfer queue is empty, rebuild it + if (_pendingTransfers.empty()) { + populateTransferQueue(); + } + + bool result = false; + if (!_pendingTransfers.empty()) { +#if THREADED_TEXTURE_BUFFERING + // If there is a current transfer, but it's not the top of the pending transfer queue, then it's an orphan, so we want to abandon it. + if (_currentTransferJob && _currentTransferJob != _pendingTransfers.front()) { + _currentTransferJob.reset(); + } + + if (!_currentTransferJob) { + // Keeping hold of a strong pointer to the transfer job ensures that if the pending transfer queue is rebuilt, the transfer job + // doesn't leave scope, causing a crash in the buffering thread + _currentTransferJob = _pendingTransfers.front(); + + // Keeping hold of a strong pointer during the transfer ensures that the transfer thread cannot try to access a destroyed texture + _currentTransferTexture = currentTexture; + } + + // transfer jobs use asynchronous buffering of the texture data because it may involve disk IO, so we execute a try here to determine if the buffering + // is complete + if (_currentTransferJob->tryTransfer()) { + _pendingTransfers.pop(); + // Once a given job is finished, release the shared pointers keeping them alive + _currentTransferTexture.reset(); + _currentTransferJob.reset(); + result = true; + } +#else + if (_pendingTransfers.front()->tryTransfer()) { + _pendingTransfers.pop(); + result = true; + } +#endif + } + return result; +} + +#if THREADED_TEXTURE_BUFFERING +void GLVariableAllocationSupport::executeNextBuffer(const TexturePointer& currentTexture) { + if (_currentTransferJob && !_currentTransferJob->bufferingCompleted()) { + return; + } + + // If the transfer queue is empty, rebuild it + if (_pendingTransfers.empty()) { + populateTransferQueue(); + } + + if (!_pendingTransfers.empty()) { + if (!_currentTransferJob) { + _currentTransferJob = _pendingTransfers.front(); + _currentTransferTexture = currentTexture; + } + + _currentTransferJob->startBuffering(); + } +} +#endif + +void GLVariableAllocationSupport::incrementPopulatedSize(Size delta) const { + _populatedSize += delta; + // Keep the 2 code paths to be able to debug + if (_size < _populatedSize) { + Backend::textureResourcePopulatedGPUMemSize.update(0, delta); + } else { + Backend::textureResourcePopulatedGPUMemSize.update(0, delta); + } +} +void GLVariableAllocationSupport::decrementPopulatedSize(Size delta) const { + _populatedSize -= delta; + // Keep the 2 code paths to be able to debug + if (_size < _populatedSize) { + Backend::textureResourcePopulatedGPUMemSize.update(delta, 0); + } else { + Backend::textureResourcePopulatedGPUMemSize.update(delta, 0); + } +} \ No newline at end of file diff --git a/libraries/gpu-gles/src/gpu/gl/GLTexture.h b/libraries/gpu-gles/src/gpu/gl/GLTexture.h index 03353ae67d..ce27d02033 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLTexture.h +++ b/libraries/gpu-gles/src/gpu/gl/GLTexture.h @@ -8,10 +8,15 @@ #ifndef hifi_gpu_gl_GLTexture_h #define hifi_gpu_gl_GLTexture_h +#include +#include + #include "GLShared.h" -#include "GLTextureTransfer.h" #include "GLBackend.h" #include "GLTexelFormat.h" +#include + +#define THREADED_TEXTURE_BUFFERING 1 namespace gpu { namespace gl { @@ -20,214 +25,187 @@ struct GLFilterMode { GLint magFilter; }; +class GLVariableAllocationSupport { + friend class GLBackend; + +public: + GLVariableAllocationSupport(); + virtual ~GLVariableAllocationSupport(); + + enum class MemoryPressureState { + Idle, + Transfer, + Oversubscribed, + Undersubscribed, + }; + + using QueuePair = std::pair; + struct QueuePairLess { + bool operator()(const QueuePair& a, const QueuePair& b) { + return a.second < b.second; + } + }; + using WorkQueue = std::priority_queue, QueuePairLess>; + + class TransferJob { + using VoidLambda = std::function; + using VoidLambdaQueue = std::queue; + const GLTexture& _parent; + Texture::PixelsPointer _mipData; + size_t _transferOffset { 0 }; + size_t _transferSize { 0 }; + + bool _bufferingRequired { true }; + VoidLambda _transferLambda; + VoidLambda _bufferingLambda; + +#if THREADED_TEXTURE_BUFFERING + // Indicates if a transfer from backing storage to interal storage has started + QFuture _bufferingStatus; + static QThreadPool* _bufferThreadPool; +#endif + + public: + TransferJob(const TransferJob& other) = delete; + TransferJob(const GLTexture& parent, std::function transferLambda); + TransferJob(const GLTexture& parent, uint16_t sourceMip, uint16_t targetMip, uint8_t face, uint32_t lines = 0, uint32_t lineOffset = 0); + ~TransferJob(); + bool tryTransfer(); + +#if THREADED_TEXTURE_BUFFERING + void startBuffering(); + bool bufferingRequired() const; + bool bufferingCompleted() const; + static void startBufferingThread(); +#endif + + private: + void transfer(); + }; + + using TransferJobPointer = std::shared_ptr; + using TransferQueue = std::queue; + static MemoryPressureState _memoryPressureState; + +public: + static void addMemoryManagedTexture(const TexturePointer& texturePointer); + +protected: + static size_t _frameTexturesCreated; + static std::atomic _memoryPressureStateStale; + static std::list _memoryManagedTextures; + static WorkQueue _transferQueue; + static WorkQueue _promoteQueue; + static WorkQueue _demoteQueue; +#if THREADED_TEXTURE_BUFFERING + static TexturePointer _currentTransferTexture; + static TransferJobPointer _currentTransferJob; +#endif + static const uvec3 INITIAL_MIP_TRANSFER_DIMENSIONS; + static const uvec3 MAX_TRANSFER_DIMENSIONS; + static const size_t MAX_TRANSFER_SIZE; + + + static void updateMemoryPressure(); + static void processWorkQueues(); + static void processWorkQueue(WorkQueue& workQueue); + static TexturePointer getNextWorkQueueItem(WorkQueue& workQueue); + static void addToWorkQueue(const TexturePointer& texture); + static WorkQueue& getActiveWorkQueue(); + + static void manageMemory(); + + //bool canPromoteNoAllocate() const { return _allocatedMip < _populatedMip; } + bool canPromote() const { return _allocatedMip > _minAllocatedMip; } + bool canDemote() const { return _allocatedMip < _maxAllocatedMip; } + bool hasPendingTransfers() const { return _populatedMip > _allocatedMip; } +#if THREADED_TEXTURE_BUFFERING + void executeNextBuffer(const TexturePointer& currentTexture); +#endif + bool executeNextTransfer(const TexturePointer& currentTexture); + virtual void populateTransferQueue() = 0; + virtual void promote() = 0; + virtual void demote() = 0; + + // THe amount of memory currently allocated + Size _size { 0 }; + + // The amount of memory currnently populated + void incrementPopulatedSize(Size delta) const; + void decrementPopulatedSize(Size delta) const; + mutable Size _populatedSize { 0 }; + + // The allocated mip level, relative to the number of mips in the gpu::Texture object + // The relationship between a given glMip to the original gpu::Texture mip is always + // glMip + _allocatedMip + uint16 _allocatedMip { 0 }; + // The populated mip level, relative to the number of mips in the gpu::Texture object + // This must always be >= the allocated mip + uint16 _populatedMip { 0 }; + // The highest (lowest resolution) mip that we will support, relative to the number + // of mips in the gpu::Texture object + uint16 _maxAllocatedMip { 0 }; + // The lowest (highest resolution) mip that we will support, relative to the number + // of mips in the gpu::Texture object + uint16 _minAllocatedMip { 0 }; + // Contains a series of lambdas that when executed will transfer data to the GPU, modify + // the _populatedMip and update the sampler in order to fully populate the allocated texture + // until _populatedMip == _allocatedMip + TransferQueue _pendingTransfers; +}; class GLTexture : public GLObject { + using Parent = GLObject; + friend class GLBackend; + friend class GLVariableAllocationSupport; public: static const uint16_t INVALID_MIP { (uint16_t)-1 }; static const uint8_t INVALID_FACE { (uint8_t)-1 }; - static void initTextureTransferHelper(); - static std::shared_ptr _textureTransferHelper; - - template - static GLTexture* sync(GLBackend& backend, const TexturePointer& texturePointer, bool needTransfer) { - const Texture& texture = *texturePointer; - - // Special case external textures - //CLIMAX_MERGE_START - //Z:/HiFi_Android/HiFi_GIT/libraries/gpu-gl-android/src/gpu/gl/../gles/../gl/GLTexture.h:37:32: error: no member named 'isExternal' in 'gpu::Texture::Usage' - // The only instance of this being used again. replace. - // if (texture.getUsage().isExternal()) { - // Texture::ExternalUpdates updates = texture.getUpdates(); - // if (!updates.empty()) { - // Texture::ExternalRecycler recycler = texture.getExternalRecycler(); - // Q_ASSERT(recycler); - // // Discard any superfluous updates - // while (updates.size() > 1) { - // const auto& update = updates.front(); - // // Superfluous updates will never have been read, but we want to ensure the previous - // // writes to them are complete before they're written again, so return them with the - // // same fences they arrived with. This can happen on any thread because no GL context - // // work is involved - // recycler(update.first, update.second); - // updates.pop_front(); - // } - - // // The last texture remaining is the one we'll use to create the GLTexture - // const auto& update = updates.front(); - // // Check for a fence, and if it exists, inject a wait into the command stream, then destroy the fence - // if (update.second) { - // GLsync fence = static_cast(update.second); - // glWaitSync(fence, 0, GL_TIMEOUT_IGNORED); - // glDeleteSync(fence); - // } - - // // Create the new texture object (replaces any previous texture object) - // new GLTextureType(backend.shared_from_this(), texture, update.first); - // } - - - // Return the texture object (if any) associated with the texture, without extensive logic - // (external textures are - //return Backend::getGPUObject(texture); - //} - //CLIMAX_MERGE_END - if (!texture.isDefined()) { - // NO texture definition yet so let's avoid thinking - return nullptr; - } - - // If the object hasn't been created, or the object definition is out of date, drop and re-create - GLTexture* object = Backend::getGPUObject(texture); - - // Create the texture if need be (force re-creation if the storage stamp changes - // for easier use of immutable storage) - if (!object || object->isInvalid()) { - // This automatically any previous texture - object = new GLTextureType(backend.shared_from_this(), texture, needTransfer); - if (!object->_transferrable) { - object->createTexture(); - object->_contentStamp = texture.getDataStamp(); - object->updateSize(); - object->postTransfer(); - } - } - - // Object maybe doens't neet to be tranasferred after creation - if (!object->_transferrable) { - return object; - } - - // If we just did a transfer, return the object after doing post-transfer work - if (GLSyncState::Transferred == object->getSyncState()) { - object->postTransfer(); - } - - if (object->isOutdated()) { - // Object might be outdated, if so, start the transfer - // (outdated objects that are already in transfer will have reported 'true' for ready() - _textureTransferHelper->transferTexture(texturePointer); - return nullptr; - } - - if (!object->isReady()) { - return nullptr; - } - - ((GLTexture*)object)->updateMips(); - - return object; - } - - template - static GLuint getId(GLBackend& backend, const TexturePointer& texture, bool shouldSync) { - if (!texture) { - return 0; - } - GLTexture* object { nullptr }; - if (shouldSync) { - object = sync(backend, texture, shouldSync); - } else { - object = Backend::getGPUObject(*texture); - } - - if (!object) { - return 0; - } - - if (!shouldSync) { - return object->_id; - } - - // Don't return textures that are in transfer state - if ((object->getSyncState() != GLSyncState::Idle) || - // Don't return transferrable textures that have never completed transfer - (!object->_transferrable || 0 != object->_transferCount)) { - return 0; - } - - return object->_id; - } - ~GLTexture(); - // Is this texture generated outside the GPU library? - const bool _external; const GLuint& _texture { _id }; const std::string _source; - const Stamp _storageStamp; const GLenum _target; - const GLenum _internalFormat; - const uint16 _maxMip; - uint16 _minMip; - const GLuint _virtualSize; // theoretical size as expected - Stamp _contentStamp { 0 }; - const bool _transferrable; - Size _transferCount { 0 }; - GLuint size() const { return _size; } - GLSyncState getSyncState() const { return _syncState; } + GLTexelFormat _texelFormat; - // Is the storage out of date relative to the gpu texture? - bool isInvalid() const; + static const std::vector& getFaceTargets(GLenum textureType); + static uint8_t getFaceCount(GLenum textureType); + static GLenum getGLTextureType(const Texture& texture); - // Is the content out of date relative to the gpu texture? - bool isOutdated() const; - - // Is the texture in a state where it can be rendered with no work? - bool isReady() const; - - // Execute any post-move operations that must occur only on the main thread - virtual void postTransfer(); - - uint16 usedMipLevels() const { return (_maxMip - _minMip) + 1; } - - static const size_t CUBE_NUM_FACES = 6; - static const GLenum CUBE_FACE_LAYOUT[6]; + static const uint8_t TEXTURE_2D_NUM_FACES = 1; + static const uint8_t TEXTURE_CUBE_NUM_FACES = 6; + static const GLenum CUBE_FACE_LAYOUT[TEXTURE_CUBE_NUM_FACES]; static const GLFilterMode FILTER_MODES[Sampler::NUM_FILTERS]; static const GLenum WRAP_MODES[Sampler::NUM_WRAP_MODES]; - // Return a floating point value indicating how much of the allowed - // texture memory we are currently consuming. A value of 0 indicates - // no texture memory usage, while a value of 1 indicates all available / allowed memory - // is consumed. A value above 1 indicates that there is a problem. - static float getMemoryPressure(); protected: - - static const std::vector& getFaceTargets(GLenum textureType); - - static GLenum getGLTextureType(const Texture& texture); - - - const GLuint _size { 0 }; // true size as reported by the gl api - std::atomic _syncState { GLSyncState::Idle }; - - GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id, bool transferrable); - GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); - - void setSyncState(GLSyncState syncState) { _syncState = syncState; } - - void createTexture(); - - virtual void updateMips() {} - virtual void allocateStorage() const = 0; - virtual void updateSize() const = 0; - virtual void syncSampler() const = 0; + virtual Size size() const = 0; virtual void generateMips() const = 0; - virtual void withPreservedTexture(std::function f) const; + virtual void syncSampler() const = 0; -protected: - void setSize(GLuint size) const; + virtual Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const = 0; + virtual Size copyMipFaceFromTexture(uint16_t sourceMip, uint16_t targetMip, uint8_t face) const final; + virtual void copyTextureMipsInGPUMem(GLuint srcId, GLuint destId, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) {} // Only relevant for Variable Allocation textures - virtual void startTransfer(); - // Returns true if this is the last block required to complete transfer - virtual bool continueTransfer() { return false; } - virtual void finishTransfer(); - -private: - friend class GLTextureTransferHelper; - friend class GLBackend; + GLTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); }; +class GLExternalTexture : public GLTexture { + using Parent = GLTexture; + friend class GLBackend; +public: + ~GLExternalTexture(); +protected: + GLExternalTexture(const std::weak_ptr& backend, const Texture& texture, GLuint id); + void generateMips() const override {} + void syncSampler() const override {} + Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override { return 0;} + + Size size() const override { return 0; } +}; + + } } #endif diff --git a/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.cpp b/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.cpp deleted file mode 100644 index cec46cb90d..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.cpp +++ /dev/null @@ -1,207 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/04/03 -// Copyright 2013-2016 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 "GLTextureTransfer.h" - -#include -#include - -#include "GLShared.h" -#include "GLTexture.h" - -#ifdef HAVE_NSIGHT -#include "nvToolsExt.h" -std::unordered_map _map; -#endif - - -#ifdef TEXTURE_TRANSFER_PBOS -#define TEXTURE_TRANSFER_BLOCK_SIZE (64 * 1024) -#define TEXTURE_TRANSFER_PBO_COUNT 128 -#endif - -using namespace gpu; -using namespace gpu::gl; - -GLTextureTransferHelper::GLTextureTransferHelper() { -#ifdef THREADED_TEXTURE_TRANSFER - setObjectName("TextureTransferThread"); - _context.create(); - initialize(true, QThread::LowPriority); - // Clean shutdown on UNIX, otherwise _canvas is freed early - connect(qApp, &QCoreApplication::aboutToQuit, [&] { terminate(); }); -#else - initialize(false, QThread::LowPriority); -#endif -} - -GLTextureTransferHelper::~GLTextureTransferHelper() { -#ifdef THREADED_TEXTURE_TRANSFER - if (isStillRunning()) { - terminate(); - } -#else - terminate(); -#endif -} - -void GLTextureTransferHelper::transferTexture(const gpu::TexturePointer& texturePointer) { - GLTexture* object = Backend::getGPUObject(*texturePointer); - - //CLIMAX_MERGE_START - //Backend::incrementTextureGPUTransferCount(); - object->setSyncState(GLSyncState::Pending); - Lock lock(_mutex); - _pendingTextures.push_back(texturePointer); -} - -void GLTextureTransferHelper::setup() { -#ifdef THREADED_TEXTURE_TRANSFER - _context.makeCurrent(); - -#ifdef TEXTURE_TRANSFER_FORCE_DRAW - // FIXME don't use opengl 4.5 DSA functionality without verifying it's present - glCreateRenderbuffers(1, &_drawRenderbuffer); - glNamedRenderbufferStorage(_drawRenderbuffer, GL_RGBA8, 128, 128); - glCreateFramebuffers(1, &_drawFramebuffer); - glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _drawRenderbuffer); - glCreateFramebuffers(1, &_readFramebuffer); -#endif - -#ifdef TEXTURE_TRANSFER_PBOS - std::array pbos; - glCreateBuffers(TEXTURE_TRANSFER_PBO_COUNT, &pbos[0]); - for (uint32_t i = 0; i < TEXTURE_TRANSFER_PBO_COUNT; ++i) { - TextureTransferBlock newBlock; - newBlock._pbo = pbos[i]; - glNamedBufferStorage(newBlock._pbo, TEXTURE_TRANSFER_BLOCK_SIZE, 0, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT); - newBlock._mapped = glMapNamedBufferRange(newBlock._pbo, 0, TEXTURE_TRANSFER_BLOCK_SIZE, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_COHERENT_BIT); - _readyQueue.push(newBlock); - } -#endif -#endif -} - -void GLTextureTransferHelper::shutdown() { -#ifdef THREADED_TEXTURE_TRANSFER - _context.makeCurrent(); -#endif - -#ifdef TEXTURE_TRANSFER_FORCE_DRAW - glNamedFramebufferRenderbuffer(_drawFramebuffer, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0); - glDeleteFramebuffers(1, &_drawFramebuffer); - _drawFramebuffer = 0; - glDeleteFramebuffers(1, &_readFramebuffer); - _readFramebuffer = 0; - - glNamedFramebufferTexture(_readFramebuffer, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0); - glDeleteRenderbuffers(1, &_drawRenderbuffer); - _drawRenderbuffer = 0; -#endif -} - -void GLTextureTransferHelper::queueExecution(VoidLambda lambda) { - Lock lock(_mutex); - _pendingCommands.push_back(lambda); -} - -#define MAX_TRANSFERS_PER_PASS 2 - -bool GLTextureTransferHelper::process() { - // Take any new textures or commands off the queue - VoidLambdaList pendingCommands; - TextureList newTransferTextures; - { - Lock lock(_mutex); - newTransferTextures.swap(_pendingTextures); - pendingCommands.swap(_pendingCommands); - } - - if (!pendingCommands.empty()) { - for (auto command : pendingCommands) { - command(); - } - glFlush(); - } - - if (!newTransferTextures.empty()) { - for (auto& texturePointer : newTransferTextures) { -#ifdef HAVE_NSIGHT - _map[texturePointer] = nvtxRangeStart("TextureTansfer"); -#endif - GLTexture* object = Backend::getGPUObject(*texturePointer); - object->startTransfer(); - _transferringTextures.push_back(texturePointer); - _textureIterator = _transferringTextures.begin(); - } - _transferringTextures.sort([](const gpu::TexturePointer& a, const gpu::TexturePointer& b)->bool { - return a->getSize() < b->getSize(); - }); - } - - // No transfers in progress, sleep - if (_transferringTextures.empty()) { -#ifdef THREADED_TEXTURE_TRANSFER - QThread::usleep(1); -#endif - return true; - } - - static auto lastReport = usecTimestampNow(); - auto now = usecTimestampNow(); - auto lastReportInterval = now - lastReport; - if (lastReportInterval > USECS_PER_SECOND * 4) { - lastReport = now; - qDebug() << "Texture list " << _transferringTextures.size(); - } - - size_t transferCount = 0; - for (_textureIterator = _transferringTextures.begin(); _textureIterator != _transferringTextures.end();) { - if (++transferCount > MAX_TRANSFERS_PER_PASS) { - break; - } - auto texture = *_textureIterator; - GLTexture* gltexture = Backend::getGPUObject(*texture); - if (gltexture->continueTransfer()) { - ++_textureIterator; - continue; - } - - gltexture->finishTransfer(); - -#ifdef TEXTURE_TRANSFER_FORCE_DRAW - // FIXME force a draw on the texture transfer thread before passing the texture to the main thread for use -#endif - -#ifdef THREADED_TEXTURE_TRANSFER - clientWait(); -#endif - gltexture->_contentStamp = gltexture->_gpuObject.getDataStamp(); - gltexture->updateSize(); - gltexture->setSyncState(gpu::gl::GLSyncState::Transferred); - //CLIMAX_MERGE_START - //Backend::decrementTextureGPUTransferCount(); -#ifdef HAVE_NSIGHT - // Mark the texture as transferred - nvtxRangeEnd(_map[texture]); - _map.erase(texture); -#endif - _textureIterator = _transferringTextures.erase(_textureIterator); - } - -#ifdef THREADED_TEXTURE_TRANSFER - if (!_transferringTextures.empty()) { - // Don't saturate the GPU - clientWait(); - } else { - // Don't saturate the CPU - QThread::msleep(1); - } -#endif - - return true; -} diff --git a/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.h b/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.h deleted file mode 100644 index a23c282fd4..0000000000 --- a/libraries/gpu-gles/src/gpu/gl/GLTextureTransfer.h +++ /dev/null @@ -1,78 +0,0 @@ -// -// Created by Bradley Austin Davis on 2016/04/03 -// Copyright 2013-2016 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// -#ifndef hifi_gpu_gl_GLTextureTransfer_h -#define hifi_gpu_gl_GLTextureTransfer_h - -#include -#include - -#include - -#include - -#include "GLShared.h" - -#ifdef Q_OS_WIN -#define THREADED_TEXTURE_TRANSFER -#endif - -#ifdef THREADED_TEXTURE_TRANSFER -// FIXME when sparse textures are enabled, it's harder to force a draw on the transfer thread -// also, the current draw code is implicitly using OpenGL 4.5 functionality -//#define TEXTURE_TRANSFER_FORCE_DRAW -// FIXME PBO's increase the complexity and don't seem to work reliably -//#define TEXTURE_TRANSFER_PBOS -#endif - -namespace gpu { namespace gl { - -using TextureList = std::list; -using TextureListIterator = TextureList::iterator; - -class GLTextureTransferHelper : public GenericThread { -public: - using VoidLambda = std::function; - using VoidLambdaList = std::list; - using Pointer = std::shared_ptr; - GLTextureTransferHelper(); - ~GLTextureTransferHelper(); - void transferTexture(const gpu::TexturePointer& texturePointer); - void queueExecution(VoidLambda lambda); - - void setup() override; - void shutdown() override; - bool process() override; - -private: -#ifdef THREADED_TEXTURE_TRANSFER - ::gl::OffscreenContext _context; -#endif - -#ifdef TEXTURE_TRANSFER_FORCE_DRAW - // Framebuffers / renderbuffers for forcing access to the texture on the transfer thread - GLuint _drawRenderbuffer { 0 }; - GLuint _drawFramebuffer { 0 }; - GLuint _readFramebuffer { 0 }; -#endif - - // A mutex for protecting items access on the render and transfer threads - Mutex _mutex; - // Commands that have been submitted for execution on the texture transfer thread - VoidLambdaList _pendingCommands; - // Textures that have been submitted for transfer - TextureList _pendingTextures; - // Textures currently in the transfer process - // Only used on the transfer thread - TextureList _transferringTextures; - TextureListIterator _textureIterator; - -}; - -} } - -#endif \ No newline at end of file diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h index 69a417d952..aeda054e72 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h @@ -27,6 +27,12 @@ class GLESBackend : public GLBackend { friend class Context; public: + static const GLint TRANSFORM_OBJECT_SLOT { 31 }; + static const GLint RESOURCE_TRANSFER_TEX_UNIT { 32 }; + static const GLint RESOURCE_TRANSFER_EXTRA_TEX_UNIT { 33 }; + static const GLint RESOURCE_BUFFER_TEXBUF_TEX_UNIT { 34 }; + static const GLint RESOURCE_BUFFER_SLOT0_TEX_UNIT { 35 }; + static bool supportedTextureFormat(const gpu::Element& format); explicit GLESBackend(bool syncCache) : Parent(syncCache) {} GLESBackend() : Parent() {} virtual ~GLESBackend() { @@ -38,33 +44,95 @@ public: static const std::string GLES_VERSION; const std::string& getVersion() const override { return GLES_VERSION; } - class GLESTexture : public GLTexture { using Parent = GLTexture; - GLuint allocate(); - public: - GLESTexture(const std::weak_ptr& backend, const Texture& buffer, GLuint externalId); - GLESTexture(const std::weak_ptr& backend, const Texture& buffer, bool transferrable); - + friend class GLESBackend; + GLuint allocate(const Texture& texture); protected: - void transferMip(uint16_t mipLevel, uint8_t face) const; - void startTransfer() override; - void allocateStorage() const override; - void updateSize() const override; - void syncSampler() const override; + GLESTexture(const std::weak_ptr& backend, const Texture& buffer); void generateMips() const override; + Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override; + void syncSampler() const override; + + void withPreservedTexture(std::function f) const; }; + // + // Textures that have fixed allocation sizes and cannot be managed at runtime + // + + class GLESFixedAllocationTexture : public GLESTexture { + using Parent = GLESTexture; + friend class GLESBackend; + + public: + GLESFixedAllocationTexture(const std::weak_ptr& backend, const Texture& texture); + ~GLESFixedAllocationTexture(); + + protected: + Size size() const override { return _size; } + void allocateStorage() const; + void syncSampler() const override; + const Size _size { 0 }; + }; + + class GLESAttachmentTexture : public GLESFixedAllocationTexture { + using Parent = GLESFixedAllocationTexture; + friend class GLESBackend; + protected: + GLESAttachmentTexture(const std::weak_ptr& backend, const Texture& texture); + ~GLESAttachmentTexture(); + }; + + class GLESStrictResourceTexture : public GLESFixedAllocationTexture { + using Parent = GLESFixedAllocationTexture; + friend class GLESBackend; + protected: + GLESStrictResourceTexture(const std::weak_ptr& backend, const Texture& texture); + ~GLESStrictResourceTexture(); + }; + + class GLESVariableAllocationTexture : public GLESTexture, public GLVariableAllocationSupport { + using Parent = GLESTexture; + friend class GLESBackend; + using PromoteLambda = std::function; + + + protected: + GLESVariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture); + ~GLESVariableAllocationTexture(); + + void allocateStorage(uint16 allocatedMip); + void syncSampler() const override; + void promote() override; + void demote() override; + void populateTransferQueue() override; + + Size copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const override; + Size copyMipsFromTexture(); + + void copyTextureMipsInGPUMem(GLuint srcId, GLuint destId, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) override; + + Size size() const override { return _size; } + }; + + class GLESResourceTexture : public GLESVariableAllocationTexture { + using Parent = GLESVariableAllocationTexture; + friend class GLESBackend; + protected: + GLESResourceTexture(const std::weak_ptr& backend, const Texture& texture); + ~GLESResourceTexture(); + }; protected: GLuint getFramebufferID(const FramebufferPointer& framebuffer) override; GLFramebuffer* syncGPUObject(const Framebuffer& framebuffer) override; GLuint getBufferID(const Buffer& buffer) override; + GLuint getResourceBufferID(const Buffer& buffer); GLBuffer* syncGPUObject(const Buffer& buffer) override; - GLuint getTextureID(const TexturePointer& texture, bool needTransfer = true) override; - GLTexture* syncGPUObject(const TexturePointer& texture, bool sync = true) override; + GLTexture* syncGPUObject(const TexturePointer& texture) override; GLuint getQueryID(const QueryPointer& query) override; GLQuery* syncGPUObject(const Query& query) override; @@ -78,17 +146,25 @@ protected: void do_multiDrawIndexedIndirect(const Batch& batch, size_t paramOffset) override; // Input Stage - void updateInput() override; void resetInputStage() override; + void updateInput() override; // Synchronize the state cache of this Backend with the actual real state of the GL Context void transferTransformState(const Batch& batch) const override; void initTransform() override; - void updateTransform(const Batch& batch); - void resetTransformStage(); + void updateTransform(const Batch& batch) override; + + // Resource Stage + bool bindResourceBuffer(uint32_t slot, BufferPointer& buffer) override; + void releaseResourceBuffer(uint32_t slot) override; // Output stage void do_blit(const Batch& batch, size_t paramOffset) override; + + std::string getBackendShaderHeader() const override; + void makeProgramBindings(ShaderObject& shaderObject) override; + int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) override; + }; } } diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp index f6bdea45af..05bda34d7f 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendBuffer.cpp @@ -67,3 +67,27 @@ GLuint GLESBackend::getBufferID(const Buffer& buffer) { GLBuffer* GLESBackend::syncGPUObject(const Buffer& buffer) { return GLESBuffer::sync(*this, buffer); } + +bool GLESBackend::bindResourceBuffer(uint32_t slot, BufferPointer& buffer) { + GLBuffer* object = syncGPUObject((*buffer)); + if (object) { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, slot, object->_id); + + (void)CHECK_GL_ERROR(); + + _resource._buffers[slot] = buffer; + + return true; + } + + return false; +} + +void GLESBackend::releaseResourceBuffer(uint32_t slot) { + auto& buf = _resource._buffers[slot]; + if (buf) { + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, slot, 0); + buf.reset(); + } +} + diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp index 0b7c525fbb..dc4025247e 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendOutput.cpp @@ -53,10 +53,12 @@ public: GL_COLOR_ATTACHMENT15 }; int unit = 0; + auto backend = _backend.lock(); for (auto& b : _gpuObject.getRenderBuffers()) { surface = b._texture; if (surface) { - gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer + Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType()); + gltexture = backend->syncGPUObject(surface); } else { gltexture = nullptr; } @@ -81,9 +83,11 @@ public: } if (_gpuObject.getDepthStamp() != _depthStamp) { + auto backend = _backend.lock(); auto surface = _gpuObject.getDepthStencilBuffer(); if (_gpuObject.hasDepthStencil() && surface) { - gltexture = gl::GLTexture::sync(*_backend.lock().get(), surface, false); // Grab the gltexture and don't transfer + Q_ASSERT(TextureUsageType::RENDERBUFFER == surface->getUsageType()); + gltexture = backend->syncGPUObject(surface); } if (gltexture) { @@ -99,8 +103,8 @@ public: if (!_colorBuffers.empty()) { glDrawBuffers((GLsizei)_colorBuffers.size(), _colorBuffers.data()); } else { - static const std::vector NO_BUFFERS{ GL_NONE }; - glDrawBuffers((GLsizei)NO_BUFFERS.size(), NO_BUFFERS.data()); + GLenum DrawBuffers[1] = {GL_NONE}; + glDrawBuffers(1, DrawBuffers); } // Now check for completness @@ -111,7 +115,7 @@ public: glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentFBO); } - checkStatus(GL_DRAW_FRAMEBUFFER); + checkStatus(); } @@ -120,7 +124,7 @@ public: : Parent(backend, framebuffer, allocate()) { } }; -gl::GLFramebuffer* gpu::gles::GLESBackend::syncGPUObject(const Framebuffer& framebuffer) { +gl::GLFramebuffer* GLESBackend::syncGPUObject(const Framebuffer& framebuffer) { return GLESFramebuffer::sync(*this, framebuffer); } diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp new file mode 100644 index 0000000000..01a87978c2 --- /dev/null +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendShader.cpp @@ -0,0 +1,113 @@ +// +// Created by Sam Gateau on 2017/04/13 +// Copyright 2013-2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "GLESBackend.h" +#include "../gl/GLShader.h" + +using namespace gpu; +using namespace gpu::gl; +using namespace gpu::gles; + +// GLSL version +std::string GLESBackend::getBackendShaderHeader() const { + return Parent::getBackendShaderHeader(); +} + +int GLESBackend::makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) { + GLint ssboCount = 0; + GLint uniformsCount = 0; + + glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount); + + for (int i = 0; i < uniformsCount; i++) { + const GLint NAME_LENGTH = 256; + GLchar name[NAME_LENGTH]; + GLint length = 0; + GLint size = 0; + GLenum type = 0; + glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name); + GLint location = glGetUniformLocation(glprogram, name); + const GLint INVALID_UNIFORM_LOCATION = -1; + + // Try to make sense of the gltype + auto elementResource = getFormatFromGLUniform(type); + + // The uniform as a standard var type + if (location != INVALID_UNIFORM_LOCATION) { + + if (elementResource._resource == Resource::BUFFER) { + if (elementResource._element.getSemantic() == gpu::RESOURCE_BUFFER) { + // Let's make sure the name doesn't contains an array element + std::string sname(name); + auto foundBracket = sname.find_first_of('['); + if (foundBracket != std::string::npos) { + // std::string arrayname = sname.substr(0, foundBracket); + + if (sname[foundBracket + 1] == '0') { + sname = sname.substr(0, foundBracket); + } else { + // skip this uniform since it's not the first element of an array + continue; + } + } + + // For texture/Sampler, the location is the actual binding value + GLint binding = -1; + glGetUniformiv(glprogram, location, &binding); + + if (binding == GLESBackend::TRANSFORM_OBJECT_SLOT) { + continue; + } + + auto requestedBinding = slotBindings.find(std::string(sname)); + if (requestedBinding != slotBindings.end()) { + GLint requestedLoc = (*requestedBinding)._location + GLESBackend::RESOURCE_BUFFER_SLOT0_TEX_UNIT; + if (binding != requestedLoc) { + binding = requestedLoc; + } + } else { + binding += GLESBackend::RESOURCE_BUFFER_SLOT0_TEX_UNIT; + } + glProgramUniform1i(glprogram, location, binding); + + ssboCount++; + resourceBuffers.insert(Shader::Slot(name, binding, elementResource._element, elementResource._resource)); + } + } + } + } + + return ssboCount; +} + +void GLESBackend::makeProgramBindings(ShaderObject& shaderObject) { + if (!shaderObject.glprogram) { + return; + } + GLuint glprogram = shaderObject.glprogram; + GLint loc = -1; + + GLBackend::makeProgramBindings(shaderObject); + + // now assign the ubo binding, then DON't relink! + + //Check for gpu specific uniform slotBindings + loc = glGetUniformLocation(glprogram, "transformObjectBuffer"); + if (loc >= 0) { + glProgramUniform1i(glprogram, loc, GLESBackend::TRANSFORM_OBJECT_SLOT); + shaderObject.transformObjectSlot = GLESBackend::TRANSFORM_OBJECT_SLOT; + } + + loc = glGetUniformBlockIndex(glprogram, "transformCameraBuffer"); + if (loc >= 0) { + glUniformBlockBinding(glprogram, loc, gpu::TRANSFORM_CAMERA_SLOT); + shaderObject.transformCameraSlot = gpu::TRANSFORM_CAMERA_SLOT; + } + + (void)CHECK_GL_ERROR(); +} + diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp index 31a98edd12..d2fa2aabab 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendTexture.cpp @@ -12,41 +12,105 @@ #include #include -#include -// #include "../gl/GLTexelFormat.h" +#include "../gl/GLTexelFormat.h" using namespace gpu; using namespace gpu::gl; using namespace gpu::gles; -//using GL41TexelFormat = GLTexelFormat; +bool GLESBackend::supportedTextureFormat(const gpu::Element& format) { + // FIXME distinguish between GLES and GL compressed formats after support + // for the former is added to gpu::Element + return !format.isCompressed(); +} + +GLTexture* GLESBackend::syncGPUObject(const TexturePointer& texturePointer) { + if (!texturePointer) { + return nullptr; + } + + const Texture& texture = *texturePointer; + if (TextureUsageType::EXTERNAL == texture.getUsageType()) { + return Parent::syncGPUObject(texturePointer); + } + + if (!texture.isDefined()) { + // NO texture definition yet so let's avoid thinking + return nullptr; + } + + // Check whether the texture is in a format we can deal with + auto format = texture.getTexelFormat(); + if (!supportedTextureFormat(format)) { + return nullptr; + } + + GLESTexture* object = Backend::getGPUObject(texture); + if (!object) { + switch (texture.getUsageType()) { + case TextureUsageType::RENDERBUFFER: + object = new GLESAttachmentTexture(shared_from_this(), texture); + break; + + case TextureUsageType::RESOURCE: +// FIXME disabling variable allocation textures for now, while debugging android rendering +// and crashes +#if 0 + qCDebug(gpugllogging) << "variable / Strict texture " << texture.source().c_str(); + object = new GLESResourceTexture(shared_from_this(), texture); + GLVariableAllocationSupport::addMemoryManagedTexture(texturePointer); + break; +#endif + case TextureUsageType::STRICT_RESOURCE: + qCDebug(gpugllogging) << "Strict texture " << texture.source().c_str(); + object = new GLESStrictResourceTexture(shared_from_this(), texture); + break; + + default: + Q_UNREACHABLE(); + } + } else { + if (texture.getUsageType() == TextureUsageType::RESOURCE) { + auto varTex = static_cast (object); + + if (varTex->_minAllocatedMip > 0) { + auto minAvailableMip = texture.minAvailableMipLevel(); + if (minAvailableMip < varTex->_minAllocatedMip) { + varTex->_minAllocatedMip = minAvailableMip; + GLESVariableAllocationTexture::_memoryPressureStateStale = true; + } + } + } + } + + return object; +} + using GLESTexture = GLESBackend::GLESTexture; -GLuint GLESTexture::allocate() { - //CLIMAX_MERGE_START - //Backend::incrementTextureGPUCount(); - //CLIMAX_MERGE_END +GLESTexture::GLESTexture(const std::weak_ptr& backend, const Texture& texture) + : GLTexture(backend, texture, allocate(texture)) { +} + +void GLESTexture::withPreservedTexture(std::function f) const { + glActiveTexture(GL_TEXTURE0 + GLESBackend::RESOURCE_TRANSFER_TEX_UNIT); + glBindTexture(_target, _texture); + (void)CHECK_GL_ERROR(); + + f(); + glBindTexture(_target, 0); + (void)CHECK_GL_ERROR(); +} + + +GLuint GLESTexture::allocate(const Texture& texture) { GLuint result; glGenTextures(1, &result); return result; } -GLuint GLESBackend::getTextureID(const TexturePointer& texture, bool transfer) { - return GLESTexture::getId(*this, texture, transfer); -} -GLTexture* GLESBackend::syncGPUObject(const TexturePointer& texture, bool transfer) { - return GLESTexture::sync(*this, texture, transfer); -} - -GLESTexture::GLESTexture(const std::weak_ptr& backend, const Texture& texture, GLuint externalId) - : GLTexture(backend, texture, externalId) { -} - -GLESTexture::GLESTexture(const std::weak_ptr& backend, const Texture& texture, bool transferrable) - : GLTexture(backend, texture, allocate(), transferrable) { -} void GLESTexture::generateMips() const { withPreservedTexture([&] { @@ -55,102 +119,88 @@ void GLESTexture::generateMips() const { (void)CHECK_GL_ERROR(); } -void GLESTexture::allocateStorage() const { - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); - glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, 0); - (void)CHECK_GL_ERROR(); - glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, _maxMip - _minMip); - (void)CHECK_GL_ERROR(); -/* if (GLEW_VERSION_4_2 && !_gpuObject.getTexelFormat().isCompressed()) { - // Get the dimensions, accounting for the downgrade level - Vec3u dimensions = _gpuObject.evalMipDimensions(_minMip); - glTexStorage2D(_target, usedMipLevels(), texelFormat.internalFormat, dimensions.x, dimensions.y); - (void)CHECK_GL_ERROR(); - } else {*/ - for (uint16_t l = _minMip; l <= _maxMip; l++) { - // Get the mip level dimensions, accounting for the downgrade level - Vec3u dimensions = _gpuObject.evalMipDimensions(l); - for (GLenum target : getFaceTargets(_target)) { - glTexImage2D(target, l - _minMip, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, NULL); - (void)CHECK_GL_ERROR(); - } - } - //} -} - -void GLESTexture::updateSize() const { - setSize(_virtualSize); - if (!_id) { - return; - } - - if (_gpuObject.getTexelFormat().isCompressed()) { - GLenum proxyType = GL_TEXTURE_2D; - GLuint numFaces = 1; - if (_gpuObject.getType() == gpu::Texture::TEX_CUBE) { - proxyType = CUBE_FACE_LAYOUT[0]; - numFaces = (GLuint)CUBE_NUM_FACES; - } - GLint gpuSize{ 0 }; - glGetTexLevelParameteriv(proxyType, 0, GL_TEXTURE_COMPRESSED, &gpuSize); - (void)CHECK_GL_ERROR(); - - if (gpuSize) { - for (GLuint level = _minMip; level < _maxMip; level++) { - GLint levelSize{ 0 }; - //glGetTexLevelParameteriv(proxyType, level, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &levelSize); - //qDebug() << "TODO: GLBackendTexture.cpp:updateSize GL_TEXTURE_COMPRESSED_IMAGE_SIZE"; - levelSize *= numFaces; - - if (levelSize <= 0) { - break; - } - gpuSize += levelSize; - } - (void)CHECK_GL_ERROR(); - setSize(gpuSize); - return; - } - } -} - -// Move content bits from the CPU to the GPU for a given mip / face -void GLESTexture::transferMip(uint16_t mipLevel, uint8_t face) const { - auto mip = _gpuObject.accessStoredMipFace(mipLevel, face); - GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat(), _gpuObject.getStoredMipFormat()); - //GLenum target = getFaceTargets()[face]; - GLenum target = _target == GL_TEXTURE_2D ? GL_TEXTURE_2D : CUBE_FACE_LAYOUT[face]; - auto size = _gpuObject.evalMipDimensions(mipLevel); - glTexSubImage2D(target, mipLevel, 0, 0, size.x, size.y, texelFormat.format, texelFormat.type, mip->readData()); - (void)CHECK_GL_ERROR(); -} - -void GLESTexture::startTransfer() { - PROFILE_RANGE(render_gpu_gl, __FUNCTION__); - Parent::startTransfer(); - - glBindTexture(_target, _id); - (void)CHECK_GL_ERROR(); - - // transfer pixels from each faces - uint8_t numFaces = (Texture::TEX_CUBE == _gpuObject.getType()) ? CUBE_NUM_FACES : 1; - for (uint8_t f = 0; f < numFaces; f++) { - for (uint16_t i = 0; i < Sampler::MAX_MIP_LEVEL; ++i) { - if (_gpuObject.isStoredMipFaceAvailable(i, f)) { - transferMip(i, f); - } - } +bool isCompressedFormat(GLenum internalFormat) { + switch (internalFormat) { + case GL_COMPRESSED_R11_EAC: + case GL_COMPRESSED_SIGNED_R11_EAC: + case GL_COMPRESSED_RG11_EAC: + case GL_COMPRESSED_SIGNED_RG11_EAC: + case GL_COMPRESSED_RGB8_ETC2: + case GL_COMPRESSED_SRGB8_ETC2: + case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: + case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: + case GL_COMPRESSED_RGBA8_ETC2_EAC: + case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: + case GL_COMPRESSED_RGBA_ASTC_4x4: + case GL_COMPRESSED_RGBA_ASTC_5x4: + case GL_COMPRESSED_RGBA_ASTC_5x5: + case GL_COMPRESSED_RGBA_ASTC_6x5: + case GL_COMPRESSED_RGBA_ASTC_6x6: + case GL_COMPRESSED_RGBA_ASTC_8x5: + case GL_COMPRESSED_RGBA_ASTC_8x6: + case GL_COMPRESSED_RGBA_ASTC_8x8: + case GL_COMPRESSED_RGBA_ASTC_10x5: + case GL_COMPRESSED_RGBA_ASTC_10x6: + case GL_COMPRESSED_RGBA_ASTC_10x8: + case GL_COMPRESSED_RGBA_ASTC_10x10: + case GL_COMPRESSED_RGBA_ASTC_12x10: + case GL_COMPRESSED_RGBA_ASTC_12x12: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10: + case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12: + return true; + default: + return false; } } -void GLESBackend::GLESTexture::syncSampler() const { + +Size GLESTexture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const { + Size amountCopied = sourceSize; + if (GL_TEXTURE_2D == _target) { + if (isCompressedFormat(internalFormat)) { + glCompressedTexSubImage2D(_target, mip, 0, yOffset, size.x, size.y, internalFormat, + static_cast(sourceSize), sourcePointer); + } else { + glTexSubImage2D(_target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer); + } + } else if (GL_TEXTURE_CUBE_MAP == _target) { + auto target = GLTexture::CUBE_FACE_LAYOUT[face]; + if (isCompressedFormat(internalFormat)) { + glCompressedTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, internalFormat, + static_cast(sourceSize), sourcePointer); + } else { + glTexSubImage2D(target, mip, 0, yOffset, size.x, size.y, format, type, sourcePointer); + } + } else { + // TODO: implement for android + assert(false); + amountCopied = 0; + } + (void)CHECK_GL_ERROR(); + return amountCopied; +} + +void GLESTexture::syncSampler() const { const Sampler& sampler = _gpuObject.getSampler(); + const auto& fm = FILTER_MODES[sampler.getFilter()]; glTexParameteri(_target, GL_TEXTURE_MIN_FILTER, fm.minFilter); glTexParameteri(_target, GL_TEXTURE_MAG_FILTER, fm.magFilter); if (sampler.doComparison()) { - glTexParameteri(_target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); + glTexParameteri(_target, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); // GL_COMPARE_R_TO_TEXTURE glTexParameteri(_target, GL_TEXTURE_COMPARE_FUNC, COMPARISON_TO_GL[sampler.getComparisonFunction()]); } else { glTexParameteri(_target, GL_TEXTURE_COMPARE_MODE, GL_NONE); @@ -160,17 +210,452 @@ void GLESBackend::GLESTexture::syncSampler() const { glTexParameteri(_target, GL_TEXTURE_WRAP_T, WRAP_MODES[sampler.getWrapModeV()]); glTexParameteri(_target, GL_TEXTURE_WRAP_R, WRAP_MODES[sampler.getWrapModeW()]); - glTexParameterfv(_target, GL_TEXTURE_BORDER_COLOR_EXT, (const float*)&sampler.getBorderColor()); - + glTexParameterfv(_target, GL_TEXTURE_BORDER_COLOR, (const float*)&sampler.getBorderColor()); + //glTexParameterf(_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); - glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, (uint16)sampler.getMipOffset()); glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.f : sampler.getMaxMip())); + (void)CHECK_GL_ERROR(); - //qDebug() << "[GPU-GL-GLBackend] syncSampler 12 " << _target << "," << sampler.getMaxAnisotropy(); - //glTexParameterf(_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy()); - //(void)CHECK_GL_ERROR(); - //qDebug() << "[GPU-GL-GLBackend] syncSampler end"; +} + +using GLESFixedAllocationTexture = GLESBackend::GLESFixedAllocationTexture; + +GLESFixedAllocationTexture::GLESFixedAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : GLESTexture(backend, texture), _size(texture.evalTotalSize()) { + withPreservedTexture([&] { + allocateStorage(); + syncSampler(); + }); +} + +GLESFixedAllocationTexture::~GLESFixedAllocationTexture() { +} + +void GLESFixedAllocationTexture::allocateStorage() const { + const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); + const auto numMips = _gpuObject.getNumMips(); + + // glTextureStorage2D(_id, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); + for (GLint level = 0; level < numMips; level++) { + Vec3u dimensions = _gpuObject.evalMipDimensions(level); + for (GLenum target : getFaceTargets(_target)) { + glTexImage2D(target, level, texelFormat.internalFormat, dimensions.x, dimensions.y, 0, texelFormat.format, texelFormat.type, nullptr); + } + } + + glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(_target, GL_TEXTURE_MAX_LEVEL, numMips - 1); + (void)CHECK_GL_ERROR(); +} + +void GLESFixedAllocationTexture::syncSampler() const { + Parent::syncSampler(); + const Sampler& sampler = _gpuObject.getSampler(); + auto baseMip = std::max(sampler.getMipOffset(), sampler.getMinMip()); + + glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, baseMip); + glTexParameterf(_target, GL_TEXTURE_MIN_LOD, (float)sampler.getMinMip()); + glTexParameterf(_target, GL_TEXTURE_MAX_LOD, (sampler.getMaxMip() == Sampler::MAX_MIP_LEVEL ? 1000.0f : sampler.getMaxMip())); +} + +// Renderbuffer attachment textures +using GLESAttachmentTexture = GLESBackend::GLESAttachmentTexture; + +GLESAttachmentTexture::GLESAttachmentTexture(const std::weak_ptr& backend, const Texture& texture) : GLESFixedAllocationTexture(backend, texture) { + Backend::textureFramebufferCount.increment(); + Backend::textureFramebufferGPUMemSize.update(0, size()); +} + +GLESAttachmentTexture::~GLESAttachmentTexture() { + Backend::textureFramebufferCount.decrement(); + Backend::textureFramebufferGPUMemSize.update(size(), 0); +} + +// Strict resource textures +using GLESStrictResourceTexture = GLESBackend::GLESStrictResourceTexture; + +GLESStrictResourceTexture::GLESStrictResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GLESFixedAllocationTexture(backend, texture) { + Backend::textureResidentCount.increment(); + Backend::textureResidentGPUMemSize.update(0, size()); + + withPreservedTexture([&] { + + auto mipLevels = _gpuObject.getNumMips(); + for (uint16_t sourceMip = 0; sourceMip < mipLevels; sourceMip++) { + uint16_t targetMip = sourceMip; + size_t maxFace = GLTexture::getFaceCount(_target); + for (uint8_t face = 0; face < maxFace; face++) { + copyMipFaceFromTexture(sourceMip, targetMip, face); + } + } + }); + + if (texture.isAutogenerateMips()) { + generateMips(); + } +} + +GLESStrictResourceTexture::~GLESStrictResourceTexture() { + Backend::textureResidentCount.decrement(); + Backend::textureResidentGPUMemSize.update(size(), 0); +} + +using GLESVariableAllocationTexture = GLESBackend::GLESVariableAllocationTexture; + +GLESVariableAllocationTexture::GLESVariableAllocationTexture(const std::weak_ptr& backend, const Texture& texture) : + GLESTexture(backend, texture) +{ + Backend::textureResourceCount.increment(); + + auto mipLevels = texture.getNumMips(); + _allocatedMip = mipLevels; + _maxAllocatedMip = _populatedMip = mipLevels; + _minAllocatedMip = texture.minAvailableMipLevel(); + + uvec3 mipDimensions; + for (uint16_t mip = _minAllocatedMip; mip < mipLevels; ++mip) { + if (glm::all(glm::lessThanEqual(texture.evalMipDimensions(mip), INITIAL_MIP_TRANSFER_DIMENSIONS))) { + _maxAllocatedMip = _populatedMip = mip; + break; + } + } + + auto targetMip = _populatedMip - std::min(_populatedMip, 2); + uint16_t allocatedMip = std::max(_minAllocatedMip, targetMip); + + allocateStorage(allocatedMip); + _memoryPressureStateStale = true; + copyMipsFromTexture(); + + syncSampler(); +} + +GLESVariableAllocationTexture::~GLESVariableAllocationTexture() { + Backend::textureResourceCount.decrement(); + Backend::textureResourceGPUMemSize.update(_size, 0); + Backend::textureResourcePopulatedGPUMemSize.update(_populatedSize, 0); +} + +void GLESVariableAllocationTexture::allocateStorage(uint16 allocatedMip) { + _allocatedMip = allocatedMip; + + const GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(_gpuObject.getTexelFormat()); + const auto dimensions = _gpuObject.evalMipDimensions(_allocatedMip); + const auto totalMips = _gpuObject.getNumMips(); + const auto mips = totalMips - _allocatedMip; + withPreservedTexture([&] { + // FIXME technically GL 4.2, but OSX includes the ARB_texture_storage extension + glTexStorage2D(_target, mips, texelFormat.internalFormat, dimensions.x, dimensions.y); CHECK_GL_ERROR(); + }); + auto mipLevels = _gpuObject.getNumMips(); + _size = 0; + for (uint16_t mip = _allocatedMip; mip < mipLevels; ++mip) { + _size += _gpuObject.evalMipSize(mip); + } + Backend::textureResourceGPUMemSize.update(0, _size); } +Size GLESVariableAllocationTexture::copyMipsFromTexture() { + auto mipLevels = _gpuObject.getNumMips(); + size_t maxFace = GLTexture::getFaceCount(_target); + Size amount = 0; + for (uint16_t sourceMip = _populatedMip; sourceMip < mipLevels; ++sourceMip) { + uint16_t targetMip = sourceMip - _allocatedMip; + for (uint8_t face = 0; face < maxFace; ++face) { + amount += copyMipFaceFromTexture(sourceMip, targetMip, face); + } + } + + + return amount; +} + +Size GLESVariableAllocationTexture::copyMipFaceLinesFromTexture(uint16_t mip, uint8_t face, const uvec3& size, uint32_t yOffset, GLenum internalFormat, GLenum format, GLenum type, Size sourceSize, const void* sourcePointer) const { + Size amountCopied = 0; + withPreservedTexture([&] { + amountCopied = Parent::copyMipFaceLinesFromTexture(mip, face, size, yOffset, internalFormat, format, type, sourceSize, sourcePointer); + }); + incrementPopulatedSize(amountCopied); + return amountCopied; +} + +void GLESVariableAllocationTexture::syncSampler() const { + withPreservedTexture([&] { + Parent::syncSampler(); + glTexParameteri(_target, GL_TEXTURE_BASE_LEVEL, _populatedMip - _allocatedMip); + }); +} + + +void copyUncompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) { + // DestID must be bound to the GLESBackend::RESOURCE_TRANSFER_TEX_UNIT + + GLuint fbo { 0 }; + glGenFramebuffers(1, &fbo); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); + + uint16_t mips = numMips; + // copy pre-existing mips + for (uint16_t mip = populatedMips; mip < mips; ++mip) { + auto mipDimensions = texture.evalMipDimensions(mip); + uint16_t targetMip = mip - destMipOffset; + uint16_t sourceMip = mip - srcMipOffset; + for (GLenum target : GLTexture::getFaceTargets(texTarget)) { + glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, srcId, sourceMip); + (void)CHECK_GL_ERROR(); + glCopyTexSubImage2D(target, targetMip, 0, 0, 0, 0, mipDimensions.x, mipDimensions.y); + (void)CHECK_GL_ERROR(); + } + } + + // destroy the transfer framebuffer + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glDeleteFramebuffers(1, &fbo); +} + +void copyCompressedTexGPUMem(const gpu::Texture& texture, GLenum texTarget, GLuint srcId, GLuint destId, uint16_t numMips, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) { + // DestID must be bound to the GLESBackend::RESOURCE_TRANSFER_TEX_UNIT + + struct MipDesc { + GLint _faceSize; + GLint _size; + GLint _offset; + GLint _width; + GLint _height; + }; + std::vector sourceMips(numMips); + + std::vector bytes; + + glActiveTexture(GL_TEXTURE0 + GLESBackend::RESOURCE_TRANSFER_EXTRA_TEX_UNIT); + glBindTexture(texTarget, srcId); + const auto& faceTargets = GLTexture::getFaceTargets(texTarget); + GLint internalFormat { 0 }; + + // Collect the mip description from the source texture + GLint bufferOffset { 0 }; + for (uint16_t mip = populatedMips; mip < numMips; ++mip) { + auto& sourceMip = sourceMips[mip]; + + uint16_t sourceLevel = mip - srcMipOffset; + + // Grab internal format once + if (internalFormat == 0) { + glGetTexLevelParameteriv(faceTargets[0], sourceLevel, GL_TEXTURE_INTERNAL_FORMAT, &internalFormat); + } + + // Collect the size of the first face, and then compute the total size offset needed for this mip level + auto mipDimensions = texture.evalMipDimensions(mip); + sourceMip._width = mipDimensions.x; + sourceMip._height = mipDimensions.y; +#ifdef DEBUG_COPY + glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_WIDTH, &sourceMip._width); + glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_HEIGHT, &sourceMip._height); +#endif + // TODO: retrieve the size of a compressed image + assert(false); + //glGetTexLevelParameteriv(faceTargets.front(), sourceLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &sourceMip._faceSize); + sourceMip._size = (GLint)faceTargets.size() * sourceMip._faceSize; + sourceMip._offset = bufferOffset; + bufferOffset += sourceMip._size; + gpu::gl::checkGLError(); + } + (void)CHECK_GL_ERROR(); + + // Allocate the PBO to accomodate for all the mips to copy + GLuint pbo { 0 }; + glGenBuffers(1, &pbo); + glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo); + glBufferData(GL_PIXEL_PACK_BUFFER, bufferOffset, nullptr, GL_STATIC_COPY); + (void)CHECK_GL_ERROR(); + + // Transfer from source texture to pbo + for (uint16_t mip = populatedMips; mip < numMips; ++mip) { + auto& sourceMip = sourceMips[mip]; + + uint16_t sourceLevel = mip - srcMipOffset; + + for (GLint f = 0; f < (GLint)faceTargets.size(); f++) { + // TODO: implement for android + //glGetCompressedTexImage(faceTargets[f], sourceLevel, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize)); + } + (void)CHECK_GL_ERROR(); + } + + // Now populate the new texture from the pbo + glBindTexture(texTarget, 0); + glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo); + + glActiveTexture(GL_TEXTURE0 + GLESBackend::RESOURCE_TRANSFER_TEX_UNIT); + + // Transfer from pbo to new texture + for (uint16_t mip = populatedMips; mip < numMips; ++mip) { + auto& sourceMip = sourceMips[mip]; + + uint16_t destLevel = mip - destMipOffset; + + for (GLint f = 0; f < (GLint)faceTargets.size(); f++) { +#ifdef DEBUG_COPY + GLint destWidth, destHeight, destSize; + glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_WIDTH, &destWidth); + glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_HEIGHT, &destHeight); + glGetTexLevelParameteriv(faceTargets.front(), destLevel, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &destSize); +#endif + glCompressedTexSubImage2D(faceTargets[f], destLevel, 0, 0, sourceMip._width, sourceMip._height, internalFormat, + sourceMip._faceSize, BUFFER_OFFSET(sourceMip._offset + f * sourceMip._faceSize)); + gpu::gl::checkGLError(); + } + } + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + glDeleteBuffers(1, &pbo); +} + +void GLESVariableAllocationTexture::copyTextureMipsInGPUMem(GLuint srcId, GLuint destId, uint16_t srcMipOffset, uint16_t destMipOffset, uint16_t populatedMips) { + uint16_t numMips = _gpuObject.getNumMips(); + withPreservedTexture([&] { + if (_texelFormat.isCompressed()) { + copyCompressedTexGPUMem(_gpuObject, _target, srcId, destId, numMips, srcMipOffset, destMipOffset, populatedMips); + } else { + copyUncompressedTexGPUMem(_gpuObject, _target, srcId, destId, numMips, srcMipOffset, destMipOffset, populatedMips); + } + }); +} + +void GLESVariableAllocationTexture::promote() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + Q_ASSERT(_allocatedMip > 0); + + uint16_t targetAllocatedMip = _allocatedMip - std::min(_allocatedMip, 2); + targetAllocatedMip = std::max(_minAllocatedMip, targetAllocatedMip); + + GLuint oldId = _id; + auto oldSize = _size; + uint16_t oldAllocatedMip = _allocatedMip; + + // create new texture + const_cast(_id) = allocate(_gpuObject); + + // allocate storage for new level + allocateStorage(targetAllocatedMip); + + // copy pre-existing mips + copyTextureMipsInGPUMem(oldId, _id, oldAllocatedMip, _allocatedMip, _populatedMip); + + // destroy the old texture + glDeleteTextures(1, &oldId); + + // Update sampler + syncSampler(); + + // update the memory usage + Backend::textureResourceGPUMemSize.update(oldSize, 0); + // no change to Backend::textureResourcePopulatedGPUMemSize + + populateTransferQueue(); +} + +void GLESVariableAllocationTexture::demote() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + Q_ASSERT(_allocatedMip < _maxAllocatedMip); + auto oldId = _id; + auto oldSize = _size; + auto oldPopulatedMip = _populatedMip; + + // allocate new texture + const_cast(_id) = allocate(_gpuObject); + uint16_t oldAllocatedMip = _allocatedMip; + allocateStorage(_allocatedMip + 1); + _populatedMip = std::max(_populatedMip, _allocatedMip); + + + // copy pre-existing mips + copyTextureMipsInGPUMem(oldId, _id, oldAllocatedMip, _allocatedMip, _populatedMip); + + // destroy the old texture + glDeleteTextures(1, &oldId); + + // Update sampler + syncSampler(); + + // update the memory usage + Backend::textureResourceGPUMemSize.update(oldSize, 0); + // Demoting unpopulate the memory delta + if (oldPopulatedMip != _populatedMip) { + auto numPopulatedDemoted = _populatedMip - oldPopulatedMip; + Size amountUnpopulated = 0; + for (int i = 0; i < numPopulatedDemoted; i++) { + amountUnpopulated += _gpuObject.evalMipSize(oldPopulatedMip + i); + } + decrementPopulatedSize(amountUnpopulated); + } + populateTransferQueue(); +} + + +void GLESVariableAllocationTexture::populateTransferQueue() { + PROFILE_RANGE(render_gpu_gl, __FUNCTION__); + if (_populatedMip <= _allocatedMip) { + return; + } + _pendingTransfers = TransferQueue(); + + const uint8_t maxFace = GLTexture::getFaceCount(_target); + uint16_t sourceMip = _populatedMip; + do { + --sourceMip; + auto targetMip = sourceMip - _allocatedMip; + auto mipDimensions = _gpuObject.evalMipDimensions(sourceMip); + for (uint8_t face = 0; face < maxFace; ++face) { + if (!_gpuObject.isStoredMipFaceAvailable(sourceMip, face)) { + continue; + } + + // If the mip is less than the max transfer size, then just do it in one transfer + if (glm::all(glm::lessThanEqual(mipDimensions, MAX_TRANSFER_DIMENSIONS))) { + // Can the mip be transferred in one go + _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face)); + continue; + } + + // break down the transfers into chunks so that no single transfer is + // consuming more than X bandwidth + // For compressed format, regions must be a multiple of the 4x4 tiles, so enforce 4 lines as the minimum block + auto mipSize = _gpuObject.getStoredMipFaceSize(sourceMip, face); + const auto lines = mipDimensions.y; + const uint32_t BLOCK_NUM_LINES { 4 }; + const auto numBlocks = (lines + (BLOCK_NUM_LINES - 1)) / BLOCK_NUM_LINES; + auto bytesPerBlock = mipSize / numBlocks; + Q_ASSERT(0 == (mipSize % lines)); + uint32_t linesPerTransfer = BLOCK_NUM_LINES * (uint32_t)(MAX_TRANSFER_SIZE / bytesPerBlock); + uint32_t lineOffset = 0; + while (lineOffset < lines) { + uint32_t linesToCopy = std::min(lines - lineOffset, linesPerTransfer); + _pendingTransfers.emplace(new TransferJob(*this, sourceMip, targetMip, face, linesToCopy, lineOffset)); + lineOffset += linesToCopy; + } + } + + // queue up the sampler and populated mip change for after the transfer has completed + _pendingTransfers.emplace(new TransferJob(*this, [=] { + _populatedMip = sourceMip; + syncSampler(); + })); + } while (sourceMip != _allocatedMip); +} + +// resource textures +using GLESResourceTexture = GLESBackend::GLESResourceTexture; + +GLESResourceTexture::GLESResourceTexture(const std::weak_ptr& backend, const Texture& texture) : GLESVariableAllocationTexture(backend, texture) { + if (texture.isAutogenerateMips()) { + generateMips(); + } +} + +GLESResourceTexture::~GLESResourceTexture() { +} + + diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackendTransform.cpp b/libraries/gpu-gles/src/gpu/gles/GLESBackendTransform.cpp index 5050db6edd..7d33ca822d 100644 --- a/libraries/gpu-gles/src/gpu/gles/GLESBackendTransform.cpp +++ b/libraries/gpu-gles/src/gpu/gles/GLESBackendTransform.cpp @@ -38,9 +38,9 @@ void GLESBackend::transferTransformState(const Batch& batch) const { } if (!batch._objects.empty()) { - glBindBuffer(GL_SHADER_STORAGE_BUFFER, _transform._objectBuffer); - glBufferData(GL_SHADER_STORAGE_BUFFER, batch._objects.size() * sizeof(Batch::TransformObject), batch._objects.data(), GL_STREAM_DRAW); - glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + glBindBuffer(GL_TEXTURE_BUFFER, _transform._objectBuffer); + glBufferData(GL_TEXTURE_BUFFER, batch._objects.size() * sizeof(Batch::TransformObject), batch._objects.data(), GL_DYNAMIC_DRAW); + glBindBuffer(GL_TEXTURE_BUFFER, 0); } if (!batch._namedData.empty()) { @@ -58,10 +58,44 @@ void GLESBackend::transferTransformState(const Batch& batch) const { glBindBuffer(GL_ARRAY_BUFFER, 0); } - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, TRANSFORM_OBJECT_SLOT, _transform._objectBuffer); + glActiveTexture(GL_TEXTURE0 + GLESBackend::TRANSFORM_OBJECT_SLOT); + glBindTexture(GL_TEXTURE_BUFFER, _transform._objectBufferTexture); + if (!batch._objects.empty()) { + glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, _transform._objectBuffer); + } CHECK_GL_ERROR(); // Make sure the current Camera offset is unknown before render Draw _transform._currentCameraOffset = INVALID_OFFSET; } + + +void GLESBackend::updateTransform(const Batch& batch) { + _transform.update(_commandIndex, _stereo); + + auto& drawCallInfoBuffer = batch.getDrawCallInfoBuffer(); + if (batch._currentNamedCall.empty()) { + auto& drawCallInfo = drawCallInfoBuffer[_currentDraw]; + if (_transform._enabledDrawcallInfoBuffer) { + glDisableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); // Make sure attrib array is disabled + _transform._enabledDrawcallInfoBuffer = false; + } + glVertexAttribI4i(gpu::Stream::DRAW_CALL_INFO, drawCallInfo.index, drawCallInfo.unused, 0, 0); + //glVertexAttribI2i(gpu::Stream::DRAW_CALL_INFO, drawCallInfo.index, drawCallInfo.unused); + } else { + if (!_transform._enabledDrawcallInfoBuffer) { + glEnableVertexAttribArray(gpu::Stream::DRAW_CALL_INFO); // Make sure attrib array is enabled +#ifdef GPU_STEREO_DRAWCALL_INSTANCED + glVertexAttribDivisor(gpu::Stream::DRAW_CALL_INFO, (isStereo() ? 2 : 1)); +#else + glVertexAttribDivisor(gpu::Stream::DRAW_CALL_INFO, 1); +#endif + _transform._enabledDrawcallInfoBuffer = true; + } + glBindBuffer(GL_ARRAY_BUFFER, _transform._drawCallInfoBuffer); + glVertexAttribIPointer(gpu::Stream::DRAW_CALL_INFO, 2, GL_UNSIGNED_SHORT, 0, _transform._drawCallInfoOffsets[batch._currentNamedCall]); + } + + (void)CHECK_GL_ERROR(); +} \ No newline at end of file diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 4f328747ac..4f4ea33679 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -269,6 +269,10 @@ public: void _glColor4f(float red, float green, float blue, float alpha); + // Maybe useful but shoudln't be public. Please convince me otherwise + // Well porting to gles i need it... + void runLambda(std::function f); + enum Command { COMMAND_draw = 0, COMMAND_drawIndexed, @@ -502,8 +506,7 @@ protected: void startNamedCall(const std::string& name); void stopNamedCall(); - // Maybe useful but shoudln't be public. Please convince me otherwise - void runLambda(std::function f); + void captureDrawCallInfoImpl(); }; diff --git a/libraries/gpu/src/gpu/Format.cpp b/libraries/gpu/src/gpu/Format.cpp index 3b153097cf..c4b6b39723 100644 --- a/libraries/gpu/src/gpu/Format.cpp +++ b/libraries/gpu/src/gpu/Format.cpp @@ -38,6 +38,7 @@ const Element Element::VEC2F_UV{ VEC2, FLOAT, UV }; const Element Element::VEC2F_XY{ VEC2, FLOAT, XY }; const Element Element::VEC3F_XYZ{ VEC3, FLOAT, XYZ }; const Element Element::VEC4F_XYZW{ VEC4, FLOAT, XYZW }; +const Element Element::VEC4F_NORMALIZED_XYZ10W2{ VEC4, NINT2_10_10_10, XYZW }; const Element Element::INDEX_UINT16 { SCALAR, UINT16, INDEX }; const Element Element::INDEX_INT32 { SCALAR, INT32, INDEX }; const Element Element::PART_DRAWCALL{ VEC4, UINT32, PART }; diff --git a/libraries/gpu/src/gpu/Format.h b/libraries/gpu/src/gpu/Format.h index 9d5d2fc49d..17102d0415 100644 --- a/libraries/gpu/src/gpu/Format.h +++ b/libraries/gpu/src/gpu/Format.h @@ -39,6 +39,7 @@ enum Type : uint8_t { NINT8, NUINT8, NUINT2, + NINT2_10_10_10, COMPRESSED, @@ -65,6 +66,7 @@ static const int TYPE_SIZE[NUM_TYPES] = { 2, 1, 1, + 4, 1 }; @@ -86,6 +88,7 @@ static const bool TYPE_IS_INTEGER[NUM_TYPES] = { false, false, false, + false, false, }; @@ -326,6 +329,7 @@ public: static const Element VEC2F_XY; static const Element VEC3F_XYZ; static const Element VEC4F_XYZW; + static const Element VEC4F_NORMALIZED_XYZ10W2; static const Element INDEX_UINT16; static const Element INDEX_INT32; static const Element PART_DRAWCALL; diff --git a/libraries/gpu/src/gpu/Framebuffer.h b/libraries/gpu/src/gpu/Framebuffer.h index b3cf0fbba3..f470cc8aa9 100755 --- a/libraries/gpu/src/gpu/Framebuffer.h +++ b/libraries/gpu/src/gpu/Framebuffer.h @@ -134,7 +134,7 @@ public: float getAspectRatio() const { return getWidth() / (float) getHeight() ; } -#ifndef ANDROID +#if !defined(Q_OS_ANDROID) static const uint32 MAX_NUM_RENDER_BUFFERS = 8; #else static const uint32 MAX_NUM_RENDER_BUFFERS = 4; diff --git a/libraries/gpu/src/gpu/Stream.cpp b/libraries/gpu/src/gpu/Stream.cpp index 427af1e78d..caa1ecbc06 100644 --- a/libraries/gpu/src/gpu/Stream.cpp +++ b/libraries/gpu/src/gpu/Stream.cpp @@ -92,6 +92,15 @@ bool Stream::Format::setAttribute(Slot slot, Slot channel, Frequency frequency) return true; } +Stream::Attribute Stream::Format::getAttribute(Slot slot) const { + auto attribIt = _attributes.find(slot); + if (attribIt != _attributes.end()) { + return attribIt->second; + } else { + return Attribute(); + } +} + void BufferStream::addBuffer(const BufferPointer& buffer, Offset offset, Offset stride) { _buffers.push_back(buffer); _offsets.push_back(offset); diff --git a/libraries/gpu/src/gpu/Stream.h b/libraries/gpu/src/gpu/Stream.h index 336e34ecb4..5562980a91 100644 --- a/libraries/gpu/src/gpu/Stream.h +++ b/libraries/gpu/src/gpu/Stream.h @@ -112,6 +112,7 @@ public: bool setAttribute(Slot slot, Slot channel, Frequency frequency = PER_VERTEX); bool hasAttribute(Slot slot) const { return (_attributes.find(slot) != _attributes.end()); } + Attribute getAttribute(Slot slot) const; const std::string& getKey() const { return _key; } diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h index 06208179e0..ad3dc5fada 100755 --- a/libraries/gpu/src/gpu/Texture.h +++ b/libraries/gpu/src/gpu/Texture.h @@ -539,7 +539,7 @@ public: static TexturePointer build(const ktx::KTXDescriptor& descriptor); static TexturePointer unserialize(const std::string& ktxFile); - static TexturePointer unserialize(const cache::FilePointer& cacheEntry); + static TexturePointer unserialize(const cache::FilePointer& cacheEntry, const std::string& source = std::string()); static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header); static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat); diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp index 883d9abf15..754bdca445 100644 --- a/libraries/gpu/src/gpu/Texture_ktx.cpp +++ b/libraries/gpu/src/gpu/Texture_ktx.cpp @@ -499,7 +499,9 @@ TexturePointer Texture::build(const ktx::KTXDescriptor& descriptor) { GPUKTXPayload gpuktxKeyValue; if (!GPUKTXPayload::findInKeyValues(descriptor.keyValues, gpuktxKeyValue)) { qCWarning(gpulogging) << "Could not find GPUKTX key values."; - return TexturePointer(); + // FIXME use sensible defaults based on the texture type and format + gpuktxKeyValue._usageType = TextureUsageType::RESOURCE; + gpuktxKeyValue._usage = Texture::Usage::Builder().withColor().withAlpha().build(); } auto texture = create(gpuktxKeyValue._usageType, @@ -525,7 +527,7 @@ TexturePointer Texture::build(const ktx::KTXDescriptor& descriptor) { return texture; } -TexturePointer Texture::unserialize(const cache::FilePointer& cacheEntry) { +TexturePointer Texture::unserialize(const cache::FilePointer& cacheEntry, const std::string& source) { std::unique_ptr ktxPointer = ktx::KTX::create(std::make_shared(cacheEntry->getFilepath().c_str())); if (!ktxPointer) { return nullptr; @@ -534,6 +536,9 @@ TexturePointer Texture::unserialize(const cache::FilePointer& cacheEntry) { auto texture = build(ktxPointer->toDescriptor()); if (texture) { texture->setKtxBacking(cacheEntry); + if (texture->source().empty()) { + texture->setSource(source); + } } return texture; @@ -548,6 +553,7 @@ TexturePointer Texture::unserialize(const std::string& ktxfile) { auto texture = build(ktxPointer->toDescriptor()); if (texture) { texture->setKtxBacking(ktxfile); + texture->setSource(ktxfile); } return texture; diff --git a/libraries/model/CMakeLists.txt b/libraries/graphics/CMakeLists.txt similarity index 50% rename from libraries/model/CMakeLists.txt rename to libraries/graphics/CMakeLists.txt index da85b6aa3d..2b15604fdf 100755 --- a/libraries/model/CMakeLists.txt +++ b/libraries/graphics/CMakeLists.txt @@ -1,4 +1,4 @@ -set(TARGET_NAME model) -AUTOSCRIBE_SHADER_LIB(gpu model) +set(TARGET_NAME graphics) +AUTOSCRIBE_SHADER_LIB(gpu graphics) setup_hifi_library() link_hifi_libraries(shared ktx gpu image) \ No newline at end of file diff --git a/libraries/model/src/model/Asset.cpp b/libraries/graphics/src/graphics/Asset.cpp similarity index 83% rename from libraries/model/src/model/Asset.cpp rename to libraries/graphics/src/graphics/Asset.cpp index d839dad809..ef5f010b4e 100644 --- a/libraries/model/src/model/Asset.cpp +++ b/libraries/graphics/src/graphics/Asset.cpp @@ -1,6 +1,6 @@ // // Asset.cpp -// libraries/model/src/model +// libraries/graphics/src/graphics // // Created by Sam Gateau on 08/21/2015. // Copyright 2015 High Fidelity, Inc. @@ -10,7 +10,7 @@ // #include "Asset.h" -using namespace model; +using namespace graphics; Asset::Asset() { } diff --git a/libraries/model/src/model/Asset.h b/libraries/graphics/src/graphics/Asset.h similarity index 97% rename from libraries/model/src/model/Asset.h rename to libraries/graphics/src/graphics/Asset.h index 51fc177538..86d98c1a69 100644 --- a/libraries/model/src/model/Asset.h +++ b/libraries/graphics/src/graphics/Asset.h @@ -1,6 +1,6 @@ // // Asset.h -// libraries/model/src/model +// libraries/graphics/src/graphics // // Created by Sam Gateau on 08/21/2015. // Copyright 2015 High Fidelity, Inc. @@ -17,7 +17,7 @@ #include "Material.h" #include "Geometry.h" -namespace model { +namespace graphics { template class Table { diff --git a/libraries/model/src/model/Forward.h b/libraries/graphics/src/graphics/Forward.h similarity index 87% rename from libraries/model/src/model/Forward.h rename to libraries/graphics/src/graphics/Forward.h index 90f55fa64f..95d2d73099 100644 --- a/libraries/model/src/model/Forward.h +++ b/libraries/graphics/src/graphics/Forward.h @@ -1,6 +1,6 @@ // // Forward.h -// libraries/model/src/model +// libraries/graphics/src/graphics // // Created by Bradley Austin Davis on 2017/06/21 // Copyright 2013-2017 High Fidelity, Inc. @@ -11,7 +11,7 @@ #ifndef hifi_model_Forward_h #define hifi_model_Forward_h -namespace model { +namespace graphics { class Mesh; using MeshPointer = std::shared_ptr; } diff --git a/libraries/model/src/model/Geometry.cpp b/libraries/graphics/src/graphics/Geometry.cpp similarity index 67% rename from libraries/model/src/model/Geometry.cpp rename to libraries/graphics/src/graphics/Geometry.cpp index 5627746c43..ba5afcbc62 100755 --- a/libraries/model/src/model/Geometry.cpp +++ b/libraries/graphics/src/graphics/Geometry.cpp @@ -1,6 +1,6 @@ // // Geometry.cpp -// libraries/model/src/model +// libraries/graphics/src/graphics // // Created by Sam Gateau on 12/5/2014. // Copyright 2014 High Fidelity, Inc. @@ -11,7 +11,9 @@ #include "Geometry.h" -using namespace model; +#include + +using namespace graphics; Mesh::Mesh() : _vertexBuffer(gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)), @@ -72,12 +74,14 @@ void Mesh::evalVertexStream() { int channelNum = 0; if (hasVertexData()) { - _vertexStream.addBuffer(_vertexBuffer._buffer, _vertexBuffer._offset, _vertexFormat->getChannelStride(channelNum)); + auto stride = glm::max(_vertexFormat->getChannelStride(channelNum), _vertexBuffer._stride); + _vertexStream.addBuffer(_vertexBuffer._buffer, _vertexBuffer._offset, stride); channelNum++; } for (auto attrib : _attributeBuffers) { BufferView& view = attrib.second; - _vertexStream.addBuffer(view._buffer, view._offset, _vertexFormat->getChannelStride(channelNum)); + auto stride = glm::max(_vertexFormat->getChannelStride(channelNum), view._stride); + _vertexStream.addBuffer(view._buffer, view._offset, stride); channelNum++; } } @@ -133,17 +137,19 @@ Box Mesh::evalPartsBound(int partStart, int partEnd) const { } -model::MeshPointer Mesh::map(std::function vertexFunc, +graphics::MeshPointer Mesh::map(std::function vertexFunc, std::function colorFunc, std::function normalFunc, std::function indexFunc) const { + const auto vertexFormat = getVertexFormat(); + // vertex data const gpu::BufferView& vertexBufferView = getVertexBuffer(); gpu::BufferView::Index numVertices = (gpu::BufferView::Index)getNumVertices(); gpu::Resource::Size vertexSize = numVertices * sizeof(glm::vec3); - unsigned char* resultVertexData = new unsigned char[vertexSize]; - unsigned char* vertexDataCursor = resultVertexData; + std::unique_ptr resultVertexData{ new unsigned char[vertexSize] }; + unsigned char* vertexDataCursor = resultVertexData.get(); for (gpu::BufferView::Index i = 0; i < numVertices; i++) { glm::vec3 pos = vertexFunc(vertexBufferView.get(i)); @@ -157,13 +163,24 @@ model::MeshPointer Mesh::map(std::function vertexFunc, gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements(); gpu::Resource::Size colorSize = numColors * sizeof(glm::vec3); - unsigned char* resultColorData = new unsigned char[colorSize]; - unsigned char* colorDataCursor = resultColorData; + std::unique_ptr resultColorData{ new unsigned char[colorSize] }; + unsigned char* colorDataCursor = resultColorData.get(); + auto colorAttribute = vertexFormat->getAttribute(attributeTypeColor); - for (gpu::BufferView::Index i = 0; i < numColors; i++) { - glm::vec3 color = colorFunc(colorsBufferView.get(i)); - memcpy(colorDataCursor, &color, sizeof(color)); - colorDataCursor += sizeof(color); + if (colorAttribute._element == gpu::Element::VEC3F_XYZ) { + for (gpu::BufferView::Index i = 0; i < numColors; i++) { + glm::vec3 color = colorFunc(colorsBufferView.get(i)); + memcpy(colorDataCursor, &color, sizeof(color)); + colorDataCursor += sizeof(color); + } + } else if (colorAttribute._element == gpu::Element::COLOR_RGBA_32) { + for (gpu::BufferView::Index i = 0; i < numColors; i++) { + auto rawColor = colorsBufferView.get(i); + auto color = glm::vec3(glm::unpackUnorm4x8(rawColor)); + color = colorFunc(color); + memcpy(colorDataCursor, &color, sizeof(color)); + colorDataCursor += sizeof(color); + } } // normal data @@ -171,22 +188,34 @@ model::MeshPointer Mesh::map(std::function vertexFunc, const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal); gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements(); gpu::Resource::Size normalSize = numNormals * sizeof(glm::vec3); - unsigned char* resultNormalData = new unsigned char[normalSize]; - unsigned char* normalDataCursor = resultNormalData; + std::unique_ptr resultNormalData{ new unsigned char[normalSize] }; + unsigned char* normalDataCursor = resultNormalData.get(); + auto normalAttribute = vertexFormat->getAttribute(attributeTypeNormal); - for (gpu::BufferView::Index i = 0; i < numNormals; i++) { - glm::vec3 normal = normalFunc(normalsBufferView.get(i)); - memcpy(normalDataCursor, &normal, sizeof(normal)); - normalDataCursor += sizeof(normal); + if (normalAttribute._element == gpu::Element::VEC3F_XYZ) { + for (gpu::BufferView::Index i = 0; i < numNormals; i++) { + glm::vec3 normal = normalFunc(normalsBufferView.get(i)); + memcpy(normalDataCursor, &normal, sizeof(normal)); + normalDataCursor += sizeof(normal); + } + } else if (normalAttribute._element == gpu::Element::VEC4F_NORMALIZED_XYZ10W2) { + for (gpu::BufferView::Index i = 0; i < numColors; i++) { + auto packedNormal = normalsBufferView.get(i); + auto normal = glm::vec3(glm::unpackSnorm3x10_1x2(packedNormal)); + normal = normalFunc(normal); + memcpy(normalDataCursor, &normal, sizeof(normal)); + normalDataCursor += sizeof(normal); + } } + // TODO -- other attributes // face data const gpu::BufferView& indexBufferView = getIndexBuffer(); gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)getNumIndices(); gpu::Resource::Size indexSize = numIndexes * sizeof(uint32_t); - unsigned char* resultIndexData = new unsigned char[indexSize]; - unsigned char* indexDataCursor = resultIndexData; + std::unique_ptr resultIndexData{ new unsigned char[indexSize] }; + unsigned char* indexDataCursor = resultIndexData.get(); for (gpu::BufferView::Index i = 0; i < numIndexes; i++) { uint32_t index = indexFunc(indexBufferView.get(i)); @@ -194,28 +223,28 @@ model::MeshPointer Mesh::map(std::function vertexFunc, indexDataCursor += sizeof(index); } - model::MeshPointer result(new model::Mesh()); + graphics::MeshPointer result(new graphics::Mesh()); gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); - gpu::Buffer* resultVertexBuffer = new gpu::Buffer(vertexSize, resultVertexData); + gpu::Buffer* resultVertexBuffer = new gpu::Buffer(vertexSize, resultVertexData.get()); gpu::BufferPointer resultVertexBufferPointer(resultVertexBuffer); gpu::BufferView resultVertexBufferView(resultVertexBufferPointer, vertexElement); result->setVertexBuffer(resultVertexBufferView); gpu::Element colorElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); - gpu::Buffer* resultColorsBuffer = new gpu::Buffer(colorSize, resultColorData); + gpu::Buffer* resultColorsBuffer = new gpu::Buffer(colorSize, resultColorData.get()); gpu::BufferPointer resultColorsBufferPointer(resultColorsBuffer); gpu::BufferView resultColorsBufferView(resultColorsBufferPointer, colorElement); result->addAttribute(attributeTypeColor, resultColorsBufferView); gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); - gpu::Buffer* resultNormalsBuffer = new gpu::Buffer(normalSize, resultNormalData); + gpu::Buffer* resultNormalsBuffer = new gpu::Buffer(normalSize, resultNormalData.get()); gpu::BufferPointer resultNormalsBufferPointer(resultNormalsBuffer); gpu::BufferView resultNormalsBufferView(resultNormalsBufferPointer, normalElement); result->addAttribute(attributeTypeNormal, resultNormalsBufferView); gpu::Element indexElement = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW); - gpu::Buffer* resultIndexesBuffer = new gpu::Buffer(indexSize, resultIndexData); + gpu::Buffer* resultIndexesBuffer = new gpu::Buffer(indexSize, resultIndexData.get()); gpu::BufferPointer resultIndexesBufferPointer(resultIndexesBuffer); gpu::BufferView resultIndexesBufferView(resultIndexesBufferPointer, indexElement); result->setIndexBuffer(resultIndexesBufferView); @@ -223,12 +252,12 @@ model::MeshPointer Mesh::map(std::function vertexFunc, // TODO -- shouldn't assume just one part - std::vector parts; - parts.emplace_back(model::Mesh::Part((model::Index)0, // startIndex - (model::Index)result->getNumIndices(), // numIndices - (model::Index)0, // baseVertex - model::Mesh::TRIANGLES)); // topology - result->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), + std::vector parts; + parts.emplace_back(graphics::Mesh::Part((graphics::Index)0, // startIndex + (graphics::Index)result->getNumIndices(), // numIndices + (graphics::Index)0, // baseVertex + graphics::Mesh::TRIANGLES)); // topology + result->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(graphics::Mesh::Part), (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); return result; @@ -239,6 +268,8 @@ void Mesh::forEach(std::function vertexFunc, std::function colorFunc, std::function normalFunc, std::function indexFunc) { + const auto vertexFormat = getVertexFormat(); + // vertex data const gpu::BufferView& vertexBufferView = getVertexBuffer(); gpu::BufferView::Index numVertices = (gpu::BufferView::Index)getNumVertices(); @@ -250,17 +281,36 @@ void Mesh::forEach(std::function vertexFunc, int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h const gpu::BufferView& colorsBufferView = getAttributeBuffer(attributeTypeColor); gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements(); - for (gpu::BufferView::Index i = 0; i < numColors; i++) { - colorFunc(colorsBufferView.get(i)); + auto colorAttribute = vertexFormat->getAttribute(attributeTypeColor); + if (colorAttribute._element == gpu::Element::VEC3F_XYZ) { + for (gpu::BufferView::Index i = 0; i < numColors; i++) { + colorFunc(colorsBufferView.get(i)); + } + } else if (colorAttribute._element == gpu::Element::COLOR_RGBA_32) { + for (gpu::BufferView::Index i = 0; i < numColors; i++) { + auto rawColor = colorsBufferView.get(i); + auto color = glm::unpackUnorm4x8(rawColor); + colorFunc(color); + } } // normal data int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal); gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements(); - for (gpu::BufferView::Index i = 0; i < numNormals; i++) { - normalFunc(normalsBufferView.get(i)); + auto normalAttribute = vertexFormat->getAttribute(attributeTypeNormal); + if (normalAttribute._element == gpu::Element::VEC3F_XYZ) { + for (gpu::BufferView::Index i = 0; i < numNormals; i++) { + normalFunc(normalsBufferView.get(i)); + } + } else if (normalAttribute._element == gpu::Element::VEC4F_NORMALIZED_XYZ10W2) { + for (gpu::BufferView::Index i = 0; i < numColors; i++) { + auto packedNormal = normalsBufferView.get(i); + auto normal = glm::unpackSnorm3x10_1x2(packedNormal); + normalFunc(normal); + } } + // TODO -- other attributes // face data @@ -300,9 +350,9 @@ MeshPointer Mesh::createIndexedTriangles_P3F(uint32_t numVertices, uint32_t numI } - std::vector parts; - parts.push_back(model::Mesh::Part(0, numIndices, 0, model::Mesh::TRIANGLES)); - mesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); + std::vector parts; + parts.push_back(graphics::Mesh::Part(0, numIndices, 0, graphics::Mesh::TRIANGLES)); + mesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(graphics::Mesh::Part), (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); return mesh; } diff --git a/libraries/model/src/model/Geometry.h b/libraries/graphics/src/graphics/Geometry.h similarity index 98% rename from libraries/model/src/model/Geometry.h rename to libraries/graphics/src/graphics/Geometry.h index 260de313ab..cd64a7c185 100755 --- a/libraries/model/src/model/Geometry.h +++ b/libraries/graphics/src/graphics/Geometry.h @@ -1,6 +1,6 @@ // // Geometry.h -// libraries/model/src/model +// libraries/graphics/src/graphics // // Created by Sam Gateau on 12/5/2014. // Copyright 2014 High Fidelity, Inc. @@ -18,7 +18,7 @@ #include #include -namespace model { +namespace graphics { typedef gpu::BufferView::Index Index; typedef gpu::BufferView BufferView; typedef AABox Box; @@ -40,7 +40,7 @@ public: typedef gpu::Stream::Format VertexFormat; typedef std::map< Slot, BufferView > BufferViewMap; - typedef model::Vec3 Vec3; + typedef graphics::Vec3 Vec3; Mesh(); Mesh(const Mesh& mesh); diff --git a/libraries/model/src/model/ModelLogging.cpp b/libraries/graphics/src/graphics/GraphicsLogging.cpp similarity index 75% rename from libraries/model/src/model/ModelLogging.cpp rename to libraries/graphics/src/graphics/GraphicsLogging.cpp index 3b3fbed82c..9c46e89331 100644 --- a/libraries/model/src/model/ModelLogging.cpp +++ b/libraries/graphics/src/graphics/GraphicsLogging.cpp @@ -6,6 +6,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "ModelLogging.h" +#include "GraphicsLogging.h" -Q_LOGGING_CATEGORY(modelLog, "hifi.model") \ No newline at end of file +Q_LOGGING_CATEGORY(graphicsLog, "hifi.graphics") diff --git a/libraries/model/src/model/ModelLogging.h b/libraries/graphics/src/graphics/GraphicsLogging.h similarity index 81% rename from libraries/model/src/model/ModelLogging.h rename to libraries/graphics/src/graphics/GraphicsLogging.h index 33ed6fb059..29e3a7ffcd 100644 --- a/libraries/model/src/model/ModelLogging.h +++ b/libraries/graphics/src/graphics/GraphicsLogging.h @@ -1,5 +1,5 @@ // -// ModelLogging.h +// GraphicsLogging.h // hifi // // Created by Sam Gateau on 9/20/15. @@ -11,4 +11,4 @@ #include -Q_DECLARE_LOGGING_CATEGORY(modelLog) +Q_DECLARE_LOGGING_CATEGORY(graphicsLog) diff --git a/libraries/model/src/model/Haze.cpp b/libraries/graphics/src/graphics/Haze.cpp similarity index 99% rename from libraries/model/src/model/Haze.cpp rename to libraries/graphics/src/graphics/Haze.cpp index b56932e131..c788bc57db 100644 --- a/libraries/model/src/model/Haze.cpp +++ b/libraries/graphics/src/graphics/Haze.cpp @@ -1,6 +1,6 @@ // // Haze.cpp -// libraries/model/src/model +// libraries/graphics/src/graphics // // Created by Nissim Hadar on 9/13/2017. // Copyright 2014 High Fidelity, Inc. @@ -12,7 +12,7 @@ #include #include "Haze.h" -using namespace model; +using namespace graphics; const float Haze::INITIAL_HAZE_RANGE{ 1000.0f }; const float Haze::INITIAL_HAZE_HEIGHT{ 200.0f }; diff --git a/libraries/model/src/model/Haze.h b/libraries/graphics/src/graphics/Haze.h similarity index 98% rename from libraries/model/src/model/Haze.h rename to libraries/graphics/src/graphics/Haze.h index 2a575eb151..608449a97e 100644 --- a/libraries/model/src/model/Haze.h +++ b/libraries/graphics/src/graphics/Haze.h @@ -1,6 +1,6 @@ // // MakeHaze.h -// libraries/model/src/model +// libraries/graphics/src/graphics // // Created by Nissim Hadar on 9/13/2017. // Copyright 2014 High Fidelity, Inc. @@ -17,7 +17,7 @@ #include "Transform.h" #include "NumericalConstants.h" -namespace model { +namespace graphics { // Haze range is defined here as the range the visibility is reduced by 95% // Haze altitude is defined here as the altitude (above 0) that the haze is reduced by 95% diff --git a/libraries/model/src/model/Light.cpp b/libraries/graphics/src/graphics/Light.cpp similarity index 98% rename from libraries/model/src/model/Light.cpp rename to libraries/graphics/src/graphics/Light.cpp index 19da084f84..cb5209d4cf 100755 --- a/libraries/model/src/model/Light.cpp +++ b/libraries/graphics/src/graphics/Light.cpp @@ -1,6 +1,6 @@ // // Light.cpp -// libraries/model/src/model +// libraries/graphics/src/graphics // // Created by Sam Gateau on 1/26/2014. // Copyright 2014 High Fidelity, Inc. @@ -10,7 +10,7 @@ // #include "Light.h" -using namespace model; +using namespace graphics; Light::Light() { updateLightRadius(); diff --git a/libraries/model/src/model/Light.h b/libraries/graphics/src/graphics/Light.h similarity index 99% rename from libraries/model/src/model/Light.h rename to libraries/graphics/src/graphics/Light.h index 4c82eb5d77..360e3f224e 100755 --- a/libraries/model/src/model/Light.h +++ b/libraries/graphics/src/graphics/Light.h @@ -1,6 +1,6 @@ // // Light.h -// libraries/model/src/model +// libraries/graphics/src/graphics // // Created by Sam Gateau on 12/10/2014. // Copyright 2014 High Fidelity, Inc. @@ -19,7 +19,7 @@ #include "gpu/Resource.h" #include "gpu/Texture.h" -namespace model { +namespace graphics { typedef gpu::BufferView UniformBufferView; typedef gpu::TextureView TextureView; typedef glm::vec3 Vec3; diff --git a/libraries/model/src/model/Light.slh b/libraries/graphics/src/graphics/Light.slh similarity index 93% rename from libraries/model/src/model/Light.slh rename to libraries/graphics/src/graphics/Light.slh index 093a87adc8..6b24f89c3c 100644 --- a/libraries/model/src/model/Light.slh +++ b/libraries/graphics/src/graphics/Light.slh @@ -11,8 +11,8 @@ <@if not MODEL_LIGHT_SLH@> <@def MODEL_LIGHT_SLH@> -<@include model/LightVolume.shared.slh@> -<@include model/LightIrradiance.shared.slh@> +<@include graphics/LightVolume.shared.slh@> +<@include graphics/LightIrradiance.shared.slh@> // NOw lets define Light struct Light { @@ -30,7 +30,7 @@ float getLightIntensity(Light l) { return lightIrradiance_getIntensity(l.irradia vec3 getLightIrradiance(Light l) { return lightIrradiance_getIrradiance(l.irradiance); } // AMbient lighting needs extra info provided from a different Buffer -<@include model/SphericalHarmonics.shared.slh@> +<@include graphics/SphericalHarmonics.shared.slh@> // Light Ambient struct LightAmbient { vec4 _ambient; diff --git a/libraries/model/src/model/LightIrradiance.shared.slh b/libraries/graphics/src/graphics/LightIrradiance.shared.slh similarity index 100% rename from libraries/model/src/model/LightIrradiance.shared.slh rename to libraries/graphics/src/graphics/LightIrradiance.shared.slh diff --git a/libraries/model/src/model/LightVolume.shared.slh b/libraries/graphics/src/graphics/LightVolume.shared.slh similarity index 98% rename from libraries/model/src/model/LightVolume.shared.slh rename to libraries/graphics/src/graphics/LightVolume.shared.slh index a78667ed6c..3f0cb135f5 100644 --- a/libraries/model/src/model/LightVolume.shared.slh +++ b/libraries/graphics/src/graphics/LightVolume.shared.slh @@ -3,7 +3,7 @@ #define LightVolume_Shared_slh // Light.shared.slh -// libraries/model/src/model +// libraries/graphics/src/graphics // // Created by Sam Gateau on 14/9/2016. // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/model/src/model/Material.cpp b/libraries/graphics/src/graphics/Material.cpp similarity index 99% rename from libraries/model/src/model/Material.cpp rename to libraries/graphics/src/graphics/Material.cpp index 4e01c4b866..ea5bd331c9 100755 --- a/libraries/model/src/model/Material.cpp +++ b/libraries/graphics/src/graphics/Material.cpp @@ -1,6 +1,6 @@ // // Material.cpp -// libraries/model/src/model +// libraries/graphics/src/graphics // // Created by Sam Gateau on 12/10/2014. // Copyright 2014 High Fidelity, Inc. @@ -12,7 +12,7 @@ #include "TextureMap.h" -using namespace model; +using namespace graphics; using namespace gpu; Material::Material() : diff --git a/libraries/model/src/model/Material.h b/libraries/graphics/src/graphics/Material.h similarity index 99% rename from libraries/model/src/model/Material.h rename to libraries/graphics/src/graphics/Material.h index 8851ef4ce9..cfbfaa61ea 100755 --- a/libraries/model/src/model/Material.h +++ b/libraries/graphics/src/graphics/Material.h @@ -1,6 +1,6 @@ // // Material.h -// libraries/model/src/model +// libraries/graphics/src/graphics // // Created by Sam Gateau on 12/10/2014. // Copyright 2014 High Fidelity, Inc. @@ -20,7 +20,7 @@ #include -namespace model { +namespace graphics { class TextureMap; typedef std::shared_ptr< TextureMap > TextureMapPointer; diff --git a/libraries/model/src/model/Material.slh b/libraries/graphics/src/graphics/Material.slh similarity index 100% rename from libraries/model/src/model/Material.slh rename to libraries/graphics/src/graphics/Material.slh diff --git a/libraries/model/src/model/Skybox.cpp b/libraries/graphics/src/graphics/Skybox.cpp similarity index 88% rename from libraries/model/src/model/Skybox.cpp rename to libraries/graphics/src/graphics/Skybox.cpp index fd3061afa5..e05a66104a 100755 --- a/libraries/model/src/model/Skybox.cpp +++ b/libraries/graphics/src/graphics/Skybox.cpp @@ -1,6 +1,6 @@ // // Skybox.cpp -// libraries/model/src/model +// libraries/graphics/src/graphics // // Created by Sam Gateau on 5/4/2015. // Copyright 2015 High Fidelity, Inc. @@ -18,7 +18,7 @@ #include "skybox_vert.h" #include "skybox_frag.h" -using namespace model; +using namespace graphics; Skybox::Skybox() { Schema schema; @@ -89,12 +89,14 @@ void Skybox::render(gpu::Batch& batch, const ViewFrustum& viewFrustum, const Sky auto skyFS = gpu::Shader::createPixel(std::string(skybox_frag)); auto skyShader = gpu::Shader::createProgram(skyVS, skyFS); - gpu::Shader::BindingSet bindings; - bindings.insert(gpu::Shader::Binding(std::string("cubeMap"), SKYBOX_SKYMAP_SLOT)); - bindings.insert(gpu::Shader::Binding(std::string("skyboxBuffer"), SKYBOX_CONSTANTS_SLOT)); - if (!gpu::Shader::makeProgram(*skyShader, bindings)) { + batch.runLambda([skyShader] { + gpu::Shader::BindingSet bindings; + bindings.insert(gpu::Shader::Binding(std::string("cubeMap"), SKYBOX_SKYMAP_SLOT)); + bindings.insert(gpu::Shader::Binding(std::string("skyboxBuffer"), SKYBOX_CONSTANTS_SLOT)); + if (!gpu::Shader::makeProgram(*skyShader, bindings)) { - } + } + }); auto skyState = std::make_shared(); // Must match PrepareStencil::STENCIL_BACKGROUND diff --git a/libraries/model/src/model/Skybox.h b/libraries/graphics/src/graphics/Skybox.h similarity index 96% rename from libraries/model/src/model/Skybox.h rename to libraries/graphics/src/graphics/Skybox.h index 90896fd8c6..d06989a457 100755 --- a/libraries/model/src/model/Skybox.h +++ b/libraries/graphics/src/graphics/Skybox.h @@ -1,6 +1,6 @@ // // Skybox.h -// libraries/model/src/model +// libraries/graphics/src/graphics // // Created by Sam Gateau on 5/4/2015. // Copyright 2015 High Fidelity, Inc. @@ -19,7 +19,7 @@ class ViewFrustum; namespace gpu { class Batch; } -namespace model { +namespace graphics { typedef glm::vec3 Color; diff --git a/libraries/model/src/model/SphericalHarmonics.shared.slh b/libraries/graphics/src/graphics/SphericalHarmonics.shared.slh similarity index 97% rename from libraries/model/src/model/SphericalHarmonics.shared.slh rename to libraries/graphics/src/graphics/SphericalHarmonics.shared.slh index 664c9e52db..312824c5a3 100644 --- a/libraries/model/src/model/SphericalHarmonics.shared.slh +++ b/libraries/graphics/src/graphics/SphericalHarmonics.shared.slh @@ -3,7 +3,7 @@ #define SphericalHarmonics_Shared_slh // SphericalHarmonics.shared.slh -// libraries/model/src/model +// libraries/graphics/src/graphics // // Created by Sam Gateau on 14/9/2016. // Copyright 2014 High Fidelity, Inc. diff --git a/libraries/model/src/model/Stage.cpp b/libraries/graphics/src/graphics/Stage.cpp similarity index 99% rename from libraries/model/src/model/Stage.cpp rename to libraries/graphics/src/graphics/Stage.cpp index 5854162cfa..312ece6889 100644 --- a/libraries/model/src/model/Stage.cpp +++ b/libraries/graphics/src/graphics/Stage.cpp @@ -1,6 +1,6 @@ // // Stage.cpp -// libraries/model/src/model +// libraries/graphics/src/graphics // // Created by Sam Gateau on 2/24/2015. // Copyright 2014 High Fidelity, Inc. @@ -15,7 +15,7 @@ #include #include -using namespace model; +using namespace graphics; void EarthSunModel::updateAll() const { diff --git a/libraries/model/src/model/Stage.h b/libraries/graphics/src/graphics/Stage.h similarity index 99% rename from libraries/model/src/model/Stage.h rename to libraries/graphics/src/graphics/Stage.h index 5f48824568..55f6145ee0 100644 --- a/libraries/model/src/model/Stage.h +++ b/libraries/graphics/src/graphics/Stage.h @@ -1,6 +1,6 @@ // // Stage.h -// libraries/model/src/model +// libraries/graphics/src/graphics // // Created by Sam Gateau on 2/24/2015. // Copyright 2014 High Fidelity, Inc. @@ -16,7 +16,7 @@ #include "Light.h" #include "Skybox.h" -namespace model { +namespace graphics { typedef glm::dvec3 Vec3d; typedef glm::dvec4 Vec4d; diff --git a/libraries/model/src/model/TextureMap.cpp b/libraries/graphics/src/graphics/TextureMap.cpp similarity index 94% rename from libraries/model/src/model/TextureMap.cpp rename to libraries/graphics/src/graphics/TextureMap.cpp index b308dd72f8..e2f24d68ff 100755 --- a/libraries/model/src/model/TextureMap.cpp +++ b/libraries/graphics/src/graphics/TextureMap.cpp @@ -1,6 +1,6 @@ // // TextureMap.cpp -// libraries/model/src/model +// libraries/graphics/src/graphics // // Created by Sam Gateau on 5/6/2015. // Copyright 2014 High Fidelity, Inc. @@ -10,7 +10,7 @@ // #include "TextureMap.h" -using namespace model; +using namespace graphics; using namespace gpu; void TextureMap::setTextureSource(TextureSourcePointer& textureSource) { diff --git a/libraries/model/src/model/TextureMap.h b/libraries/graphics/src/graphics/TextureMap.h similarity index 95% rename from libraries/model/src/model/TextureMap.h rename to libraries/graphics/src/graphics/TextureMap.h index 1785d44730..1678d9df98 100755 --- a/libraries/model/src/model/TextureMap.h +++ b/libraries/graphics/src/graphics/TextureMap.h @@ -1,6 +1,6 @@ // // TextureMap.h -// libraries/model/src/model +// libraries/graphics/src/graphics // // Created by Sam Gateau on 5/6/2015. // Copyright 2014 High Fidelity, Inc. @@ -15,7 +15,7 @@ #include "Transform.h" -namespace model { +namespace graphics { class TextureMap { public: diff --git a/libraries/model/src/model/skybox.slf b/libraries/graphics/src/graphics/skybox.slf similarity index 100% rename from libraries/model/src/model/skybox.slf rename to libraries/graphics/src/graphics/skybox.slf diff --git a/libraries/model/src/model/skybox.slv b/libraries/graphics/src/graphics/skybox.slv similarity index 100% rename from libraries/model/src/model/skybox.slv rename to libraries/graphics/src/graphics/skybox.slv diff --git a/libraries/image/src/image/Image.cpp b/libraries/image/src/image/Image.cpp index f78ed1a583..9115dd09e2 100644 --- a/libraries/image/src/image/Image.cpp +++ b/libraries/image/src/image/Image.cpp @@ -19,15 +19,6 @@ #include #include - -#if defined(Q_OS_ANDROID) -#define CPU_MIPMAPS 0 -#else -#define CPU_MIPMAPS 1 -#include -#endif - - #include #include #include @@ -37,6 +28,12 @@ using namespace gpu; +#if defined(Q_OS_ANDROID) +#define CPU_MIPMAPS 1 +#else +#define CPU_MIPMAPS 1 +#include +#endif static const glm::uvec2 SPARSE_PAGE_SIZE(128); static const glm::uvec2 MAX_TEXTURE_SIZE(4096); @@ -51,25 +48,24 @@ static std::atomic compressNormalTextures { false }; static std::atomic compressGrayscaleTextures { false }; static std::atomic compressCubeTextures { false }; -bool needsSparseRectification(const glm::uvec2& size) { - // Don't attempt to rectify small textures (textures less than the sparse page size in any dimension) - if (glm::any(glm::lessThan(size, SPARSE_PAGE_SIZE))) { - return false; +uint rectifyDimension(const uint& dimension) { + if (dimension == 0) { + return 0; } - - // Don't rectify textures that are already an exact multiple of sparse page size - if (glm::uvec2(0) == (size % SPARSE_PAGE_SIZE)) { - return false; + if (dimension < SPARSE_PAGE_SIZE.x) { + uint newSize = SPARSE_PAGE_SIZE.x; + while (dimension <= newSize / 2) { + newSize /= 2; + } + return newSize; + } else { + uint pages = (dimension / SPARSE_PAGE_SIZE.x) + (dimension % SPARSE_PAGE_SIZE.x == 0 ? 0 : 1); + return pages * SPARSE_PAGE_SIZE.x; } - - // Texture is not sparse compatible, but is bigger than the sparse page size in both dimensions, rectify! - return true; } -glm::uvec2 rectifyToSparseSize(const glm::uvec2& size) { - glm::uvec2 pages = ((size / SPARSE_PAGE_SIZE) + glm::clamp(size % SPARSE_PAGE_SIZE, glm::uvec2(0), glm::uvec2(1))); - glm::uvec2 result = pages * SPARSE_PAGE_SIZE; - return result; +glm::uvec2 rectifySize(const glm::uvec2& size) { + return { rectifyDimension(size.x), rectifyDimension(size.y) }; } @@ -329,9 +325,12 @@ QImage processSourceImage(QImage&& srcImage, bool cubemap) { ++DECIMATED_TEXTURE_COUNT; } - if (!cubemap && needsSparseRectification(targetSize)) { - ++RECTIFIED_TEXTURE_COUNT; - targetSize = rectifyToSparseSize(targetSize); + if (!cubemap) { + auto rectifiedSize = rectifySize(targetSize); + if (rectifiedSize != targetSize) { + ++RECTIFIED_TEXTURE_COUNT; + targetSize = rectifiedSize; + } } if (DEV_DECIMATE_TEXTURES && glm::all(glm::greaterThanEqual(targetSize / SPARSE_PAGE_SIZE, glm::uvec2(2)))) { @@ -680,6 +679,7 @@ void generateLDRMips(gpu::Texture* texture, QImage&& image, const std::atomic& abortProcessing = false, int face = -1) { #if CPU_MIPMAPS +#if !defined(Q_OS_ANDROID) PROFILE_RANGE(resource_parse, "generateMips"); if (image.format() == QIMAGE_HDR_FORMAT) { @@ -687,6 +687,16 @@ void generateMips(gpu::Texture* texture, QImage&& image, const std::atomic } else { generateLDRMips(texture, std::move(image), abortProcessing, face); } + +#else + //texture->setAutoGenerateMips(false); + texture->assignStoredMip(0, image.byteCount(), image.constBits()); + for (uint16 level = 1; level < texture->getNumMips(); ++level) { + QSize mipSize(texture->evalMipWidth(level), texture->evalMipHeight(level)); + QImage mipImage = image.scaled(mipSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + texture->assignStoredMip(level, mipImage.byteCount(), mipImage.constBits()); + } +#endif #else texture->setAutoGenerateMips(true); #endif @@ -727,9 +737,15 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma bool validAlpha = image.hasAlphaChannel(); bool alphaAsMask = false; +#if !defined(Q_OS_ANDROID) if (image.format() != QImage::Format_ARGB32) { image = image.convertToFormat(QImage::Format_ARGB32); } +#else + if (image.format() != QImage::Format_RGBA8888) { + image = image.convertToFormat(QImage::Format_RGBA8888); + } +#endif if (validAlpha) { processTextureAlpha(image, validAlpha, alphaAsMask); @@ -769,6 +785,7 @@ gpu::TexturePointer TextureUsage::process2DTextureColorFromImage(QImage&& srcIma } theTexture->setUsage(usage.build()); theTexture->setStoredMipFormat(formatMip); + theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); generateMips(theTexture.get(), std::move(image), abortProcessing); } @@ -875,6 +892,7 @@ gpu::TexturePointer TextureUsage::process2DTextureNormalMapFromImage(QImage&& sr theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); theTexture->setSource(srcImageName); theTexture->setStoredMipFormat(formatMip); + theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); generateMips(theTexture.get(), std::move(image), abortProcessing); } @@ -911,6 +929,7 @@ gpu::TexturePointer TextureUsage::process2DTextureGrayscaleFromImage(QImage&& sr theTexture = gpu::Texture::create2D(formatGPU, image.width(), image.height(), gpu::Texture::MAX_NUM_MIPS, gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR)); theTexture->setSource(srcImageName); theTexture->setStoredMipFormat(formatMip); + theTexture->assignStoredMip(0, image.byteCount(), image.constBits()); generateMips(theTexture.get(), std::move(image), abortProcessing); } diff --git a/libraries/midi/src/Midi.cpp b/libraries/midi/src/Midi.cpp index 6c2471f680..1f190111f2 100644 --- a/libraries/midi/src/Midi.cpp +++ b/libraries/midi/src/Midi.cpp @@ -3,6 +3,7 @@ // libraries/midi/src // // Created by Burt Sloane +// Modified by Bruce Brown // Copyright 2015 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. @@ -14,30 +15,45 @@ #include - #if defined Q_OS_WIN32 #include "Windows.h" #endif - #if defined Q_OS_WIN32 const int MIDI_BYTE_MASK = 0x0FF; +const int MIDI_NIBBLE_MASK = 0x00F; +const int MIDI_PITCH_BEND_MASK = 0x3F80; +const int MIDI_SHIFT_STATUS = 4; const int MIDI_SHIFT_NOTE = 8; const int MIDI_SHIFT_VELOCITY = 16; +const int MIDI_SHIFT_PITCH_BEND = 9; +// Status Decode +const int MIDI_NOTE_OFF = 0x8; +const int MIDI_NOTE_ON = 0x9; +const int MIDI_POLYPHONIC_KEY_PRESSURE = 0xa; +const int MIDI_PROGRAM_CHANGE = 0xc; +const int MIDI_CHANNEL_PRESSURE = 0xd; +const int MIDI_PITCH_BEND_CHANGE = 0xe; +const int MIDI_SYSTEM_MESSAGE = 0xf; #endif -const int MIDI_STATUS_MASK = 0x0F0; -const int MIDI_NOTE_OFF = 0x080; -const int MIDI_NOTE_ON = 0x090; -const int MIDI_CONTROL_CHANGE = 0x0b0; + +const int MIDI_CONTROL_CHANGE = 0xb; const int MIDI_CHANNEL_MODE_ALL_NOTES_OFF = 0x07b; - -static Midi* instance = NULL; // communicate this to non-class callbacks +static Midi* instance = NULL; // communicate this to non-class callbacks static bool thruModeEnabled = false; +static bool broadcastEnabled = false; +static bool typeNoteOffEnabled = true; +static bool typeNoteOnEnabled = true; +static bool typePolyKeyPressureEnabled = false; +static bool typeControlChangeEnabled = true; +static bool typeProgramChangeEnabled = true; +static bool typeChanPressureEnabled = false; +static bool typePitchBendEnabled = true; +static bool typeSystemMessageEnabled = false; -std::vector Midi::midiinexclude; -std::vector Midi::midioutexclude; - +std::vector Midi::midiInExclude; +std::vector Midi::midiOutExclude; #if defined Q_OS_WIN32 @@ -47,7 +63,6 @@ std::vector Midi::midioutexclude; std::vector midihin; std::vector midihout; - void CALLBACK MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { switch (wMsg) { case MIM_OPEN: @@ -58,23 +73,64 @@ void CALLBACK MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD if (midihin[i] == hMidiIn) { midihin[i] = NULL; instance->allNotesOff(); + instance->midiHardwareChange(); } } break; case MIM_DATA: { - int status = MIDI_BYTE_MASK & dwParam1; - int note = MIDI_BYTE_MASK & (dwParam1 >> MIDI_SHIFT_NOTE); - int vel = MIDI_BYTE_MASK & (dwParam1 >> MIDI_SHIFT_VELOCITY); - if (thruModeEnabled) { - instance->sendNote(status, note, vel); // relay the note on to all other midi devices + int device = -1; + for (int i = 0; i < midihin.size(); i++) { + if (midihin[i] == hMidiIn) { + device = i; + } } - instance->noteReceived(status, note, vel); // notify the javascript + int raw = dwParam1; + int channel = (MIDI_NIBBLE_MASK & dwParam1) + 1; + int status = MIDI_BYTE_MASK & dwParam1; + int type = MIDI_NIBBLE_MASK & (dwParam1 >> MIDI_SHIFT_STATUS); + int note = MIDI_BYTE_MASK & (dwParam1 >> MIDI_SHIFT_NOTE); + int velocity = MIDI_BYTE_MASK & (dwParam1 >> MIDI_SHIFT_VELOCITY); + int bend = 0; + int program = 0; + if (!typeNoteOffEnabled && type == MIDI_NOTE_OFF) { + return; + } + if (!typeNoteOnEnabled && type == MIDI_NOTE_ON) { + return; + } + if (!typePolyKeyPressureEnabled && type == MIDI_POLYPHONIC_KEY_PRESSURE) { + return; + } + if (!typeControlChangeEnabled && type == MIDI_CONTROL_CHANGE) { + return; + } + if (typeProgramChangeEnabled && type == MIDI_PROGRAM_CHANGE) { + program = note; + note = 0; + } + if (typeChanPressureEnabled && type == MIDI_CHANNEL_PRESSURE) { + velocity = note; + note = 0; + } + if (typePitchBendEnabled && type == MIDI_PITCH_BEND_CHANGE) { + bend = ((MIDI_BYTE_MASK & (dwParam1 >> MIDI_SHIFT_NOTE)) | + (MIDI_PITCH_BEND_MASK & (dwParam1 >> MIDI_SHIFT_PITCH_BEND))) - 8192; + channel = 0; // Weird values on different instruments + note = 0; + velocity = 0; + } + if (!typeSystemMessageEnabled && type == MIDI_SYSTEM_MESSAGE) { + return; + } + if (thruModeEnabled) { + instance->sendNote(status, note, velocity); // relay the message on to all other midi devices. + } + instance->midiReceived(device, raw, channel, status, type, note, velocity, bend, program); // notify the javascript break; } } } - void CALLBACK MidiOutProc(HMIDIOUT hmo, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2) { switch (wMsg) { case MOM_OPEN: @@ -85,21 +141,45 @@ void CALLBACK MidiOutProc(HMIDIOUT hmo, UINT wMsg, DWORD_PTR dwInstance, DWORD_P if (midihout[i] == hmo) { midihout[i] = NULL; instance->allNotesOff(); + instance->midiHardwareChange(); } } break; } } - -void Midi::sendNote(int status, int note, int vel) { - for (int i = 0; i < midihout.size(); i++) { - if (midihout[i] != NULL) { - midiOutShortMsg(midihout[i], status + (note << MIDI_SHIFT_NOTE) + (vel << MIDI_SHIFT_VELOCITY)); +void Midi::sendRawMessage(int device, int raw) { + if (broadcastEnabled) { + for (int i = 0; i < midihout.size(); i++) { + if (midihout[i] != NULL) { + midiOutShortMsg(midihout[i], raw); + } } + } else { + midiOutShortMsg(midihout[device], raw); } } +void Midi::sendMessage(int device, int channel, int type, int note, int velocity) { + int message = (channel - 1) | (type << MIDI_SHIFT_STATUS); + if (broadcastEnabled) { + for (int i = 1; i < midihout.size(); i++) { // Skip 0 (Microsoft GS Wavetable Synth) + if (midihout[i] != NULL) { + midiOutShortMsg(midihout[i], message | (note << MIDI_SHIFT_NOTE) | (velocity << MIDI_SHIFT_VELOCITY)); + } + } + } else { + midiOutShortMsg(midihout[device], message | (note << MIDI_SHIFT_NOTE) | (velocity << MIDI_SHIFT_VELOCITY)); + } +} + +void Midi::sendNote(int status, int note, int velocity) { + for (int i = 1; i < midihout.size(); i++) { // Skip 0 (Microsoft GS Wavetable Synth) + if (midihout[i] != NULL) { + midiOutShortMsg(midihout[i], status | (note << MIDI_SHIFT_NOTE) | (velocity << MIDI_SHIFT_VELOCITY)); + } + } +} void Midi::MidiSetup() { midihin.clear(); @@ -110,8 +190,8 @@ void Midi::MidiSetup() { midiInGetDevCaps(i, &incaps, sizeof(MIDIINCAPS)); bool found = false; - for (int j = 0; j < midiinexclude.size(); j++) { - if (midiinexclude[j].toStdString().compare(incaps.szPname) == 0) { + for (int j = 0; j < midiInExclude.size(); j++) { + if (midiInExclude[j].toStdString().compare(incaps.szPname) == 0) { found = true; break; } @@ -122,7 +202,6 @@ void Midi::MidiSetup() { midiInStart(tmphin); midihin.push_back(tmphin); } - } MIDIOUTCAPS outcaps; @@ -130,8 +209,8 @@ void Midi::MidiSetup() { midiOutGetDevCaps(i, &outcaps, sizeof(MIDIINCAPS)); bool found = false; - for (int j = 0; j < midioutexclude.size(); j++) { - if (midioutexclude[j].toStdString().compare(outcaps.szPname) == 0) { + for (int j = 0; j < midiOutExclude.size(); j++) { + if (midiOutExclude[j].toStdString().compare(outcaps.szPname) == 0) { found = true; break; } @@ -164,7 +243,13 @@ void Midi::MidiCleanup() { midihout.clear(); } #else -void Midi::sendNote(int status, int note, int vel) { +void Midi::sendRawMessage(int device, int raw) { +} + +void Midi::sendNote(int status, int note, int velocity) { +} + +void Midi::sendMessage(int device, int channel, int type, int note, int velocity){ } void Midi::MidiSetup() { @@ -176,37 +261,46 @@ void Midi::MidiCleanup() { } #endif -void Midi::noteReceived(int status, int note, int velocity) { - if (((status & MIDI_STATUS_MASK) != MIDI_NOTE_OFF) && - ((status & MIDI_STATUS_MASK) != MIDI_NOTE_ON) && - ((status & MIDI_STATUS_MASK) != MIDI_CONTROL_CHANGE)) { - return; // NOTE: only sending note-on, note-off, and control-change to Javascript - } - +void Midi::midiReceived(int device, int raw, int channel, int status, int type, int note, int velocity, int bend, int program) { QVariantMap eventData; + eventData["device"] = device; + eventData["raw"] = raw; + eventData["channel"] = channel; eventData["status"] = status; + eventData["type"] = type; eventData["note"] = note; eventData["velocity"] = velocity; - emit midiNote(eventData); + eventData["bend"] = bend; + eventData["program"] = program; + emit midiNote(eventData);// Legacy + emit midiMessage(eventData); } +void Midi::midiHardwareChange() { + emit midiReset(); +} // Midi::Midi() { instance = this; -#if defined Q_OS_WIN32 - midioutexclude.push_back("Microsoft GS Wavetable Synth"); // we don't want to hear this thing -#endif MidiSetup(); } Midi::~Midi() { } +void Midi::sendRawDword(int device, int raw) { + sendRawMessage(device, raw); +} + void Midi::playMidiNote(int status, int note, int velocity) { sendNote(status, note, velocity); } +void Midi::sendMidiMessage(int device, int channel, int type, int note, int velocity) { + sendMessage(device, channel, type, note, velocity); +} + void Midi::allNotesOff() { sendNote(MIDI_CONTROL_CHANGE, MIDI_CHANNEL_MODE_ALL_NOTES_OFF, 0); // all notes off } @@ -219,6 +313,7 @@ void Midi::resetDevices() { void Midi::USBchanged() { instance->MidiCleanup(); instance->MidiSetup(); + instance->midiHardwareChange(); } // @@ -245,16 +340,16 @@ QStringList Midi::listMidiDevices(bool output) { void Midi::unblockMidiDevice(QString name, bool output) { if (output) { - for (unsigned long i = 0; i < midioutexclude.size(); i++) { - if (midioutexclude[i].toStdString().compare(name.toStdString()) == 0) { - midioutexclude.erase(midioutexclude.begin() + i); + for (unsigned long i = 0; i < midiOutExclude.size(); i++) { + if (midiOutExclude[i].toStdString().compare(name.toStdString()) == 0) { + midiOutExclude.erase(midiOutExclude.begin() + i); break; } } } else { - for (unsigned long i = 0; i < midiinexclude.size(); i++) { - if (midiinexclude[i].toStdString().compare(name.toStdString()) == 0) { - midiinexclude.erase(midiinexclude.begin() + i); + for (unsigned long i = 0; i < midiInExclude.size(); i++) { + if (midiInExclude[i].toStdString().compare(name.toStdString()) == 0) { + midiInExclude.erase(midiInExclude.begin() + i); break; } } @@ -264,9 +359,9 @@ void Midi::unblockMidiDevice(QString name, bool output) { void Midi::blockMidiDevice(QString name, bool output) { unblockMidiDevice(name, output); // make sure it's only in there once if (output) { - midioutexclude.push_back(name); + midiOutExclude.push_back(name); } else { - midiinexclude.push_back(name); + midiInExclude.push_back(name); } } @@ -274,3 +369,38 @@ void Midi::thruModeEnable(bool enable) { thruModeEnabled = enable; } +void Midi::broadcastEnable(bool enable) { + broadcastEnabled = enable; +} + +void Midi::typeNoteOffEnable(bool enable) { + typeNoteOffEnabled = enable; +} + +void Midi::typeNoteOnEnable(bool enable) { + typeNoteOnEnabled = enable; +} + +void Midi::typePolyKeyPressureEnable(bool enable) { + typePolyKeyPressureEnabled = enable; +} + +void Midi::typeControlChangeEnable(bool enable) { + typeControlChangeEnabled = enable; +} + +void Midi::typeProgramChangeEnable(bool enable) { + typeProgramChangeEnabled = enable; +} + +void Midi::typeChanPressureEnable(bool enable) { + typeChanPressureEnabled = enable; +} + +void Midi::typePitchBendEnable(bool enable) { + typePitchBendEnabled = enable; +} + +void Midi::typeSystemMessageEnable(bool enable) { + typeSystemMessageEnabled = enable; +} diff --git a/libraries/midi/src/Midi.h b/libraries/midi/src/Midi.h index 013ec056e3..f7940bbe5d 100644 --- a/libraries/midi/src/Midi.h +++ b/libraries/midi/src/Midi.h @@ -3,6 +3,7 @@ // libraries/midi/src // // Created by Burt Sloane +// Modified by Bruce Brown // Copyright 2015 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. @@ -24,13 +25,16 @@ class Midi : public QObject, public Dependency { SINGLETON_DEPENDENCY public: - void noteReceived(int status, int note, int velocity); // relay a note to Javascript - void sendNote(int status, int note, int vel); // relay a note to MIDI outputs + void midiReceived(int device, int raw, int channel, int status, int type, int note, int velocity, int bend, int program); // relay a note to Javascript + void midiHardwareChange(); // relay hardware change to Javascript + void sendRawMessage(int device, int raw); // relay midi message to MIDI outputs + void sendNote(int status, int note, int velocity); // relay a note to MIDI outputs + void sendMessage(int device, int channel, int type, int note, int velocity); // relay a message to MIDI outputs static void USBchanged(); private: - static std::vector midiinexclude; - static std::vector midioutexclude; + static std::vector midiInExclude; + static std::vector midiOutExclude; private: void MidiSetup(); @@ -38,31 +42,63 @@ private: signals: void midiNote(QVariantMap eventData); + void midiMessage(QVariantMap eventData); + void midiReset(); -public slots: -/// play a note on all connected devices -/// @param {int} status: 0x80 is noteoff, 0x90 is noteon (if velocity=0, noteoff), etc -/// @param {int} note: midi note number -/// @param {int} velocity: note velocity (0 means noteoff) -Q_INVOKABLE void playMidiNote(int status, int note, int velocity); + public slots: + // Send Raw Midi Packet to all connected devices + Q_INVOKABLE void sendRawDword(int device, int raw); + /// Send Raw Midi message to selected device + /// @param {int} device: device number + /// @param {int} raw: raw midi message (DWORD) -/// turn off all notes on all connected devices -Q_INVOKABLE void allNotesOff(); + // Send Midi Message to all connected devices + Q_INVOKABLE void sendMidiMessage(int device, int channel, int type, int note, int velocity); + /// Send midi message to selected device/devices + /// @param {int} device: device number + /// @param {int} channel: channel number + /// @param {int} type: 0x8 is noteoff, 0x9 is noteon (if velocity=0, noteoff), etc + /// @param {int} note: midi note number + /// @param {int} velocity: note velocity (0 means noteoff) -/// clean up and re-discover attached devices -Q_INVOKABLE void resetDevices(); + // Send Midi Message to all connected devices + Q_INVOKABLE void playMidiNote(int status, int note, int velocity); + /// play a note on all connected devices + /// @param {int} status: 0x80 is noteoff, 0x90 is noteon (if velocity=0, noteoff), etc + /// @param {int} note: midi note number + /// @param {int} velocity: note velocity (0 means noteoff) -/// ask for a list of inputs/outputs -Q_INVOKABLE QStringList listMidiDevices(bool output); + /// turn off all notes on all connected devices + Q_INVOKABLE void allNotesOff(); -/// block an input/output by name -Q_INVOKABLE void blockMidiDevice(QString name, bool output); + /// clean up and re-discover attached devices + Q_INVOKABLE void resetDevices(); -/// unblock an input/output by name -Q_INVOKABLE void unblockMidiDevice(QString name, bool output); + /// ask for a list of inputs/outputs + Q_INVOKABLE QStringList listMidiDevices(bool output); + + /// block an input/output by name + Q_INVOKABLE void blockMidiDevice(QString name, bool output); + + /// unblock an input/output by name + Q_INVOKABLE void unblockMidiDevice(QString name, bool output); + + /// repeat all incoming notes to all outputs (default disabled) + Q_INVOKABLE void thruModeEnable(bool enable); + + /// broadcast on all unblocked devices + Q_INVOKABLE void broadcastEnable(bool enable); + + /// filter by event types + Q_INVOKABLE void typeNoteOffEnable(bool enable); + Q_INVOKABLE void typeNoteOnEnable(bool enable); + Q_INVOKABLE void typePolyKeyPressureEnable(bool enable); + Q_INVOKABLE void typeControlChangeEnable(bool enable); + Q_INVOKABLE void typeProgramChangeEnable(bool enable); + Q_INVOKABLE void typeChanPressureEnable(bool enable); + Q_INVOKABLE void typePitchBendEnable(bool enable); + Q_INVOKABLE void typeSystemMessageEnable(bool enable); -/// repeat all incoming notes to all outputs (default disabled) -Q_INVOKABLE void thruModeEnable(bool enable); public: Midi(); diff --git a/libraries/model-networking/CMakeLists.txt b/libraries/model-networking/CMakeLists.txt index db5563d7ea..696f4feb9a 100644 --- a/libraries/model-networking/CMakeLists.txt +++ b/libraries/model-networking/CMakeLists.txt @@ -1,4 +1,4 @@ set(TARGET_NAME model-networking) setup_hifi_library() -link_hifi_libraries(shared networking model fbx ktx image) +link_hifi_libraries(shared networking graphics fbx ktx image) include_hifi_library_headers(gpu) diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp index 6a14e6d6b7..07f7283bfa 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.cpp +++ b/libraries/model-networking/src/model-networking/ModelCache.cpp @@ -516,13 +516,13 @@ QUrl NetworkMaterial::getTextureUrl(const QUrl& baseUrl, const FBXTexture& textu } } -model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, const FBXTexture& fbxTexture, +graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, const FBXTexture& fbxTexture, image::TextureUsage::Type type, MapChannel channel) { const auto url = getTextureUrl(baseUrl, fbxTexture); const auto texture = DependencyManager::get()->getTexture(url, type, fbxTexture.content, fbxTexture.maxNumPixels); _textures[channel] = Texture { fbxTexture.name, texture }; - auto map = std::make_shared(); + auto map = std::make_shared(); if (texture) { map->setTextureSource(texture->_textureSource); } @@ -531,18 +531,18 @@ model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& baseUrl, c return map; } -model::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel) { +graphics::TextureMapPointer NetworkMaterial::fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel) { const auto texture = DependencyManager::get()->getTexture(url, type); _textures[channel].texture = texture; - auto map = std::make_shared(); + auto map = std::make_shared(); map->setTextureSource(texture->_textureSource); return map; } NetworkMaterial::NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl) : - model::Material(*material._material) + graphics::Material(*material._material) { _textures = Textures(MapChannel::NUM_MAP_CHANNELS); if (!material.albedoTexture.filename.isEmpty()) { diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h index a122e03eb9..f650b3f2eb 100644 --- a/libraries/model-networking/src/model-networking/ModelCache.h +++ b/libraries/model-networking/src/model-networking/ModelCache.h @@ -15,8 +15,8 @@ #include #include -#include -#include +#include +#include #include "FBXReader.h" #include "TextureCache.h" @@ -38,7 +38,7 @@ public: Geometry(const Geometry& geometry); // Immutable over lifetime - using GeometryMeshes = std::vector>; + using GeometryMeshes = std::vector>; using GeometryMeshParts = std::vector>; // Mutable, but must retain structure of vector @@ -157,9 +157,9 @@ private: virtual ~ModelCache() = default; }; -class NetworkMaterial : public model::Material { +class NetworkMaterial : public graphics::Material { public: - using MapChannel = model::Material::MapChannel; + using MapChannel = graphics::Material::MapChannel; NetworkMaterial(const FBXMaterial& material, const QUrl& textureBaseUrl); @@ -185,9 +185,9 @@ protected: private: // Helpers for the ctors QUrl getTextureUrl(const QUrl& baseUrl, const FBXTexture& fbxTexture); - model::TextureMapPointer fetchTextureMap(const QUrl& baseUrl, const FBXTexture& fbxTexture, + graphics::TextureMapPointer fetchTextureMap(const QUrl& baseUrl, const FBXTexture& fbxTexture, image::TextureUsage::Type type, MapChannel channel); - model::TextureMapPointer fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel); + graphics::TextureMapPointer fetchTextureMap(const QUrl& url, image::TextureUsage::Type type, MapChannel channel); Transform _albedoTransform; Transform _lightmapTransform; diff --git a/libraries/model-networking/src/model-networking/SimpleMeshProxy.cpp b/libraries/model-networking/src/model-networking/SimpleMeshProxy.cpp index b44ea1ff56..741478789e 100644 --- a/libraries/model-networking/src/model-networking/SimpleMeshProxy.cpp +++ b/libraries/model-networking/src/model-networking/SimpleMeshProxy.cpp @@ -11,7 +11,7 @@ #include "SimpleMeshProxy.h" -#include +#include MeshPointer SimpleMeshProxy::getMeshPointer() const { return _mesh; diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index 8153dba3a5..fb75123b43 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -50,8 +50,10 @@ Q_LOGGING_CATEGORY(trace_resource_parse_image, "trace.resource.parse.image") Q_LOGGING_CATEGORY(trace_resource_parse_image_raw, "trace.resource.parse.image.raw") Q_LOGGING_CATEGORY(trace_resource_parse_image_ktx, "trace.resource.parse.image.ktx") +#if !defined(DISABLE_KTX_CACHE) const std::string TextureCache::KTX_DIRNAME { "ktx_cache" }; const std::string TextureCache::KTX_EXT { "ktx" }; +#endif static const QString RESOURCE_SCHEME = "resource"; static const QUrl SPECTATOR_CAMERA_FRAME_URL("resource://spectatorCameraFrame"); @@ -61,7 +63,9 @@ static const float SKYBOX_LOAD_PRIORITY { 10.0f }; // Make sure skybox loads fir static const float HIGH_MIPS_LOAD_PRIORITY { 9.0f }; // Make sure high mips loads after skybox but before models TextureCache::TextureCache() { +#if !defined(DISABLE_KTX_CACHE) _ktxCache->initialize(); +#endif setUnusedResourceCacheSize(0); setObjectName("TextureCache"); } @@ -261,8 +265,12 @@ gpu::TexturePointer getFallbackTextureForType(image::TextureUsage::Type type) { /// Returns a texture version of an image file gpu::TexturePointer TextureCache::getImageTexture(const QString& path, image::TextureUsage::Type type, QVariantMap options) { QImage image = QImage(path); + if (image.isNull()) { + qCWarning(networking) << "Unable to load required resource texture" << path; + return nullptr; + } auto loader = image::TextureUsage::getTextureLoaderForType(type, options); - return gpu::TexturePointer(loader(std::move(image), QUrl::fromLocalFile(path).fileName().toStdString(), false)); + return gpu::TexturePointer(loader(std::move(image), path.toStdString(), false)); } QSharedPointer TextureCache::createResource(const QUrl& url, const QSharedPointer& fallback, @@ -742,16 +750,20 @@ void NetworkTexture::handleFinishedInitialLoad() { gpu::TexturePointer texture = textureCache->getTextureByHash(hash); +#if !defined(DISABLE_KTX_CACHE) if (!texture) { auto ktxFile = textureCache->_ktxCache->getFile(hash); if (ktxFile) { texture = gpu::Texture::unserialize(ktxFile); if (texture) { texture = textureCache->cacheTextureByHash(hash, texture); + if (texture->source().empty()) { + texture->setSource(url.toString().toStdString()); + } } } } - +#endif if (!texture) { auto memKtx = ktx::KTX::createBare(*header, keyValues); @@ -766,6 +778,7 @@ void NetworkTexture::handleFinishedInitialLoad() { // Move ktx to file const char* data = reinterpret_cast(memKtx->_storage->data()); +#if !defined(DISABLE_KTX_CACHE) size_t length = memKtx->_storage->size(); cache::FilePointer file; auto& ktxCache = textureCache->_ktxCache; @@ -777,11 +790,14 @@ void NetworkTexture::handleFinishedInitialLoad() { Q_ARG(int, 0)); return; } +#endif auto newKtxDescriptor = memKtx->toDescriptor(); texture = gpu::Texture::build(newKtxDescriptor); +#if !defined(DISABLE_KTX_CACHE) texture->setKtxBacking(file); +#endif texture->setSource(filename); auto& images = originalKtxDescriptor->images; @@ -924,11 +940,12 @@ void ImageReader::read() { // If we already have a live texture with the same hash, use it auto texture = textureCache->getTextureByHash(hash); +#if !defined(DISABLE_KTX_CACHE) // If there is no live texture, check if there's an existing KTX file if (!texture) { auto ktxFile = textureCache->_ktxCache->getFile(hash); if (ktxFile) { - texture = gpu::Texture::unserialize(ktxFile); + texture = gpu::Texture::unserialize(ktxFile, _url.toString().toStdString()); if (texture) { texture = textureCache->cacheTextureByHash(hash, texture); } else { @@ -936,6 +953,7 @@ void ImageReader::read() { } } } +#endif // If we found the texture either because it's in use or via KTX deserialization, // set the image and return immediately. @@ -971,6 +989,7 @@ void ImageReader::read() { // Save the image into a KTXFile if (texture && textureCache) { +#if !defined(DISABLE_KTX_CACHE) auto memKtx = gpu::Texture::serialize(*texture); // Move the texture into a memory mapped file @@ -987,7 +1006,7 @@ void ImageReader::read() { } else { qCWarning(modelnetworking) << "Unable to serialize texture to KTX " << _url; } - +#endif // We replace the texture with the one stored in the cache. This deals with the possible race condition of two different // images with the same hash being loaded concurrently. Only one of them will make it into the cache by hash first and will // be the winner diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 5a96fcf5e6..74395d8948 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include @@ -194,10 +194,12 @@ private: TextureCache(); virtual ~TextureCache(); +#if !defined(DISABLE_KTX_CACHE) static const std::string KTX_DIRNAME; static const std::string KTX_EXT; std::shared_ptr _ktxCache { std::make_shared(KTX_DIRNAME, KTX_EXT) }; +#endif // Map from image hashes to texture weak pointers std::unordered_map> _texturesByHashes; std::mutex _texturesByHashesMutex; diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 21815e065a..fa5507fcf7 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -28,15 +29,11 @@ #include "UserActivityLogger.h" #include "udt/PacketHeaders.h" -#ifdef Q_OS_ANDROID -const QString DEFAULT_HIFI_ADDRESS = "hifi://android/0.0,0.0,-200"; -#else #if USE_STABLE_GLOBAL_SERVICES const QString DEFAULT_HIFI_ADDRESS = "hifi://welcome/hello"; #else const QString DEFAULT_HIFI_ADDRESS = "hifi://dev-welcome/hello"; #endif -#endif const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager"; const QString SETTINGS_CURRENT_ADDRESS_KEY = "address"; @@ -761,11 +758,21 @@ void AddressManager::refreshPreviousLookup() { } void AddressManager::copyAddress() { + if (QThread::currentThread() != qApp->thread()) { + QMetaObject::invokeMethod(this, "copyAddress"); + return; + } + // assume that the address is being copied because the user wants a shareable address QApplication::clipboard()->setText(currentShareableAddress().toString()); } void AddressManager::copyPath() { + if (QThread::currentThread() != qApp->thread()) { + QMetaObject::invokeMethod(this, "copyPath"); + return; + } + QApplication::clipboard()->setText(currentPath()); } diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 2f3d896509..0173a1fad7 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -31,6 +31,32 @@ const QString INDEX_PATH = "/"; const QString GET_PLACE = "/api/v1/places/%1"; +/**jsdoc + * The location API provides facilities related to your current location in the metaverse. + * + * @namespace location + * @property {Uuid} domainId - A UUID uniquely identifying the domain you're visiting. Is {@link Uuid|Uuid.NULL} if you're not + * connected to the domain. + * Read-only. + * @property {string} hostname - The name of the domain for your current metaverse address (e.g., "AvatarIsland", + * localhost, or an IP address). + * Read-only. + * @property {string} href - Your current metaverse address (e.g., "hifi://avatarisland/15,-10,26/0,0,0,1") + * regardless of whether or not you're connected to the domain. + * Read-only. + * @property {boolean} isConnected - true if you're connected to the domain in your current href + * metaverse address, otherwise false. + * Read-only. + * @property {string} pathname - The location and orientation in your current href metaverse address + * (e.g., "/15,-10,26/0,0,0,1"). + * Read-only. + * @property {string} placename - The place name in your current href metaverse address + * (e.g., "AvatarIsland"). Is blank if your hostname is an IP address. + * Read-only. + * @property {string} protocol - The protocol of your current href metaverse address (e.g., "hifi"). + * Read-only. + */ + class AddressManager : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY @@ -42,10 +68,78 @@ class AddressManager : public QObject, public Dependency { Q_PROPERTY(QString placename READ getPlaceName) Q_PROPERTY(QString domainId READ getDomainId) public: + + /**jsdoc + * Get Interface's protocol version. + * @function location.protocolVersion + * @returns {string} A string uniquely identifying the version of the metaverse protocol that Interface is using. + * @deprecated This function is deprecated and will be removed. Use {@link Window.protocolSignature} instead. + */ Q_INVOKABLE QString protocolVersion(); + using PositionGetter = std::function; using OrientationGetter = std::function; + /**jsdoc + *

The reasons for an address lookup via the metaverse API are defined by numeric values:

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
NameValueDescription
UserInput0User-typed input.
Back1Address from a {@link location.goBack|goBack} call.
Forward2Address from a {@link location.goForward|goForward} call.
StartupFromSettings3Initial location at Interface start-up from settings.
DomainPathResponse4A named path in the domain.
Internal5An internal attempt to resolve an alternative path.
AttemptedRefresh6A refresh after connecting to a domain.
Suggestions7Address from the Goto dialog.
VisitUserFromPAL8User from the People dialog.
+ * @typedef location.LookupTrigger + */ enum LookupTrigger { UserInput, Back, @@ -83,43 +177,240 @@ public: const QStack& getForwardStack() const { return _forwardStack; } public slots: + /**jsdoc + * Go to a specified metaverse address. + * @function location.handleLookupString + * @param {string} address - The address to go to: a "hifi:/" address, an IP address (e.g., + * "127.0.0.1" or "localhost"), a domain name, a named path on a domain (starts with + * "/"), a position or position and orientation, or a user (starts with "@"). + * @param {boolean} fromSuggestions=false - Set to true if the address is obtained from the "Goto" dialog. + * Helps ensure that user's location history is correctly maintained. + */ void handleLookupString(const QString& lookupString, bool fromSuggestions = false); - + + /**jsdoc + * Go to a position and orientation resulting from a lookup for a named path in the domain (set in the domain server's + * settings). + * @function location.goToViewpointForPath + * @param {string} path - The position and orientation corresponding to the named path. + * @param {string} namedPath - The named path that was looked up on the server. + * @deprecated This function is deprecated and will be removed. + */ + // This function is marked as deprecated in anticipation that it will not be included in the JavaScript API if and when the + // functions and signals that should be exposed are moved to a scripting interface class. + // // we currently expect this to be called from NodeList once handleLookupString has been called with a path bool goToViewpointForPath(const QString& viewpointString, const QString& pathString) { return handleViewpoint(viewpointString, false, DomainPathResponse, false, pathString); } + /**jsdoc + * Go back to the previous location in your navigation history, if there is one. + * @function location.goBack + */ void goBack(); + + /**jsdoc + * Go forward to the next location in your navigation history, if there is one. + * @function location.goForward + */ void goForward(); + + /**jsdoc + * Go to the local Sandbox server that's running on the same PC as Interface. + * @function location.goToLocalSandbox + * @param {string} path="" - The position and orientation to go to (e.g., "/0,0,0"). + * @param {location.LookupTrigger} trigger=StartupFromSettings - The reason for the function call. Helps ensure that user's + * location history is correctly maintained. + */ void goToLocalSandbox(QString path = "", LookupTrigger trigger = LookupTrigger::StartupFromSettings) { handleUrl(SANDBOX_HIFI_ADDRESS + path, trigger); } + + /**jsdoc + * Go to the default "welcome" metaverse address. + * @function location.goToEntry + * @param {location.LookupTrigger} trigger=StartupFromSettings - The reason for the function call. Helps ensure that user's + * location history is correctly maintained. + */ void goToEntry(LookupTrigger trigger = LookupTrigger::StartupFromSettings) { handleUrl(DEFAULT_HIFI_ADDRESS, trigger); } + /**jsdoc + * Go to the specified user's location. + * @function location.goToUser + * @param {string} username - The user's username. + * @param {boolean} matchOrientation=true - If true then go to a location just in front of the user and turn to face + * them, otherwise go to the user's exact location and orientation. + */ void goToUser(const QString& username, bool shouldMatchOrientation = true); + /**jsdoc + * Refresh the current address, e.g., after connecting to a domain in order to position the user to the desired location. + * @function location.refreshPreviousLookup + * @deprecated This function is deprecated and will be removed. + */ + // This function is marked as deprecated in anticipation that it will not be included in the JavaScript API if and when the + // functions and signals that should be exposed are moved to a scripting interface class. void refreshPreviousLookup(); + /**jsdoc + * Update your current metaverse location in Interface's {@link Settings} file as your last-known address. This can be used + * to ensure that you start up at that address if you exit Interface without a later address automatically being saved. + * @function location.storeCurrentAddress + */ void storeCurrentAddress(); + /**jsdoc + * Copy your current metaverse address (i.e., location.href property value) to the OS clipboard. + * @function location.copyAddress + */ void copyAddress(); + + /**jsdoc + * Copy your current metaverse location and orientation (i.e., location.pathname property value) to the OS + * clipboard. + * @function location.copyPath + */ void copyPath(); + /**jsdoc + * Retrieve and remember the place name for the given domain ID if the place name is not already known. + * @function location.lookupShareableNameForDomainID + * @param {Uuid} domainID - The UUID of the domain. + * @deprecated This function is deprecated and will be removed. + */ + // This function is marked as deprecated in anticipation that it will not be included in the JavaScript API if and when the + // functions and signals that should be exposed are moved to a scripting interface class. void lookupShareableNameForDomainID(const QUuid& domainID); signals: + /**jsdoc + * Triggered when looking up the details of a metaverse user or location to go to has completed (successfully or + * unsuccessfully). + * @function location.lookupResultsFinished + * @returns {Signal} + */ void lookupResultsFinished(); + + /**jsdoc + * Triggered when looking up the details of a metaverse user or location to go to has completed and the domain or user is + * offline. + * @function location.lookupResultIsOffline + * @returns {Signal} + */ void lookupResultIsOffline(); + + /**jsdoc + * Triggered when looking up the details of a metaverse user or location to go to has completed and the domain or user could + * not be found. + * @function location.lookupResultIsNotFound + * @returns {Signal} + */ void lookupResultIsNotFound(); + /**jsdoc + * Triggered when a request is made to go to an IP address. + * @function location.possibleDomainChangeRequired + * @param {string} hostName - The name of the domain to go do. + * @param {number} port - The integer number of the network port to connect to. + * @param {Uuid} domainID - The UUID of the domain to go to. + * @returns {Signal} + */ + // No example because this function isn't typically used in scripts. void possibleDomainChangeRequired(const QString& newHostname, quint16 newPort, const QUuid& domainID); + + /**jsdoc + * Triggered when a request is made to go to a named domain or user. + * @function location.possibleDomainChangeRequiredViaICEForID + * @param {string} iceServerHostName - IP address of the ICE server. + * @param {Uuid} domainID - The UUID of the domain to go to. + * @returns {Signal} + */ + // No example because this function isn't typically used in scripts. void possibleDomainChangeRequiredViaICEForID(const QString& iceServerHostname, const QUuid& domainID); + /**jsdoc + * Triggered when an attempt is made to send your avatar to a specified position on the current domain. For example, when + * you change domains or enter a position to go to in the "Goto" dialog. + * @function location.locationChangeRequired + * @param {Vec3} position - The position to go to. + * @param {boolean} hasOrientationChange - If true then a new orientation has been requested. + * @param {Quat} orientation - The orientation to change to. Is {@link Quat(0)|Quat.IDENTITY} if + * hasOrientationChange is false. + * @param {boolean} shouldFaceLocation - If true then the request is to go to a position near that specified + * and orient your avatar to face it. For example when you visit someone from the "People" dialog. + * @returns {Signal} + * @example Report location change requests. + * function onLocationChangeRequired(newPosition, hasOrientationChange, newOrientation, shouldFaceLocation) { + * print("Location change required:"); + * print("- New position = " + JSON.stringify(newPosition)); + * print("- Has orientation change = " + hasOrientationChange); + * print("- New orientation = " + JSON.stringify(newOrientation)); + * print("- Should face location = " + shouldFaceLocation); + * } + * + * location.locationChangeRequired.connect(onLocationChangeRequired); + */ void locationChangeRequired(const glm::vec3& newPosition, bool hasOrientationChange, const glm::quat& newOrientation, bool shouldFaceLocation); + + /**jsdoc + * Triggered when an attempt is made to send your avatar to a new named path on the domain (set in the domain server's + * settings). For example, when you enter a "/" followed by the path's name in the "GOTO" dialog. + * @function location.pathChangeRequired + * @param {string} path - The name of the path to go to. + * @returns {Signal} + * @example Report path change requests. + * function onPathChangeRequired(newPath) { + * print("onPathChangeRequired: newPath = " + newPath); + * } + * + * location.pathChangeRequired.connect(onPathChangeRequired); + */ void pathChangeRequired(const QString& newPath); + + /**jsdoc + * Triggered when you navigate to a new domain. + * @function location.hostChanged + * @param {string} hostname - The new domain's host name. + * @returns {Signal} + * @example Report when you navigate to a new domain. + * function onHostChanged(host) { + * print("Host changed to: " + host); + * } + * + * location.hostChanged.connect(onHostChanged); + */ void hostChanged(const QString& newHost); + /**jsdoc + * Triggered when there's a change in whether or not there's a previous location that can be navigated to using + * {@link location.goBack|goBack}. (Reflects changes in the state of the "Goto" dialog's back arrow.) + * @function location.goBackPossible + * @param {boolean} isPossible - true if there's a previous location to navigate to, otherwise + * false. + * @returns {Signal} + * @example Report when ability to navigate back changes. + * function onGoBackPossible(isPossible) { + * print("Go back possible: " + isPossible); + * } + * + * location.goBackPossible.connect(onGoBackPossible); + */ void goBackPossible(bool isPossible); + + /**jsdoc + * Triggered when there's a change in whether or not there's a forward location that can be navigated to using + * {@link location.goForward|goForward}. (Reflects changes in the state of the "Goto" dialog's forward arrow.) + * @function location.goForwardPossible + * @param {boolean} isPossible - true if there's a forward location to navigate to, otherwise + * false. + * @returns {Signal} + * @example Report when ability to navigate forward changes. + * function onGoForwardPossible(isPossible) { + * print("Go forward possible: " + isPossible); + * } + * + * location.goForwardPossible.connect(onGoForwardPossible); + */ void goForwardPossible(bool isPossible); protected: diff --git a/libraries/networking/src/FileResourceRequest.cpp b/libraries/networking/src/FileResourceRequest.cpp index dfff21dae6..b55f1839a0 100644 --- a/libraries/networking/src/FileResourceRequest.cpp +++ b/libraries/networking/src/FileResourceRequest.cpp @@ -11,24 +11,37 @@ #include "FileResourceRequest.h" -#include - -#include +#include +#include +#include #include +#include + +#include "ResourceManager.h" void FileResourceRequest::doSend() { auto statTracker = DependencyManager::get(); statTracker->incrementStat(STAT_FILE_REQUEST_STARTED); int fileSize = 0; - QString filename = _url.toLocalFile(); - - // sometimes on windows, we see the toLocalFile() return null, - // in this case we will attempt to simply use the url as a string - if (filename.isEmpty()) { - filename = _url.toString(); + QString filename; + if (_url.scheme() == URL_SCHEME_QRC) { + filename = ":/" + _url.path(); + } else { + filename = _url.toLocalFile(); + // sometimes on windows, we see the toLocalFile() return null, + // in this case we will attempt to simply use the url as a string + if (filename.isEmpty()) { + filename = _url.toString(); + } } + + // Allow platform specific versions of files loaded out of a resource cache via file:// + QFileSelector fileSelector; + fileSelector.setExtraSelectors(FileUtils::getFileSelectors()); + filename = fileSelector.select(filename); + if (!_byteRange.isValid()) { _result = ResourceRequest::InvalidByteRange; } else { diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 1b80c3b3af..0950cb5556 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -63,13 +63,6 @@ LimitedNodeList::LimitedNodeList(int socketListenPort, int dtlsListenPort) : _packetStatTimer(), _permissions(NodePermissions()) { - static bool firstCall = true; - if (firstCall) { - NodeType::init(); - - firstCall = false; - } - qRegisterMetaType("ConnectionStep"); auto port = (socketListenPort != INVALID_PORT) ? socketListenPort : LIMITED_NODELIST_LOCAL_PORT.get(); _nodeSocket.bind(QHostAddress::AnyIPv4, port); diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index 25eef38dbd..bd895c8ef1 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -29,28 +29,25 @@ int NodePtrMetaTypeId = qRegisterMetaType("Node*"); int sharedPtrNodeMetaTypeId = qRegisterMetaType>("QSharedPointer"); int sharedNodePtrMetaTypeId = qRegisterMetaType("SharedNodePointer"); -void NodeType::init() { - QHash& TypeNameHash = Node::getTypeNameHash(); - - TypeNameHash.insert(NodeType::DomainServer, "Domain Server"); - TypeNameHash.insert(NodeType::EntityServer, "Entity Server"); - TypeNameHash.insert(NodeType::Agent, "Agent"); - TypeNameHash.insert(NodeType::AudioMixer, "Audio Mixer"); - TypeNameHash.insert(NodeType::AvatarMixer, "Avatar Mixer"); - TypeNameHash.insert(NodeType::MessagesMixer, "Messages Mixer"); - TypeNameHash.insert(NodeType::AssetServer, "Asset Server"); - TypeNameHash.insert(NodeType::EntityScriptServer, "Entity Script Server"); - TypeNameHash.insert(NodeType::UpstreamAudioMixer, "Upstream Audio Mixer"); - TypeNameHash.insert(NodeType::UpstreamAvatarMixer, "Upstream Avatar Mixer"); - TypeNameHash.insert(NodeType::DownstreamAudioMixer, "Downstream Audio Mixer"); - TypeNameHash.insert(NodeType::DownstreamAvatarMixer, "Downstream Avatar Mixer"); - TypeNameHash.insert(NodeType::Unassigned, "Unassigned"); -} +static const QHash TYPE_NAME_HASH { + { NodeType::DomainServer, "Domain Server" }, + { NodeType::EntityServer, "Entity Server" }, + { NodeType::Agent, "Agent" }, + { NodeType::AudioMixer, "Audio Mixer" }, + { NodeType::AvatarMixer, "Avatar Mixer" }, + { NodeType::MessagesMixer, "Messages Mixer" }, + { NodeType::AssetServer, "Asset Server" }, + { NodeType::EntityScriptServer, "Entity Script Server" }, + { NodeType::UpstreamAudioMixer, "Upstream Audio Mixer" }, + { NodeType::UpstreamAvatarMixer, "Upstream Avatar Mixer" }, + { NodeType::DownstreamAudioMixer, "Downstream Audio Mixer" }, + { NodeType::DownstreamAvatarMixer, "Downstream Avatar Mixer" }, + { NodeType::Unassigned, "Unassigned" } +}; const QString& NodeType::getNodeTypeName(NodeType_t nodeType) { - QHash& TypeNameHash = Node::getTypeNameHash(); - QHash::iterator matchedTypeName = TypeNameHash.find(nodeType); - return matchedTypeName != TypeNameHash.end() ? matchedTypeName.value() : UNKNOWN_NodeType_t_NAME; + const auto matchedTypeName = TYPE_NAME_HASH.find(nodeType); + return matchedTypeName != TYPE_NAME_HASH.end() ? matchedTypeName.value() : UNKNOWN_NodeType_t_NAME; } bool NodeType::isUpstream(NodeType_t nodeType) { @@ -84,8 +81,7 @@ NodeType_t NodeType::downstreamType(NodeType_t primaryType) { } NodeType_t NodeType::fromString(QString type) { - QHash& TypeNameHash = Node::getTypeNameHash(); - return TypeNameHash.key(type, NodeType::Unassigned); + return TYPE_NAME_HASH.key(type, NodeType::Unassigned); } diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index 00d3c61fd0..93b6a649d4 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -89,11 +89,6 @@ public: bool isIgnoreRadiusEnabled() const { return _ignoreRadiusEnabled; } - static QHash& getTypeNameHash() { - static QHash TypeNameHash; - return TypeNameHash; - } - private: // privatize copy and assignment operator to disallow Node copying Node(const Node &otherNode); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 5a72006a8c..1c18125433 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -437,7 +437,7 @@ void NodeList::sendPendingDSPathQuery() { QString pendingPath = _domainHandler.getPendingPath(); if (!pendingPath.isEmpty()) { - qCDebug(networking) << "Attemping to send pending query to DS for path" << pendingPath; + qCDebug(networking) << "Attempting to send pending query to DS for path" << pendingPath; // this is a slot triggered if we just established a network link with a DS and want to send a path query sendDSPathQuery(_domainHandler.getPendingPath()); diff --git a/libraries/networking/src/NodeType.h b/libraries/networking/src/NodeType.h index 0130e92cfc..2b2cc4e011 100644 --- a/libraries/networking/src/NodeType.h +++ b/libraries/networking/src/NodeType.h @@ -31,8 +31,6 @@ namespace NodeType { const NodeType_t DownstreamAvatarMixer = 'w'; const NodeType_t Unassigned = 1; - void init(); - const QString& getNodeTypeName(NodeType_t nodeType); bool isUpstream(NodeType_t nodeType); bool isDownstream(NodeType_t nodeType); diff --git a/libraries/networking/src/ResourceManager.cpp b/libraries/networking/src/ResourceManager.cpp index 3ee66f89c1..17dcd9728d 100644 --- a/libraries/networking/src/ResourceManager.cpp +++ b/libraries/networking/src/ResourceManager.cpp @@ -11,6 +11,8 @@ #include "ResourceManager.h" +#include + #include #include #include @@ -24,7 +26,6 @@ #include "NetworkAccessManager.h" #include "NetworkLogging.h" - ResourceManager::ResourceManager() { _thread.setObjectName("Resource Manager Thread"); @@ -53,7 +54,7 @@ QString ResourceManager::normalizeURL(const QString& urlString) { copy = _prefixMap; } - foreach(const auto& entry, copy) { + foreach (const auto& entry, copy) { const auto& prefix = entry.first; const auto& replacement = entry.second; if (result.startsWith(prefix)) { @@ -64,13 +65,24 @@ QString ResourceManager::normalizeURL(const QString& urlString) { return result; } +const QSet& getKnownUrls() { + static QSet knownUrls; + static std::once_flag once; + std::call_once(once, [] { + knownUrls.insert(URL_SCHEME_QRC); + knownUrls.insert(URL_SCHEME_FILE); + knownUrls.insert(URL_SCHEME_HTTP); + knownUrls.insert(URL_SCHEME_HTTPS); + knownUrls.insert(URL_SCHEME_FTP); + knownUrls.insert(URL_SCHEME_ATP); + }); + return knownUrls; +} + QUrl ResourceManager::normalizeURL(const QUrl& originalUrl) { QUrl url = QUrl(normalizeURL(originalUrl.toString())); auto scheme = url.scheme(); - if (!(scheme == URL_SCHEME_FILE || - scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS || scheme == URL_SCHEME_FTP || - scheme == URL_SCHEME_ATP)) { - + if (!getKnownUrls().contains(scheme)) { // check the degenerative file case: on windows we can often have urls of the form c:/filename // this checks for and works around that case. QUrl urlWithFileScheme{ URL_SCHEME_FILE + ":///" + url.toString() }; @@ -94,7 +106,7 @@ ResourceRequest* ResourceManager::createResourceRequest(QObject* parent, const Q ResourceRequest* request = nullptr; - if (scheme == URL_SCHEME_FILE) { + if (scheme == URL_SCHEME_FILE || scheme == URL_SCHEME_QRC) { request = new FileResourceRequest(normalizedURL); } else if (scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS || scheme == URL_SCHEME_FTP) { request = new HTTPResourceRequest(normalizedURL); @@ -113,15 +125,14 @@ ResourceRequest* ResourceManager::createResourceRequest(QObject* parent, const Q return request; } - bool ResourceManager::resourceExists(const QUrl& url) { auto scheme = url.scheme(); if (scheme == URL_SCHEME_FILE) { - QFileInfo file { url.toString() }; + QFileInfo file{ url.toString() }; return file.exists(); } else if (scheme == URL_SCHEME_HTTP || scheme == URL_SCHEME_HTTPS || scheme == URL_SCHEME_FTP) { auto& networkAccessManager = NetworkAccessManager::getInstance(); - QNetworkRequest request { url }; + QNetworkRequest request{ url }; request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); request.setHeader(QNetworkRequest::UserAgentHeader, HIGH_FIDELITY_USER_AGENT); @@ -159,4 +170,3 @@ bool ResourceManager::resourceExists(const QUrl& url) { qCDebug(networking) << "Unknown scheme (" << scheme << ") for URL: " << url.url(); return false; } - diff --git a/libraries/networking/src/ResourceManager.h b/libraries/networking/src/ResourceManager.h index fdfd05736e..5728a7bd32 100644 --- a/libraries/networking/src/ResourceManager.h +++ b/libraries/networking/src/ResourceManager.h @@ -22,6 +22,7 @@ #include "ResourceRequest.h" +const QString URL_SCHEME_QRC = "qrc"; const QString URL_SCHEME_FILE = "file"; const QString URL_SCHEME_HTTP = "http"; const QString URL_SCHEME_HTTPS = "https"; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 44f86baf40..c48c6bfc0b 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -30,7 +30,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return static_cast(EntityVersion::OwnershipChallengeFix); + return static_cast(EntityVersion::SoftEntities); case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::RemovedJurisdictions); @@ -38,7 +38,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarData: case PacketType::BulkAvatarData: case PacketType::KillAvatar: - return static_cast(AvatarMixerPacketVersion::UpdatedMannequinDefaultAvatar); + return static_cast(AvatarMixerPacketVersion::AvatarJointDefaultPoseFlags); case PacketType::MessagesData: return static_cast(MessageDataVersion::TextOrBinaryData); case PacketType::ICEServerHeartbeat: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index b2f22e2a1f..0f69691bd5 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -196,12 +196,17 @@ void sendWrongProtocolVersionsSignature(bool sendWrongVersion); /// for debuggin uint qHash(const PacketType& key, uint seed); QDebug operator<<(QDebug debug, const PacketType& type); +// Due to the different legacy behaviour, we need special processing for domains that were created before +// the zone inheritance modes were added. These have version numbers up to 80 enum class EntityVersion : PacketVersion { StrokeColorProperty = 0, HasDynamicOwnershipTests, HazeEffect, StaticCertJsonVersionOne, OwnershipChallengeFix, + ZoneLightInheritModes = 82, + ZoneStageRemoved, + SoftEntities }; enum class EntityScriptCallMethodVersion : PacketVersion { @@ -242,7 +247,8 @@ enum class AvatarMixerPacketVersion : PacketVersion { AvatarIdentitySequenceFront, IsReplicatedInAvatarIdentity, AvatarIdentityLookAtSnapping, - UpdatedMannequinDefaultAvatar + UpdatedMannequinDefaultAvatar, + AvatarJointDefaultPoseFlags }; enum class DomainConnectRequestVersion : PacketVersion { diff --git a/libraries/octree/src/OctreePacketData.h b/libraries/octree/src/OctreePacketData.h index 09eb134124..ef4e98798f 100644 --- a/libraries/octree/src/OctreePacketData.h +++ b/libraries/octree/src/OctreePacketData.h @@ -28,7 +28,6 @@ #include #include -#include #include #include #include @@ -258,7 +257,6 @@ public: static int unpackDataFromBytes(const unsigned char* dataBytes, rgbColor& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, glm::quat& result) { int bytes = unpackOrientationQuatFromBytes(dataBytes, result); return bytes; } static int unpackDataFromBytes(const unsigned char* dataBytes, ShapeType& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } - static int unpackDataFromBytes(const unsigned char* dataBytes, BackgroundMode& result) { memcpy(&result, dataBytes, sizeof(result)); return sizeof(result); } static int unpackDataFromBytes(const unsigned char* dataBytes, QString& result); static int unpackDataFromBytes(const unsigned char* dataBytes, QUuid& result); static int unpackDataFromBytes(const unsigned char* dataBytes, xColor& result); diff --git a/libraries/physics/CMakeLists.txt b/libraries/physics/CMakeLists.txt index e219d3dbcd..ad082c1a6e 100644 --- a/libraries/physics/CMakeLists.txt +++ b/libraries/physics/CMakeLists.txt @@ -1,6 +1,6 @@ set(TARGET_NAME physics) setup_hifi_library() -link_hifi_libraries(shared fbx entities model) +link_hifi_libraries(shared fbx entities graphics) include_hifi_library_headers(networking) include_hifi_library_headers(gpu) include_hifi_library_headers(avatars) diff --git a/libraries/physics/src/CollisionRenderMeshCache.cpp b/libraries/physics/src/CollisionRenderMeshCache.cpp index 40a8a4aff9..6f66b9af10 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.cpp +++ b/libraries/physics/src/CollisionRenderMeshCache.cpp @@ -21,7 +21,7 @@ const int32_t MAX_HULL_INDICES = 6 * MAX_HULL_POINTS; const int32_t MAX_HULL_NORMALS = MAX_HULL_INDICES; float tempVertices[MAX_HULL_NORMALS]; -model::Index tempIndexBuffer[MAX_HULL_INDICES]; +graphics::Index tempIndexBuffer[MAX_HULL_INDICES]; bool copyShapeToMesh(const btTransform& transform, const btConvexShape* shape, gpu::BufferView& vertices, gpu::BufferView& indices, gpu::BufferView& parts, @@ -40,21 +40,21 @@ bool copyShapeToMesh(const btTransform& transform, const btConvexShape* shape, assert(numHullVertices <= MAX_HULL_POINTS); { // new part - model::Mesh::Part part; - part._startIndex = (model::Index)indices.getNumElements(); - part._numIndices = (model::Index)numHullIndices; + graphics::Mesh::Part part; + part._startIndex = (graphics::Index)indices.getNumElements(); + part._numIndices = (graphics::Index)numHullIndices; // FIXME: the render code cannot handle the case where part._baseVertex != 0 //part._baseVertex = vertices.getNumElements(); // DOES NOT WORK part._baseVertex = 0; - gpu::BufferView::Size numBytes = sizeof(model::Mesh::Part); + gpu::BufferView::Size numBytes = sizeof(graphics::Mesh::Part); const gpu::Byte* data = reinterpret_cast(&part); parts._buffer->append(numBytes, data); parts._size = parts._buffer->getSize(); } const int32_t SIZE_OF_VEC3 = 3 * sizeof(float); - model::Index indexOffset = (model::Index)vertices.getNumElements(); + graphics::Index indexOffset = (graphics::Index)vertices.getNumElements(); { // new indices const uint32_t* hullIndices = hull.getIndexPointer(); @@ -64,7 +64,7 @@ bool copyShapeToMesh(const btTransform& transform, const btConvexShape* shape, tempIndexBuffer[i] = hullIndices[i] + indexOffset; } const gpu::Byte* data = reinterpret_cast(tempIndexBuffer); - gpu::BufferView::Size numBytes = (gpu::BufferView::Size)(sizeof(model::Index) * numHullIndices); + gpu::BufferView::Size numBytes = (gpu::BufferView::Size)(sizeof(graphics::Index) * numHullIndices); indices._buffer->append(numBytes, data); indices._size = indices._buffer->getSize(); } @@ -105,8 +105,8 @@ bool copyShapeToMesh(const btTransform& transform, const btConvexShape* shape, return true; } -model::MeshPointer createMeshFromShape(const void* pointer) { - model::MeshPointer mesh; +graphics::MeshPointer createMeshFromShape(const void* pointer) { + graphics::MeshPointer mesh; if (!pointer) { return mesh; } @@ -147,7 +147,7 @@ model::MeshPointer createMeshFromShape(const void* pointer) { } } if (numSuccesses > 0) { - mesh = std::make_shared(); + mesh = std::make_shared(); mesh->setVertexBuffer(vertices); mesh->setIndexBuffer(indices); mesh->setPartBuffer(parts); @@ -167,8 +167,8 @@ CollisionRenderMeshCache::~CollisionRenderMeshCache() { _pendingGarbage.clear(); } -model::MeshPointer CollisionRenderMeshCache::getMesh(CollisionRenderMeshCache::Key key) { - model::MeshPointer mesh; +graphics::MeshPointer CollisionRenderMeshCache::getMesh(CollisionRenderMeshCache::Key key) { + graphics::MeshPointer mesh; if (key) { CollisionMeshMap::const_iterator itr = _meshMap.find(key); if (itr == _meshMap.end()) { diff --git a/libraries/physics/src/CollisionRenderMeshCache.h b/libraries/physics/src/CollisionRenderMeshCache.h index 10b2440db2..c5b643c0cc 100644 --- a/libraries/physics/src/CollisionRenderMeshCache.h +++ b/libraries/physics/src/CollisionRenderMeshCache.h @@ -16,7 +16,7 @@ #include #include -#include +#include class CollisionRenderMeshCache { @@ -27,7 +27,7 @@ public: ~CollisionRenderMeshCache(); /// \return pointer to geometry - model::MeshPointer getMesh(Key key); + graphics::MeshPointer getMesh(Key key); /// \return true if geometry was found and released bool releaseMesh(Key key); @@ -40,7 +40,7 @@ public: bool hasMesh(Key key) const { return _meshMap.find(key) == _meshMap.end(); } private: - using CollisionMeshMap = std::unordered_map; + using CollisionMeshMap = std::unordered_map; CollisionMeshMap _meshMap; std::vector _pendingGarbage; }; diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index 33ac887f4f..420da5a1e0 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -256,25 +256,32 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { assert(_entity); assert(entityTreeIsLocked()); measureBodyAcceleration(); - bool positionSuccess; - _entity->setWorldPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(), positionSuccess, false); - if (!positionSuccess) { - static QString repeatedMessage = - LogHandler::getInstance().addRepeatedMessageRegex("EntityMotionState::setWorldTransform " - "setPosition failed.*"); - qCDebug(physics) << "EntityMotionState::setWorldTransform setPosition failed" << _entity->getID(); + + // If transform or velocities are flagged as dirty it means a network or scripted change + // occured between the beginning and end of the stepSimulation() and we DON'T want to apply + // these physics simulation results. + uint32_t flags = _entity->getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES); + if (!flags) { + // flags are clear + _entity->setWorldTransform(bulletToGLM(worldTrans.getOrigin()), bulletToGLM(worldTrans.getRotation())); + _entity->setWorldVelocity(getBodyLinearVelocity()); + _entity->setWorldAngularVelocity(getBodyAngularVelocity()); + _entity->setLastSimulated(usecTimestampNow()); + } else { + // only set properties NOT flagged + if (!(flags & Simulation::DIRTY_TRANSFORM)) { + _entity->setWorldTransform(bulletToGLM(worldTrans.getOrigin()), bulletToGLM(worldTrans.getRotation())); + } + if (!(flags & Simulation::DIRTY_LINEAR_VELOCITY)) { + _entity->setWorldVelocity(getBodyLinearVelocity()); + } + if (!(flags & Simulation::DIRTY_ANGULAR_VELOCITY)) { + _entity->setWorldAngularVelocity(getBodyAngularVelocity()); + } + if (flags != (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES)) { + _entity->setLastSimulated(usecTimestampNow()); + } } - bool orientationSuccess; - _entity->setWorldOrientation(bulletToGLM(worldTrans.getRotation()), orientationSuccess, false); - if (!orientationSuccess) { - static QString repeatedMessage = - LogHandler::getInstance().addRepeatedMessageRegex("EntityMotionState::setWorldTransform " - "setOrientation failed.*"); - qCDebug(physics) << "EntityMotionState::setWorldTransform setOrientation failed" << _entity->getID(); - } - _entity->setVelocity(getBodyLinearVelocity()); - _entity->setAngularVelocity(getBodyAngularVelocity()); - _entity->setLastSimulated(usecTimestampNow()); if (_entity->getSimulatorID().isNull()) { _loopsWithoutOwner++; @@ -530,9 +537,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ if (!_body->isActive()) { // make sure all derivatives are zero - _entity->setVelocity(Vectors::ZERO); - _entity->setAngularVelocity(Vectors::ZERO); - _entity->setAcceleration(Vectors::ZERO); + zeroCleanObjectVelocities(); _numInactiveUpdates++; } else { glm::vec3 gravity = _entity->getGravity(); @@ -559,9 +564,7 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_ if (movingSlowly) { // velocities might not be zero, but we'll fake them as such, which will hopefully help convince // other simulating observers to deactivate their own copies - glm::vec3 zero(0.0f); - _entity->setVelocity(zero); - _entity->setAngularVelocity(zero); + zeroCleanObjectVelocities(); } } _numInactiveUpdates = 0; @@ -818,3 +821,22 @@ bool EntityMotionState::shouldBeLocallyOwned() const { void EntityMotionState::upgradeOutgoingPriority(uint8_t priority) { _outgoingPriority = glm::max(_outgoingPriority, priority); } + +void EntityMotionState::zeroCleanObjectVelocities() const { + // If transform or velocities are flagged as dirty it means a network or scripted change + // occured between the beginning and end of the stepSimulation() and we DON'T want to apply + // these physics simulation results. + uint32_t flags = _entity->getDirtyFlags() & (Simulation::DIRTY_TRANSFORM | Simulation::DIRTY_VELOCITIES); + if (!flags) { + _entity->setWorldVelocity(glm::vec3(0.0f)); + _entity->setWorldAngularVelocity(glm::vec3(0.0f)); + } else { + if (!(flags & Simulation::DIRTY_LINEAR_VELOCITY)) { + _entity->setWorldVelocity(glm::vec3(0.0f)); + } + if (!(flags & Simulation::DIRTY_ANGULAR_VELOCITY)) { + _entity->setWorldAngularVelocity(glm::vec3(0.0f)); + } + } + _entity->setAcceleration(glm::vec3(0.0f)); +} diff --git a/libraries/physics/src/EntityMotionState.h b/libraries/physics/src/EntityMotionState.h index ddfd7e1e4c..784273d600 100644 --- a/libraries/physics/src/EntityMotionState.h +++ b/libraries/physics/src/EntityMotionState.h @@ -87,6 +87,7 @@ public: protected: // changes _outgoingPriority only if priority is larger void upgradeOutgoingPriority(uint8_t priority); + void zeroCleanObjectVelocities() const; #ifdef WANT_DEBUG_ENTITY_TREE_LOCKS bool entityTreeIsLocked() const; diff --git a/libraries/procedural/CMakeLists.txt b/libraries/procedural/CMakeLists.txt index daf6fefccc..9ec7cb6431 100644 --- a/libraries/procedural/CMakeLists.txt +++ b/libraries/procedural/CMakeLists.txt @@ -1,5 +1,5 @@ set(TARGET_NAME procedural) -AUTOSCRIBE_SHADER_LIB(gpu model) +AUTOSCRIBE_SHADER_LIB(gpu graphics) setup_hifi_library() -link_hifi_libraries(shared gpu networking model model-networking ktx image) +link_hifi_libraries(shared gpu networking graphics model-networking ktx image) diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.cpp b/libraries/procedural/src/procedural/ProceduralSkybox.cpp index 9544759037..f0c60e2101 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.cpp +++ b/libraries/procedural/src/procedural/ProceduralSkybox.cpp @@ -15,10 +15,10 @@ #include #include -#include -#include +#include +#include -ProceduralSkybox::ProceduralSkybox() : model::Skybox() { +ProceduralSkybox::ProceduralSkybox() : graphics::Skybox() { _procedural._vertexSource = skybox_vert; _procedural._fragmentSource = skybox_frag; // Adjust the pipeline state for background using the stencil test diff --git a/libraries/procedural/src/procedural/ProceduralSkybox.h b/libraries/procedural/src/procedural/ProceduralSkybox.h index 5a1133a766..5db1078a5f 100644 --- a/libraries/procedural/src/procedural/ProceduralSkybox.h +++ b/libraries/procedural/src/procedural/ProceduralSkybox.h @@ -13,11 +13,11 @@ #ifndef hifi_ProceduralSkybox_h #define hifi_ProceduralSkybox_h -#include +#include #include "Procedural.h" -class ProceduralSkybox: public model::Skybox { +class ProceduralSkybox: public graphics::Skybox { public: ProceduralSkybox(); diff --git a/libraries/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index 94232c81b2..6be3057c93 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -1,9 +1,9 @@ set(TARGET_NAME render-utils) -AUTOSCRIBE_SHADER_LIB(gpu model render) +AUTOSCRIBE_SHADER_LIB(gpu graphics render) # pull in the resources.qrc file qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc") setup_hifi_library(Widgets OpenGL Network Qml Quick Script) -link_hifi_libraries(shared ktx gpu model model-networking render animation fbx image procedural) +link_hifi_libraries(shared ktx gpu graphics model-networking render animation fbx image procedural) include_hifi_library_headers(networking) include_hifi_library_headers(octree) include_hifi_library_headers(audio) diff --git a/libraries/render-utils/src/BackgroundStage.cpp b/libraries/render-utils/src/BackgroundStage.cpp index f246ab7e60..886795ec79 100644 --- a/libraries/render-utils/src/BackgroundStage.cpp +++ b/libraries/render-utils/src/BackgroundStage.cpp @@ -64,8 +64,8 @@ void DrawBackgroundStage::run(const render::RenderContextPointer& renderContext, auto backgroundStage = renderContext->_scene->getStage(); assert(backgroundStage); - model::SunSkyStagePointer background; - model::SkyboxPointer skybox; + graphics::SunSkyStagePointer background; + graphics::SkyboxPointer skybox; if (backgroundStage->_currentFrame._backgrounds.size()) { auto backgroundId = backgroundStage->_currentFrame._backgrounds.front(); auto background = backgroundStage->getBackground(backgroundId); @@ -76,7 +76,7 @@ void DrawBackgroundStage::run(const render::RenderContextPointer& renderContext, /* auto backgroundMode = skyStage->getBackgroundMode(); switch (backgroundMode) { - case model::SunSkyStage::SKY_DEFAULT: { + case graphics::SunSkyStage::SKY_DEFAULT: { auto scene = DependencyManager::get()->getStage(); auto sceneKeyLight = scene->getKeyLight(); @@ -88,7 +88,7 @@ void DrawBackgroundStage::run(const render::RenderContextPointer& renderContext, // fall through: render a skybox (if available), or the defaults (if requested) } - case model::SunSkyStage::SKY_BOX: {*/ + case graphics::SunSkyStage::SKY_BOX: {*/ if (skybox && !skybox->empty()) { PerformanceTimer perfTimer("skybox"); auto args = renderContext->args; @@ -118,7 +118,7 @@ void DrawBackgroundStage::run(const render::RenderContextPointer& renderContext, // fall through: render defaults (if requested) // } /* - case model::SunSkyStage::SKY_DEFAULT_AMBIENT_TEXTURE: { + case graphics::SunSkyStage::SKY_DEFAULT_AMBIENT_TEXTURE: { if (Menu::getInstance()->isOptionChecked(MenuOption::DefaultSkybox)) { auto scene = DependencyManager::get()->getStage(); auto sceneKeyLight = scene->getKeyLight(); diff --git a/libraries/render-utils/src/BackgroundStage.h b/libraries/render-utils/src/BackgroundStage.h index 4e0e09db5b..db876b1993 100644 --- a/libraries/render-utils/src/BackgroundStage.h +++ b/libraries/render-utils/src/BackgroundStage.h @@ -11,7 +11,7 @@ #ifndef hifi_render_utils_BackgroundStage_h #define hifi_render_utils_BackgroundStage_h -#include +#include #include #include #include @@ -30,8 +30,8 @@ public: static const Index INVALID_INDEX; static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } - using BackgroundPointer = model::SunSkyStagePointer; - using Backgrounds = render::indexed_container::IndexedPointerVector; + using BackgroundPointer = graphics::SunSkyStagePointer; + using Backgrounds = render::indexed_container::IndexedPointerVector; using BackgroundMap = std::unordered_map; using BackgroundIndices = std::vector; diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp index 9de973480a..3d213840dd 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.cpp +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.cpp @@ -20,16 +20,16 @@ using namespace render; CauterizedMeshPartPayload::CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : ModelMeshPartPayload(model, meshIndex, partIndex, shapeIndex, transform, offsetTransform) {} -void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector& clusterMatrices, const std::vector& cauterizedClusterMatrices) { - ModelMeshPartPayload::updateClusterBuffer(clusterMatrices); +void CauterizedMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms) { + ModelMeshPartPayload::updateClusterBuffer(clusterTransforms); - if (cauterizedClusterMatrices.size() > 1) { + if (cauterizedClusterTransforms.size() > 1) { if (!_cauterizedClusterBuffer) { - _cauterizedClusterBuffer = std::make_shared(cauterizedClusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) cauterizedClusterMatrices.data()); + _cauterizedClusterBuffer = std::make_shared(cauterizedClusterTransforms.size() * sizeof(TransformType), + (const gpu::Byte*) cauterizedClusterTransforms.data()); } else { - _cauterizedClusterBuffer->setSubData(0, cauterizedClusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) cauterizedClusterMatrices.data()); + _cauterizedClusterBuffer->setSubData(0, cauterizedClusterTransforms.size() * sizeof(TransformType), + (const gpu::Byte*) cauterizedClusterTransforms.data()); } } } diff --git a/libraries/render-utils/src/CauterizedMeshPartPayload.h b/libraries/render-utils/src/CauterizedMeshPartPayload.h index 44eddc6e31..2337632047 100644 --- a/libraries/render-utils/src/CauterizedMeshPartPayload.h +++ b/libraries/render-utils/src/CauterizedMeshPartPayload.h @@ -15,7 +15,13 @@ class CauterizedMeshPartPayload : public ModelMeshPartPayload { public: CauterizedMeshPartPayload(ModelPointer model, int meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform); - void updateClusterBuffer(const std::vector& clusterMatrices, const std::vector& cauterizedClusterMatrices); +#if defined(SKIN_DQ) + using TransformType = Model::TransformDualQuaternion; +#else + using TransformType = glm::mat4; +#endif + + void updateClusterBuffer(const std::vector& clusterTransforms, const std::vector& cauterizedClusterTransforms); void updateTransformForCauterizedMesh(const Transform& renderTransform); diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index dbb82ab638..e3f26a43d8 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -9,13 +9,13 @@ #include "CauterizedModel.h" #include +#include #include "AbstractViewStateInterface.h" #include "MeshPartPayload.h" #include "CauterizedMeshPartPayload.h" #include "RenderUtilsLogging.h" - CauterizedModel::CauterizedModel(QObject* parent) : Model(parent) { } @@ -35,7 +35,7 @@ bool CauterizedModel::updateGeometry() { const FBXGeometry& fbxGeometry = getFBXGeometry(); foreach (const FBXMesh& mesh, fbxGeometry.meshes) { Model::MeshState state; - state.clusterMatrices.resize(mesh.clusters.size()); + state.clusterTransforms.resize(mesh.clusters.size()); _cauterizeMeshStates.append(state); } } @@ -109,30 +109,52 @@ void CauterizedModel::updateClusterMatrices() { const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); +#if defined(SKIN_DQ) + auto jointPose = _rig.getJointPose(cluster.jointIndex); + Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); + Transform clusterTransform; + Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + state.clusterTransforms[j] = Model::TransformDualQuaternion(clusterTransform); +#else auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); +#endif } } // as an optimization, don't build cautrizedClusterMatrices if the boneSet is empty. if (!_cauterizeBoneSet.empty()) { +#if defined(SKIN_DQ) + AnimPose cauterizePose = _rig.getJointPose(geometry.neckJointIndex); + cauterizePose.scale() = glm::vec3(0.0001f, 0.0001f, 0.0001f); +#else static const glm::mat4 zeroScale( - glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), - glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), - glm::vec4(0.0f, 0.0f, 0.0f, 0.0f), + glm::vec4(0.0001f, 0.0f, 0.0f, 0.0f), + glm::vec4(0.0f, 0.0001f, 0.0f, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0001f, 0.0f), glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); auto cauterizeMatrix = _rig.getJointTransform(geometry.neckJointIndex) * zeroScale; - +#endif for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); + for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); - auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - if (_cauterizeBoneSet.find(cluster.jointIndex) != _cauterizeBoneSet.end()) { - jointMatrix = cauterizeMatrix; + + if (_cauterizeBoneSet.find(cluster.jointIndex) == _cauterizeBoneSet.end()) { + // not cauterized so just copy the value from the non-cauterized version. + state.clusterTransforms[j] = _meshStates[i].clusterTransforms[j]; + } else { +#if defined(SKIN_DQ) + Transform jointTransform(cauterizePose.rot(), cauterizePose.scale(), cauterizePose.trans()); + Transform clusterTransform; + Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + state.clusterTransforms[j] = Model::TransformDualQuaternion(clusterTransform); +#else + glm_mat4u_mul(cauterizeMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); +#endif } - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); } } } @@ -189,24 +211,38 @@ void CauterizedModel::updateRenderItems() { auto itemID = self->_modelMeshRenderItemIDs[i]; auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex; - auto clusterMatrices(self->getMeshState(meshIndex).clusterMatrices); - auto clusterMatricesCauterized(self->getCauterizeMeshState(meshIndex).clusterMatrices); + auto clusterTransforms(self->getMeshState(meshIndex).clusterTransforms); + auto clusterTransformsCauterized(self->getCauterizeMeshState(meshIndex).clusterTransforms); bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex); - transaction.updateItem(itemID, [modelTransform, clusterMatrices, clusterMatricesCauterized, invalidatePayloadShapeKey, + transaction.updateItem(itemID, [modelTransform, clusterTransforms, clusterTransformsCauterized, invalidatePayloadShapeKey, isWireframe, isVisible, isLayeredInFront, isLayeredInHUD, enableCauterization](CauterizedMeshPartPayload& data) { - data.updateClusterBuffer(clusterMatrices, clusterMatricesCauterized); + data.updateClusterBuffer(clusterTransforms, clusterTransformsCauterized); Transform renderTransform = modelTransform; - if (clusterMatrices.size() == 1) { - renderTransform = modelTransform.worldTransform(Transform(clusterMatrices[0])); + if (clusterTransforms.size() == 1) { +#if defined(SKIN_DQ) + Transform transform(clusterTransforms[0].getRotation(), + clusterTransforms[0].getScale(), + clusterTransforms[0].getTranslation()); + renderTransform = modelTransform.worldTransform(transform); +#else + renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0])); +#endif } data.updateTransformForSkinnedMesh(renderTransform, modelTransform); renderTransform = modelTransform; - if (clusterMatricesCauterized.size() == 1) { - renderTransform = modelTransform.worldTransform(Transform(clusterMatricesCauterized[0])); + if (clusterTransformsCauterized.size() == 1) { +#if defined(SKIN_DQ) + Transform transform(clusterTransforms[0].getRotation(), + clusterTransforms[0].getScale(), + clusterTransforms[0].getTranslation()); + renderTransform = modelTransform.worldTransform(Transform(transform)); +#else + renderTransform = modelTransform.worldTransform(Transform(clusterTransformsCauterized[0])); +#endif } data.updateTransformForCauterizedMesh(renderTransform); diff --git a/libraries/render-utils/src/DeferredBufferRead.slh b/libraries/render-utils/src/DeferredBufferRead.slh index fbca241bb9..3cbed3fcef 100644 --- a/libraries/render-utils/src/DeferredBufferRead.slh +++ b/libraries/render-utils/src/DeferredBufferRead.slh @@ -46,6 +46,15 @@ struct DeferredFragment { float depthVal; }; +<@if not GETFRESNEL0@> +<@def GETFRESNEL0@> +vec3 getFresnelF0(float metallic, vec3 metalF0) { + // Enable continuous metallness value by lerping between dielectric + // and metal fresnel F0 value based on the "metallic" parameter + return mix(vec3(0.03), metalF0, metallic); +} +<@endif@> + DeferredFragment unpackDeferredFragmentNoPosition(vec2 texcoord) { vec4 normalVal; vec4 diffuseVal; @@ -73,13 +82,7 @@ DeferredFragment unpackDeferredFragmentNoPosition(vec2 texcoord) { frag.scattering = specularVal.x; } - if (frag.metallic <= 0.5) { - frag.metallic = 0.0; - frag.fresnel = vec3(0.03); // Default Di-electric fresnel value - } else { - frag.fresnel = vec3(diffuseVal.xyz); - frag.metallic = 1.0; - } + frag.fresnel = getFresnelF0(frag.metallic, diffuseVal.xyz); return frag; } @@ -106,14 +109,7 @@ DeferredFragment unpackDeferredFragmentNoPositionNoAmbient(vec2 texcoord) { //frag.emissive = specularVal.xyz; frag.obscurance = 1.0; - - if (frag.metallic <= 0.5) { - frag.metallic = 0.0; - frag.fresnel = vec3(0.03); // Default Di-electric fresnel value - } else { - frag.fresnel = vec3(diffuseVal.xyz); - frag.metallic = 1.0; - } + frag.fresnel = getFresnelF0(frag.metallic, diffuseVal.xyz); return frag; } diff --git a/libraries/render-utils/src/DeferredGlobalLight.slh b/libraries/render-utils/src/DeferredGlobalLight.slh index de2d41be6b..2901b4796e 100644 --- a/libraries/render-utils/src/DeferredGlobalLight.slh +++ b/libraries/render-utils/src/DeferredGlobalLight.slh @@ -11,7 +11,7 @@ <@if not DEFERRED_GLOBAL_LIGHT_SLH@> <@def DEFERRED_GLOBAL_LIGHT_SLH@> -<@include model/Light.slh@> +<@include graphics/Light.slh@> <@include LightingModel.slh@> <$declareLightBuffer()$> @@ -65,10 +65,12 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness <$prepareGlobalLight($supportScattering$)$> + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + // Ambient vec3 ambientDiffuse; vec3 ambientSpecular; - evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> ); @@ -79,7 +81,7 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> ); @@ -110,10 +112,12 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu ) { <$prepareGlobalLight($supportScattering$)$> + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + // Ambient vec3 ambientDiffuse; vec3 ambientSpecular; - evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> @@ -123,7 +127,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> @@ -174,19 +178,21 @@ vec3 evalLightmappedColor(mat4 invViewMat, float shadowAttenuation, float obscur vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float metallic, vec3 emissive, float roughness, float opacity) { <$prepareGlobalLight()$> + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + color += emissive * isEmissiveEnabled(); // Ambient vec3 ambientDiffuse; vec3 ambientSpecular; - evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance); + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance); color += ambientDiffuse; color += ambientSpecular / opacity; // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation); + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation); color += directionalDiffuse; color += directionalSpecular / opacity; @@ -199,19 +205,21 @@ vec3 evalGlobalLightingAlphaBlendedWithHaze( { <$prepareGlobalLight()$> + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + color += emissive * isEmissiveEnabled(); // Ambient vec3 ambientDiffuse; vec3 ambientSpecular; - evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance); + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance); color += ambientDiffuse; color += ambientSpecular / opacity; // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation); + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation); color += directionalDiffuse; color += directionalSpecular / opacity; diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index dd262b8213..d095fcdcc2 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -11,9 +11,12 @@ #include "DeferredLightingEffect.h" +#include + #include #include #include +#include #include #include @@ -103,13 +106,13 @@ void DeferredLightingEffect::init() { void DeferredLightingEffect::setupKeyLightBatch(const RenderArgs* args, gpu::Batch& batch, int lightBufferUnit, int ambientBufferUnit, int skyboxCubemapUnit) { PerformanceTimer perfTimer("DLE->setupBatch()"); - model::LightPointer keySunLight; + graphics::LightPointer keySunLight; auto lightStage = args->_scene->getStage(); if (lightStage) { keySunLight = lightStage->getCurrentKeyLight(); } - model::LightPointer keyAmbiLight; + graphics::LightPointer keyAmbiLight; if (lightStage) { keyAmbiLight = lightStage->getCurrentAmbientLight(); } @@ -229,9 +232,9 @@ static void loadLightProgram(const char* vertSource, const char* fragSource, boo #include -model::MeshPointer DeferredLightingEffect::getPointLightMesh() { +graphics::MeshPointer DeferredLightingEffect::getPointLightMesh() { if (!_pointLightMesh) { - _pointLightMesh = std::make_shared(); + _pointLightMesh = std::make_shared(); // let's use a icosahedron auto solid = geometry::icosahedron(); @@ -256,19 +259,19 @@ model::MeshPointer DeferredLightingEffect::getPointLightMesh() { delete[] indexData; - std::vector parts; - parts.push_back(model::Mesh::Part(0, nbIndices, 0, model::Mesh::TRIANGLES)); - parts.push_back(model::Mesh::Part(0, nbIndices, 0, model::Mesh::LINE_STRIP)); // outline version + std::vector parts; + parts.push_back(graphics::Mesh::Part(0, nbIndices, 0, graphics::Mesh::TRIANGLES)); + parts.push_back(graphics::Mesh::Part(0, nbIndices, 0, graphics::Mesh::LINE_STRIP)); // outline version - _pointLightMesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); + _pointLightMesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(graphics::Mesh::Part), (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); } return _pointLightMesh; } -model::MeshPointer DeferredLightingEffect::getSpotLightMesh() { +graphics::MeshPointer DeferredLightingEffect::getSpotLightMesh() { if (!_spotLightMesh) { - _spotLightMesh = std::make_shared(); + _spotLightMesh = std::make_shared(); int slices = 16; int rings = 3; @@ -353,12 +356,12 @@ model::MeshPointer DeferredLightingEffect::getSpotLightMesh() { delete[] indexData; - std::vector parts; - parts.push_back(model::Mesh::Part(0, indices, 0, model::Mesh::TRIANGLES)); - parts.push_back(model::Mesh::Part(0, indices, 0, model::Mesh::LINE_STRIP)); // outline version + std::vector parts; + parts.push_back(graphics::Mesh::Part(0, indices, 0, graphics::Mesh::TRIANGLES)); + parts.push_back(graphics::Mesh::Part(0, indices, 0, graphics::Mesh::LINE_STRIP)); // outline version - _spotLightMesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); + _spotLightMesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(graphics::Mesh::Part), (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); } return _spotLightMesh; } @@ -439,7 +442,7 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, const DeferredFrameTransformPointer& frameTransform, const DeferredFramebufferPointer& deferredFramebuffer, const LightingModelPointer& lightingModel, - const model::HazePointer& haze, + const graphics::HazePointer& haze, const SurfaceGeometryFramebufferPointer& surfaceGeometryFramebuffer, const AmbientOcclusionFramebufferPointer& ambientOcclusionFramebuffer, const SubsurfaceScatteringResourcePointer& subsurfaceScatteringResource) { @@ -513,7 +516,7 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, auto keyLight = lightAndShadow.first; - model::LightPointer keyAmbientLight; + graphics::LightPointer keyAmbientLight; if (lightStage && lightStage->_currentFrame._ambientLights.size()) { keyAmbientLight = lightStage->getLight(lightStage->_currentFrame._ambientLights.front()); } @@ -562,9 +565,9 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, // Haze if (haze) { - batch.setUniformBuffer(HAZE_MODEL_BUFFER_SLOT, haze->getHazeParametersBuffer()); + batch.setUniformBuffer(HAZE_MODEL_BUFFER_SLOT, haze->getHazeParametersBuffer()); } - + batch.draw(gpu::TRIANGLE_STRIP, 4); deferredLightingEffect->unsetKeyLightBatch(batch, locations->lightBufferUnit, locations->ambientBufferUnit, SKYBOX_MAP_UNIT); @@ -731,9 +734,11 @@ void DefaultLightingSetup::run(const RenderContextPointer& renderContext) { PROFILE_RANGE(render, "Process Default Skybox"); auto textureCache = DependencyManager::get(); - auto skyboxUrl = PathUtils::resourcesPath().toStdString() + "images/Default-Sky-9-cubemap.ktx"; + QFileSelector fileSelector; + fileSelector.setExtraSelectors(FileUtils::getFileSelectors()); + auto skyboxUrl = fileSelector.select(PathUtils::resourcesPath() + "images/Default-Sky-9-cubemap.ktx"); - _defaultSkyboxTexture = gpu::Texture::unserialize(skyboxUrl); + _defaultSkyboxTexture = gpu::Texture::unserialize(skyboxUrl.toStdString()); _defaultSkyboxAmbientTexture = _defaultSkyboxTexture; _defaultSkybox->setCubemap(_defaultSkyboxTexture); @@ -744,12 +749,12 @@ void DefaultLightingSetup::run(const RenderContextPointer& renderContext) { if (lightStage) { // Allocate a default global light directional and ambient - auto lp = std::make_shared(); - lp->setType(model::Light::SUN); + auto lp = std::make_shared(); + lp->setType(graphics::Light::SUN); lp->setDirection(glm::vec3(-1.0f)); lp->setColor(glm::vec3(1.0f)); lp->setIntensity(1.0f); - lp->setType(model::Light::SUN); + lp->setType(graphics::Light::SUN); lp->setAmbientSpherePreset(gpu::SphericalHarmonics::Preset::OLD_TOWN_SQUARE); lp->setAmbientIntensity(0.5f); @@ -763,14 +768,15 @@ void DefaultLightingSetup::run(const RenderContextPointer& renderContext) { _defaultLight = lp; // Add the global light to the light stage (for later shadow rendering) - _defaultLightID = lightStage->addLight(lp); + // Set this light to be the default + _defaultLightID = lightStage->addLight(lp, true); lightStage->addShadow(_defaultLightID); } auto backgroundStage = renderContext->_scene->getStage(); if (backgroundStage) { - auto background = std::make_shared(); + auto background = std::make_shared(); background->setSkybox(_defaultSkybox); // capture deault background @@ -785,7 +791,7 @@ void DefaultLightingSetup::run(const RenderContextPointer& renderContext) { auto hazeStage = renderContext->_scene->getStage(); if (hazeStage) { - auto haze = std::make_shared(); + auto haze = std::make_shared(); _defaultHaze = haze; _defaultHazeID = hazeStage->addHaze(_defaultHaze); diff --git a/libraries/render-utils/src/DeferredLightingEffect.h b/libraries/render-utils/src/DeferredLightingEffect.h index 212d17db12..6d2c0a6819 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.h +++ b/libraries/render-utils/src/DeferredLightingEffect.h @@ -17,8 +17,8 @@ #include #include -#include "model/Light.h" -#include "model/Geometry.h" +#include "graphics/Light.h" +#include "graphics/Geometry.h" #include @@ -61,10 +61,10 @@ private: bool _shadowMapEnabled{ false }; bool _ambientOcclusionEnabled{ false }; - model::MeshPointer _pointLightMesh; - model::MeshPointer getPointLightMesh(); - model::MeshPointer _spotLightMesh; - model::MeshPointer getSpotLightMesh(); + graphics::MeshPointer _pointLightMesh; + graphics::MeshPointer getPointLightMesh(); + graphics::MeshPointer _spotLightMesh; + graphics::MeshPointer getSpotLightMesh(); gpu::PipelinePointer _directionalSkyboxLight; gpu::PipelinePointer _directionalAmbientSphereLight; @@ -121,7 +121,7 @@ public: const DeferredFrameTransformPointer& frameTransform, const DeferredFramebufferPointer& deferredFramebuffer, const LightingModelPointer& lightingModel, - const model::HazePointer& haze, + const graphics::HazePointer& haze, const SurfaceGeometryFramebufferPointer& surfaceGeometryFramebuffer, const AmbientOcclusionFramebufferPointer& ambientOcclusionFramebuffer, const SubsurfaceScatteringResourcePointer& subsurfaceScatteringResource); @@ -158,7 +158,7 @@ class RenderDeferred { public: using Inputs = render::VaryingSet8 < DeferredFrameTransformPointer, DeferredFramebufferPointer, LightingModelPointer, SurfaceGeometryFramebufferPointer, - AmbientOcclusionFramebufferPointer, SubsurfaceScatteringResourcePointer, LightClustersPointer, model::HazePointer>; + AmbientOcclusionFramebufferPointer, SubsurfaceScatteringResourcePointer, LightClustersPointer, graphics::HazePointer>; using Config = RenderDeferredConfig; using JobModel = render::Job::ModelI; @@ -184,13 +184,13 @@ public: void run(const render::RenderContextPointer& renderContext); protected: - model::LightPointer _defaultLight; + graphics::LightPointer _defaultLight; LightStage::Index _defaultLightID{ LightStage::INVALID_INDEX }; - model::SunSkyStagePointer _defaultBackground; + graphics::SunSkyStagePointer _defaultBackground; BackgroundStage::Index _defaultBackgroundID{ BackgroundStage::INVALID_INDEX }; - model::HazePointer _defaultHaze{ nullptr }; + graphics::HazePointer _defaultHaze{ nullptr }; HazeStage::Index _defaultHazeID{ HazeStage::INVALID_INDEX }; - model::SkyboxPointer _defaultSkybox { new ProceduralSkybox() }; + graphics::SkyboxPointer _defaultSkybox { new ProceduralSkybox() }; gpu::TexturePointer _defaultSkyboxTexture; gpu::TexturePointer _defaultSkyboxAmbientTexture; }; diff --git a/libraries/render-utils/src/DrawHaze.cpp b/libraries/render-utils/src/DrawHaze.cpp index c3ff70d2a4..29169f194b 100644 --- a/libraries/render-utils/src/DrawHaze.cpp +++ b/libraries/render-utils/src/DrawHaze.cpp @@ -78,12 +78,12 @@ void HazeConfig::setHazeBackgroundBlend(const float value) { } MakeHaze::MakeHaze() { - _haze = std::make_shared(); + _haze = std::make_shared(); } void MakeHaze::configure(const Config& config) { _haze->setHazeColor(config.hazeColor); - _haze->setHazeGlareBlend(model::Haze::convertGlareAngleToPower(config.hazeGlareAngle)); + _haze->setHazeGlareBlend(graphics::Haze::convertGlareAngleToPower(config.hazeGlareAngle)); _haze->setHazeGlareColor(config.hazeGlareColor); _haze->setHazeBaseReference(config.hazeBaseReference); @@ -94,16 +94,16 @@ void MakeHaze::configure(const Config& config) { _haze->setModulateColorActive(config.isModulateColorActive); _haze->setHazeEnableGlare(config.isHazeEnableGlare); - _haze->setHazeRangeFactor(model::Haze::convertHazeRangeToHazeRangeFactor(config.hazeRange)); - _haze->setHazeAltitudeFactor(model::Haze::convertHazeAltitudeToHazeAltitudeFactor(config.hazeHeight)); + _haze->setHazeRangeFactor(graphics::Haze::convertHazeRangeToHazeRangeFactor(config.hazeRange)); + _haze->setHazeAltitudeFactor(graphics::Haze::convertHazeAltitudeToHazeAltitudeFactor(config.hazeHeight)); - _haze->setHazeKeyLightRangeFactor(model::Haze::convertHazeRangeToHazeRangeFactor(config.hazeKeyLightRange)); - _haze->setHazeKeyLightAltitudeFactor(model::Haze::convertHazeAltitudeToHazeAltitudeFactor(config.hazeKeyLightAltitude)); + _haze->setHazeKeyLightRangeFactor(graphics::Haze::convertHazeRangeToHazeRangeFactor(config.hazeKeyLightRange)); + _haze->setHazeKeyLightAltitudeFactor(graphics::Haze::convertHazeAltitudeToHazeAltitudeFactor(config.hazeKeyLightAltitude)); _haze->setHazeBackgroundBlend(config.hazeBackgroundBlend); } -void MakeHaze::run(const render::RenderContextPointer& renderContext, model::HazePointer& haze) { +void MakeHaze::run(const render::RenderContextPointer& renderContext, graphics::HazePointer& haze) { haze = _haze; } @@ -168,7 +168,7 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu auto hazeStage = args->_scene->getStage(); if (hazeStage && hazeStage->_currentFrame._hazes.size() > 0) { - model::HazePointer hazePointer = hazeStage->getHaze(hazeStage->_currentFrame._hazes.front()); + graphics::HazePointer hazePointer = hazeStage->getHaze(hazeStage->_currentFrame._hazes.front()); if (hazePointer) { batch.setUniformBuffer(HazeEffect_ParamsSlot, hazePointer->getHazeParametersBuffer()); } else { @@ -181,7 +181,7 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu auto lightStage = args->_scene->getStage(); if (lightStage) { - model::LightPointer keyLight; + graphics::LightPointer keyLight; keyLight = lightStage->getCurrentKeyLight(); if (keyLight) { batch.setUniformBuffer(HazeEffect_LightingMapSlot, keyLight->getLightSchemaBuffer()); diff --git a/libraries/render-utils/src/DrawHaze.h b/libraries/render-utils/src/DrawHaze.h index f158daa0c6..e7d4e15d77 100644 --- a/libraries/render-utils/src/DrawHaze.h +++ b/libraries/render-utils/src/DrawHaze.h @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include "SurfaceGeometryPass.h" @@ -51,11 +51,11 @@ class MakeHazeConfig : public render::Job::Config { public: MakeHazeConfig() : render::Job::Config() {} - glm::vec3 hazeColor{ model::Haze::INITIAL_HAZE_COLOR }; - float hazeGlareAngle{ model::Haze::INITIAL_HAZE_GLARE_ANGLE }; + glm::vec3 hazeColor{ graphics::Haze::INITIAL_HAZE_COLOR }; + float hazeGlareAngle{ graphics::Haze::INITIAL_HAZE_GLARE_ANGLE }; - glm::vec3 hazeGlareColor{ model::Haze::INITIAL_HAZE_GLARE_COLOR }; - float hazeBaseReference{ model::Haze::INITIAL_HAZE_BASE_REFERENCE }; + glm::vec3 hazeGlareColor{ graphics::Haze::INITIAL_HAZE_GLARE_COLOR }; + float hazeBaseReference{ graphics::Haze::INITIAL_HAZE_BASE_REFERENCE }; bool isHazeActive{ false }; bool isAltitudeBased{ false }; @@ -63,13 +63,13 @@ public: bool isModulateColorActive{ false }; bool isHazeEnableGlare{ false }; - float hazeRange{ model::Haze::INITIAL_HAZE_RANGE }; - float hazeHeight{ model::Haze::INITIAL_HAZE_HEIGHT }; + float hazeRange{ graphics::Haze::INITIAL_HAZE_RANGE }; + float hazeHeight{ graphics::Haze::INITIAL_HAZE_HEIGHT }; - float hazeKeyLightRange{ model::Haze::INITIAL_KEY_LIGHT_RANGE }; - float hazeKeyLightAltitude{ model::Haze::INITIAL_KEY_LIGHT_ALTITUDE }; + float hazeKeyLightRange{ graphics::Haze::INITIAL_KEY_LIGHT_RANGE }; + float hazeKeyLightAltitude{ graphics::Haze::INITIAL_KEY_LIGHT_ALTITUDE }; - float hazeBackgroundBlend{ model::Haze::INITIAL_HAZE_BACKGROUND_BLEND }; + float hazeBackgroundBlend{ graphics::Haze::INITIAL_HAZE_BACKGROUND_BLEND }; public slots: void setHazeColor(const glm::vec3 value) { hazeColor = value; emit dirty(); } @@ -99,15 +99,15 @@ signals: class MakeHaze { public: using Config = MakeHazeConfig; - using JobModel = render::Job::ModelO; + using JobModel = render::Job::ModelO; MakeHaze(); void configure(const Config& config); - void run(const render::RenderContextPointer& renderContext, model::HazePointer& haze); + void run(const render::RenderContextPointer& renderContext, graphics::HazePointer& haze); private: - model::HazePointer _haze; + graphics::HazePointer _haze; }; class HazeConfig : public render::Job::Config { @@ -115,11 +115,11 @@ public: HazeConfig() : render::Job::Config(true) {} // attributes - glm::vec3 hazeColor{ model::Haze::INITIAL_HAZE_COLOR }; - float hazeGlareAngle{ model::Haze::INITIAL_HAZE_GLARE_ANGLE }; + glm::vec3 hazeColor{ graphics::Haze::INITIAL_HAZE_COLOR }; + float hazeGlareAngle{ graphics::Haze::INITIAL_HAZE_GLARE_ANGLE }; - glm::vec3 hazeGlareColor{ model::Haze::INITIAL_HAZE_GLARE_COLOR }; - float hazeBaseReference{ model::Haze::INITIAL_HAZE_BASE_REFERENCE }; + glm::vec3 hazeGlareColor{ graphics::Haze::INITIAL_HAZE_GLARE_COLOR }; + float hazeBaseReference{ graphics::Haze::INITIAL_HAZE_BASE_REFERENCE }; bool isHazeActive{ false }; // Setting this to true will set haze to on bool isAltitudeBased{ false }; @@ -127,13 +127,13 @@ public: bool isModulateColorActive{ false }; bool isHazeEnableGlare{ false }; - float hazeRange{ model::Haze::INITIAL_HAZE_RANGE }; - float hazeHeight{ model::Haze::INITIAL_HAZE_HEIGHT }; + float hazeRange{ graphics::Haze::INITIAL_HAZE_RANGE }; + float hazeHeight{ graphics::Haze::INITIAL_HAZE_HEIGHT }; - float hazeKeyLightRange{ model::Haze::INITIAL_KEY_LIGHT_RANGE }; - float hazeKeyLightAltitude{ model::Haze::INITIAL_KEY_LIGHT_ALTITUDE }; + float hazeKeyLightRange{ graphics::Haze::INITIAL_KEY_LIGHT_RANGE }; + float hazeKeyLightAltitude{ graphics::Haze::INITIAL_KEY_LIGHT_ALTITUDE }; - float hazeBackgroundBlend{ model::Haze::INITIAL_HAZE_BACKGROUND_BLEND }; + float hazeBackgroundBlend{ graphics::Haze::INITIAL_HAZE_BACKGROUND_BLEND }; // methods void setHazeColor(const glm::vec3 value); @@ -159,7 +159,7 @@ public: class DrawHaze { public: - using Inputs = render::VaryingSet5; + using Inputs = render::VaryingSet5; using Config = HazeConfig; using JobModel = render::Job::ModelI; diff --git a/libraries/render-utils/src/Fade.slh b/libraries/render-utils/src/Fade.slh index b85a824ca3..a06c8c869e 100644 --- a/libraries/render-utils/src/Fade.slh +++ b/libraries/render-utils/src/Fade.slh @@ -34,7 +34,7 @@ vec2 hash2D(vec3 position) { } float noise3D(vec3 position) { - float n = textureLod(fadeMaskMap, hash2D(position), 0).r; + float n = textureLod(fadeMaskMap, hash2D(position), 0.0).r; return pow(n, 1.0/2.2); // Remove sRGB. Need to fix this later directly in the texture } @@ -44,7 +44,7 @@ float evalFadeNoiseGradient(FadeObjectParams params, vec3 position) { vec3 noisePositionFloored = floor(noisePosition); vec3 noisePositionFraction = fract(noisePosition); - noisePositionFraction = noisePositionFraction*noisePositionFraction*(3 - 2*noisePositionFraction); + noisePositionFraction = noisePositionFraction*noisePositionFraction*(3.0 - 2.0*noisePositionFraction); float noiseLowXLowYLowZ = noise3D(noisePositionFloored); float noiseLowXHighYLowZ = noise3D(noisePositionFloored+vec3(0,1,0)); @@ -84,7 +84,7 @@ float evalFadeAlpha(FadeObjectParams params, vec3 position) { } void applyFadeClip(FadeObjectParams params, vec3 position) { - if (evalFadeAlpha(params, position) < 0) { + if (evalFadeAlpha(params, position) < 0.0) { discard; } } @@ -95,14 +95,14 @@ void applyFade(FadeObjectParams params, vec3 position, out vec3 emissive) { alpha = -alpha; } - if (alpha < 0) { + if (alpha < 0.0) { discard; } float edgeMask = alpha * fadeParameters[params.category]._edgeWidthInvWidth.y; - float edgeAlpha = 1.0-clamp(edgeMask, 0, 1); + float edgeAlpha = 1.0-clamp(edgeMask, 0.0, 1.0); - edgeMask = step(edgeMask, 1.f); + edgeMask = step(edgeMask, 1.0); edgeAlpha *= edgeAlpha; // Square to have a nice ease out vec4 color = mix(fadeParameters[params.category]._innerEdgeColor, fadeParameters[params.category]._outerEdgeColor, edgeAlpha); emissive = color.rgb * edgeMask * color.a; diff --git a/libraries/render-utils/src/ForwardGlobalLight.slh b/libraries/render-utils/src/ForwardGlobalLight.slh index aba0498ef5..e86f0c7fe3 100644 --- a/libraries/render-utils/src/ForwardGlobalLight.slh +++ b/libraries/render-utils/src/ForwardGlobalLight.slh @@ -11,7 +11,7 @@ <@if not DEFERRED_GLOBAL_LIGHT_SLH@> <@def DEFERRED_GLOBAL_LIGHT_SLH@> -<@include model/Light.slh@> +<@include graphics/Light.slh@> <@include LightingModel.slh@> <$declareLightBuffer()$> @@ -65,10 +65,12 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness <$prepareGlobalLight($supportScattering$)$> + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + // Ambient vec3 ambientDiffuse; vec3 ambientSpecular; - evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> ); @@ -79,7 +81,7 @@ vec3 albedo, vec3 fresnel, float metallic, float roughness // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> ); @@ -109,10 +111,12 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu ) { <$prepareGlobalLight($supportScattering$)$> + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + // Ambient vec3 ambientDiffuse; vec3 ambientSpecular; - evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> @@ -124,7 +128,7 @@ vec3 evalSkyboxGlobalColor(mat4 invViewMat, float shadowAttenuation, float obscu // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> @@ -173,19 +177,21 @@ vec3 evalLightmappedColor(mat4 invViewMat, float shadowAttenuation, float obscur vec3 evalGlobalLightingAlphaBlended(mat4 invViewMat, float shadowAttenuation, float obscurance, vec3 position, vec3 normal, vec3 albedo, vec3 fresnel, float metallic, vec3 emissive, float roughness, float opacity) { <$prepareGlobalLight()$> + SurfaceData surface = initSurfaceData(roughness, fragNormal, fragEyeDir); + color += emissive * isEmissiveEnabled(); // Ambient vec3 ambientDiffuse; vec3 ambientSpecular; - evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, obscurance); + evalLightingAmbient(ambientDiffuse, ambientSpecular, lightAmbient, surface, metallic, fresnel, albedo, obscurance); color += ambientDiffuse; color += ambientSpecular / opacity; // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation); + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation); color += directionalDiffuse; color += directionalSpecular / opacity; diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 2616d08600..23473e74f2 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -31,7 +31,7 @@ #include "gpu/StandardShaderLib.h" -#include "model/TextureMap.h" +#include "graphics/TextureMap.h" #include "render/Args.h" #include "standardTransformPNTC_vert.h" @@ -639,7 +639,7 @@ render::ShapePipelinePointer GeometryCache::_simpleWirePipeline; uint8_t GeometryCache::CUSTOM_PIPELINE_NUMBER = 0; -render::ShapePipelinePointer GeometryCache::shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key) { +render::ShapePipelinePointer GeometryCache::shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key, gpu::Batch& batch) { initializeShapePipelines(); if (key.isWireframe()) { @@ -2001,11 +2001,11 @@ void GeometryCache::renderGlowLine(gpu::Batch& batch, const glm::vec3& p1, const } void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { - if (!_standardDrawPipeline) { + static std::once_flag once; + std::call_once(once, [&]() { auto vs = gpu::Shader::createVertex(std::string(standardTransformPNTC_vert)); auto ps = gpu::Shader::createPixel(std::string(standardDrawTexture_frag)); auto program = gpu::Shader::createProgram(vs, ps); - gpu::Shader::makeProgram((*program)); auto state = std::make_shared(); @@ -2021,9 +2021,15 @@ void GeometryCache::useSimpleDrawPipeline(gpu::Batch& batch, bool noBlend) { auto noBlendPS = gpu::StandardShaderLib::getDrawTextureOpaquePS(); auto programNoBlend = gpu::Shader::createProgram(vs, noBlendPS); - gpu::Shader::makeProgram((*programNoBlend)); + _standardDrawPipelineNoBlend = gpu::Pipeline::create(programNoBlend, stateNoBlend); - } + + batch.runLambda([program, programNoBlend] { + gpu::Shader::makeProgram((*program)); + gpu::Shader::makeProgram((*programNoBlend)); + }); + }); + if (noBlend) { batch.setPipeline(_standardDrawPipelineNoBlend); } else { diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 0585cc9e55..63af30bb79 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -28,8 +28,8 @@ #include -#include -#include +#include +#include class SimpleProgramKey; @@ -158,7 +158,7 @@ public: static void computeSimpleHullPointListForShape(int entityShape, const glm::vec3 &entityExtents, QVector &outPointList); static uint8_t CUSTOM_PIPELINE_NUMBER; - static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key); + static render::ShapePipelinePointer shapePipelineFactory(const render::ShapePlumber& plumber, const render::ShapeKey& key, gpu::Batch& batch); static void registerShapePipeline() { if (!CUSTOM_PIPELINE_NUMBER) { CUSTOM_PIPELINE_NUMBER = render::ShapePipeline::registerCustomShapePipelineFactory(shapePipelineFactory); diff --git a/libraries/render-utils/src/Haze.slf b/libraries/render-utils/src/Haze.slf index 0270aa58f0..2d7daf1f98 100644 --- a/libraries/render-utils/src/Haze.slf +++ b/libraries/render-utils/src/Haze.slf @@ -12,7 +12,7 @@ <@include DeferredTransform.slh@> <$declareDeferredFrameTransform()$> -<@include model/Light.slh@> +<@include graphics/Light.slh@> <@include LightingModel.slh@> <$declareLightBuffer()$> diff --git a/libraries/render-utils/src/HazeStage.cpp b/libraries/render-utils/src/HazeStage.cpp index 016282d16f..e56b715b8c 100644 --- a/libraries/render-utils/src/HazeStage.cpp +++ b/libraries/render-utils/src/HazeStage.cpp @@ -17,12 +17,12 @@ std::string HazeStage::_stageName { "HAZE_STAGE"}; const HazeStage::Index HazeStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; FetchHazeStage::FetchHazeStage() { - _haze = std::make_shared(); + _haze = std::make_shared(); } void FetchHazeStage::configure(const Config& config) { _haze->setHazeColor(config.hazeColor); - _haze->setHazeGlareBlend(model::Haze::convertGlareAngleToPower(config.hazeGlareAngle)); + _haze->setHazeGlareBlend(graphics::Haze::convertGlareAngleToPower(config.hazeGlareAngle)); _haze->setHazeGlareColor(config.hazeGlareColor); _haze->setHazeBaseReference(config.hazeBaseReference); @@ -33,11 +33,11 @@ void FetchHazeStage::configure(const Config& config) { _haze->setModulateColorActive(config.isModulateColorActive); _haze->setHazeEnableGlare(config.isHazeEnableGlare); - _haze->setHazeRangeFactor(model::Haze::convertHazeRangeToHazeRangeFactor(config.hazeRange)); - _haze->setHazeAltitudeFactor(model::Haze::convertHazeAltitudeToHazeAltitudeFactor(config.hazeHeight)); + _haze->setHazeRangeFactor(graphics::Haze::convertHazeRangeToHazeRangeFactor(config.hazeRange)); + _haze->setHazeAltitudeFactor(graphics::Haze::convertHazeAltitudeToHazeAltitudeFactor(config.hazeHeight)); - _haze->setHazeKeyLightRangeFactor(model::Haze::convertHazeRangeToHazeRangeFactor(config.hazeKeyLightRange)); - _haze->setHazeKeyLightAltitudeFactor(model::Haze::convertHazeAltitudeToHazeAltitudeFactor(config.hazeKeyLightAltitude)); + _haze->setHazeKeyLightRangeFactor(graphics::Haze::convertHazeRangeToHazeRangeFactor(config.hazeKeyLightRange)); + _haze->setHazeKeyLightAltitudeFactor(graphics::Haze::convertHazeAltitudeToHazeAltitudeFactor(config.hazeKeyLightAltitude)); _haze->setHazeBackgroundBlend(config.hazeBackgroundBlend); } @@ -86,7 +86,7 @@ void HazeStageSetup::run(const render::RenderContextPointer& renderContext) { } } -void FetchHazeStage::run(const render::RenderContextPointer& renderContext, model::HazePointer& haze) { +void FetchHazeStage::run(const render::RenderContextPointer& renderContext, graphics::HazePointer& haze) { auto hazeStage = renderContext->_scene->getStage(); assert(hazeStage); diff --git a/libraries/render-utils/src/HazeStage.h b/libraries/render-utils/src/HazeStage.h index c355f06644..8f137cb280 100644 --- a/libraries/render-utils/src/HazeStage.h +++ b/libraries/render-utils/src/HazeStage.h @@ -11,7 +11,7 @@ #ifndef hifi_render_utils_HazeStage_h #define hifi_render_utils_HazeStage_h -#include +#include #include #include #include @@ -19,7 +19,7 @@ #include #include -#include +#include // Haze stage to set up haze-related rendering tasks class HazeStage : public render::Stage { @@ -31,8 +31,8 @@ public: static const Index INVALID_INDEX; static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } - using HazePointer = model::HazePointer; - using Hazes = render::indexed_container::IndexedPointerVector; + using HazePointer = graphics::HazePointer; + using Hazes = render::indexed_container::IndexedPointerVector; using HazeMap = std::unordered_map; using HazeIndices = std::vector; @@ -106,11 +106,11 @@ class FetchHazeConfig : public render::Job::Config { public: FetchHazeConfig() : render::Job::Config() {} - glm::vec3 hazeColor{ model::Haze::INITIAL_HAZE_COLOR }; - float hazeGlareAngle{ model::Haze::INITIAL_HAZE_GLARE_ANGLE }; + glm::vec3 hazeColor{ graphics::Haze::INITIAL_HAZE_COLOR }; + float hazeGlareAngle{ graphics::Haze::INITIAL_HAZE_GLARE_ANGLE }; - glm::vec3 hazeGlareColor{ model::Haze::INITIAL_HAZE_GLARE_COLOR }; - float hazeBaseReference{ model::Haze::INITIAL_HAZE_BASE_REFERENCE }; + glm::vec3 hazeGlareColor{ graphics::Haze::INITIAL_HAZE_GLARE_COLOR }; + float hazeBaseReference{ graphics::Haze::INITIAL_HAZE_BASE_REFERENCE }; bool isHazeActive{ false }; bool isAltitudeBased{ false }; @@ -118,13 +118,13 @@ public: bool isModulateColorActive{ false }; bool isHazeEnableGlare{ false }; - float hazeRange{ model::Haze::INITIAL_HAZE_RANGE }; - float hazeHeight{ model::Haze::INITIAL_HAZE_HEIGHT }; + float hazeRange{ graphics::Haze::INITIAL_HAZE_RANGE }; + float hazeHeight{ graphics::Haze::INITIAL_HAZE_HEIGHT }; - float hazeKeyLightRange{ model::Haze::INITIAL_KEY_LIGHT_RANGE }; - float hazeKeyLightAltitude{ model::Haze::INITIAL_KEY_LIGHT_ALTITUDE }; + float hazeKeyLightRange{ graphics::Haze::INITIAL_KEY_LIGHT_RANGE }; + float hazeKeyLightAltitude{ graphics::Haze::INITIAL_KEY_LIGHT_ALTITUDE }; - float hazeBackgroundBlend{ model::Haze::INITIAL_HAZE_BACKGROUND_BLEND }; + float hazeBackgroundBlend{ graphics::Haze::INITIAL_HAZE_BACKGROUND_BLEND }; public slots: void setHazeColor(const glm::vec3 value) { hazeColor = value; emit dirty(); } @@ -154,15 +154,15 @@ signals: class FetchHazeStage { public: using Config = FetchHazeConfig; - using JobModel = render::Job::ModelO; + using JobModel = render::Job::ModelO; FetchHazeStage(); void configure(const Config& config); - void run(const render::RenderContextPointer& renderContext, model::HazePointer& haze); + void run(const render::RenderContextPointer& renderContext, graphics::HazePointer& haze); private: - model::HazePointer _haze; + graphics::HazePointer _haze; gpu::PipelinePointer _hazePipeline; }; #endif diff --git a/libraries/render-utils/src/LightAmbient.slh b/libraries/render-utils/src/LightAmbient.slh index 5f74b46d3e..eb565d60e4 100644 --- a/libraries/render-utils/src/LightAmbient.slh +++ b/libraries/render-utils/src/LightAmbient.slh @@ -16,22 +16,27 @@ uniform samplerCube skyboxMap; vec4 evalSkyboxLight(vec3 direction, float lod) { // textureQueryLevels is not available until #430, so we require explicit lod // float mipmapLevel = lod * textureQueryLevels(skyboxMap); + float filterLod = textureQueryLod(skyboxMap, direction).x; + // Keep texture filtering LOD as limit to prevent aliasing on specular reflection + lod = max(lod, filterLod); return textureLod(skyboxMap, direction, lod); } <@endfunc@> <@func declareEvalAmbientSpecularIrradiance(supportAmbientSphere, supportAmbientMap, supportIfAmbientMapElseAmbientSphere)@> -vec3 fresnelSchlickAmbient(vec3 fresnelColor, vec3 lightDir, vec3 halfDir, float gloss) { - return fresnelColor + (max(vec3(gloss), fresnelColor) - fresnelColor) * pow(1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0), 5.0); +vec3 fresnelSchlickAmbient(vec3 fresnelColor, float ndotd, float gloss) { + float f = pow(1.0 - ndotd, 5.0); + return fresnelColor + (max(vec3(gloss), fresnelColor) - fresnelColor) * f; +// return fresnelColor + (vec3(1.0) - fresnelColor) * f; } <@if supportAmbientMap@> <$declareSkyboxMap()$> <@endif@> -vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 fragNormal, float roughness) { - vec3 direction = -reflect(fragEyeDir, fragNormal); +vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, SurfaceData surface) { + vec3 lightDir = -reflect(surface.eyeDir, surface.normal); vec3 specularLight; <@if supportIfAmbientMapElseAmbientSphere@> if (getLightHasAmbientMap(ambient)) @@ -39,8 +44,10 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 f <@if supportAmbientMap@> { float levels = getLightAmbientMapNumMips(ambient); - float lod = min(((roughness)* levels), levels); - specularLight = evalSkyboxLight(direction, lod).xyz; + float m = 12.0 / (1.0+11.0*surface.roughness); + float lod = levels - m; + lod = max(lod, 0); + specularLight = evalSkyboxLight(lightDir, lod).xyz; } <@endif@> <@if supportIfAmbientMapElseAmbientSphere@> @@ -48,11 +55,11 @@ vec3 evalAmbientSpecularIrradiance(LightAmbient ambient, vec3 fragEyeDir, vec3 f <@endif@> <@if supportAmbientSphere@> { - specularLight = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), direction).xyz; + specularLight = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), lightDir).xyz; } <@endif@> - return specularLight; + return specularLight; } <@endfunc@> @@ -66,21 +73,21 @@ float curvatureAO(in float k) { } <@endif@> -void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambient, vec3 eyeDir, vec3 normal, - float roughness, float metallic, vec3 fresnel, vec3 albedo, float obscurance +void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambient, SurfaceData surface, + float metallic, vec3 fresnelF0, vec3 albedo, float obscurance <@if supportScattering@> , float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature <@endif@> ) { // Fresnel - vec3 ambientFresnel = fresnelSchlickAmbient(fresnel, eyeDir, normal, 1.0 - roughness); + vec3 ambientFresnel = fresnelSchlickAmbient(fresnelF0, surface.ndotv, 1.0-surface.roughness); // Diffuse from ambient - diffuse = (1.0 - metallic) * (vec3(1.0) - ambientFresnel) * sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), normal).xyz; + diffuse = (1.0 - metallic) * (vec3(1.0) - ambientFresnel) * sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), surface.normal).xyz; // Specular highlight from ambient - specular = evalAmbientSpecularIrradiance(ambient, eyeDir, normal, roughness) * ambientFresnel; + specular = evalAmbientSpecularIrradiance(ambient, surface) * ambientFresnel; <@if supportScattering@> if (scattering * isScatteringEnabled() > 0.0) { @@ -92,7 +99,7 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie // Diffuse from ambient diffuse = sphericalHarmonics_evalSphericalLight(getLightAmbientSphere(ambient), lowNormalCurvature.xyz).xyz; - + diffuse /= 3.1415926; specular = vec3(0.0); } <@endif@> @@ -107,8 +114,9 @@ void evalLightingAmbient(out vec3 diffuse, out vec3 specular, LightAmbient ambie diffuse *= albedo; } - diffuse *= lightEnergy * isDiffuseEnabled() * isAmbientEnabled(); - specular *= lightEnergy * isSpecularEnabled() * isAmbientEnabled(); + lightEnergy *= isAmbientEnabled(); + diffuse *= lightEnergy * isDiffuseEnabled(); + specular *= lightEnergy * isSpecularEnabled(); } <@endfunc@> diff --git a/libraries/render-utils/src/LightClusters.cpp b/libraries/render-utils/src/LightClusters.cpp index eedb9053c7..d6ac7fd2e2 100644 --- a/libraries/render-utils/src/LightClusters.cpp +++ b/libraries/render-utils/src/LightClusters.cpp @@ -37,14 +37,13 @@ enum LightClusterGridShader_MapSlot { }; enum LightClusterGridShader_BufferSlot { - LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT = 0, - DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT =1, - CAMERA_CORRECTION_BUFFER_SLOT = 2, + DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT = 0, + CAMERA_CORRECTION_BUFFER_SLOT = 1, LIGHT_GPU_SLOT = render::ShapePipeline::Slot::LIGHT, - LIGHT_INDEX_GPU_SLOT = 5, - - LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT = 6, - LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT = 7, + LIGHT_INDEX_GPU_SLOT = 7, + LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT = 8, + LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT = 9, + LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT = 10, }; FrustumGrid::FrustumGrid(const FrustumGrid& source) : diff --git a/libraries/render-utils/src/LightDirectional.slh b/libraries/render-utils/src/LightDirectional.slh index 749709c600..b6e1720a2c 100644 --- a/libraries/render-utils/src/LightDirectional.slh +++ b/libraries/render-utils/src/LightDirectional.slh @@ -12,7 +12,7 @@ <@func declareLightingDirectional(supportScattering)@> void evalLightingDirectional(out vec3 diffuse, out vec3 specular, vec3 lightDir, vec3 lightIrradiance, - vec3 eyeDir, vec3 normal, float roughness, + SurfaceData surface, float metallic, vec3 fresnel, vec3 albedo, float shadow <@if supportScattering@> , float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature @@ -22,14 +22,17 @@ void evalLightingDirectional(out vec3 diffuse, out vec3 specular, vec3 lightDir, // Attenuation vec3 lightEnergy = shadow * lightIrradiance; - evalFragShading(diffuse, specular, normal, -lightDir, eyeDir, metallic, fresnel, roughness, albedo + updateSurfaceDataWithLight(surface, -lightDir); + + evalFragShading(diffuse, specular, metallic, fresnel, surface, albedo <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> ); - diffuse *= lightEnergy * isDiffuseEnabled() * isDirectionalEnabled(); - specular *= lightEnergy * isSpecularEnabled() * isDirectionalEnabled(); + lightEnergy *= isDirectionalEnabled(); + diffuse *= lightEnergy * isDiffuseEnabled(); + specular *= lightEnergy * isSpecularEnabled(); } <@endfunc@> diff --git a/libraries/render-utils/src/LightPayload.cpp b/libraries/render-utils/src/LightPayload.cpp index afa17bee19..09334faa13 100644 --- a/libraries/render-utils/src/LightPayload.cpp +++ b/libraries/render-utils/src/LightPayload.cpp @@ -40,7 +40,7 @@ namespace render { } LightPayload::LightPayload() : - _light(std::make_shared()) + _light(std::make_shared()) { } @@ -109,7 +109,7 @@ namespace render { } KeyLightPayload::KeyLightPayload() : -_light(std::make_shared()) +_light(std::make_shared()) { } diff --git a/libraries/render-utils/src/LightPayload.h b/libraries/render-utils/src/LightPayload.h index 0cf8f8e2de..44b79ce10c 100644 --- a/libraries/render-utils/src/LightPayload.h +++ b/libraries/render-utils/src/LightPayload.h @@ -12,7 +12,7 @@ #define hifi_LightPayload_h -#include +#include #include #include "LightStage.h" #include "TextureCache.h" @@ -26,14 +26,14 @@ public: ~LightPayload(); void render(RenderArgs* args); - model::LightPointer editLight() { _needUpdate = true; return _light; } + graphics::LightPointer editLight() { _needUpdate = true; return _light; } render::Item::Bound& editBound() { _needUpdate = true; return _bound; } void setVisible(bool visible) { _isVisible = visible; } bool isVisible() const { return _isVisible; } protected: - model::LightPointer _light; + graphics::LightPointer _light; render::Item::Bound _bound; LightStagePointer _stage; LightStage::Index _index { LightStage::INVALID_INDEX }; @@ -56,7 +56,7 @@ public: ~KeyLightPayload(); void render(RenderArgs* args); - model::LightPointer editLight() { _needUpdate = true; return _light; } + graphics::LightPointer editLight() { _needUpdate = true; return _light; } render::Item::Bound& editBound() { _needUpdate = true; return _bound; } void setVisible(bool visible) { _isVisible = visible; } @@ -67,10 +67,9 @@ public: NetworkTexturePointer _ambientTexture; QString _ambientTextureURL; bool _pendingAmbientTexture { false }; - bool _validAmbientTextureURL { false }; protected: - model::LightPointer _light; + graphics::LightPointer _light; render::Item::Bound _bound; LightStagePointer _stage; LightStage::Index _index { LightStage::INVALID_INDEX }; diff --git a/libraries/render-utils/src/LightPoint.slh b/libraries/render-utils/src/LightPoint.slh index 7e389e11f6..91a1260fcc 100644 --- a/libraries/render-utils/src/LightPoint.slh +++ b/libraries/render-utils/src/LightPoint.slh @@ -12,7 +12,7 @@ <@func declareLightingPoint(supportScattering)@> void evalLightingPoint(out vec3 diffuse, out vec3 specular, Light light, - vec4 fragLightDirLen, vec3 fragEyeDir, vec3 normal, float roughness, + vec4 fragLightDirLen, SurfaceData surface, float metallic, vec3 fresnel, vec3 albedo, float shadow <@if supportScattering@> , float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature @@ -23,19 +23,22 @@ void evalLightingPoint(out vec3 diffuse, out vec3 specular, Light light, float fragLightDistance = fragLightDirLen.w; vec3 fragLightDir = fragLightDirLen.xyz; + updateSurfaceDataWithLight(surface, fragLightDir); + // Eval attenuation float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance); vec3 lightEnergy = radialAttenuation * shadow * getLightIrradiance(light); // Eval shading - evalFragShading(diffuse, specular, normal, fragLightDir, fragEyeDir, metallic, fresnel, roughness, albedo + evalFragShading(diffuse, specular, metallic, fresnel, surface, albedo <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> ); - diffuse *= lightEnergy * isDiffuseEnabled() * isPointEnabled(); - specular *= lightEnergy * isSpecularEnabled() * isPointEnabled(); + lightEnergy *= isPointEnabled(); + diffuse *= lightEnergy * isDiffuseEnabled(); + specular *= lightEnergy * isSpecularEnabled(); if (isShowLightContour() > 0.0) { // Show edge diff --git a/libraries/render-utils/src/LightSpot.slh b/libraries/render-utils/src/LightSpot.slh index 8627dae0eb..73c5bd9559 100644 --- a/libraries/render-utils/src/LightSpot.slh +++ b/libraries/render-utils/src/LightSpot.slh @@ -12,7 +12,7 @@ <@func declareLightingSpot(supportScattering)@> void evalLightingSpot(out vec3 diffuse, out vec3 specular, Light light, - vec4 fragLightDirLen, float cosSpotAngle, vec3 fragEyeDir, vec3 normal, float roughness, + vec4 fragLightDirLen, float cosSpotAngle, SurfaceData surface, float metallic, vec3 fresnel, vec3 albedo, float shadow <@if supportScattering@> , float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature @@ -23,6 +23,7 @@ void evalLightingSpot(out vec3 diffuse, out vec3 specular, Light light, float fragLightDistance = fragLightDirLen.w; vec3 fragLightDir = fragLightDirLen.xyz; + updateSurfaceDataWithLight(surface, fragLightDir); // Eval attenuation float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance); @@ -30,14 +31,15 @@ void evalLightingSpot(out vec3 diffuse, out vec3 specular, Light light, vec3 lightEnergy = angularAttenuation * radialAttenuation * shadow *getLightIrradiance(light); // Eval shading - evalFragShading(diffuse, specular, normal, fragLightDir, fragEyeDir, metallic, fresnel, roughness, albedo + evalFragShading(diffuse, specular, metallic, fresnel, surface, albedo <@if supportScattering@> ,scattering, midNormalCurvature, lowNormalCurvature <@endif@> ); - diffuse *= lightEnergy * isDiffuseEnabled() * isSpotEnabled(); - specular *= lightEnergy * isSpecularEnabled() * isSpotEnabled(); + lightEnergy *= isSpotEnabled(); + diffuse *= lightEnergy * isDiffuseEnabled(); + specular *= lightEnergy * isSpecularEnabled(); if (isShowLightContour() > 0.0) { // Show edges diff --git a/libraries/render-utils/src/LightStage.cpp b/libraries/render-utils/src/LightStage.cpp index e568554452..f146cd6e0a 100644 --- a/libraries/render-utils/src/LightStage.cpp +++ b/libraries/render-utils/src/LightStage.cpp @@ -28,6 +28,37 @@ static const auto MAX_BIAS = 0.006f; const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; LightStage::LightStage() { + // Add off lights + const LightPointer ambientOffLight { std::make_shared() }; + ambientOffLight->setAmbientIntensity(0.0f); + ambientOffLight->setColor(graphics::Vec3(0.0)); + ambientOffLight->setIntensity(0.0f); + ambientOffLight->setType(graphics::Light::Type::AMBIENT); + _ambientOffLightId = addLight(ambientOffLight); + + const LightPointer pointOffLight { std::make_shared() }; + pointOffLight->setAmbientIntensity(0.0f); + pointOffLight->setColor(graphics::Vec3(0.0)); + pointOffLight->setIntensity(0.0f); + pointOffLight->setType(graphics::Light::Type::POINT); + _pointOffLightId = addLight(pointOffLight); + + const LightPointer spotOffLight { std::make_shared() }; + spotOffLight->setAmbientIntensity(0.0f); + spotOffLight->setColor(graphics::Vec3(0.0)); + spotOffLight->setIntensity(0.0f); + spotOffLight->setType(graphics::Light::Type::SPOT); + _spotOffLightId = addLight(spotOffLight); + + const LightPointer sunOffLight { std::make_shared() }; + sunOffLight->setAmbientIntensity(0.0f); + sunOffLight->setColor(graphics::Vec3(0.0)); + sunOffLight->setIntensity(0.0f); + sunOffLight->setType(graphics::Light::Type::SUN); + _sunOffLightId = addLight(sunOffLight); + + // Set default light to the off ambient light (until changed) + _defaultLightId = _ambientOffLightId; } LightStage::Shadow::Schema::Schema() { @@ -92,7 +123,7 @@ float LightStage::Shadow::Cascade::computeFarDistance(const ViewFrustum& viewFru return far; } -LightStage::Shadow::Shadow(model::LightPointer light, float maxDistance, unsigned int cascadeCount) : +LightStage::Shadow::Shadow(graphics::LightPointer light, float maxDistance, unsigned int cascadeCount) : _light{ light } { cascadeCount = std::min(cascadeCount, (unsigned int)SHADOW_CASCADE_MAX_COUNT); Schema schema; @@ -266,10 +297,12 @@ LightStage::Index LightStage::findLight(const LightPointer& light) const { } } -LightStage::Index LightStage::addLight(const LightPointer& light) { +LightStage::Index LightStage::addLight(const LightPointer& light, const bool shouldSetAsDefault) { + Index lightId; + auto found = _lightMap.find(light); if (found == _lightMap.end()) { - auto lightId = _lights.newElement(light); + lightId = _lights.newElement(light); // Avoid failing to allocate a light, just pass if (lightId != INVALID_INDEX) { @@ -286,10 +319,15 @@ LightStage::Index LightStage::addLight(const LightPointer& light) { updateLightArrayBuffer(lightId); } - return lightId; } else { - return (*found).second; + lightId = (*found).second; } + + if (shouldSetAsDefault) { + _defaultLightId = lightId; + } + + return lightId; } LightStage::Index LightStage::addShadow(Index lightIndex, float maxDistance, unsigned int cascadeCount) { @@ -321,7 +359,7 @@ LightStage::LightPointer LightStage::removeLight(Index index) { } LightStage::LightPointer LightStage::getCurrentKeyLight() const { - Index keyLightId{ 0 }; + Index keyLightId{ _defaultLightId }; if (!_currentFrame._sunLights.empty()) { keyLightId = _currentFrame._sunLights.front(); } @@ -329,7 +367,7 @@ LightStage::LightPointer LightStage::getCurrentKeyLight() const { } LightStage::LightPointer LightStage::getCurrentAmbientLight() const { - Index keyLightId{ 0 }; + Index keyLightId { _defaultLightId }; if (!_currentFrame._ambientLights.empty()) { keyLightId = _currentFrame._ambientLights.front(); } @@ -337,7 +375,7 @@ LightStage::LightPointer LightStage::getCurrentAmbientLight() const { } LightStage::ShadowPointer LightStage::getCurrentKeyShadow() const { - Index keyLightId{ 0 }; + Index keyLightId { _defaultLightId }; if (!_currentFrame._sunLights.empty()) { keyLightId = _currentFrame._sunLights.front(); } @@ -347,7 +385,7 @@ LightStage::ShadowPointer LightStage::getCurrentKeyShadow() const { } LightStage::LightAndShadow LightStage::getCurrentKeyLightAndShadow() const { - Index keyLightId{ 0 }; + Index keyLightId { _defaultLightId }; if (!_currentFrame._sunLights.empty()) { keyLightId = _currentFrame._sunLights.front(); } @@ -366,14 +404,14 @@ LightStage::Index LightStage::getShadowId(Index lightId) const { } void LightStage::updateLightArrayBuffer(Index lightId) { - auto lightSize = sizeof(model::Light::LightSchema); + auto lightSize = sizeof(graphics::Light::LightSchema); if (!_lightArrayBuffer) { _lightArrayBuffer = std::make_shared(lightSize); } assert(checkLightId(lightId)); - if (lightId > (Index)_lightArrayBuffer->getNumTypedElements()) { + if (lightId > (Index)_lightArrayBuffer->getNumTypedElements()) { _lightArrayBuffer->resize(lightSize * (lightId + 10)); } @@ -381,7 +419,7 @@ void LightStage::updateLightArrayBuffer(Index lightId) { auto light = _lights._elements[lightId]; if (light) { const auto& lightSchema = light->getLightSchemaBuffer().get(); - _lightArrayBuffer->setSubData(lightId, lightSchema); + _lightArrayBuffer->setSubData(lightId, lightSchema); } else { // this should not happen ? } diff --git a/libraries/render-utils/src/LightStage.h b/libraries/render-utils/src/LightStage.h index 508e67ec17..d1a8680706 100644 --- a/libraries/render-utils/src/LightStage.h +++ b/libraries/render-utils/src/LightStage.h @@ -17,7 +17,7 @@ #include -#include +#include #include #include @@ -35,8 +35,8 @@ public: static const Index INVALID_INDEX; static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } - using LightPointer = model::LightPointer; - using Lights = render::indexed_container::IndexedPointerVector; + using LightPointer = graphics::LightPointer; + using Lights = render::indexed_container::IndexedPointerVector; using LightMap = std::unordered_map; using LightIndices = std::vector; @@ -75,7 +75,7 @@ public: float left, float right, float bottom, float top, float viewMaxShadowDistance) const; }; - Shadow(model::LightPointer light, float maxDistance, unsigned int cascadeCount = 1); + Shadow(graphics::LightPointer light, float maxDistance, unsigned int cascadeCount = 1); void setKeylightFrustum(const ViewFrustum& viewFrustum, float nearDepth = 1.0f, float farDepth = 1000.0f); @@ -91,7 +91,7 @@ public: float getMaxDistance() const { return _maxDistance; } void setMaxDistance(float value); - const model::LightPointer& getLight() const { return _light; } + const graphics::LightPointer& getLight() const { return _light; } protected: @@ -101,7 +101,7 @@ public: static const glm::mat4 _biasMatrix; - model::LightPointer _light; + graphics::LightPointer _light; float _maxDistance; Cascades _cascades; @@ -118,7 +118,9 @@ public: using Shadows = render::indexed_container::IndexedPointerVector; Index findLight(const LightPointer& light) const; - Index addLight(const LightPointer& light); + Index addLight(const LightPointer& light, const bool shouldSetAsDefault = false); + + Index getDefaultLight() { return _defaultLightId; } Index addShadow(Index lightIndex, float maxDistance = 20.0f, unsigned int cascadeCount = 1U); @@ -163,12 +165,12 @@ public: Frame() {} void clear() { _pointLights.clear(); _spotLights.clear(); _sunLights.clear(); _ambientLights.clear(); } - void pushLight(LightStage::Index index, model::Light::Type type) { + void pushLight(LightStage::Index index, graphics::Light::Type type) { switch (type) { - case model::Light::POINT: { pushPointLight(index); break; } - case model::Light::SPOT: { pushSpotLight(index); break; } - case model::Light::SUN: { pushSunLight(index); break; } - case model::Light::AMBIENT: { pushAmbientLight(index); break; } + case graphics::Light::POINT: { pushPointLight(index); break; } + case graphics::Light::SPOT: { pushSpotLight(index); break; } + case graphics::Light::SUN: { pushSunLight(index); break; } + case graphics::Light::AMBIENT: { pushAmbientLight(index); break; } default: { break; } } } @@ -185,6 +187,11 @@ public: Frame _currentFrame; + Index getAmbientOffLight() { return _ambientOffLightId; } + Index getPointOffLight() { return _pointOffLightId; } + Index getSpotOffLight() { return _spotOffLightId; } + Index getSunOffLight() { return _sunOffLightId; } + protected: struct Desc { @@ -199,6 +206,13 @@ protected: Descs _descs; LightMap _lightMap; + // define off lights + Index _ambientOffLightId; + Index _pointOffLightId; + Index _spotOffLightId; + Index _sunOffLightId; + + Index _defaultLightId; }; using LightStagePointer = std::shared_ptr; diff --git a/libraries/render-utils/src/LightingModel.slh b/libraries/render-utils/src/LightingModel.slh index 521c4894dc..be8330f198 100644 --- a/libraries/render-utils/src/LightingModel.slh +++ b/libraries/render-utils/src/LightingModel.slh @@ -77,6 +77,30 @@ float isWireframeEnabled() { <@endfunc@> <$declareLightingModel()$> +struct SurfaceData { + vec3 normal; + vec3 eyeDir; + vec3 lightDir; + vec3 halfDir; + float roughness; + float roughness2; + float roughness4; + float ndotv; + float ndotl; + float ndoth; + float ldoth; + float smithInvG1NdotV; +}; + +<@if not GETFRESNEL0@> +<@def GETFRESNEL0@> +vec3 getFresnelF0(float metallic, vec3 metalF0) { + // Enable continuous metallness value by lerping between dielectric + // and metal fresnel F0 value based on the "metallic" parameter + return mix(vec3(0.03), metalF0, metallic); +} +<@endif@> + <@func declareBeckmannSpecular()@> uniform sampler2D scatteringSpecularBeckmann; @@ -85,17 +109,13 @@ float fetchSpecularBeckmann(float ndoth, float roughness) { return pow(2.0 * texture(scatteringSpecularBeckmann, vec2(ndoth, roughness)).r, 10.0); } -vec2 skinSpecular(vec3 N, vec3 L, vec3 V, float roughness, float intensity) { +vec2 skinSpecular(SurfaceData surface, float intensity) { vec2 result = vec2(0.0, 1.0); - float ndotl = dot(N, L); - if (ndotl > 0.0) { - vec3 h = L + V; - vec3 H = normalize(h); - float ndoth = dot(N, H); - float PH = fetchSpecularBeckmann(ndoth, roughness); - float F = fresnelSchlickScalar(0.028, H, V); - float frSpec = max(PH * F / dot(h, h), 0.0); - result.x = ndotl * intensity * frSpec; + if (surface.ndotl > 0.0) { + float PH = fetchSpecularBeckmann(surface.ndoth, surface.roughness); + float F = fresnelSchlickScalar(0.028, surface); + float frSpec = max(PH * F / dot(surface.halfDir, surface.halfDir), 0.0); + result.x = surface.ndotl * intensity * frSpec; result.y -= F; } @@ -105,117 +125,136 @@ vec2 skinSpecular(vec3 N, vec3 L, vec3 V, float roughness, float intensity) { <@func declareEvalPBRShading()@> -vec3 fresnelSchlickColor(vec3 fresnelColor, vec3 lightDir, vec3 halfDir) { - float base = 1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0); +float evalSmithInvG1(float roughness4, float ndotd) { + return ndotd + sqrt(roughness4+ndotd*ndotd*(1.0-roughness4)); +} + +SurfaceData initSurfaceData(float roughness, vec3 normal, vec3 eyeDir) { + SurfaceData surface; + surface.eyeDir = eyeDir; + surface.normal = normal; + surface.roughness = mix(0.01, 1.0, roughness); + surface.roughness2 = surface.roughness * surface.roughness; + surface.roughness4 = surface.roughness2 * surface.roughness2; + surface.ndotv = clamp(dot(normal, eyeDir), 0.0, 1.0); + surface.smithInvG1NdotV = evalSmithInvG1(surface.roughness4, surface.ndotv); + + // These values will be set when we know the light direction, in updateSurfaceDataWithLight + surface.ndoth = 0.0; + surface.ndotl = 0.0; + surface.ldoth = 0.0; + surface.lightDir = vec3(0,0,1); + surface.halfDir = vec3(0,0,1); + + return surface; +} + +void updateSurfaceDataWithLight(inout SurfaceData surface, vec3 lightDir) { + surface.lightDir = lightDir; + surface.halfDir = normalize(surface.eyeDir + lightDir); + vec3 dots; + dots.x = dot(surface.normal, surface.halfDir); + dots.y = dot(surface.normal, surface.lightDir); + dots.z = dot(surface.halfDir, surface.lightDir); + dots = clamp(dots, vec3(0), vec3(1)); + surface.ndoth = dots.x; + surface.ndotl = dots.y; + surface.ldoth = dots.z; +} + +vec3 fresnelSchlickColor(vec3 fresnelColor, SurfaceData surface) { + float base = 1.0 - surface.ldoth; //float exponential = pow(base, 5.0); float base2 = base * base; float exponential = base * base2 * base2; return vec3(exponential) + fresnelColor * (1.0 - exponential); } -float fresnelSchlickScalar(float fresnelScalar, vec3 lightDir, vec3 halfDir) { - float base = 1.0 - clamp(dot(lightDir, halfDir), 0.0, 1.0); +float fresnelSchlickScalar(float fresnelScalar, SurfaceData surface) { + float base = 1.0 - surface.ldoth; //float exponential = pow(base, 5.0); float base2 = base * base; float exponential = base * base2 * base2; return (exponential) + fresnelScalar * (1.0 - exponential); } -float specularDistribution(float roughness, vec3 normal, vec3 halfDir) { - float ndoth = clamp(dot(halfDir, normal), 0.0, 1.0); -// float gloss2 = pow(0.001 + roughness, 4); - float gloss2 = (0.001 + roughness); - gloss2 *= gloss2; // pow 2 - gloss2 *= gloss2; // pow 4 - float denom = (ndoth * ndoth*(gloss2 - 1.0) + 1.0); - float power = gloss2 / (3.14159 * denom * denom); +float specularDistribution(SurfaceData surface) { + // See https://www.khronos.org/assets/uploads/developers/library/2017-web3d/glTF-2.0-Launch_Jun17.pdf + // for details of equations, especially page 20 + float denom = (surface.ndoth*surface.ndoth * (surface.roughness4 - 1.0) + 1.0); + denom *= denom; + // Add geometric factors G1(n,l) and G1(n,v) + float smithInvG1NdotL = evalSmithInvG1(surface.roughness4, surface.ndotl); + denom *= surface.smithInvG1NdotV * smithInvG1NdotL; + // Don't divide by PI as this is part of the light normalization factor + float power = surface.roughness4 / denom; return power; } -float specularDistributionGloss(float gloss2, vec3 normal, vec3 halfDir) { - float ndoth = clamp(dot(halfDir, normal), 0.0, 1.0); -// float gloss2 = pow(0.001 + roughness, 4); - float denom = (ndoth * ndoth*(gloss2 - 1.0) + 1.0); - float power = gloss2 / (3.14159 * denom * denom); - return power; -} - -// Frag Shading returns the diffuse amount as W and the specular rgb as xyz -vec4 evalPBRShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float metallic, vec3 fresnel, float roughness) { - // Diffuse Lighting - float diffuse = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); - - // Specular Lighting - vec3 halfDir = normalize(fragEyeDir + fragLightDir); - vec3 fresnelColor = fresnelSchlickColor(fresnel, fragLightDir, halfDir); - float power = specularDistribution(roughness, fragNormal, halfDir); - vec3 specular = fresnelColor * power * diffuse; - - return vec4(specular, (1.0 - metallic) * diffuse * (1.0 - fresnelColor.x)); -} // Frag Shading returns the diffuse amount as W and the specular rgb as xyz -vec4 evalPBRShadingDielectric(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float roughness, float fresnel) { - // Diffuse Lighting - float diffuse = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); +vec4 evalPBRShading(float metallic, vec3 fresnel, SurfaceData surface) { + // Incident angle attenuation + float angleAttenuation = surface.ndotl; // Specular Lighting - vec3 halfDir = normalize(fragEyeDir + fragLightDir); - float fresnelScalar = fresnelSchlickScalar(fresnel, fragLightDir, halfDir); - float power = specularDistribution(roughness, fragNormal, halfDir); - vec3 specular = vec3(fresnelScalar) * power * diffuse; + vec3 fresnelColor = fresnelSchlickColor(fresnel, surface); + float power = specularDistribution(surface); + vec3 specular = fresnelColor * power * angleAttenuation; + float diffuse = (1.0 - metallic) * angleAttenuation * (1.0 - fresnelColor.x); - return vec4(specular, diffuse * (1.0 - fresnelScalar)); + // We don't divided by PI, as the "normalized" equations state we should, because we decide, as Naty Hoffman, that + // we wish to have a similar color as raw albedo on a perfectly diffuse surface perpendicularly lit + // by a white light of intensity 1. But this is an arbitrary normalization of what light intensity "means". + // (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf + // page 23 paragraph "Punctual light sources") + return vec4(specular, diffuse); } -vec4 evalPBRShadingMetallic(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float roughness, vec3 fresnel) { - // Diffuse Lighting - float diffuse = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); +// Frag Shading returns the diffuse amount as W and the specular rgb as xyz +vec4 evalPBRShadingDielectric(SurfaceData surface, float fresnel) { + // Incident angle attenuation + float angleAttenuation = surface.ndotl; // Specular Lighting - vec3 halfDir = normalize(fragEyeDir + fragLightDir); - vec3 fresnelColor = fresnelSchlickColor(fresnel, fragLightDir, halfDir); - float power = specularDistribution(roughness, fragNormal, halfDir); - vec3 specular = fresnelColor * power * diffuse; + float fresnelScalar = fresnelSchlickScalar(fresnel, surface); + float power = specularDistribution(surface); + vec3 specular = vec3(fresnelScalar) * power * angleAttenuation; + float diffuse = angleAttenuation * (1.0 - fresnelScalar); + // We don't divided by PI, as the "normalized" equations state we should, because we decide, as Naty Hoffman, that + // we wish to have a similar color as raw albedo on a perfectly diffuse surface perpendicularly lit + // by a white light of intensity 1. But this is an arbitrary normalization of what light intensity "means". + // (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf + // page 23 paragraph "Punctual light sources") + return vec4(specular, diffuse); +} + +vec4 evalPBRShadingMetallic(SurfaceData surface, vec3 fresnel) { + // Incident angle attenuation + float angleAttenuation = surface.ndotl; + + // Specular Lighting + vec3 fresnelColor = fresnelSchlickColor(fresnel, surface); + float power = specularDistribution(surface); + vec3 specular = fresnelColor * power * angleAttenuation; + + // We don't divided by PI, as the "normalized" equations state we should, because we decide, as Naty Hoffman, that + // we wish to have a similar color as raw albedo on a perfectly diffuse surface perpendicularly lit + // by a white light of intensity 1. But this is an arbitrary normalization of what light intensity "means". + // (see http://blog.selfshadow.com/publications/s2013-shading-course/hoffman/s2013_pbs_physics_math_notes.pdf + // page 23 paragraph "Punctual light sources") return vec4(specular, 0.f); } -// Frag Shading returns the diffuse amount as W and the specular rgb as xyz -vec4 evalPBRShadingGloss(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float metallic, vec3 fresnel, float gloss2) { - // Diffuse Lighting - float diffuse = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); - - // Specular Lighting - vec3 halfDir = normalize(fragEyeDir + fragLightDir); - vec3 fresnelColor = fresnelSchlickColor(fresnel, fragLightDir, halfDir); - float power = specularDistributionGloss(gloss2, fragNormal, halfDir); - vec3 specular = fresnelColor * power * diffuse; - - return vec4(specular, (1.0 - metallic) * diffuse * (1.0 - fresnelColor.x)); -} - <@endfunc@> <$declareEvalPBRShading()$> -// Return xyz the specular/reflection component and w the diffuse component -//vec4 evalFragShading(vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, float metallic, vec3 fresnel, float roughness) { -// return evalPBRShading(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, roughness); -//} - void evalFragShading(out vec3 diffuse, out vec3 specular, - vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, - float metallic, vec3 fresnel, float roughness, vec3 albedo) { - vec4 shading = evalPBRShading(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, roughness); + float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo) { + vec4 shading = evalPBRShading(metallic, fresnel, surface); diffuse = vec3(shading.w); if (isAlbedoEnabled() > 0.0) { diffuse *= albedo; @@ -229,22 +268,19 @@ void evalFragShading(out vec3 diffuse, out vec3 specular, void evalFragShading(out vec3 diffuse, out vec3 specular, - vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, - float metallic, vec3 fresnel, float roughness, vec3 albedo, + float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo, float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature) { if (scattering * isScatteringEnabled() > 0.0) { - vec3 brdf = evalSkinBRDF(fragLightDir, fragNormal, midNormalCurvature.xyz, lowNormalCurvature.xyz, lowNormalCurvature.w); - float NdotL = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); - diffuse = mix(vec3(NdotL), brdf, scattering); + vec3 brdf = evalSkinBRDF(surface.lightDir, surface.normal, midNormalCurvature.xyz, lowNormalCurvature.xyz, lowNormalCurvature.w); + diffuse = mix(vec3(surface.ndotl), brdf, scattering); // Specular Lighting - vec3 halfDir = normalize(fragEyeDir + fragLightDir); - vec2 specularBrdf = skinSpecular(fragNormal, fragLightDir, fragEyeDir, roughness, 1.0); + vec2 specularBrdf = skinSpecular(surface, 1.0); diffuse *= specularBrdf.y; specular = vec3(specularBrdf.x); } else { - vec4 shading = evalPBRShadingGloss(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, roughness); + vec4 shading = evalPBRShading(metallic, fresnel, surface); diffuse = vec3(shading.w); specular = shading.xyz; } @@ -253,17 +289,15 @@ void evalFragShading(out vec3 diffuse, out vec3 specular, void evalFragShadingScattering(out vec3 diffuse, out vec3 specular, - vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, - float metallic, vec3 fresnel, float roughness, vec3 albedo + float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo ,float scattering, vec4 midNormalCurvature, vec4 lowNormalCurvature ) { - vec3 brdf = evalSkinBRDF(fragLightDir, fragNormal, midNormalCurvature.xyz, lowNormalCurvature.xyz, lowNormalCurvature.w); - float NdotL = clamp(dot(fragNormal, fragLightDir), 0.0, 1.0); + vec3 brdf = evalSkinBRDF(surface.lightDir, surface.normal, midNormalCurvature.xyz, lowNormalCurvature.xyz, lowNormalCurvature.w); + float NdotL = surface.ndotl; diffuse = mix(vec3(NdotL), brdf, scattering); // Specular Lighting - vec3 halfDir = normalize(fragEyeDir + fragLightDir); - vec2 specularBrdf = skinSpecular(fragNormal, fragLightDir, fragEyeDir, roughness, 1.0); + vec2 specularBrdf = skinSpecular(surface, 1.0); diffuse *= specularBrdf.y; specular = vec3(specularBrdf.x); @@ -271,10 +305,9 @@ void evalFragShadingScattering(out vec3 diffuse, out vec3 specular, } void evalFragShadingGloss(out vec3 diffuse, out vec3 specular, - vec3 fragNormal, vec3 fragLightDir, vec3 fragEyeDir, - float metallic, vec3 fresnel, float gloss, vec3 albedo + float metallic, vec3 fresnel, SurfaceData surface, vec3 albedo ) { - vec4 shading = evalPBRShadingGloss(fragNormal, fragLightDir, fragEyeDir, metallic, fresnel, gloss); + vec4 shading = evalPBRShading(metallic, fresnel, surface); diffuse = vec3(shading.w); diffuse *= mix(vec3(1.0), albedo, isAlbedoEnabled()); specular = shading.xyz; diff --git a/libraries/render-utils/src/MaterialTextures.slh b/libraries/render-utils/src/MaterialTextures.slh index 6c77fc4a91..40709b3668 100644 --- a/libraries/render-utils/src/MaterialTextures.slh +++ b/libraries/render-utils/src/MaterialTextures.slh @@ -17,7 +17,9 @@ const int MAX_TEXCOORDS = 2; struct TexMapArray { - mat4 _texcoordTransforms[MAX_TEXCOORDS]; +// mat4 _texcoordTransforms[MAX_TEXCOORDS]; + mat4 _texcoordTransforms0; + mat4 _texcoordTransforms1; vec4 _lightmapParams; }; @@ -31,13 +33,13 @@ TexMapArray getTexMapArray() { <@func evalTexMapArrayTexcoord0(texMapArray, inTexcoord0, outTexcoord0)@> { - <$outTexcoord0$> = (<$texMapArray$>._texcoordTransforms[0] * vec4(<$inTexcoord0$>.st, 0.0, 1.0)).st; + <$outTexcoord0$> = (<$texMapArray$>._texcoordTransforms0 * vec4(<$inTexcoord0$>.st, 0.0, 1.0)).st; } <@endfunc@> <@func evalTexMapArrayTexcoord1(texMapArray, inTexcoord1, outTexcoord1)@> { - <$outTexcoord1$> = (<$texMapArray$>._texcoordTransforms[1] * vec4(<$inTexcoord1$>.st, 0.0, 1.0)).st; + <$outTexcoord1$> = (<$texMapArray$>._texcoordTransforms1 * vec4(<$inTexcoord1$>.st, 0.0, 1.0)).st; } <@endfunc@> diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index c16f7f952e..c506887fc4 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -12,6 +12,7 @@ #include "MeshPartPayload.h" #include +#include #include "DeferredLightingEffect.h" @@ -44,17 +45,17 @@ template <> void payloadRender(const MeshPartPayload::Pointer& payload, RenderAr } } -MeshPartPayload::MeshPartPayload(const std::shared_ptr& mesh, int partIndex, model::MaterialPointer material) { +MeshPartPayload::MeshPartPayload(const std::shared_ptr& mesh, int partIndex, graphics::MaterialPointer material) { updateMeshPart(mesh, partIndex); updateMaterial(material); } -void MeshPartPayload::updateMeshPart(const std::shared_ptr& drawMesh, int partIndex) { +void MeshPartPayload::updateMeshPart(const std::shared_ptr& drawMesh, int partIndex) { _drawMesh = drawMesh; if (_drawMesh) { auto vertexFormat = _drawMesh->getVertexFormat(); _hasColorAttrib = vertexFormat->hasAttribute(gpu::Stream::COLOR); - _drawPart = _drawMesh->getPartBuffer().get(partIndex); + _drawPart = _drawMesh->getPartBuffer().get(partIndex); _localBound = _drawMesh->evalPartBound(partIndex); } } @@ -66,7 +67,7 @@ void MeshPartPayload::updateTransform(const Transform& transform, const Transfor _worldBound.transform(_drawTransform); } -void MeshPartPayload::updateMaterial(model::MaterialPointer drawMaterial) { +void MeshPartPayload::updateMaterial(graphics::MaterialPointer drawMaterial) { _drawMaterial = drawMaterial; } @@ -89,7 +90,7 @@ Item::Bound MeshPartPayload::getBound() const { } ShapeKey MeshPartPayload::getShapeKey() const { - model::MaterialKey drawMaterialKey; + graphics::MaterialKey drawMaterialKey; if (_drawMaterial) { drawMaterialKey = _drawMaterial->getKey(); } @@ -155,7 +156,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat // Albedo if (materialKey.isAlbedoMap()) { - auto itr = textureMaps.find(model::MaterialKey::ALBEDO_MAP); + auto itr = textureMaps.find(graphics::MaterialKey::ALBEDO_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { batch.setResourceTexture(ShapePipeline::Slot::ALBEDO, itr->second->getTextureView()); } else { @@ -165,7 +166,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat // Roughness map if (materialKey.isRoughnessMap()) { - auto itr = textureMaps.find(model::MaterialKey::ROUGHNESS_MAP); + auto itr = textureMaps.find(graphics::MaterialKey::ROUGHNESS_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { batch.setResourceTexture(ShapePipeline::Slot::MAP::ROUGHNESS, itr->second->getTextureView()); @@ -177,7 +178,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat // Normal map if (materialKey.isNormalMap()) { - auto itr = textureMaps.find(model::MaterialKey::NORMAL_MAP); + auto itr = textureMaps.find(graphics::MaterialKey::NORMAL_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { batch.setResourceTexture(ShapePipeline::Slot::MAP::NORMAL, itr->second->getTextureView()); @@ -189,7 +190,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat // Metallic map if (materialKey.isMetallicMap()) { - auto itr = textureMaps.find(model::MaterialKey::METALLIC_MAP); + auto itr = textureMaps.find(graphics::MaterialKey::METALLIC_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { batch.setResourceTexture(ShapePipeline::Slot::MAP::METALLIC, itr->second->getTextureView()); @@ -201,7 +202,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat // Occlusion map if (materialKey.isOcclusionMap()) { - auto itr = textureMaps.find(model::MaterialKey::OCCLUSION_MAP); + auto itr = textureMaps.find(graphics::MaterialKey::OCCLUSION_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { batch.setResourceTexture(ShapePipeline::Slot::MAP::OCCLUSION, itr->second->getTextureView()); @@ -213,7 +214,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat // Scattering map if (materialKey.isScatteringMap()) { - auto itr = textureMaps.find(model::MaterialKey::SCATTERING_MAP); + auto itr = textureMaps.find(graphics::MaterialKey::SCATTERING_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { batch.setResourceTexture(ShapePipeline::Slot::MAP::SCATTERING, itr->second->getTextureView()); @@ -225,7 +226,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat // Emissive / Lightmap if (materialKey.isLightmapMap()) { - auto itr = textureMaps.find(model::MaterialKey::LIGHTMAP_MAP); + auto itr = textureMaps.find(graphics::MaterialKey::LIGHTMAP_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, itr->second->getTextureView()); @@ -233,7 +234,7 @@ void MeshPartPayload::bindMaterial(gpu::Batch& batch, const ShapePipeline::Locat batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, textureCache->getGrayTexture()); } } else if (materialKey.isEmissiveMap()) { - auto itr = textureMaps.find(model::MaterialKey::EMISSIVE_MAP); + auto itr = textureMaps.find(graphics::MaterialKey::EMISSIVE_MAP); if (itr != textureMaps.end() && itr->second->isDefined()) { batch.setResourceTexture(ShapePipeline::Slot::MAP::EMISSIVE_LIGHTMAP, itr->second->getTextureView()); @@ -325,12 +326,20 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in const Model::MeshState& state = model->getMeshState(_meshIndex); updateMeshPart(modelMesh, partIndex); - computeAdjustedLocalBound(state.clusterMatrices); + computeAdjustedLocalBound(state.clusterTransforms); updateTransform(transform, offsetTransform); Transform renderTransform = transform; - if (state.clusterMatrices.size() == 1) { - renderTransform = transform.worldTransform(Transform(state.clusterMatrices[0])); + if (state.clusterTransforms.size() == 1) { +#if defined(SKIN_DQ) + Transform transform(state.clusterTransforms[0].getRotation(), + state.clusterTransforms[0].getScale(), + state.clusterTransforms[0].getTranslation()); + renderTransform = transform.worldTransform(Transform(transform)); +#else + renderTransform = transform.worldTransform(Transform(state.clusterTransforms[0])); +#endif + } updateTransformForSkinnedMesh(renderTransform, transform); @@ -360,17 +369,16 @@ void ModelMeshPartPayload::notifyLocationChanged() { } - -void ModelMeshPartPayload::updateClusterBuffer(const std::vector& clusterMatrices) { +void ModelMeshPartPayload::updateClusterBuffer(const std::vector& clusterTransforms) { // Once computed the cluster matrices, update the buffer(s) - if (clusterMatrices.size() > 1) { + if (clusterTransforms.size() > 1) { if (!_clusterBuffer) { - _clusterBuffer = std::make_shared(clusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) clusterMatrices.data()); + _clusterBuffer = std::make_shared(clusterTransforms.size() * sizeof(TransformType), + (const gpu::Byte*) clusterTransforms.data()); } else { - _clusterBuffer->setSubData(0, clusterMatrices.size() * sizeof(glm::mat4), - (const gpu::Byte*) clusterMatrices.data()); + _clusterBuffer->setSubData(0, clusterTransforms.size() * sizeof(TransformType), + (const gpu::Byte*) clusterTransforms.data()); } } } @@ -431,7 +439,7 @@ void ModelMeshPartPayload::setShapeKey(bool invalidateShapeKey, bool isWireframe return; } - model::MaterialKey drawMaterialKey; + graphics::MaterialKey drawMaterialKey; if (_drawMaterial) { drawMaterialKey = _drawMaterial->getKey(); } @@ -484,7 +492,8 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) { batch.setInputFormat((_drawMesh->getVertexFormat())); if (_isBlendShaped && _blendedVertexBuffer) { batch.setInputBuffer(0, _blendedVertexBuffer, 0, sizeof(glm::vec3)); - batch.setInputBuffer(1, _blendedVertexBuffer, _drawMesh->getNumVertices() * sizeof(glm::vec3), sizeof(glm::vec3)); + // Stride is 2*sizeof(glm::vec3) because normal and tangents are interleaved + batch.setInputBuffer(1, _blendedVertexBuffer, _drawMesh->getNumVertices() * sizeof(glm::vec3), 2 * sizeof(NormalType)); batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); } else { batch.setInputStream(0, _drawMesh->getVertexStream()); @@ -529,13 +538,29 @@ void ModelMeshPartPayload::render(RenderArgs* args) { args->_details._trianglesRendered += _drawPart._numIndices / INDICES_PER_TRIANGLE; } -void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterMatrices) { + +void ModelMeshPartPayload::computeAdjustedLocalBound(const std::vector& clusterTransforms) { _adjustedLocalBound = _localBound; - if (clusterMatrices.size() > 0) { - _adjustedLocalBound.transform(clusterMatrices[0]); - for (int i = 1; i < (int)clusterMatrices.size(); ++i) { + if (clusterTransforms.size() > 0) { +#if defined(SKIN_DQ) + Transform rootTransform(clusterTransforms[0].getRotation(), + clusterTransforms[0].getScale(), + clusterTransforms[0].getTranslation()); + _adjustedLocalBound.transform(rootTransform); +#else + _adjustedLocalBound.transform(clusterTransforms[0]); +#endif + + for (int i = 1; i < (int)clusterTransforms.size(); ++i) { AABox clusterBound = _localBound; - clusterBound.transform(clusterMatrices[i]); +#if defined(SKIN_DQ) + Transform transform(clusterTransforms[i].getRotation(), + clusterTransforms[i].getScale(), + clusterTransforms[i].getTranslation()); + clusterBound.transform(transform); +#else + clusterBound.transform(clusterTransforms[i]); +#endif _adjustedLocalBound += clusterBound; } } diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index fb55883101..8160b9f009 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -19,7 +19,7 @@ #include #include -#include +#include #include "Model.h" @@ -28,17 +28,17 @@ class Model; class MeshPartPayload { public: MeshPartPayload() {} - MeshPartPayload(const std::shared_ptr& mesh, int partIndex, model::MaterialPointer material); + MeshPartPayload(const std::shared_ptr& mesh, int partIndex, graphics::MaterialPointer material); typedef render::Payload Payload; typedef Payload::DataPointer Pointer; - virtual void updateMeshPart(const std::shared_ptr& drawMesh, int partIndex); + virtual void updateMeshPart(const std::shared_ptr& drawMesh, int partIndex); virtual void notifyLocationChanged() {} void updateTransform(const Transform& transform, const Transform& offsetTransform); - virtual void updateMaterial(model::MaterialPointer drawMaterial); + virtual void updateMaterial(graphics::MaterialPointer drawMaterial); // Render Item interface virtual render::ItemKey getKey() const; @@ -58,13 +58,13 @@ public: int _partIndex = 0; bool _hasColorAttrib { false }; - model::Box _localBound; - model::Box _adjustedLocalBound; - mutable model::Box _worldBound; - std::shared_ptr _drawMesh; + graphics::Box _localBound; + graphics::Box _adjustedLocalBound; + mutable graphics::Box _worldBound; + std::shared_ptr _drawMesh; - std::shared_ptr _drawMaterial; - model::Mesh::Part _drawPart; + std::shared_ptr _drawMaterial; + graphics::Mesh::Part _drawPart; size_t getVerticesCount() const { return _drawMesh ? _drawMesh->getNumVertices() : 0; } size_t getMaterialTextureSize() { return _drawMaterial ? _drawMaterial->getTextureSize() : 0; } @@ -87,7 +87,14 @@ public: typedef Payload::DataPointer Pointer; void notifyLocationChanged() override; - void updateClusterBuffer(const std::vector& clusterMatrices); + +#if defined(SKIN_DQ) + using TransformType = Model::TransformDualQuaternion; +#else + using TransformType = glm::mat4; +#endif + + void updateClusterBuffer(const std::vector& clusterTransforms); void updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform); // Render Item interface @@ -104,7 +111,7 @@ public: void bindMesh(gpu::Batch& batch) override; void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; - void computeAdjustedLocalBound(const std::vector& clusterMatrices); + void computeAdjustedLocalBound(const std::vector& clusterTransforms); gpu::BufferPointer _clusterBuffer; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 536e76e44e..45c77a606d 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -24,7 +24,12 @@ #include #include #include +#include + #include +#include + +#include #include "AbstractViewStateInterface.h" #include "MeshPartPayload.h" @@ -41,7 +46,7 @@ float Model::FAKE_DIMENSION_PLACEHOLDER = -1.0f; #define HTTP_INVALID_COM "http://invalid.com" const int NUM_COLLISION_HULL_COLORS = 24; -std::vector _collisionMaterials; +std::vector _collisionMaterials; void initCollisionMaterials() { // generates bright colors in red, green, blue, yellow, magenta, and cyan spectrums @@ -67,8 +72,8 @@ void initCollisionMaterials() { // so they don't tend to group together for large models for (int i = 0; i < sectionWidth; ++i) { for (int j = 0; j < numComponents; ++j) { - model::MaterialPointer material; - material = std::make_shared(); + graphics::MaterialPointer material; + material = std::make_shared(); int index = j * sectionWidth + i; float red = component[index]; float green = component[(index + greenPhase) % NUM_COLLISION_HULL_COLORS]; @@ -140,7 +145,13 @@ void Model::setTransformNoUpdateRenderItems(const Transform& transform) { } Transform Model::getTransform() const { - if (_spatiallyNestableOverride) { + if (_overrideModelTransform) { + Transform transform; + transform.setTranslation(getOverrideTranslation()); + transform.setRotation(getOverrideRotation()); + transform.setScale(getScale()); + return transform; + } else if (_spatiallyNestableOverride) { bool success; Transform transform = _spatiallyNestableOverride->getTransform(success); if (success) { @@ -265,16 +276,24 @@ void Model::updateRenderItems() { auto itemID = self->_modelMeshRenderItemIDs[i]; auto meshIndex = self->_modelMeshRenderItemShapes[i].meshIndex; - auto clusterMatrices(self->getMeshState(meshIndex).clusterMatrices); + auto clusterTransforms(self->getMeshState(meshIndex).clusterTransforms); bool invalidatePayloadShapeKey = self->shouldInvalidatePayloadShapeKey(meshIndex); - transaction.updateItem(itemID, [modelTransform, clusterMatrices, invalidatePayloadShapeKey, + transaction.updateItem(itemID, [modelTransform, clusterTransforms, invalidatePayloadShapeKey, isWireframe, isVisible, isLayeredInFront, isLayeredInHUD](ModelMeshPartPayload& data) { - data.updateClusterBuffer(clusterMatrices); + data.updateClusterBuffer(clusterTransforms); + Transform renderTransform = modelTransform; - if (clusterMatrices.size() == 1) { - renderTransform = modelTransform.worldTransform(Transform(clusterMatrices[0])); + if (clusterTransforms.size() == 1) { +#if defined(SKIN_DQ) + Transform transform(clusterTransforms[0].getRotation(), + clusterTransforms[0].getScale(), + clusterTransforms[0].getTranslation()); + renderTransform = modelTransform.worldTransform(Transform(transform)); +#else + renderTransform = modelTransform.worldTransform(Transform(clusterTransforms[0])); +#endif } data.updateTransformForSkinnedMesh(renderTransform, modelTransform); @@ -310,6 +329,34 @@ void Model::reset() { } } +#if FBX_PACK_NORMALS +static void packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, glm::uint32& packedNormal, glm::uint32& packedTangent) { + auto absNormal = glm::abs(normal); + auto absTangent = glm::abs(tangent); + normal /= glm::max(1e-6f, glm::max(glm::max(absNormal.x, absNormal.y), absNormal.z)); + tangent /= glm::max(1e-6f, glm::max(glm::max(absTangent.x, absTangent.y), absTangent.z)); + normal = glm::clamp(normal, -1.0f, 1.0f); + tangent = glm::clamp(tangent, -1.0f, 1.0f); + normal *= 511.0f; + tangent *= 511.0f; + normal = glm::round(normal); + tangent = glm::round(tangent); + + glm::detail::i10i10i10i2 normalStruct; + glm::detail::i10i10i10i2 tangentStruct; + normalStruct.data.x = int(normal.x); + normalStruct.data.y = int(normal.y); + normalStruct.data.z = int(normal.z); + normalStruct.data.w = 0; + tangentStruct.data.x = int(tangent.x); + tangentStruct.data.y = int(tangent.y); + tangentStruct.data.z = int(tangent.z); + tangentStruct.data.w = 0; + packedNormal = normalStruct.pack; + packedTangent = tangentStruct.pack; +} +#endif + bool Model::updateGeometry() { bool needFullUpdate = false; @@ -327,7 +374,7 @@ bool Model::updateGeometry() { const FBXGeometry& fbxGeometry = getFBXGeometry(); foreach (const FBXMesh& mesh, fbxGeometry.meshes) { MeshState state; - state.clusterMatrices.resize(mesh.clusters.size()); + state.clusterTransforms.resize(mesh.clusters.size()); _meshStates.push_back(state); // Note: we add empty buffers for meshes that lack blendshapes so we can access the buffers by index @@ -335,10 +382,28 @@ bool Model::updateGeometry() { // TODO? make _blendedVertexBuffers a map instead of vector and only add for meshes with blendshapes? auto buffer = std::make_shared(); if (!mesh.blendshapes.isEmpty()) { - buffer->resize((mesh.vertices.size() + mesh.normals.size()) * sizeof(glm::vec3)); - buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.vertices.constData()); + std::vector normalsAndTangents; + normalsAndTangents.reserve(mesh.normals.size() + mesh.tangents.size()); + + for (auto normalIt = mesh.normals.begin(), tangentIt = mesh.tangents.begin(); + normalIt != mesh.normals.end(); + ++normalIt, ++tangentIt) { +#if FBX_PACK_NORMALS + glm::uint32 finalNormal; + glm::uint32 finalTangent; + packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent); +#else + const auto finalNormal = *normalIt; + const auto finalTangent = *tangentIt; +#endif + normalsAndTangents.push_back(finalNormal); + normalsAndTangents.push_back(finalTangent); + } + + buffer->resize(mesh.vertices.size() * (sizeof(glm::vec3) + 2 * sizeof(NormalType))); + buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (const gpu::Byte*) mesh.vertices.constData()); buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3), - mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) mesh.normals.constData()); + mesh.normals.size() * 2 * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data()); } _blendedVertexBuffers.push_back(buffer); } @@ -500,7 +565,7 @@ MeshProxyList Model::getMeshes() const { offset.postTranslate(_offset); glm::mat4 offsetMat = offset.getMatrix(); - for (std::shared_ptr mesh : meshes) { + for (std::shared_ptr mesh : meshes) { if (!mesh) { continue; } @@ -1006,7 +1071,7 @@ Blender::Blender(ModelPointer model, int blendNumber, const Geometry::WeakPointe void Blender::run() { DETAILED_PROFILE_RANGE_EX(simulation_animation, __FUNCTION__, 0xFFFF0000, 0, { { "url", _model->getURL().toString() } }); - QVector vertices, normals; + QVector vertices, normals, tangents; if (_model) { int offset = 0; foreach (const FBXMesh& mesh, _meshes) { @@ -1015,8 +1080,10 @@ void Blender::run() { } vertices += mesh.vertices; normals += mesh.normals; + tangents += mesh.tangents; glm::vec3* meshVertices = vertices.data() + offset; glm::vec3* meshNormals = normals.data() + offset; + glm::vec3* meshTangents = tangents.data() + offset; offset += mesh.vertices.size(); const float NORMAL_COEFFICIENT_SCALE = 0.01f; for (int i = 0, n = qMin(_blendshapeCoefficients.size(), mesh.blendshapes.size()); i < n; i++) { @@ -1031,6 +1098,9 @@ void Blender::run() { int index = blendshape.indices.at(j); meshVertices[index] += blendshape.vertices.at(j) * vertexCoefficient; meshNormals[index] += blendshape.normals.at(j) * normalCoefficient; + if (blendshape.tangents.size() > j) { + meshTangents[index] += blendshape.tangents.at(j) * normalCoefficient; + } } } } @@ -1039,7 +1109,7 @@ void Blender::run() { QMetaObject::invokeMethod(DependencyManager::get().data(), "setBlendedVertices", Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber), Q_ARG(const Geometry::WeakPointer&, _geometry), Q_ARG(const QVector&, vertices), - Q_ARG(const QVector&, normals)); + Q_ARG(const QVector&, normals), Q_ARG(const QVector&, tangents)); } void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions, bool forceRescale) { @@ -1158,7 +1228,7 @@ void Model::updateRig(float deltaTime, glm::mat4 parentTransform) { void Model::computeMeshPartLocalBounds() { for (auto& part : _modelMeshRenderItems) { const Model::MeshState& state = _meshStates.at(part->_meshIndex); - part->computeAdjustedLocalBound(state.clusterMatrices); + part->computeAdjustedLocalBound(state.clusterTransforms); } } @@ -1169,6 +1239,7 @@ void Model::updateClusterMatrices() { if (!_needsUpdateClusterMatrices || !isLoaded()) { return; } + _needsUpdateClusterMatrices = false; const FBXGeometry& geometry = getFBXGeometry(); for (int i = 0; i < (int) _meshStates.size(); i++) { @@ -1176,8 +1247,16 @@ void Model::updateClusterMatrices() { const FBXMesh& mesh = geometry.meshes.at(i); for (int j = 0; j < mesh.clusters.size(); j++) { const FBXCluster& cluster = mesh.clusters.at(j); +#if defined(SKIN_DQ) + auto jointPose = _rig.getJointPose(cluster.jointIndex); + Transform jointTransform(jointPose.rot(), jointPose.scale(), jointPose.trans()); + Transform clusterTransform; + Transform::mult(clusterTransform, jointTransform, cluster.inverseBindTransform); + state.clusterTransforms[j] = Model::TransformDualQuaternion(clusterTransform); +#else auto jointMatrix = _rig.getJointTransform(cluster.jointIndex); - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); +#endif } } @@ -1188,25 +1267,6 @@ void Model::updateClusterMatrices() { } } -void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority) { - const FBXGeometry& geometry = getFBXGeometry(); - const QVector& freeLineage = geometry.joints.at(endIndex).freeLineage; - glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset); - _rig.inverseKinematics(endIndex, targetPosition, targetRotation, priority, freeLineage, parentTransform); -} - -bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) { - const FBXGeometry& geometry = getFBXGeometry(); - const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - return _rig.restoreJointPosition(jointIndex, fraction, priority, freeLineage); -} - -float Model::getLimbLength(int jointIndex) const { - const FBXGeometry& geometry = getFBXGeometry(); - const QVector& freeLineage = geometry.joints.at(jointIndex).freeLineage; - return _rig.getLimbLength(jointIndex, freeLineage, _scale, geometry.joints); -} - bool Model::maybeStartBlender() { if (isLoaded()) { const FBXGeometry& fbxGeometry = getFBXGeometry(); @@ -1220,7 +1280,7 @@ bool Model::maybeStartBlender() { } void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geometry, - const QVector& vertices, const QVector& normals) { + const QVector& vertices, const QVector& normals, const QVector& tangents) { auto geometryRef = geometry.lock(); if (!geometryRef || _renderGeometry != geometryRef || _blendedVertexBuffers.empty() || blendNumber < _appliedBlendNumber) { return; @@ -1228,6 +1288,7 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo _appliedBlendNumber = blendNumber; const FBXGeometry& fbxGeometry = getFBXGeometry(); int index = 0; + std::vector normalsAndTangents; for (int i = 0; i < fbxGeometry.meshes.size(); i++) { const FBXMesh& mesh = fbxGeometry.meshes.at(i); if (mesh.blendshapes.isEmpty()) { @@ -1235,11 +1296,67 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo } gpu::BufferPointer& buffer = _blendedVertexBuffers[i]; - buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (gpu::Byte*) vertices.constData() + index*sizeof(glm::vec3)); - buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3), - mesh.normals.size() * sizeof(glm::vec3), (gpu::Byte*) normals.constData() + index*sizeof(glm::vec3)); + const auto vertexCount = mesh.vertices.size(); + const auto verticesSize = vertexCount * sizeof(glm::vec3); + const auto offset = index * sizeof(glm::vec3); - index += mesh.vertices.size(); + normalsAndTangents.clear(); + normalsAndTangents.resize(normals.size()+tangents.size()); +// assert(normalsAndTangents.size() == 2 * vertexCount); + + // Interleave normals and tangents +#if 0 + // Sequential version for debugging + auto normalsRange = std::make_pair(normals.begin() + index, normals.begin() + index + vertexCount); + auto tangentsRange = std::make_pair(tangents.begin() + index, tangents.begin() + index + vertexCount); + auto normalsAndTangentsIt = normalsAndTangents.begin(); + + for (auto normalIt = normalsRange.first, tangentIt = tangentsRange.first; + normalIt != normalsRange.second; + ++normalIt, ++tangentIt) { +#if FBX_PACK_NORMALS + glm::uint32 finalNormal; + glm::uint32 finalTangent; + packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent); +#else + const auto finalNormal = *normalIt; + const auto finalTangent = *tangentIt; +#endif + *normalsAndTangentsIt = finalNormal; + ++normalsAndTangentsIt; + *normalsAndTangentsIt = finalTangent; + ++normalsAndTangentsIt; + } +#else + // Parallel version for performance + tbb::parallel_for(tbb::blocked_range(index, index+vertexCount), [&](const tbb::blocked_range& range) { + auto normalsRange = std::make_pair(normals.begin() + range.begin(), normals.begin() + range.end()); + auto tangentsRange = std::make_pair(tangents.begin() + range.begin(), tangents.begin() + range.end()); + auto normalsAndTangentsIt = normalsAndTangents.begin() + (range.begin()-index)*2; + + for (auto normalIt = normalsRange.first, tangentIt = tangentsRange.first; + normalIt != normalsRange.second; + ++normalIt, ++tangentIt) { +#if FBX_PACK_NORMALS + glm::uint32 finalNormal; + glm::uint32 finalTangent; + packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent); +#else + const auto finalNormal = *normalIt; + const auto finalTangent = *tangentIt; +#endif + *normalsAndTangentsIt = finalNormal; + ++normalsAndTangentsIt; + *normalsAndTangentsIt = finalTangent; + ++normalsAndTangentsIt; + } + }); +#endif + + buffer->setSubData(0, verticesSize, (gpu::Byte*) vertices.constData() + offset); + buffer->setSubData(verticesSize, 2 * vertexCount * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data()); + + index += vertexCount; } } @@ -1253,6 +1370,14 @@ void Model::deleteGeometry() { _collisionGeometry.reset(); } +void Model::overrideModelTransformAndOffset(const Transform& transform, const glm::vec3& offset) { + _overrideTranslation = transform.getTranslation(); + _overrideRotation = transform.getRotation(); + _overrideModelTransform = true; + setScale(transform.getScale()); + setOffset(offset); +} + AABox Model::getRenderableMeshBound() const { if (!isLoaded()) { return AABox(); @@ -1353,7 +1478,7 @@ void Model::createCollisionRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { - model::MaterialPointer& material = _collisionMaterials[partIndex % NUM_COLLISION_HULL_COLORS]; + graphics::MaterialPointer& material = _collisionMaterials[partIndex % NUM_COLLISION_HULL_COLORS]; auto payload = std::make_shared(mesh, partIndex, material); payload->updateTransform(identity, offset); _collisionRenderItems << payload; @@ -1367,7 +1492,7 @@ bool Model::isRenderable() const { class CollisionRenderGeometry : public Geometry { public: - CollisionRenderGeometry(model::MeshPointer mesh) { + CollisionRenderGeometry(graphics::MeshPointer mesh) { _fbxGeometry = std::make_shared(); std::shared_ptr meshes = std::make_shared(); meshes->push_back(mesh); @@ -1376,7 +1501,7 @@ public: } }; -void Model::setCollisionMesh(model::MeshPointer mesh) { +void Model::setCollisionMesh(graphics::MeshPointer mesh) { if (mesh) { _collisionGeometry = std::make_shared(mesh); } else { @@ -1406,10 +1531,11 @@ void ModelBlender::noteRequiresBlend(ModelPointer model) { } } -void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, - const Geometry::WeakPointer& geometry, const QVector& vertices, const QVector& normals) { +void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, + const QVector& vertices, const QVector& normals, + const QVector& tangents) { if (model) { - model->setBlendedVertices(blendNumber, geometry, vertices, normals); + model->setBlendedVertices(blendNumber, geometry, vertices, normals, tangents); } _pendingBlenders--; { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 9c5b980ed1..560aa82f0c 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -30,11 +30,15 @@ #include #include #include +#include #include "GeometryCache.h" #include "TextureCache.h" #include "Rig.h" +// Use dual quaternion skinning! +// Must match define in Skinning.slh +#define SKIN_DQ class AbstractViewStateInterface; class QScriptEngine; @@ -114,7 +118,7 @@ public: /// Sets blended vertices computed in a separate thread. void setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geometry, - const QVector& vertices, const QVector& normals); + const QVector& vertices, const QVector& normals, const QVector& tangents); bool isLoaded() const { return (bool)_renderGeometry && _renderGeometry->isGeometryLoaded(); } bool isAddedToScene() const { return _addedToScene; } @@ -195,8 +199,6 @@ public: /// Returns the index of the parent of the indexed joint, or -1 if not found. int getParentJointIndex(int jointIndex) const; - void inverseKinematics(int jointIndex, glm::vec3 position, const glm::quat& rotation, float priority); - /// Returns the extents of the model in its bind pose. Extents getBindExtents() const; @@ -208,10 +210,15 @@ public: void setTranslation(const glm::vec3& translation); void setRotation(const glm::quat& rotation); + void overrideModelTransformAndOffset(const Transform& transform, const glm::vec3& offset); + bool isOverridingModelTransformAndOffset() { return _overrideModelTransform; }; + void stopTransformAndOffsetOverride() { _overrideModelTransform = false; }; void setTransformNoUpdateRenderItems(const Transform& transform); // temporary HACK const glm::vec3& getTranslation() const { return _translation; } const glm::quat& getRotation() const { return _rotation; } + const glm::vec3& getOverrideTranslation() const { return _overrideTranslation; } + const glm::quat& getOverrideRotation() const { return _overrideRotation; } glm::vec3 getNaturalDimensions() const; @@ -236,7 +243,7 @@ public: // returns 'true' if needs fullUpdate after geometry change virtual bool updateGeometry(); - void setCollisionMesh(model::MeshPointer mesh); + void setCollisionMesh(graphics::MeshPointer mesh); void setLoadingPriority(float priority) { _loadingPriority = priority; } @@ -246,9 +253,46 @@ public: int getRenderInfoDrawCalls() const { return _renderInfoDrawCalls; } bool getRenderInfoHasTransparent() const { return _renderInfoHasTransparent; } + +#if defined(SKIN_DQ) + class TransformDualQuaternion { + public: + TransformDualQuaternion() {} + TransformDualQuaternion(const glm::mat4& m) { + AnimPose p(m); + _scale.x = p.scale().x; + _scale.y = p.scale().y; + _scale.z = p.scale().z; + _dq = DualQuaternion(p.rot(), p.trans()); + } + TransformDualQuaternion(const glm::vec3& scale, const glm::quat& rot, const glm::vec3& trans) { + _scale.x = scale.x; + _scale.y = scale.y; + _scale.z = scale.z; + _dq = DualQuaternion(rot, trans); + } + TransformDualQuaternion(const Transform& transform) { + _scale = glm::vec4(transform.getScale(), 0.0f); + _dq = DualQuaternion(transform.getRotation(), transform.getTranslation()); + } + glm::vec3 getScale() const { return glm::vec3(_scale); } + glm::quat getRotation() const { return _dq.getRotation(); } + glm::vec3 getTranslation() const { return _dq.getTranslation(); } + glm::mat4 getMatrix() const { return createMatFromScaleQuatAndPos(getScale(), getRotation(), getTranslation()); }; + protected: + glm::vec4 _scale { 1.0f, 1.0f, 1.0f, 0.0f }; + DualQuaternion _dq; + glm::vec4 _padding; + }; +#endif + class MeshState { public: - std::vector clusterMatrices; +#if defined(SKIN_DQ) + std::vector clusterTransforms; +#else + std::vector clusterTransforms; +#endif }; const MeshState& getMeshState(int index) { return _meshStates.at(index); } @@ -302,6 +346,9 @@ protected: glm::quat _rotation; glm::vec3 _scale; + glm::vec3 _overrideTranslation; + glm::quat _overrideRotation; + // For entity models this is the translation for the minimum extent of the model (in original mesh coordinate space) // to the model's registration point. For avatar models this is the translation from the avatar's hips, as determined // by the default pose, to the origin. @@ -327,16 +374,6 @@ protected: void computeMeshPartLocalBounds(); virtual void updateRig(float deltaTime, glm::mat4 parentTransform); - /// Restores the indexed joint to its default position. - /// \param fraction the fraction of the default position to apply (i.e., 0.25f to slerp one fourth of the way to - /// the original position - /// \return true if the joint was found - bool restoreJointPosition(int jointIndex, float fraction = 1.0f, float priority = 0.0f); - - /// Computes and returns the extended length of the limb terminating at the specified joint and starting at the joint's - /// first free ancestor. - float getLimbLength(int jointIndex) const; - /// Allow sub classes to force invalidating the bboxes void invalidCalculatedMeshBoxes() { _triangleSetsValid = false; @@ -362,6 +399,7 @@ protected: QMutex _mutex; + bool _overrideModelTransform { false }; bool _triangleSetsValid { false }; void calculateTriangleSets(); QVector _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes @@ -437,7 +475,7 @@ public: public slots: void setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, - const QVector& vertices, const QVector& normals); + const QVector& vertices, const QVector& normals, const QVector& tangents); private: using Mutex = std::mutex; diff --git a/libraries/render-utils/src/RenderCommonTask.cpp b/libraries/render-utils/src/RenderCommonTask.cpp new file mode 100644 index 0000000000..d69d1c5f18 --- /dev/null +++ b/libraries/render-utils/src/RenderCommonTask.cpp @@ -0,0 +1,240 @@ +// +// Created by Bradley Austin Davis on 2018/01/09 +// Copyright 2013-2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "RenderCommonTask.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "LightingModel.h" +#include "StencilMaskPass.h" +#include "DebugDeferredBuffer.h" +#include "DeferredFramebuffer.h" +#include "DeferredLightingEffect.h" +#include "SurfaceGeometryPass.h" +#include "FramebufferCache.h" +#include "TextureCache.h" +#include "ZoneRenderer.h" +#include "FadeEffect.h" +#include "RenderUtilsLogging.h" + +#include "AmbientOcclusionEffect.h" +#include "AntialiasingEffect.h" +#include "ToneMappingEffect.h" +#include "SubsurfaceScattering.h" +#include "DrawHaze.h" +#include "BloomEffect.h" +#include "HighlightEffect.h" + +#include + +using namespace render; +extern void initOverlay3DPipelines(render::ShapePlumber& plumber, bool depthTest = false); + +void BeginGPURangeTimer::run(const render::RenderContextPointer& renderContext, gpu::RangeTimerPointer& timer) { + timer = _gpuTimer; + gpu::doInBatch("BeginGPURangeTimer", renderContext->args->_context, [&](gpu::Batch& batch) { + _gpuTimer->begin(batch); + }); +} + +void EndGPURangeTimer::run(const render::RenderContextPointer& renderContext, const gpu::RangeTimerPointer& timer) { + gpu::doInBatch("EndGPURangeTimer", renderContext->args->_context, [&](gpu::Batch& batch) { + timer->end(batch); + }); + + auto config = std::static_pointer_cast(renderContext->jobConfig); + config->setGPUBatchRunTime(timer->getGPUAverage(), timer->getBatchAverage()); +} + +DrawOverlay3D::DrawOverlay3D(bool opaque) : + _shapePlumber(std::make_shared()), + _opaquePass(opaque) { + initOverlay3DPipelines(*_shapePlumber); +} + +void DrawOverlay3D::run(const RenderContextPointer& renderContext, const Inputs& inputs) { + assert(renderContext->args); + assert(renderContext->args->hasViewFrustum()); + + auto config = std::static_pointer_cast(renderContext->jobConfig); + + const auto& inItems = inputs.get0(); + const auto& lightingModel = inputs.get1(); + + config->setNumDrawn((int)inItems.size()); + emit config->numDrawnChanged(); + + if (!inItems.empty()) { + RenderArgs* args = renderContext->args; + + // Clear the framebuffer without stereo + // Needs to be distinct from the other batch because using the clear call + // while stereo is enabled triggers a warning + if (_opaquePass) { + gpu::doInBatch("DrawOverlay3D::opaquePass", args->_context, [&](gpu::Batch& batch){ + batch.enableStereo(false); + batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, false); + }); + } + + // Render the items + gpu::doInBatch("DrawOverlay3D::main", args->_context, [&](gpu::Batch& batch) { + args->_batch = &batch; + batch.setViewportTransform(args->_viewport); + batch.setStateScissorRect(args->_viewport); + + glm::mat4 projMat; + Transform viewMat; + args->getViewFrustum().evalProjectionMatrix(projMat); + args->getViewFrustum().evalViewTransform(viewMat); + + batch.setProjectionTransform(projMat); + batch.setViewTransform(viewMat); + + // Setup lighting model for all items; + batch.setUniformBuffer(render::ShapePipeline::Slot::LIGHTING_MODEL, lightingModel->getParametersBuffer()); + + renderShapes(renderContext, _shapePlumber, inItems, _maxDrawn); + args->_batch = nullptr; + }); + } +} + +void CompositeHUD::run(const RenderContextPointer& renderContext) { + assert(renderContext->args); + assert(renderContext->args->_context); + + // We do not want to render HUD elements in secondary camera + if (renderContext->args->_renderMode == RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) { + return; + } + + // Grab the HUD texture +#if !defined(DISABLE_QML) + gpu::doInBatch("CompositeHUD", renderContext->args->_context, [&](gpu::Batch& batch) { + if (renderContext->args->_hudOperator) { + renderContext->args->_hudOperator(batch, renderContext->args->_hudTexture, renderContext->args->_renderMode == RenderArgs::RenderMode::MIRROR_RENDER_MODE); + } + }); +#endif +} + +void Blit::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer) { + assert(renderContext->args); + assert(renderContext->args->_context); + + RenderArgs* renderArgs = renderContext->args; + auto blitFbo = renderArgs->_blitFramebuffer; + + if (!blitFbo) { + qCWarning(renderutils) << "Blit::run - no blit frame buffer."; + return; + } + + // Determine size from viewport + int width = renderArgs->_viewport.z; + int height = renderArgs->_viewport.w; + + // Blit primary to blit FBO + auto primaryFbo = srcFramebuffer; + + gpu::doInBatch("Blit", renderArgs->_context, [&](gpu::Batch& batch) { + batch.setFramebuffer(blitFbo); + + if (renderArgs->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { + if (renderArgs->isStereo()) { + gpu::Vec4i srcRectLeft; + srcRectLeft.z = width / 2; + srcRectLeft.w = height; + + gpu::Vec4i srcRectRight; + srcRectRight.x = width / 2; + srcRectRight.z = width; + srcRectRight.w = height; + + gpu::Vec4i destRectLeft; + destRectLeft.x = srcRectLeft.z; + destRectLeft.z = srcRectLeft.x; + destRectLeft.y = srcRectLeft.y; + destRectLeft.w = srcRectLeft.w; + + gpu::Vec4i destRectRight; + destRectRight.x = srcRectRight.z; + destRectRight.z = srcRectRight.x; + destRectRight.y = srcRectRight.y; + destRectRight.w = srcRectRight.w; + + // Blit left to right and right to left in stereo + batch.blit(primaryFbo, srcRectRight, blitFbo, destRectLeft); + batch.blit(primaryFbo, srcRectLeft, blitFbo, destRectRight); + } else { + gpu::Vec4i srcRect; + srcRect.z = width; + srcRect.w = height; + + gpu::Vec4i destRect; + destRect.x = width; + destRect.y = 0; + destRect.z = 0; + destRect.w = height; + + batch.blit(primaryFbo, srcRect, blitFbo, destRect); + } + } else { + gpu::Vec4i rect; + rect.z = width; + rect.w = height; + + batch.blit(primaryFbo, rect, blitFbo, rect); + } + }); +} + +void ExtractFrustums::run(const render::RenderContextPointer& renderContext, Output& output) { + assert(renderContext->args); + assert(renderContext->args->_context); + + RenderArgs* args = renderContext->args; + + // Return view frustum + auto& viewFrustum = output[VIEW_FRUSTUM].edit(); + if (!viewFrustum) { + viewFrustum = std::make_shared(args->getViewFrustum()); + } else { + *viewFrustum = args->getViewFrustum(); + } + + // Return shadow frustum + auto lightStage = args->_scene->getStage(LightStage::getName()); + for (auto i = 0; i < SHADOW_CASCADE_FRUSTUM_COUNT; i++) { + auto& shadowFrustum = output[SHADOW_CASCADE0_FRUSTUM+i].edit(); + if (lightStage) { + auto globalShadow = lightStage->getCurrentKeyShadow(); + + if (globalShadow && i<(int)globalShadow->getCascadeCount()) { + auto& cascade = globalShadow->getCascade(i); + shadowFrustum = cascade.getFrustum(); + } else { + shadowFrustum.reset(); + } + } else { + shadowFrustum.reset(); + } + } +} diff --git a/libraries/render-utils/src/RenderCommonTask.h b/libraries/render-utils/src/RenderCommonTask.h new file mode 100644 index 0000000000..767b6893cd --- /dev/null +++ b/libraries/render-utils/src/RenderCommonTask.h @@ -0,0 +1,116 @@ +// +// Created by Bradley Austin Davis on 2018/01/09 +// Copyright 2013-2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_RenderCommonTask_h +#define hifi_RenderCommonTask_h + +#include +#include +#include "LightingModel.h" + +class BeginGPURangeTimer { +public: + using JobModel = render::Job::ModelO; + + BeginGPURangeTimer(const std::string& name) : _gpuTimer(std::make_shared(name)) {} + + void run(const render::RenderContextPointer& renderContext, gpu::RangeTimerPointer& timer); + +protected: + gpu::RangeTimerPointer _gpuTimer; +}; + +using GPURangeTimerConfig = render::GPUJobConfig; + +class EndGPURangeTimer { +public: + using Config = GPURangeTimerConfig; + using JobModel = render::Job::ModelI; + + EndGPURangeTimer() {} + + void configure(const Config& config) {} + void run(const render::RenderContextPointer& renderContext, const gpu::RangeTimerPointer& timer); + +protected: +}; + +class DrawOverlay3DConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY numDrawnChanged) + Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty) +public: + int getNumDrawn() { return numDrawn; } + void setNumDrawn(int num) { numDrawn = num; emit numDrawnChanged(); } + + int maxDrawn{ -1 }; + +signals: + void numDrawnChanged(); + void dirty(); + +protected: + int numDrawn{ 0 }; +}; + +class DrawOverlay3D { +public: + using Inputs = render::VaryingSet2 ; + + using Config = DrawOverlay3DConfig; + using JobModel = render::Job::ModelI; + + DrawOverlay3D(bool opaque); + + void configure(const Config& config) { _maxDrawn = config.maxDrawn; } + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); + +protected: + render::ShapePlumberPointer _shapePlumber; + int _maxDrawn; // initialized by Config + bool _opaquePass{ true }; +}; + +class CompositeHUD { +public: + using JobModel = render::Job::Model; + + CompositeHUD() {} + void run(const render::RenderContextPointer& renderContext); +}; + +class Blit { +public: + using JobModel = render::Job::ModelI; + + void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer); +}; + +class ExtractFrustums { +public: + + enum Frustum { + SHADOW_CASCADE0_FRUSTUM = 0, + SHADOW_CASCADE1_FRUSTUM, + SHADOW_CASCADE2_FRUSTUM, + SHADOW_CASCADE3_FRUSTUM, + + SHADOW_CASCADE_FRUSTUM_COUNT, + + VIEW_FRUSTUM = SHADOW_CASCADE_FRUSTUM_COUNT, + + FRUSTUM_COUNT + }; + + using Output = render::VaryingArray; + using JobModel = render::Job::ModelO; + + void run(const render::RenderContextPointer& renderContext, Output& output); +}; + +#endif // hifi_RenderDeferredTask_h diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index dc4b46651a..537c8c0f0a 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -25,6 +25,7 @@ #include #include +#include "RenderCommonTask.h" #include "LightingModel.h" #include "StencilMaskPass.h" #include "DebugDeferredBuffer.h" @@ -299,23 +300,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("Blit", primaryFramebuffer); } -void BeginGPURangeTimer::run(const render::RenderContextPointer& renderContext, gpu::RangeTimerPointer& timer) { - timer = _gpuTimer; - gpu::doInBatch("BeginGPURangeTimer::run", renderContext->args->_context, [&](gpu::Batch& batch) { - _gpuTimer->begin(batch); - }); -} - -void EndGPURangeTimer::run(const render::RenderContextPointer& renderContext, const gpu::RangeTimerPointer& timer) { - gpu::doInBatch("EndGPURangeTimer::run", renderContext->args->_context, [&](gpu::Batch& batch) { - timer->end(batch); - }); - - auto config = std::static_pointer_cast(renderContext->jobConfig); - config->setGPUBatchRunTime(timer->getGPUAverage(), timer->getBatchAverage()); -} - - void DrawDeferred::run(const RenderContextPointer& renderContext, const Inputs& inputs) { assert(renderContext->args); assert(renderContext->args->hasViewFrustum()); @@ -345,11 +329,13 @@ void DrawDeferred::run(const RenderContextPointer& renderContext, const Inputs& // Setup lighting model for all items; batch.setUniformBuffer(render::ShapePipeline::Slot::LIGHTING_MODEL, lightingModel->getParametersBuffer()); - // Setup haze iff curretn zone has haze + // Setup haze iff current zone has haze auto hazeStage = args->_scene->getStage(); if (hazeStage && hazeStage->_currentFrame._hazes.size() > 0) { - model::HazePointer hazePointer = hazeStage->getHaze(hazeStage->_currentFrame._hazes.front()); - batch.setUniformBuffer(render::ShapePipeline::Slot::HAZE_MODEL, hazePointer->getHazeParametersBuffer()); + graphics::HazePointer hazePointer = hazeStage->getHaze(hazeStage->_currentFrame._hazes.front()); + if (hazePointer) { + batch.setUniformBuffer(render::ShapePipeline::Slot::HAZE_MODEL, hazePointer->getHazeParametersBuffer()); + } } // From the lighting model define a global shapKey ORED with individiual keys @@ -419,178 +405,3 @@ void DrawStateSortDeferred::run(const RenderContextPointer& renderContext, const config->setNumDrawn((int)inItems.size()); } - -DrawOverlay3D::DrawOverlay3D(bool opaque) : - _shapePlumber(std::make_shared()), - _opaquePass(opaque) { - initOverlay3DPipelines(*_shapePlumber); -} - -void DrawOverlay3D::run(const RenderContextPointer& renderContext, const Inputs& inputs) { - assert(renderContext->args); - assert(renderContext->args->hasViewFrustum()); - - auto config = std::static_pointer_cast(renderContext->jobConfig); - - const auto& inItems = inputs.get0(); - const auto& lightingModel = inputs.get1(); - - config->setNumDrawn((int)inItems.size()); - emit config->numDrawnChanged(); - - if (!inItems.empty()) { - RenderArgs* args = renderContext->args; - - // Clear the framebuffer without stereo - // Needs to be distinct from the other batch because using the clear call - // while stereo is enabled triggers a warning - if (_opaquePass) { - gpu::doInBatch("DrawOverlay3D::run::clear", args->_context, [&](gpu::Batch& batch){ - batch.enableStereo(false); - batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, false); - }); - } - - // Render the items - gpu::doInBatch("DrawOverlay3D::run", args->_context, [&](gpu::Batch& batch) { - args->_batch = &batch; - batch.setViewportTransform(args->_viewport); - batch.setStateScissorRect(args->_viewport); - - glm::mat4 projMat; - Transform viewMat; - args->getViewFrustum().evalProjectionMatrix(projMat); - args->getViewFrustum().evalViewTransform(viewMat); - - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); - - // Setup lighting model for all items; - batch.setUniformBuffer(render::ShapePipeline::Slot::LIGHTING_MODEL, lightingModel->getParametersBuffer()); - - renderShapes(renderContext, _shapePlumber, inItems, _maxDrawn); - args->_batch = nullptr; - }); - } -} - -void CompositeHUD::run(const RenderContextPointer& renderContext) { - assert(renderContext->args); - assert(renderContext->args->_context); - - // We do not want to render HUD elements in secondary camera - if (renderContext->args->_renderMode == RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE) { - return; - } - - // Grab the HUD texture - gpu::doInBatch("CompositeHUD::run", renderContext->args->_context, [&](gpu::Batch& batch) { - if (renderContext->args->_hudOperator) { - renderContext->args->_hudOperator(batch, renderContext->args->_hudTexture, renderContext->args->_renderMode == RenderArgs::RenderMode::MIRROR_RENDER_MODE); - } - }); -} - -void Blit::run(const RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer) { - assert(renderContext->args); - assert(renderContext->args->_context); - - RenderArgs* renderArgs = renderContext->args; - auto blitFbo = renderArgs->_blitFramebuffer; - - if (!blitFbo) { - qCWarning(renderutils) << "Blit::run - no blit frame buffer."; - return; - } - - // Determine size from viewport - int width = renderArgs->_viewport.z; - int height = renderArgs->_viewport.w; - - // Blit primary to blit FBO - auto primaryFbo = srcFramebuffer; - - gpu::doInBatch("Blit::run", renderArgs->_context, [&](gpu::Batch& batch) { - batch.setFramebuffer(blitFbo); - - if (renderArgs->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { - if (renderArgs->isStereo()) { - gpu::Vec4i srcRectLeft; - srcRectLeft.z = width / 2; - srcRectLeft.w = height; - - gpu::Vec4i srcRectRight; - srcRectRight.x = width / 2; - srcRectRight.z = width; - srcRectRight.w = height; - - gpu::Vec4i destRectLeft; - destRectLeft.x = srcRectLeft.z; - destRectLeft.z = srcRectLeft.x; - destRectLeft.y = srcRectLeft.y; - destRectLeft.w = srcRectLeft.w; - - gpu::Vec4i destRectRight; - destRectRight.x = srcRectRight.z; - destRectRight.z = srcRectRight.x; - destRectRight.y = srcRectRight.y; - destRectRight.w = srcRectRight.w; - - // Blit left to right and right to left in stereo - batch.blit(primaryFbo, srcRectRight, blitFbo, destRectLeft); - batch.blit(primaryFbo, srcRectLeft, blitFbo, destRectRight); - } else { - gpu::Vec4i srcRect; - srcRect.z = width; - srcRect.w = height; - - gpu::Vec4i destRect; - destRect.x = width; - destRect.y = 0; - destRect.z = 0; - destRect.w = height; - - batch.blit(primaryFbo, srcRect, blitFbo, destRect); - } - } else { - gpu::Vec4i rect; - rect.z = width; - rect.w = height; - - batch.blit(primaryFbo, rect, blitFbo, rect); - } - }); -} - -void ExtractFrustums::run(const render::RenderContextPointer& renderContext, Output& output) { - assert(renderContext->args); - assert(renderContext->args->_context); - - RenderArgs* args = renderContext->args; - - // Return view frustum - auto& viewFrustum = output[VIEW_FRUSTUM].edit(); - if (!viewFrustum) { - viewFrustum = std::make_shared(args->getViewFrustum()); - } else { - *viewFrustum = args->getViewFrustum(); - } - - // Return shadow frustum - auto lightStage = args->_scene->getStage(LightStage::getName()); - for (auto i = 0; i < SHADOW_CASCADE_FRUSTUM_COUNT; i++) { - auto& shadowFrustum = output[SHADOW_CASCADE0_FRUSTUM+i].edit(); - if (lightStage) { - auto globalShadow = lightStage->getCurrentKeyShadow(); - - if (globalShadow && i<(int)globalShadow->getCascadeCount()) { - auto& cascade = globalShadow->getCascade(i); - shadowFrustum = cascade.getFrustum(); - } else { - shadowFrustum.reset(); - } - } else { - shadowFrustum.reset(); - } - } -} diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index e41b7edc77..00804f7e7b 100644 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -16,42 +16,17 @@ #include #include "LightingModel.h" -class BeginGPURangeTimer { -public: - using JobModel = render::Job::ModelO; - - BeginGPURangeTimer(const std::string& name) : _gpuTimer(std::make_shared(name)) {} - - void run(const render::RenderContextPointer& renderContext, gpu::RangeTimerPointer& timer); - -protected: - gpu::RangeTimerPointer _gpuTimer; -}; - -using GPURangeTimerConfig = render::GPUJobConfig; - -class EndGPURangeTimer { -public: - using Config = GPURangeTimerConfig; - using JobModel = render::Job::ModelI; - - EndGPURangeTimer() {} - - void configure(const Config& config) {} - void run(const render::RenderContextPointer& renderContext, const gpu::RangeTimerPointer& timer); - -protected: -}; - -class DrawConfig : public render::Job::Config { +class DrawDeferredConfig : public render::Job::Config { Q_OBJECT Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY newStats) Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty) public: - int getNumDrawn() { return _numDrawn; } - void setNumDrawn(int numDrawn) { _numDrawn = numDrawn; emit newStats(); } + void setNumDrawn(int numDrawn) { + _numDrawn = numDrawn; + emit newStats(); + } int maxDrawn{ -1 }; @@ -65,29 +40,32 @@ protected: class DrawDeferred { public: - using Inputs = render::VaryingSet2 ; - using Config = DrawConfig; + using Inputs = render::VaryingSet2; + using Config = DrawDeferredConfig; using JobModel = render::Job::ModelI; - DrawDeferred(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} + DrawDeferred(render::ShapePlumberPointer shapePlumber) + : _shapePlumber{ shapePlumber } {} void configure(const Config& config) { _maxDrawn = config.maxDrawn; } void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); protected: render::ShapePlumberPointer _shapePlumber; - int _maxDrawn; // initialized by Config + int _maxDrawn; // initialized by Config }; class DrawStateSortConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY numDrawnChanged) - Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty) - Q_PROPERTY(bool stateSort MEMBER stateSort NOTIFY dirty) + Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY numDrawnChanged) + Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty) + Q_PROPERTY(bool stateSort MEMBER stateSort NOTIFY dirty) public: - int getNumDrawn() { return numDrawn; } - void setNumDrawn(int num) { numDrawn = num; emit numDrawnChanged(); } + void setNumDrawn(int num) { + numDrawn = num; + emit numDrawnChanged(); + } int maxDrawn{ -1 }; bool stateSort{ true }; @@ -102,101 +80,32 @@ protected: class DrawStateSortDeferred { public: - using Inputs = render::VaryingSet2 ; + using Inputs = render::VaryingSet2; using Config = DrawStateSortConfig; using JobModel = render::Job::ModelI; - DrawStateSortDeferred(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} + DrawStateSortDeferred(render::ShapePlumberPointer shapePlumber) + : _shapePlumber{ shapePlumber } {} - void configure(const Config& config) { _maxDrawn = config.maxDrawn; _stateSort = config.stateSort; } + void configure(const Config& config) { + _maxDrawn = config.maxDrawn; + _stateSort = config.stateSort; + } void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); protected: render::ShapePlumberPointer _shapePlumber; - int _maxDrawn; // initialized by Config + int _maxDrawn; // initialized by Config bool _stateSort; }; -class DrawOverlay3DConfig : public render::Job::Config { - Q_OBJECT - Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY numDrawnChanged) - Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty) -public: - int getNumDrawn() { return numDrawn; } - void setNumDrawn(int num) { numDrawn = num; emit numDrawnChanged(); } - - int maxDrawn{ -1 }; - -signals: - void numDrawnChanged(); - void dirty(); - -protected: - int numDrawn{ 0 }; -}; - -class DrawOverlay3D { -public: - using Inputs = render::VaryingSet2 ; - - using Config = DrawOverlay3DConfig; - using JobModel = render::Job::ModelI; - - DrawOverlay3D(bool opaque); - - void configure(const Config& config) { _maxDrawn = config.maxDrawn; } - void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); - -protected: - render::ShapePlumberPointer _shapePlumber; - int _maxDrawn; // initialized by Config - bool _opaquePass { true }; -}; - -class CompositeHUD { -public: - using JobModel = render::Job::Model; - - CompositeHUD() {} - void run(const render::RenderContextPointer& renderContext); -}; - -class Blit { -public: - using JobModel = render::Job::ModelI; - - void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& srcFramebuffer); -}; - -class ExtractFrustums { -public: - - enum Frustum { - SHADOW_CASCADE0_FRUSTUM = 0, - SHADOW_CASCADE1_FRUSTUM, - SHADOW_CASCADE2_FRUSTUM, - SHADOW_CASCADE3_FRUSTUM, - - SHADOW_CASCADE_FRUSTUM_COUNT, - - VIEW_FRUSTUM = SHADOW_CASCADE_FRUSTUM_COUNT, - - FRUSTUM_COUNT - }; - - using Output = render::VaryingArray; - using JobModel = render::Job::ModelO; - - void run(const render::RenderContextPointer& renderContext, Output& output); -}; - class RenderDeferredTaskConfig : public render::Task::Config { Q_OBJECT - Q_PROPERTY(float fadeScale MEMBER fadeScale NOTIFY dirty) - Q_PROPERTY(float fadeDuration MEMBER fadeDuration NOTIFY dirty) - Q_PROPERTY(bool debugFade MEMBER debugFade NOTIFY dirty) - Q_PROPERTY(float debugFadePercent MEMBER debugFadePercent NOTIFY dirty) + Q_PROPERTY(float fadeScale MEMBER fadeScale NOTIFY dirty) + Q_PROPERTY(float fadeDuration MEMBER fadeDuration NOTIFY dirty) + Q_PROPERTY(bool debugFade MEMBER debugFade NOTIFY dirty) + Q_PROPERTY(float debugFadePercent MEMBER debugFadePercent NOTIFY dirty) public: float fadeScale{ 0.5f }; float fadeDuration{ 3.0f }; @@ -205,7 +114,6 @@ public: signals: void dirty(); - }; class RenderDeferredTask { @@ -220,9 +128,11 @@ public: void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); private: - - static const render::Varying addSelectItemJobs(JobModel& task, const char* selectionName, - const render::Varying& metas, const render::Varying& opaques, const render::Varying& transparents); + static const render::Varying addSelectItemJobs(JobModel& task, + const char* selectionName, + const render::Varying& metas, + const render::Varying& opaques, + const render::Varying& transparents); }; -#endif // hifi_RenderDeferredTask_h +#endif // hifi_RenderDeferredTask_h diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 6aaeae7bcc..460aee9d9f 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -11,59 +11,95 @@ // #include "RenderForwardTask.h" -#include "RenderDeferredTask.h" #include #include #include #include -#include "StencilMaskPass.h" +#include +#include +#include "StencilMaskPass.h" +#include "ZoneRenderer.h" +#include "FadeEffect.h" +#include "BackgroundStage.h" #include "FramebufferCache.h" #include "TextureCache.h" - -#include +#include "RenderCommonTask.h" +#include "LightStage.h" #include "nop_frag.h" using namespace render; -extern void initForwardPipelines(ShapePlumber& plumber); +extern void initForwardPipelines(ShapePlumber& plumber, + const render::ShapePipeline::BatchSetter& batchSetter, + const render::ShapePipeline::ItemSetter& itemSetter); void RenderForwardTask::build(JobModel& task, const render::Varying& input, render::Varying& output) { auto items = input.get(); + auto fadeEffect = DependencyManager::get(); // Prepare the ShapePipelines ShapePlumberPointer shapePlumber = std::make_shared(); - initForwardPipelines(*shapePlumber); + initForwardPipelines(*shapePlumber, fadeEffect->getBatchSetter(), fadeEffect->getItemUniformSetter()); // Extract opaques / transparents / lights / metas / overlays / background const auto& opaques = items.get0()[RenderFetchCullSortTask::OPAQUE_SHAPE]; -// const auto& transparents = items.get0()[RenderFetchCullSortTask::TRANSPARENT_SHAPE]; -// const auto& lights = items.get0()[RenderFetchCullSortTask::LIGHT]; -// const auto& metas = items.get0()[RenderFetchCullSortTask::META]; -// const auto& overlayOpaques = items.get0()[RenderFetchCullSortTask::OVERLAY_OPAQUE_SHAPE]; -// const auto& overlayTransparents = items.get0()[RenderFetchCullSortTask::OVERLAY_TRANSPARENT_SHAPE]; - const auto& background = items.get0()[RenderFetchCullSortTask::BACKGROUND]; -// const auto& spatialSelection = items[1]; + const auto& transparents = items.get0()[RenderFetchCullSortTask::TRANSPARENT_SHAPE]; + // const auto& lights = items.get0()[RenderFetchCullSortTask::LIGHT]; + const auto& metas = items.get0()[RenderFetchCullSortTask::META]; + // const auto& overlayOpaques = items.get0()[RenderFetchCullSortTask::OVERLAY_OPAQUE_SHAPE]; + // const auto& overlayTransparents = items.get0()[RenderFetchCullSortTask::OVERLAY_TRANSPARENT_SHAPE]; + //const auto& background = items.get0()[RenderFetchCullSortTask::BACKGROUND]; + // const auto& spatialSelection = items[1]; + fadeEffect->build(task, opaques); + + // Prepare objects shared by several jobs + const auto lightingModel = task.addJob("LightingModel"); + + // Filter zones from the general metas bucket + const auto zones = task.addJob("ZoneRenderer", metas); + + // GPU jobs: Start preparing the main framebuffer const auto framebuffer = task.addJob("PrepareFramebuffer"); - task.addJob("DrawOpaques", opaques, shapePlumber); - task.addJob("Stencil"); - task.addJob("DrawBackground", background); + task.addJob("PrepareForward", lightingModel); - // Bounds do not draw on stencil buffer, so they must come last - task.addJob("DrawBounds", opaques); + // draw a stencil mask in hidden regions of the framebuffer. + task.addJob("PrepareStencil", framebuffer); + + // Draw opaques forward + const auto opaqueInputs = DrawForward::Inputs(opaques, lightingModel).asVarying(); + task.addJob("DrawOpaques", opaqueInputs, shapePlumber); + + // Similar to light stage, background stage has been filled by several potential render items and resolved for the frame in this job + task.addJob("DrawBackgroundDeferred", lightingModel); + + // Draw transparent objects forward + const auto transparentInputs = DrawForward::Inputs(transparents, lightingModel).asVarying(); + task.addJob("DrawTransparents", transparentInputs, shapePlumber); + + { // Debug the bounds of the rendered items, still look at the zbuffer + + task.addJob("DrawMetaBounds", metas); + task.addJob("DrawBounds", opaques); + task.addJob("DrawTransparentBounds", transparents); + + task.addJob("DrawZones", zones); + } + + // Layered Overlays + + // Composite the HUD and HUD overlays + task.addJob("HUD"); // Blit! task.addJob("Blit", framebuffer); } -void PrepareFramebuffer::run(const RenderContextPointer& renderContext, - gpu::FramebufferPointer& framebuffer) { - auto framebufferCache = DependencyManager::get(); - auto framebufferSize = framebufferCache->getFrameBufferSize(); - glm::uvec2 frameSize(framebufferSize.width(), framebufferSize.height()); +void PrepareFramebuffer::run(const RenderContextPointer& renderContext, gpu::FramebufferPointer& framebuffer) { + glm::uvec2 frameSize(renderContext->args->_viewport.z, renderContext->args->_viewport.w); // Resizing framebuffers instead of re-building them seems to cause issues with threaded rendering if (_framebuffer && _framebuffer->getSize() != frameSize) { @@ -75,11 +111,13 @@ void PrepareFramebuffer::run(const RenderContextPointer& renderContext, auto colorFormat = gpu::Element::COLOR_SRGBA_32; auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT); - auto colorTexture = gpu::Texture::create2D(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler); + auto colorTexture = + gpu::Texture::createRenderBuffer(colorFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler); _framebuffer->setRenderBuffer(0, colorTexture); - auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format - auto depthTexture = gpu::Texture::create2D(depthFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler); + auto depthFormat = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::DEPTH_STENCIL); // Depth24_Stencil8 texel format + auto depthTexture = + gpu::Texture::createRenderBuffer(depthFormat, frameSize.x, frameSize.y, gpu::Texture::SINGLE_MIP, defaultSampler); _framebuffer->setDepthStencilBuffer(depthTexture, depthFormat); } @@ -90,23 +128,87 @@ void PrepareFramebuffer::run(const RenderContextPointer& renderContext, batch.setStateScissorRect(args->_viewport); batch.setFramebuffer(_framebuffer); - batch.clearFramebuffer( - gpu::Framebuffer::BUFFER_COLOR0 | - gpu::Framebuffer::BUFFER_DEPTH | + batch.clearFramebuffer(gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH | gpu::Framebuffer::BUFFER_STENCIL, - vec4(vec3(0), 1), 1.0, 0, true); + vec4(vec3(0, 1.0, 0.0), 0), 1.0, 0, true); }); framebuffer = _framebuffer; } -void Draw::run(const RenderContextPointer& renderContext, - const Inputs& items) { +enum ForwardShader_MapSlot { + DEFERRED_BUFFER_COLOR_UNIT = 0, + DEFERRED_BUFFER_NORMAL_UNIT = 1, + DEFERRED_BUFFER_EMISSIVE_UNIT = 2, + DEFERRED_BUFFER_DEPTH_UNIT = 3, + DEFERRED_BUFFER_OBSCURANCE_UNIT = 4, + SHADOW_MAP_UNIT = 5, + SKYBOX_MAP_UNIT = SHADOW_MAP_UNIT + 4, + DEFERRED_BUFFER_LINEAR_DEPTH_UNIT, + DEFERRED_BUFFER_CURVATURE_UNIT, + DEFERRED_BUFFER_DIFFUSED_CURVATURE_UNIT, + SCATTERING_LUT_UNIT, + SCATTERING_SPECULAR_UNIT, +}; +enum ForwardShader_BufferSlot { + DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT = 0, + CAMERA_CORRECTION_BUFFER_SLOT, + SCATTERING_PARAMETERS_BUFFER_SLOT, + LIGHTING_MODEL_BUFFER_SLOT = render::ShapePipeline::Slot::LIGHTING_MODEL, + LIGHT_GPU_SLOT = render::ShapePipeline::Slot::LIGHT, + LIGHT_AMBIENT_SLOT = render::ShapePipeline::Slot::LIGHT_AMBIENT_BUFFER, + HAZE_MODEL_BUFFER_SLOT = render::ShapePipeline::Slot::HAZE_MODEL, + LIGHT_INDEX_GPU_SLOT, + LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT, + LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, + LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, +}; + +void PrepareForward::run(const RenderContextPointer& renderContext, const Inputs& inputs) { RenderArgs* args = renderContext->args; gpu::doInBatch("RenderForward::Draw::run", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; + graphics::LightPointer keySunLight; + auto lightStage = args->_scene->getStage(); + if (lightStage) { + keySunLight = lightStage->getCurrentKeyLight(); + } + + graphics::LightPointer keyAmbiLight; + if (lightStage) { + keyAmbiLight = lightStage->getCurrentAmbientLight(); + } + + if (keySunLight) { + if (LIGHT_GPU_SLOT >= 0) { + batch.setUniformBuffer(LIGHT_GPU_SLOT, keySunLight->getLightSchemaBuffer()); + } + } + + if (keyAmbiLight) { + if (LIGHT_AMBIENT_SLOT >= 0) { + batch.setUniformBuffer(LIGHT_AMBIENT_SLOT, keyAmbiLight->getAmbientSchemaBuffer()); + } + + if (keyAmbiLight->getAmbientMap() && (SKYBOX_MAP_UNIT >= 0)) { + batch.setResourceTexture(SKYBOX_MAP_UNIT, keyAmbiLight->getAmbientMap()); + } + } + }); +} + +void DrawForward::run(const RenderContextPointer& renderContext, const Inputs& inputs) { + RenderArgs* args = renderContext->args; + + const auto& inItems = inputs.get0(); + const auto& lightingModel = inputs.get1(); + + gpu::doInBatch("RenderForward::DrawBackground::run", args->_context, [&](gpu::Batch& batch) { + args->_batch = &batch; + + // Setup projection glm::mat4 projMat; Transform viewMat; @@ -116,66 +218,23 @@ void Draw::run(const RenderContextPointer& renderContext, batch.setViewTransform(viewMat); batch.setModelTransform(Transform()); + // Setup lighting model for all items; + batch.setUniformBuffer(render::ShapePipeline::Slot::LIGHTING_MODEL, lightingModel->getParametersBuffer()); + + // From the lighting model define a global shapeKey ORED with individiual keys + ShapeKey::Builder keyBuilder; + if (lightingModel->isWireframeEnabled()) { + keyBuilder.withWireframe(); + } + ShapeKey globalKey = keyBuilder.build(); + args->_globalShapeKey = globalKey._flags.to_ulong(); + // Render items - renderStateSortShapes(renderContext, _shapePlumber, items, -1); + renderStateSortShapes(renderContext, _shapePlumber, inItems, -1, globalKey); + + args->_batch = nullptr; + args->_globalShapeKey = 0; }); - args->_batch = nullptr; -} - -const gpu::PipelinePointer Stencil::getPipeline() { - if (!_stencilPipeline) { - auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); - auto ps = gpu::Shader::createPixel(std::string(nop_frag)); - gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); - gpu::Shader::makeProgram(*program); - - auto state = std::make_shared(); - state->setDepthTest(true, false, gpu::LESS_EQUAL); - PrepareStencil::drawBackground(*state); - - _stencilPipeline = gpu::Pipeline::create(program, state); - } - return _stencilPipeline; -} - -void Stencil::run(const RenderContextPointer& renderContext) { - RenderArgs* args = renderContext->args; - - gpu::doInBatch("RenderForward::Stencil::run", args->_context, [&](gpu::Batch& batch) { - args->_batch = &batch; - - batch.enableStereo(false); - batch.setViewportTransform(args->_viewport); - batch.setStateScissorRect(args->_viewport); - - batch.setPipeline(getPipeline()); - batch.draw(gpu::TRIANGLE_STRIP, 4); - }); - args->_batch = nullptr; -} - -void DrawBackground::run(const RenderContextPointer& renderContext, - const Inputs& background) { - RenderArgs* args = renderContext->args; - - gpu::doInBatch("RenderForward::DrawBackground::run", args->_context, [&](gpu::Batch& batch) { - args->_batch = &batch; - - batch.enableSkybox(true); - batch.setViewportTransform(args->_viewport); - batch.setStateScissorRect(args->_viewport); - - // Setup projection - glm::mat4 projMat; - Transform viewMat; - args->getViewFrustum().evalProjectionMatrix(projMat); - args->getViewFrustum().evalViewTransform(viewMat); - batch.setProjectionTransform(projMat); - batch.setViewTransform(viewMat); - - renderItems(renderContext, background); - }); - args->_batch = nullptr; } diff --git a/libraries/render-utils/src/RenderForwardTask.h b/libraries/render-utils/src/RenderForwardTask.h index 63d087b3c5..22e75d4bdc 100755 --- a/libraries/render-utils/src/RenderForwardTask.h +++ b/libraries/render-utils/src/RenderForwardTask.h @@ -38,37 +38,28 @@ private: gpu::FramebufferPointer _framebuffer; }; -class Draw { +class PrepareForward { public: - using Inputs = render::ItemBounds; - using JobModel = render::Job::ModelI; + using Inputs = LightingModelPointer; + using JobModel = render::Job::ModelI; - Draw(const render::ShapePlumberPointer& shapePlumber) : _shapePlumber(shapePlumber) {} void run(const render::RenderContextPointer& renderContext, - const Inputs& items); + const Inputs& inputs); + +private: +}; + +class DrawForward{ +public: + using Inputs = render::VaryingSet2; + using JobModel = render::Job::ModelI; + + DrawForward(const render::ShapePlumberPointer& shapePlumber) : _shapePlumber(shapePlumber) {} + void run(const render::RenderContextPointer& renderContext, + const Inputs& inputs); private: render::ShapePlumberPointer _shapePlumber; }; -class Stencil { -public: - using JobModel = render::Job::Model; - - void run(const render::RenderContextPointer& renderContext); - -private: - const gpu::PipelinePointer getPipeline(); - gpu::PipelinePointer _stencilPipeline; -}; - -class DrawBackground { -public: - using Inputs = render::ItemBounds; - using JobModel = render::Job::ModelI; - - void run(const render::RenderContextPointer& renderContext, - const Inputs& background); -}; - #endif // hifi_RenderForwardTask_h diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index 7f644add72..b6458ea80a 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -106,7 +106,7 @@ using namespace std::placeholders; void initOverlay3DPipelines(ShapePlumber& plumber, bool depthTest = false); void initDeferredPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter); -void initForwardPipelines(ShapePlumber& plumber); +void initForwardPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter); void initZPassPipelines(ShapePlumber& plumber, gpu::StatePointer state); void addPlumberPipeline(ShapePlumber& plumber, @@ -436,12 +436,13 @@ void initDeferredPipelines(render::ShapePlumber& plumber, const render::ShapePip skinModelShadowFadeVertex, modelShadowFadePixel, batchSetter, itemSetter); } -void initForwardPipelines(render::ShapePlumber& plumber) { +void initForwardPipelines(ShapePlumber& plumber, const render::ShapePipeline::BatchSetter& batchSetter, const render::ShapePipeline::ItemSetter& itemSetter) { // Vertex shaders auto modelVertex = gpu::Shader::createVertex(std::string(model_vert)); auto modelNormalMapVertex = gpu::Shader::createVertex(std::string(model_normal_map_vert)); auto skinModelVertex = gpu::Shader::createVertex(std::string(skin_model_vert)); auto skinModelNormalMapVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_vert)); + auto skinModelNormalMapFadeVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_fade_vert)); // Pixel shaders auto modelPixel = gpu::Shader::createPixel(std::string(forward_model_frag)); @@ -449,38 +450,43 @@ void initForwardPipelines(render::ShapePlumber& plumber) { auto modelNormalMapPixel = gpu::Shader::createPixel(std::string(forward_model_normal_map_frag)); auto modelSpecularMapPixel = gpu::Shader::createPixel(std::string(forward_model_specular_map_frag)); auto modelNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(forward_model_normal_specular_map_frag)); + auto modelNormalMapFadePixel = gpu::Shader::createPixel(std::string(model_normal_map_fade_frag)); using Key = render::ShapeKey; - auto addPipeline = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, _3, nullptr, nullptr); + auto addPipeline = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, _3, _4, _5); // Opaques addPipeline( Key::Builder().withMaterial(), - modelVertex, modelPixel); + modelVertex, modelPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withUnlit(), - modelVertex, modelUnlitPixel); + modelVertex, modelUnlitPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withTangents(), - modelNormalMapVertex, modelNormalMapPixel); + modelNormalMapVertex, modelNormalMapPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withSpecular(), - modelVertex, modelSpecularMapPixel); + modelVertex, modelSpecularMapPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withTangents().withSpecular(), - modelNormalMapVertex, modelNormalSpecularMapPixel); + modelNormalMapVertex, modelNormalSpecularMapPixel, nullptr, nullptr); // Skinned addPipeline( Key::Builder().withMaterial().withSkinned(), - skinModelVertex, modelPixel); + skinModelVertex, modelPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withSkinned().withTangents(), - skinModelNormalMapVertex, modelNormalMapPixel); + skinModelNormalMapVertex, modelNormalMapPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withSkinned().withSpecular(), - skinModelVertex, modelSpecularMapPixel); + skinModelVertex, modelSpecularMapPixel, nullptr, nullptr); addPipeline( Key::Builder().withMaterial().withSkinned().withTangents().withSpecular(), - skinModelNormalMapVertex, modelNormalSpecularMapPixel); + skinModelNormalMapVertex, modelNormalSpecularMapPixel, nullptr, nullptr); + addPipeline( + Key::Builder().withMaterial().withSkinned().withTangents().withFade(), + skinModelNormalMapFadeVertex, modelNormalMapFadePixel, batchSetter, itemSetter, nullptr, nullptr); + } void addPlumberPipeline(ShapePlumber& plumber, @@ -546,7 +552,7 @@ void batchSetter(const ShapePipeline& pipeline, gpu::Batch& batch, RenderArgs* a if (pipeline.locations->materialBufferUnit >= 0) { // Create a default schema static bool isMaterialSet = false; - static model::Material material; + static graphics::Material material; if (!isMaterialSet) { material.setAlbedo(vec3(1.0f)); material.setOpacity(1.0f); diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index 56fc241fd1..6b5f9ea8f0 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -271,7 +271,7 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon // Set the keylight render args args->pushViewFrustum(*(globalShadow->getCascade(_cascadeIndex).getFrustum())); args->_renderMode = RenderArgs::SHADOW_RENDER_MODE; - if (lightStage->getCurrentKeyLight()->getType() == model::Light::SUN) { + if (lightStage->getCurrentKeyLight()->getType() == graphics::Light::SUN) { const float shadowSizeScale = 1e16f; // Set the size scale to a ridiculously high value to prevent small object culling which assumes // the view frustum is a perspective projection. But this isn't the case for the sun which diff --git a/libraries/render-utils/src/Skinning.slh b/libraries/render-utils/src/Skinning.slh index 2d1f010029..a7edfb14a6 100644 --- a/libraries/render-utils/src/Skinning.slh +++ b/libraries/render-utils/src/Skinning.slh @@ -11,6 +11,10 @@ <@if not SKINNING_SLH@> <@def SKINNING_SLH@> +// Use dual quaternion skinning +// Must match #define SKIN_DQ in Model.h +<@def SKIN_DQ@> + const int MAX_CLUSTERS = 128; const int INDICES_PER_VERTEX = 4; @@ -18,6 +22,156 @@ layout(std140) uniform skinClusterBuffer { mat4 clusterMatrices[MAX_CLUSTERS]; }; +<@if SKIN_DQ@> + +mat4 dualQuatToMat4(vec4 real, vec4 dual) { + float twoRealXSq = 2.0 * real.x * real.x; + float twoRealYSq = 2.0 * real.y * real.y; + float twoRealZSq = 2.0 * real.z * real.z; + float twoRealXY = 2.0 * real.x * real.y; + float twoRealXZ = 2.0 * real.x * real.z; + float twoRealXW = 2.0 * real.x * real.w; + float twoRealZW = 2.0 * real.z * real.w; + float twoRealYZ = 2.0 * real.y * real.z; + float twoRealYW = 2.0 * real.y * real.w; + vec4 col0 = vec4(1.0 - twoRealYSq - twoRealZSq, + twoRealXY + twoRealZW, + twoRealXZ - twoRealYW, + 0.0); + vec4 col1 = vec4(twoRealXY - twoRealZW, + 1.0 - twoRealXSq - twoRealZSq, + twoRealYZ + twoRealXW, + 0.0); + vec4 col2 = vec4(twoRealXZ + twoRealYW, + twoRealYZ - twoRealXW, + 1.0 - twoRealXSq - twoRealYSq, + 0.0); + vec4 col3 = vec4(2.0 * (-dual.w * real.x + dual.x * real.w - dual.y * real.z + dual.z * real.y), + 2.0 * (-dual.w * real.y + dual.x * real.z + dual.y * real.w - dual.z * real.x), + 2.0 * (-dual.w * real.z - dual.x * real.y + dual.y * real.x + dual.z * real.w), + 1.0); + + return mat4(col0, col1, col2, col3); +} + +// dual quaternion linear blending +void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { + + // linearly blend scale and dual quaternion components + vec3 sAccum = vec3(0.0, 0.0, 0.0); + vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 dAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1]; + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + + vec3 scale = vec3(clusterMatrix[0]); + vec4 real = clusterMatrix[1]; + vec4 dual = clusterMatrix[2]; + + // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. + float dqClusterWeight = clusterWeight; + if (dot(real, polarityReference) < 0.0) { + dqClusterWeight = -clusterWeight; + } + + sAccum += scale * clusterWeight; + rAccum += real * dqClusterWeight; + dAccum += dual * dqClusterWeight; + } + + // normalize dual quaternion + float norm = length(rAccum); + rAccum /= norm; + dAccum /= norm; + + // conversion from dual quaternion to 4x4 matrix. + mat4 m = dualQuatToMat4(rAccum, dAccum); + skinnedPosition = m * (vec4(sAccum, 1) * inPosition); +} + +void skinPositionNormal(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, + out vec4 skinnedPosition, out vec3 skinnedNormal) { + + // linearly blend scale and dual quaternion components + vec3 sAccum = vec3(0.0, 0.0, 0.0); + vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 dAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1]; + + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + + vec3 scale = vec3(clusterMatrix[0]); + vec4 real = clusterMatrix[1]; + vec4 dual = clusterMatrix[2]; + + // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. + float dqClusterWeight = clusterWeight; + if (dot(real, polarityReference) < 0.0) { + dqClusterWeight = -clusterWeight; + } + + sAccum += scale * clusterWeight; + rAccum += real * dqClusterWeight; + dAccum += dual * dqClusterWeight; + } + + // normalize dual quaternion + float norm = length(rAccum); + rAccum /= norm; + dAccum /= norm; + + // conversion from dual quaternion to 4x4 matrix. + mat4 m = dualQuatToMat4(rAccum, dAccum); + skinnedPosition = m * (vec4(sAccum, 1) * inPosition); + skinnedNormal = vec3(m * vec4(inNormal, 0)); +} + +void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, vec3 inNormal, vec3 inTangent, + out vec4 skinnedPosition, out vec3 skinnedNormal, out vec3 skinnedTangent) { + + // linearly blend scale and dual quaternion components + vec3 sAccum = vec3(0.0, 0.0, 0.0); + vec4 rAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 dAccum = vec4(0.0, 0.0, 0.0, 0.0); + vec4 polarityReference = clusterMatrices[skinClusterIndex[0]][1]; + + for (int i = 0; i < INDICES_PER_VERTEX; i++) { + mat4 clusterMatrix = clusterMatrices[(skinClusterIndex[i])]; + float clusterWeight = skinClusterWeight[i]; + + vec3 scale = vec3(clusterMatrix[0]); + vec4 real = clusterMatrix[1]; + vec4 dual = clusterMatrix[2]; + + // to ensure that we rotate along the shortest arc, reverse dual quaternions with negative polarity. + float dqClusterWeight = clusterWeight; + if (dot(real, polarityReference) < 0.0) { + dqClusterWeight = -clusterWeight; + } + + sAccum += scale * clusterWeight; + rAccum += real * dqClusterWeight; + dAccum += dual * dqClusterWeight; + } + + // normalize dual quaternion + float norm = length(rAccum); + rAccum /= norm; + dAccum /= norm; + + // conversion from dual quaternion to 4x4 matrix. + mat4 m = dualQuatToMat4(rAccum, dAccum); + skinnedPosition = m * (vec4(sAccum, 1) * inPosition); + skinnedNormal = vec3(m * vec4(inNormal, 0)); + skinnedTangent = vec3(m * vec4(inTangent, 0)); +} + +<@else@> // SKIN_DQ + void skinPosition(ivec4 skinClusterIndex, vec4 skinClusterWeight, vec4 inPosition, out vec4 skinnedPosition) { vec4 newPosition = vec4(0.0, 0.0, 0.0, 0.0); @@ -65,5 +219,6 @@ void skinPositionNormalTangent(ivec4 skinClusterIndex, vec4 skinClusterWeight, v skinnedTangent = newTangent.xyz; } +<@endif@> // if SKIN_DQ -<@endif@> \ No newline at end of file +<@endif@> // if not SKINNING_SLH diff --git a/libraries/render-utils/src/SoftAttachmentModel.cpp b/libraries/render-utils/src/SoftAttachmentModel.cpp index 63991f9422..0d0db7cbe3 100644 --- a/libraries/render-utils/src/SoftAttachmentModel.cpp +++ b/libraries/render-utils/src/SoftAttachmentModel.cpp @@ -52,13 +52,27 @@ void SoftAttachmentModel::updateClusterMatrices() { // TODO: cache these look-ups as an optimization int jointIndexOverride = getJointIndexOverride(cluster.jointIndex); +#if defined(SKIN_DQ) glm::mat4 jointMatrix; if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { jointMatrix = _rigOverride.getJointTransform(jointIndexOverride); } else { jointMatrix = _rig.getJointTransform(cluster.jointIndex); } - glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterMatrices[j]); + + glm::mat4 m; + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, m); + state.clusterTransforms[j] = Model::TransformDualQuaternion(m); +#else + glm::mat4 jointMatrix; + if (jointIndexOverride >= 0 && jointIndexOverride < _rigOverride.getJointStateCount()) { + jointMatrix = _rigOverride.getJointTransform(jointIndexOverride); + } else { + jointMatrix = _rig.getJointTransform(cluster.jointIndex); + } + + glm_mat4u_mul(jointMatrix, cluster.inverseBindMatrix, state.clusterTransforms[j]); +#endif } } diff --git a/libraries/render-utils/src/StencilMaskPass.cpp b/libraries/render-utils/src/StencilMaskPass.cpp index 54811937fd..47d79c2a30 100644 --- a/libraries/render-utils/src/StencilMaskPass.cpp +++ b/libraries/render-utils/src/StencilMaskPass.cpp @@ -26,7 +26,7 @@ void PrepareStencil::configure(const Config& config) { _forceDraw = config.forceDraw; } -model::MeshPointer PrepareStencil::getMesh() { +graphics::MeshPointer PrepareStencil::getMesh() { if (!_mesh) { std::vector vertices { @@ -36,7 +36,7 @@ model::MeshPointer PrepareStencil::getMesh() { { 1.0f, -1.0f, 0.0f }, { 0.0f, -1.0f, 0.0f } }; std::vector indices { 0, 7, 1, 1, 3, 2, 3, 5, 4, 5, 7, 6 }; - _mesh = model::Mesh::createIndexedTriangles_P3F((uint32_t) vertices.size(), (uint32_t) indices.size(), vertices.data(), indices.data()); + _mesh = graphics::Mesh::createIndexedTriangles_P3F((uint32_t) vertices.size(), (uint32_t) indices.size(), vertices.data(), indices.data()); } return _mesh; } @@ -95,7 +95,7 @@ void PrepareStencil::run(const RenderContextPointer& renderContext, const gpu::F batch.setInputStream(0, mesh->getVertexStream()); // Draw - auto part = mesh->getPartBuffer().get(0); + auto part = mesh->getPartBuffer().get(0); batch.drawIndexed(gpu::TRIANGLES, part._numIndices, part._startIndex); } else { batch.setPipeline(getPaintStencilPipeline()); @@ -129,7 +129,7 @@ void PrepareStencil::testNoAA(gpu::State& state) { } // Pass if this area WAS marked as BACKGROUND -// (see: model/src/Skybox.cpp, procedural/src/ProceduralSkybox.cpp) +// (see: graphics/src/Skybox.cpp, procedural/src/ProceduralSkybox.cpp) void PrepareStencil::testBackground(gpu::State& state) { state.setStencilTest(true, 0x00, gpu::State::StencilTest(STENCIL_BACKGROUND, 0xFF, gpu::EQUAL, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP, gpu::State::STENCIL_OP_KEEP)); diff --git a/libraries/render-utils/src/StencilMaskPass.h b/libraries/render-utils/src/StencilMaskPass.h index fc258b607a..a8e4d1e1f2 100644 --- a/libraries/render-utils/src/StencilMaskPass.h +++ b/libraries/render-utils/src/StencilMaskPass.h @@ -14,7 +14,7 @@ #include #include -#include +#include class PrepareStencilConfig : public render::Job::Config { Q_OBJECT @@ -63,8 +63,8 @@ private: gpu::PipelinePointer _paintStencilPipeline; gpu::PipelinePointer getPaintStencilPipeline(); - model::MeshPointer _mesh; - model::MeshPointer getMesh(); + graphics::MeshPointer _mesh; + graphics::MeshPointer getMesh(); int _maskMode { 0 }; bool _forceDraw { false }; diff --git a/libraries/render-utils/src/SubsurfaceScattering.slh b/libraries/render-utils/src/SubsurfaceScattering.slh index 201ec2291a..233dfd7a0c 100644 --- a/libraries/render-utils/src/SubsurfaceScattering.slh +++ b/libraries/render-utils/src/SubsurfaceScattering.slh @@ -63,38 +63,6 @@ vec3 scatter(float r) { <@endfunc@> -<@func declareSkinSpecularLighting()@> - -uniform sampler2D scatteringSpecularBeckmann; - -float fetchSpecularBeckmann(float ndoth, float roughness) { - return pow( 2.0 * texture(scatteringSpecularBeckmann, vec2(ndoth, roughness)).r, 10.0); -} - -float fresnelReflectance(vec3 H, vec3 V, float Fo) { - float base = 1.0 - dot(V, H); - float exponential = pow(base, 5.0); - return exponential + Fo * (1.0 - exponential); -} - -float skinSpecular(vec3 N, vec3 L, vec3 V, float roughness, float intensity) { - float result = 0.0; - float ndotl = dot(N, L); - if (ndotl > 0.0) { - vec3 h = L + V; - vec3 H = normalize(h); - float ndoth = dot(N, H); - float PH = fetchSpecularBeckmann(ndoth, roughness); - float F = fresnelReflectance(H, V, 0.028); - float frSpec = max(PH * F / dot(h, h), 0.0); - result = ndotl * intensity * frSpec; - } - - return result; -} - -<@endfunc@> - <@func declareSubsurfaceScatteringIntegrate(NumIntegrationSteps)@> diff --git a/libraries/render-utils/src/ZoneRenderer.cpp b/libraries/render-utils/src/ZoneRenderer.cpp index 06e2971402..c394083f7a 100644 --- a/libraries/render-utils/src/ZoneRenderer.cpp +++ b/libraries/render-utils/src/ZoneRenderer.cpp @@ -68,10 +68,11 @@ void SetupZones::run(const RenderContextPointer& context, const Inputs& inputs) auto lightStage = context->_scene->getStage(); assert(lightStage); - lightStage->_currentFrame.pushSunLight(0); - lightStage->_currentFrame.pushAmbientLight(0); - hazeStage->_currentFrame.pushHaze(0); + lightStage->_currentFrame.pushSunLight(lightStage->getDefaultLight()); + lightStage->_currentFrame.pushAmbientLight(lightStage->getDefaultLight()); + backgroundStage->_currentFrame.pushBackground(0); + hazeStage->_currentFrame.pushHaze(0); } const gpu::PipelinePointer& DebugZoneLighting::getKeyLightPipeline() { @@ -144,14 +145,14 @@ void DebugZoneLighting::run(const render::RenderContextPointer& context, const I auto deferredTransform = inputs; auto lightStage = context->_scene->getStage(LightStage::getName()); - std::vector keyLightStack; + std::vector keyLightStack; if (lightStage && lightStage->_currentFrame._sunLights.size()) { for (auto index : lightStage->_currentFrame._sunLights) { keyLightStack.push_back(lightStage->getLight(index)); } } - std::vector ambientLightStack; + std::vector ambientLightStack; if (lightStage && lightStage->_currentFrame._ambientLights.size()) { for (auto index : lightStage->_currentFrame._ambientLights) { ambientLightStack.push_back(lightStage->getLight(index)); @@ -159,7 +160,7 @@ void DebugZoneLighting::run(const render::RenderContextPointer& context, const I } auto backgroundStage = context->_scene->getStage(BackgroundStage::getName()); - std::vector skyboxStack; + std::vector skyboxStack; if (backgroundStage && backgroundStage->_currentFrame._backgrounds.size()) { for (auto index : backgroundStage->_currentFrame._backgrounds) { auto background = backgroundStage->getBackground(index); diff --git a/libraries/render-utils/src/deferred_light_point.slv b/libraries/render-utils/src/deferred_light_point.slv index 88da7dd04c..2b75ee3915 100644 --- a/libraries/render-utils/src/deferred_light_point.slv +++ b/libraries/render-utils/src/deferred_light_point.slv @@ -18,7 +18,7 @@ <$declareStandardTransform()$> -<@include model/Light.slh@> +<@include graphics/Light.slh@> <$declareLightBuffer(256)$> diff --git a/libraries/render-utils/src/deferred_light_spot.slv b/libraries/render-utils/src/deferred_light_spot.slv index f6dc7faaf1..7e3e45b3b6 100644 --- a/libraries/render-utils/src/deferred_light_spot.slv +++ b/libraries/render-utils/src/deferred_light_spot.slv @@ -18,7 +18,7 @@ <$declareStandardTransform()$> -<@include model/Light.slh@> +<@include graphics/Light.slh@> <$declareLightBuffer(256)$> uniform lightIndexBuffer { diff --git a/libraries/render-utils/src/forward_model.slf b/libraries/render-utils/src/forward_model.slf index 7b708a1d24..65556e2939 100644 --- a/libraries/render-utils/src/forward_model.slf +++ b/libraries/render-utils/src/forward_model.slf @@ -16,7 +16,7 @@ <@include ForwardGlobalLight.slh@> <$declareEvalSkyboxGlobalColor()$> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> @@ -68,7 +68,7 @@ void main(void) { vec3 fragNormal; <$transformEyeToWorldDir(cam, _normal, fragNormal)$> - /* vec4 color = vec4(evalSkyboxGlobalColor( + vec4 color = vec4(evalSkyboxGlobalColor( cam._viewInverse, 1.0, 1.0, @@ -81,8 +81,6 @@ void main(void) { opacity); color.rgb += emissive * isEmissiveEnabled(); - */ - - _fragColor = vec4(albedo, opacity); - +// _fragColor = vec4(albedo, opacity); + _fragColor = color; } diff --git a/libraries/render-utils/src/forward_model_normal_map.slf b/libraries/render-utils/src/forward_model_normal_map.slf index a199483b9f..d22cc667e1 100644 --- a/libraries/render-utils/src/forward_model_normal_map.slf +++ b/libraries/render-utils/src/forward_model_normal_map.slf @@ -12,9 +12,15 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include ForwardBufferWrite.slh@> + !> -<@include model/Material.slh@> +<@include ForwardGlobalLight.slh@> +<$declareEvalSkyboxGlobalColor()$> + +<@include graphics/Material.slh@> + +<@include gpu/Transform.slh@> +<$declareStandardCameraTransform()$> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, _SCRIBE_NULL, EMISSIVE, OCCLUSION, SCATTERING)$> @@ -26,6 +32,8 @@ in vec3 _normal; in vec3 _tangent; in vec3 _color; +out vec4 _fragColor; + void main(void) { Material mat = getMaterial(); int matKey = getMaterialKey(mat); @@ -40,6 +48,15 @@ void main(void) { <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; albedo *= _color; + float metallic = getMaterialMetallic(mat); + vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value + if (metallic <= 0.5) { + metallic = 0.0; + } else { + fresnel = albedo; + metallic = 1.0; + } + float roughness = getMaterialRoughness(mat); <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; @@ -52,13 +69,24 @@ void main(void) { float scattering = getMaterialScattering(mat); <$evalMaterialScattering(scatteringTex, scattering, matKey, scattering)$>; - packForwardFragment( - viewNormal, - opacity, + vec3 fragPosition = _position.xyz; + + TransformCamera cam = getTransformCamera(); + + vec4 color = vec4(evalSkyboxGlobalColor( + cam._viewInverse, + 1.0, + 1.0, + fragPosition, + viewNormal, albedo, - roughness, - getMaterialMetallic(mat), - emissive, - occlusionTex, - scattering); + fresnel, + metallic, + roughness), + opacity); + + color.rgb += emissive * isEmissiveEnabled(); + +// _fragColor = vec4(albedo, opacity); + _fragColor = color; } diff --git a/libraries/render-utils/src/forward_model_normal_specular_map.slf b/libraries/render-utils/src/forward_model_normal_specular_map.slf index ca6bbec3da..68e5b755f8 100644 --- a/libraries/render-utils/src/forward_model_normal_specular_map.slf +++ b/libraries/render-utils/src/forward_model_normal_specular_map.slf @@ -12,9 +12,16 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include ForwardBufferWrite.slh@> -<@include model/Material.slh@> + !> + +<@include ForwardGlobalLight.slh@> +<$declareEvalSkyboxGlobalColor()$> + +<@include graphics/Material.slh@> + +<@include gpu/Transform.slh@> +<$declareStandardCameraTransform()$> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, METALLIC, EMISSIVE, OCCLUSION)$> @@ -26,6 +33,8 @@ in vec3 _normal; in vec3 _tangent; in vec3 _color; +out vec4 _fragColor; + void main(void) { Material mat = getMaterial(); int matKey = getMaterialKey(mat); @@ -51,16 +60,32 @@ void main(void) { float metallic = getMaterialMetallic(mat); <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; + vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value + if (metallic <= 0.5) { + metallic = 0.0; + } + else { + fresnel = albedo; + metallic = 1.0; + } + vec3 fragPosition = _position.xyz; - float scattering = getMaterialScattering(mat); + TransformCamera cam = getTransformCamera(); - packForwardFragment( - normalize(viewNormal.xyz), - opacity, + vec4 color = vec4(evalSkyboxGlobalColor( + cam._viewInverse, + 1.0, + 1.0, + fragPosition, + viewNormal, albedo, - roughness, + fresnel, metallic, - emissive, - occlusionTex, - scattering); + roughness), + opacity); + + color.rgb += emissive * isEmissiveEnabled(); + + // _fragColor = vec4(albedo, opacity); + _fragColor = color; } diff --git a/libraries/render-utils/src/forward_model_specular_map.slf b/libraries/render-utils/src/forward_model_specular_map.slf index d2fdd18794..057ec2cf53 100644 --- a/libraries/render-utils/src/forward_model_specular_map.slf +++ b/libraries/render-utils/src/forward_model_specular_map.slf @@ -12,9 +12,16 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include ForwardBufferWrite.slh@> -<@include model/Material.slh@> + !> + +<@include ForwardGlobalLight.slh@> +<$declareEvalSkyboxGlobalColor()$> + +<@include graphics/Material.slh@> + +<@include gpu/Transform.slh@> +<$declareStandardCameraTransform()$> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC, EMISSIVE, OCCLUSION)$> @@ -25,12 +32,13 @@ in vec2 _texCoord1; in vec3 _normal; in vec3 _color; +out vec4 _fragColor; void main(void) { Material mat = getMaterial(); int matKey = getMaterialKey(mat); <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, metallicTex, emissiveTex)$> - <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> + !> float opacity = 1.0; <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; @@ -48,7 +56,14 @@ void main(void) { float metallic = getMaterialMetallic(mat); <$evalMaterialMetallic(metallicTex, metallic, matKey, metallic)$>; - + vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value + if (metallic <= 0.5) { + metallic = 0.0; + } + else { + fresnel = albedo; + metallic = 1.0; + }/* float scattering = getMaterialScattering(mat); packForwardFragment( @@ -60,4 +75,26 @@ void main(void) { emissive, occlusionTex, scattering); + */ + vec3 fragPosition = _position.xyz; + + TransformCamera cam = getTransformCamera(); + vec3 fragNormal; + <$transformEyeToWorldDir(cam, _normal, fragNormal)$> + + vec4 color = vec4(evalSkyboxGlobalColor( + cam._viewInverse, + 1.0, + 1.0, + fragPosition, + fragNormal, + albedo, + fresnel, + metallic, + roughness), + opacity); + color.rgb += emissive * isEmissiveEnabled(); + + // _fragColor = vec4(albedo, opacity); + _fragColor = color; } diff --git a/libraries/render-utils/src/forward_model_translucent.slf b/libraries/render-utils/src/forward_model_translucent.slf index 906393db1f..7f752d893a 100644 --- a/libraries/render-utils/src/forward_model_translucent.slf +++ b/libraries/render-utils/src/forward_model_translucent.slf @@ -12,7 +12,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include ForwardGlobalLight.slh@> diff --git a/libraries/render-utils/src/forward_model_unlit.slf b/libraries/render-utils/src/forward_model_unlit.slf index fb760467c9..72ee61e28c 100644 --- a/libraries/render-utils/src/forward_model_unlit.slf +++ b/libraries/render-utils/src/forward_model_unlit.slf @@ -14,7 +14,7 @@ <@include ForwardBufferWrite.slh@> <@include LightingModel.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO)$> diff --git a/libraries/render-utils/src/lightClusters_drawClusterContent.slf b/libraries/render-utils/src/lightClusters_drawClusterContent.slf index 447f8bd634..739709418d 100644 --- a/libraries/render-utils/src/lightClusters_drawClusterContent.slf +++ b/libraries/render-utils/src/lightClusters_drawClusterContent.slf @@ -15,7 +15,7 @@ <@include DeferredBufferRead.slh@> -<@include model/Light.slh@> +<@include graphics/Light.slh@> <$declareLightBuffer(256)$> <@include LightClusterGrid.slh@> diff --git a/libraries/render-utils/src/local_lights_drawOutline.slf b/libraries/render-utils/src/local_lights_drawOutline.slf index 3aa210a241..56ce1e61a5 100644 --- a/libraries/render-utils/src/local_lights_drawOutline.slf +++ b/libraries/render-utils/src/local_lights_drawOutline.slf @@ -18,7 +18,7 @@ <$declareDeferredCurvature()$> // Everything about light -<@include model/Light.slh@> +<@include graphics/Light.slh@> <$declareLightBuffer(128)$> <@include LightingModel.slh@> diff --git a/libraries/render-utils/src/local_lights_shading.slf b/libraries/render-utils/src/local_lights_shading.slf index c6310cb079..c3208de726 100644 --- a/libraries/render-utils/src/local_lights_shading.slf +++ b/libraries/render-utils/src/local_lights_shading.slf @@ -18,7 +18,7 @@ <$declareDeferredCurvature()$> // Everything about light -<@include model/Light.slh@> +<@include graphics/Light.slh@> <$declareLightBuffer(256)$> <@include LightingModel.slh@> @@ -84,9 +84,8 @@ void main(void) { // Frag to eye vec vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0); vec3 fragEyeDir = normalize(fragEyeVector.xyz); - - // Compute the rougness into gloss2 once: - float fragGloss2 = pow(frag.roughness + 0.001, 4.0); + + SurfaceData surface = initSurfaceData(frag.roughness, frag.normal, fragEyeDir); bool withScattering = (frag.scattering * isScatteringEnabled() > 0.0); int numLightTouching = 0; @@ -119,16 +118,18 @@ void main(void) { float fragLightDistance = fragLightDirLen.w; vec3 fragLightDir = fragLightDirLen.xyz; + updateSurfaceDataWithLight(surface, fragLightDir); + // Eval attenuation float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance); vec3 lightEnergy = radialAttenuation * getLightIrradiance(light); // Eval shading if (withScattering) { - evalFragShadingScattering(diffuse, specular, frag.normal, fragLightDir, fragEyeDir, frag.metallic, frag.fresnel, frag.roughness, frag.albedo + evalFragShadingScattering(diffuse, specular, frag.metallic, frag.fresnel, surface, frag.albedo ,frag.scattering, midNormalCurvature, lowNormalCurvature ); } else { - evalFragShadingGloss(diffuse, specular, frag.normal, fragLightDir, fragEyeDir, frag.metallic, frag.fresnel, fragGloss2, frag.albedo); + evalFragShadingGloss(diffuse, specular, frag.metallic, frag.fresnel, surface, frag.albedo); } diffuse *= lightEnergy * isDiffuseEnabled(); @@ -173,6 +174,8 @@ void main(void) { float fragLightDistance = fragLightDirLen.w; vec3 fragLightDir = fragLightDirLen.xyz; + updateSurfaceDataWithLight(surface, fragLightDir); + // Eval attenuation float radialAttenuation = lightIrradiance_evalLightAttenuation(light.irradiance, fragLightDistance); float angularAttenuation = lightIrradiance_evalLightSpotAttenuation(light.irradiance, cosSpotAngle); @@ -180,10 +183,10 @@ void main(void) { // Eval shading if (withScattering) { - evalFragShadingScattering(diffuse, specular, frag.normal, fragLightDir, fragEyeDir, frag.metallic, frag.fresnel, frag.roughness, frag.albedo + evalFragShadingScattering(diffuse, specular, frag.metallic, frag.fresnel, surface, frag.albedo ,frag.scattering, midNormalCurvature, lowNormalCurvature ); } else { - evalFragShadingGloss(diffuse, specular, frag.normal, fragLightDir, fragEyeDir, frag.metallic, frag.fresnel, fragGloss2, frag.albedo); + evalFragShadingGloss(diffuse, specular, frag.metallic, frag.fresnel, surface, frag.albedo); } diffuse *= lightEnergy * isDiffuseEnabled(); diff --git a/libraries/render-utils/src/model.slf b/libraries/render-utils/src/model.slf index daeead65ec..4747b69278 100644 --- a/libraries/render-utils/src/model.slf +++ b/libraries/render-utils/src/model.slf @@ -13,7 +13,7 @@ <@include DeferredBufferWrite.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> diff --git a/libraries/render-utils/src/model_fade.slf b/libraries/render-utils/src/model_fade.slf index d232667660..40c156a3a4 100644 --- a/libraries/render-utils/src/model_fade.slf +++ b/libraries/render-utils/src/model_fade.slf @@ -13,7 +13,7 @@ <@include DeferredBufferWrite.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> diff --git a/libraries/render-utils/src/model_lightmap.slf b/libraries/render-utils/src/model_lightmap.slf index 3a8cfde290..b22a1029f9 100644 --- a/libraries/render-utils/src/model_lightmap.slf +++ b/libraries/render-utils/src/model_lightmap.slf @@ -14,7 +14,7 @@ <@include DeferredBufferWrite.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS)$> diff --git a/libraries/render-utils/src/model_lightmap_fade.slf b/libraries/render-utils/src/model_lightmap_fade.slf index 92d00a2046..55cb24d35d 100644 --- a/libraries/render-utils/src/model_lightmap_fade.slf +++ b/libraries/render-utils/src/model_lightmap_fade.slf @@ -14,7 +14,7 @@ <@include DeferredBufferWrite.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS)$> diff --git a/libraries/render-utils/src/model_lightmap_normal_map.slf b/libraries/render-utils/src/model_lightmap_normal_map.slf index 81de1e5d5b..1510ea9ba3 100644 --- a/libraries/render-utils/src/model_lightmap_normal_map.slf +++ b/libraries/render-utils/src/model_lightmap_normal_map.slf @@ -14,7 +14,7 @@ <@include DeferredBufferWrite.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL)$> diff --git a/libraries/render-utils/src/model_lightmap_normal_map_fade.slf b/libraries/render-utils/src/model_lightmap_normal_map_fade.slf index 825e84d666..6b63943a9a 100644 --- a/libraries/render-utils/src/model_lightmap_normal_map_fade.slf +++ b/libraries/render-utils/src/model_lightmap_normal_map_fade.slf @@ -14,7 +14,7 @@ <@include DeferredBufferWrite.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL)$> diff --git a/libraries/render-utils/src/model_lightmap_normal_specular_map.slf b/libraries/render-utils/src/model_lightmap_normal_specular_map.slf index 944da27b01..3a66aaad3d 100644 --- a/libraries/render-utils/src/model_lightmap_normal_specular_map.slf +++ b/libraries/render-utils/src/model_lightmap_normal_specular_map.slf @@ -14,7 +14,7 @@ <@include DeferredBufferWrite.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, METALLIC)$> diff --git a/libraries/render-utils/src/model_lightmap_normal_specular_map_fade.slf b/libraries/render-utils/src/model_lightmap_normal_specular_map_fade.slf index 791d5bf552..b785a8a6ab 100644 --- a/libraries/render-utils/src/model_lightmap_normal_specular_map_fade.slf +++ b/libraries/render-utils/src/model_lightmap_normal_specular_map_fade.slf @@ -14,7 +14,7 @@ <@include DeferredBufferWrite.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, METALLIC)$> diff --git a/libraries/render-utils/src/model_lightmap_specular_map.slf b/libraries/render-utils/src/model_lightmap_specular_map.slf index 4dbc10a834..1c97d435bd 100644 --- a/libraries/render-utils/src/model_lightmap_specular_map.slf +++ b/libraries/render-utils/src/model_lightmap_specular_map.slf @@ -14,7 +14,7 @@ <@include DeferredBufferWrite.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC)$> diff --git a/libraries/render-utils/src/model_lightmap_specular_map_fade.slf b/libraries/render-utils/src/model_lightmap_specular_map_fade.slf index e82018eefb..1adedb9328 100644 --- a/libraries/render-utils/src/model_lightmap_specular_map_fade.slf +++ b/libraries/render-utils/src/model_lightmap_specular_map_fade.slf @@ -14,7 +14,7 @@ <@include DeferredBufferWrite.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC)$> diff --git a/libraries/render-utils/src/model_normal_map.slf b/libraries/render-utils/src/model_normal_map.slf index 063950609a..bed85b4b15 100644 --- a/libraries/render-utils/src/model_normal_map.slf +++ b/libraries/render-utils/src/model_normal_map.slf @@ -14,7 +14,7 @@ <@include DeferredBufferWrite.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, _SCRIBE_NULL, EMISSIVE, OCCLUSION, SCATTERING)$> diff --git a/libraries/render-utils/src/model_normal_map_fade.slf b/libraries/render-utils/src/model_normal_map_fade.slf index d8b864260c..5a166b1c2c 100644 --- a/libraries/render-utils/src/model_normal_map_fade.slf +++ b/libraries/render-utils/src/model_normal_map_fade.slf @@ -14,7 +14,7 @@ <@include DeferredBufferWrite.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, _SCRIBE_NULL, EMISSIVE, OCCLUSION, SCATTERING)$> diff --git a/libraries/render-utils/src/model_normal_specular_map.slf b/libraries/render-utils/src/model_normal_specular_map.slf index 9e079b33a0..3eb3d43fdc 100644 --- a/libraries/render-utils/src/model_normal_specular_map.slf +++ b/libraries/render-utils/src/model_normal_specular_map.slf @@ -14,7 +14,7 @@ <@include DeferredBufferWrite.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, METALLIC, EMISSIVE, OCCLUSION)$> diff --git a/libraries/render-utils/src/model_normal_specular_map_fade.slf b/libraries/render-utils/src/model_normal_specular_map_fade.slf index 5492b24763..0985d5d493 100644 --- a/libraries/render-utils/src/model_normal_specular_map_fade.slf +++ b/libraries/render-utils/src/model_normal_specular_map_fade.slf @@ -14,7 +14,7 @@ <@include DeferredBufferWrite.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, METALLIC, EMISSIVE, OCCLUSION)$> diff --git a/libraries/render-utils/src/model_specular_map.slf b/libraries/render-utils/src/model_specular_map.slf index 47b5e3389d..e65dbaeda6 100644 --- a/libraries/render-utils/src/model_specular_map.slf +++ b/libraries/render-utils/src/model_specular_map.slf @@ -14,7 +14,7 @@ <@include DeferredBufferWrite.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC, EMISSIVE, OCCLUSION)$> diff --git a/libraries/render-utils/src/model_specular_map_fade.slf b/libraries/render-utils/src/model_specular_map_fade.slf index 6eb56c0929..17173d8bc3 100644 --- a/libraries/render-utils/src/model_specular_map_fade.slf +++ b/libraries/render-utils/src/model_specular_map_fade.slf @@ -14,7 +14,7 @@ <@include DeferredBufferWrite.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, METALLIC, EMISSIVE, OCCLUSION)$> diff --git a/libraries/render-utils/src/model_translucent.slf b/libraries/render-utils/src/model_translucent.slf index 38f162fdc3..0924389dba 100644 --- a/libraries/render-utils/src/model_translucent.slf +++ b/libraries/render-utils/src/model_translucent.slf @@ -12,7 +12,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include DeferredGlobalLight.slh@> @@ -50,13 +50,7 @@ void main(void) { <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; float metallic = getMaterialMetallic(mat); - vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value - if (metallic <= 0.5) { - metallic = 0.0; - } else { - fresnel = albedo; - metallic = 1.0; - } + vec3 fresnel = getFresnelF0(metallic, albedo); vec3 emissive = getMaterialEmissive(mat); <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; diff --git a/libraries/render-utils/src/model_translucent_fade.slf b/libraries/render-utils/src/model_translucent_fade.slf index 9d5477304c..8ac442476f 100644 --- a/libraries/render-utils/src/model_translucent_fade.slf +++ b/libraries/render-utils/src/model_translucent_fade.slf @@ -12,7 +12,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include DeferredGlobalLight.slh@> @@ -60,13 +60,7 @@ void main(void) { <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; float metallic = getMaterialMetallic(mat); - vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value - if (metallic <= 0.5) { - metallic = 0.0; - } else { - fresnel = albedo; - metallic = 1.0; - } + vec3 fresnel = getFresnelF0(metallic, albedo); vec3 emissive = getMaterialEmissive(mat); <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; diff --git a/libraries/render-utils/src/model_translucent_unlit.slf b/libraries/render-utils/src/model_translucent_unlit.slf index b397cea5aa..4c3a0f0195 100644 --- a/libraries/render-utils/src/model_translucent_unlit.slf +++ b/libraries/render-utils/src/model_translucent_unlit.slf @@ -12,7 +12,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> diff --git a/libraries/render-utils/src/model_translucent_unlit_fade.slf b/libraries/render-utils/src/model_translucent_unlit_fade.slf index 6a77efe4ca..210060d54d 100644 --- a/libraries/render-utils/src/model_translucent_unlit_fade.slf +++ b/libraries/render-utils/src/model_translucent_unlit_fade.slf @@ -12,7 +12,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> diff --git a/libraries/render-utils/src/model_unlit.slf b/libraries/render-utils/src/model_unlit.slf index 750b51fe8c..ccfc7d4935 100644 --- a/libraries/render-utils/src/model_unlit.slf +++ b/libraries/render-utils/src/model_unlit.slf @@ -14,7 +14,7 @@ <@include DeferredBufferWrite.slh@> <@include LightingModel.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO)$> diff --git a/libraries/render-utils/src/model_unlit_fade.slf b/libraries/render-utils/src/model_unlit_fade.slf index 0fe9f2ebac..65c97f9e21 100644 --- a/libraries/render-utils/src/model_unlit_fade.slf +++ b/libraries/render-utils/src/model_unlit_fade.slf @@ -14,7 +14,7 @@ <@include DeferredBufferWrite.slh@> <@include LightingModel.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include Fade.slh@> <$declareFadeFragment()$> diff --git a/libraries/render-utils/src/overlay3D.slf b/libraries/render-utils/src/overlay3D.slf index 0cb3340845..dcded917b4 100644 --- a/libraries/render-utils/src/overlay3D.slf +++ b/libraries/render-utils/src/overlay3D.slf @@ -12,7 +12,7 @@ // -<@include model/Light.slh@> +<@include graphics/Light.slh@> <$declareLightBuffer()$> <$declareLightAmbientBuffer()$> @@ -40,12 +40,14 @@ vec4 evalGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 a vec3 fragEyeDir; <$transformEyeToWorldDir(cam, fragEyeVectorView, fragEyeDir)$> + SurfaceData surface = initSurfaceData(roughness, normal, fragEyeDir); + vec3 color = opacity * albedo * getLightColor(light) * getLightAmbientIntensity(ambient); // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation); + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation); color += directionalDiffuse * isDiffuseEnabled() * isDirectionalEnabled(); color += directionalSpecular * isSpecularEnabled() * isDirectionalEnabled(); diff --git a/libraries/render-utils/src/overlay3D_model.slf b/libraries/render-utils/src/overlay3D_model.slf index bb0d84a513..d4de0ee69f 100644 --- a/libraries/render-utils/src/overlay3D_model.slf +++ b/libraries/render-utils/src/overlay3D_model.slf @@ -14,7 +14,7 @@ <@include DeferredGlobalLight.slh@> <$declareEvalSkyboxGlobalColor()$> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> @@ -46,13 +46,7 @@ void main(void) { albedo *= _color; float metallic = getMaterialMetallic(mat); - vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value - if (metallic <= 0.5) { - metallic = 0.0; - } else { - fresnel = albedo; - metallic = 1.0; - } + vec3 fresnel = getFresnelF0(metallic, albedo); float roughness = getMaterialRoughness(mat); <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; diff --git a/libraries/render-utils/src/overlay3D_model_translucent.slf b/libraries/render-utils/src/overlay3D_model_translucent.slf index b26e70f465..8dd3a81443 100644 --- a/libraries/render-utils/src/overlay3D_model_translucent.slf +++ b/libraries/render-utils/src/overlay3D_model_translucent.slf @@ -13,7 +13,7 @@ <@include DeferredGlobalLight.slh@> <$declareEvalGlobalLightingAlphaBlended()$> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include gpu/Transform.slh@> <$declareStandardCameraTransform()$> @@ -44,13 +44,7 @@ void main(void) { albedo *= _color; float metallic = getMaterialMetallic(mat); - vec3 fresnel = vec3(0.03); // Default Di-electric fresnel value - if (metallic <= 0.5) { - metallic = 0.0; - } else { - fresnel = albedo; - metallic = 1.0; - } + vec3 fresnel = getFresnelF0(metallic, albedo); float roughness = getMaterialRoughness(mat); <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; diff --git a/libraries/render-utils/src/overlay3D_model_translucent_unlit.slf b/libraries/render-utils/src/overlay3D_model_translucent_unlit.slf index 3dd8138272..3b79818cd9 100644 --- a/libraries/render-utils/src/overlay3D_model_translucent_unlit.slf +++ b/libraries/render-utils/src/overlay3D_model_translucent_unlit.slf @@ -12,7 +12,7 @@ // <@include LightingModel.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO)$> diff --git a/libraries/render-utils/src/overlay3D_model_unlit.slf b/libraries/render-utils/src/overlay3D_model_unlit.slf index 80c2bb971e..eab975e810 100644 --- a/libraries/render-utils/src/overlay3D_model_unlit.slf +++ b/libraries/render-utils/src/overlay3D_model_unlit.slf @@ -12,7 +12,7 @@ // <@include LightingModel.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include MaterialTextures.slh@> <$declareMaterialTextures(ALBEDO)$> diff --git a/libraries/render-utils/src/overlay3D_translucent.slf b/libraries/render-utils/src/overlay3D_translucent.slf index 9bdac2d21f..64035ac984 100644 --- a/libraries/render-utils/src/overlay3D_translucent.slf +++ b/libraries/render-utils/src/overlay3D_translucent.slf @@ -12,7 +12,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -<@include model/Light.slh@> +<@include graphics/Light.slh@> <$declareLightBuffer()$> <$declareLightAmbientBuffer()$> @@ -40,12 +40,14 @@ vec4 evalGlobalColor(float shadowAttenuation, vec3 position, vec3 normal, vec3 a vec3 fragEyeDir; <$transformEyeToWorldDir(cam, fragEyeVectorView, fragEyeDir)$> + SurfaceData surface = initSurfaceData(roughness, normal, fragEyeDir); + vec3 color = opacity * albedo * getLightColor(light) * getLightAmbientIntensity(ambient); // Directional vec3 directionalDiffuse; vec3 directionalSpecular; - evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, fragEyeDir, fragNormal, roughness, metallic, fresnel, albedo, shadowAttenuation); + evalLightingDirectional(directionalDiffuse, directionalSpecular, lightDirection, lightIrradiance, surface, metallic, fresnel, albedo, shadowAttenuation); color += directionalDiffuse; color += directionalSpecular / opacity; diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf index fd45c6c134..7b32541c63 100644 --- a/libraries/render-utils/src/simple.slf +++ b/libraries/render-utils/src/simple.slf @@ -13,7 +13,7 @@ // <@include DeferredBufferWrite.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> // the interpolated normal in vec3 _normal; diff --git a/libraries/render-utils/src/simple_fade.slf b/libraries/render-utils/src/simple_fade.slf index 245d32e81e..ce9251b9a5 100644 --- a/libraries/render-utils/src/simple_fade.slf +++ b/libraries/render-utils/src/simple_fade.slf @@ -13,7 +13,7 @@ // <@include DeferredBufferWrite.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include Fade.slh@> <$declareFadeFragmentInstanced()$> diff --git a/libraries/render-utils/src/simple_textured.slf b/libraries/render-utils/src/simple_textured.slf index 385270c72c..4fd734aad5 100644 --- a/libraries/render-utils/src/simple_textured.slf +++ b/libraries/render-utils/src/simple_textured.slf @@ -14,7 +14,7 @@ <@include gpu/Color.slh@> <@include DeferredBufferWrite.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> // the albedo texture uniform sampler2D originalTexture; diff --git a/libraries/render-utils/src/simple_textured_fade.slf b/libraries/render-utils/src/simple_textured_fade.slf index 75dac714fe..d378e7a5c1 100644 --- a/libraries/render-utils/src/simple_textured_fade.slf +++ b/libraries/render-utils/src/simple_textured_fade.slf @@ -14,7 +14,7 @@ <@include gpu/Color.slh@> <@include DeferredBufferWrite.slh@> -<@include model/Material.slh@> +<@include graphics/Material.slh@> <@include Fade.slh@> diff --git a/libraries/render-utils/src/subsurfaceScattering_drawScattering.slf b/libraries/render-utils/src/subsurfaceScattering_drawScattering.slf index 981993615c..ad6918d727 100644 --- a/libraries/render-utils/src/subsurfaceScattering_drawScattering.slf +++ b/libraries/render-utils/src/subsurfaceScattering_drawScattering.slf @@ -12,7 +12,7 @@ <@include DeferredBufferRead.slh@> -<@include model/Light.slh@> +<@include graphics/Light.slh@> <$declareLightBuffer()$> <$declareDeferredCurvature()$> diff --git a/libraries/render-utils/src/taa.slh b/libraries/render-utils/src/taa.slh index 0ca2e13716..232d1e9f70 100644 --- a/libraries/render-utils/src/taa.slh +++ b/libraries/render-utils/src/taa.slh @@ -287,11 +287,7 @@ mat3 taa_evalNeighbourColorVariance(vec3 sourceColor, vec2 fragUV, vec2 fragVelo vec3 mu = sumSamples / vec3(9.0); -<<<<<<< HEAD - vec3 sigma = sqrt(max(sumSamples2 / vec3(9.0) - mu * mu, vec3(0))); -======= vec3 sigma = sqrt(max(sumSamples2 / vec3(9.0) - mu * mu, vec3(0.0))); ->>>>>>> 9bc02ec564666ba55ef2a79866fa969e6c58283d float gamma = params.covarianceGamma; vec3 cmin = mu - gamma * sigma; diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp index 8449c58c7c..b6868a2cd4 100644 --- a/libraries/render-utils/src/text/Font.cpp +++ b/libraries/render-utils/src/text/Font.cpp @@ -369,11 +369,15 @@ void Font::drawString(gpu::Batch& batch, float x, float y, const QString& str, c batch.setPipeline(((*color).a < 1.0f || layered) ? _transparentPipeline : _pipeline); batch.setResourceTexture(_fontLoc, _texture); - batch._glUniform1i(_outlineLoc, (effectType == OUTLINE_EFFECT)); + if (_outlineLoc >= 0) { + batch._glUniform1i(_outlineLoc, (effectType == OUTLINE_EFFECT)); + } // need the gamma corrected color here glm::vec4 lrgba = ColorUtils::sRGBToLinearVec4(*color); - batch._glUniform4fv(_colorLoc, 1, (const float*)&lrgba); + if (_colorLoc >= 0) { + batch._glUniform4fv(_colorLoc, 1, (const float*)&lrgba); + } batch.setInputFormat(_format); batch.setInputBuffer(0, _verticesBuffer, 0, _format->getChannels().at(0)._stride); diff --git a/libraries/render-utils/src/zone_drawAmbient.slf b/libraries/render-utils/src/zone_drawAmbient.slf index f104e5be44..3407fe8467 100644 --- a/libraries/render-utils/src/zone_drawAmbient.slf +++ b/libraries/render-utils/src/zone_drawAmbient.slf @@ -10,7 +10,7 @@ // <@include zone_draw.slh@> -<@include model/Light.slh@> +<@include graphics/Light.slh@> <@include LightingModel.slh@> <$declareLightAmbientBuffer()$> diff --git a/libraries/render-utils/src/zone_drawKeyLight.slf b/libraries/render-utils/src/zone_drawKeyLight.slf index e96239b6dc..ea11c775b4 100644 --- a/libraries/render-utils/src/zone_drawKeyLight.slf +++ b/libraries/render-utils/src/zone_drawKeyLight.slf @@ -10,7 +10,7 @@ // <@include zone_draw.slh@> -<@include model/Light.slh@> +<@include graphics/Light.slh@> <@include LightingModel.slh@> <$declareLightBuffer()$> diff --git a/libraries/render/CMakeLists.txt b/libraries/render/CMakeLists.txt index 8fd05bd320..1d88c3e5f5 100644 --- a/libraries/render/CMakeLists.txt +++ b/libraries/render/CMakeLists.txt @@ -1,8 +1,8 @@ set(TARGET_NAME render) -AUTOSCRIBE_SHADER_LIB(gpu model) +AUTOSCRIBE_SHADER_LIB(gpu graphics) setup_hifi_library() # render needs octree only for getAccuracyAngle(float, int) -link_hifi_libraries(shared ktx gpu model octree) +link_hifi_libraries(shared ktx gpu graphics octree) target_nsight() diff --git a/libraries/render/src/render/Item.h b/libraries/render/src/render/Item.h index 77f5910b9e..e977c95fa0 100644 --- a/libraries/render/src/render/Item.h +++ b/libraries/render/src/render/Item.h @@ -25,7 +25,7 @@ #include "Args.h" -#include +#include #include "ShapePipeline.h" namespace render { diff --git a/libraries/render/src/render/ShapePipeline.cpp b/libraries/render/src/render/ShapePipeline.cpp index 4254280fa1..eb279f18af 100644 --- a/libraries/render/src/render/ShapePipeline.cpp +++ b/libraries/render/src/render/ShapePipeline.cpp @@ -87,7 +87,7 @@ void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& p slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), Slot::MAP::LIGHT_AMBIENT)); slotBindings.insert(gpu::Shader::Binding(std::string("fadeMaskMap"), Slot::MAP::FADE_MASK)); slotBindings.insert(gpu::Shader::Binding(std::string("fadeParametersBuffer"), Slot::BUFFER::FADE_PARAMETERS)); - slotBindings.insert(gpu::Shader::Binding(std::string("hazeParametersBuffer"), Slot::BUFFER::HAZE_MODEL)); + slotBindings.insert(gpu::Shader::Binding(std::string("hazeBuffer"), Slot::BUFFER::HAZE_MODEL)); gpu::Shader::makeProgram(*program, slotBindings); @@ -131,7 +131,7 @@ const ShapePipelinePointer ShapePlumber::pickPipeline(RenderArgs* args, const Ke auto factoryIt = ShapePipeline::_globalCustomFactoryMap.find(key.getCustom()); if ((factoryIt != ShapePipeline::_globalCustomFactoryMap.end()) && (factoryIt)->second) { // found a factory for the custom key, can now generate a shape pipeline for this case: - addPipelineHelper(Filter(key), key, 0, (factoryIt)->second(*this, key)); + addPipelineHelper(Filter(key), key, 0, (factoryIt)->second(*this, key, *(args->_batch))); return pickPipeline(args, key); } else { diff --git a/libraries/render/src/render/ShapePipeline.h b/libraries/render/src/render/ShapePipeline.h index be77e2f95e..9e05cfb81b 100644 --- a/libraries/render/src/render/ShapePipeline.h +++ b/libraries/render/src/render/ShapePipeline.h @@ -301,7 +301,7 @@ protected: ItemSetter _itemSetter; public: using CustomKey = uint8_t; - using CustomFactory = std::function (const ShapePlumber& plumber, const ShapeKey& key)>; + using CustomFactory = std::function (const ShapePlumber& plumber, const ShapeKey& key, gpu::Batch& batch)>; using CustomFactoryMap = std::map; static CustomFactoryMap _globalCustomFactoryMap; diff --git a/libraries/script-engine/CMakeLists.txt b/libraries/script-engine/CMakeLists.txt index 87296b187f..478e4c4765 100644 --- a/libraries/script-engine/CMakeLists.txt +++ b/libraries/script-engine/CMakeLists.txt @@ -16,6 +16,6 @@ if (NOT ANDROID) endif () -link_hifi_libraries(shared networking octree gpu procedural model model-networking ktx recording avatars fbx entities controllers animation audio physics image midi) +link_hifi_libraries(shared networking octree gpu procedural graphics model-networking ktx recording avatars fbx entities controllers animation audio physics image midi) # ui includes gl, but link_hifi_libraries does not use transitive includes, so gl must be explicit include_hifi_library_headers(gl) diff --git a/libraries/script-engine/src/ArrayBufferClass.h b/libraries/script-engine/src/ArrayBufferClass.h index 69c2cc0799..166a21b773 100644 --- a/libraries/script-engine/src/ArrayBufferClass.h +++ b/libraries/script-engine/src/ArrayBufferClass.h @@ -19,6 +19,7 @@ #include #include #include +#include class ScriptEngine; diff --git a/libraries/script-engine/src/AssetScriptingInterface.cpp b/libraries/script-engine/src/AssetScriptingInterface.cpp index 25e8c0dcf3..e0e04a1e25 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.cpp +++ b/libraries/script-engine/src/AssetScriptingInterface.cpp @@ -16,9 +16,11 @@ #include #include #include -#include #include +#include "ScriptEngineLogging.h" + + AssetScriptingInterface::AssetScriptingInterface(QScriptEngine* engine) : _engine(engine) { @@ -53,10 +55,32 @@ void AssetScriptingInterface::setMapping(QString path, QString hash, QScriptValu setMappingRequest->start(); } +void AssetScriptingInterface::getMapping(QString path, QScriptValue callback) { + auto request = DependencyManager::get()->createGetMappingRequest(path); + QObject::connect(request, &GetMappingRequest::finished, this, [=](GetMappingRequest* request) mutable { + auto result = request->getError(); + if (callback.isFunction()) { + if (result == GetMappingRequest::NotFound) { + QScriptValueList args { "", true }; + callback.call(_engine->currentContext()->thisObject(), args); + } else if (result == GetMappingRequest::NoError) { + QScriptValueList args { request->getHash(), true }; + callback.call(_engine->currentContext()->thisObject(), args); + } else { + qCDebug(scriptengine) << "error -- " << request->getError() << " -- " << request->getErrorString(); + QScriptValueList args { "", false }; + callback.call(_engine->currentContext()->thisObject(), args); + } + request->deleteLater(); + } + }); + request->start(); +} void AssetScriptingInterface::downloadData(QString urlString, QScriptValue callback) { if (!urlString.startsWith(ATP_SCHEME)) { + qCDebug(scriptengine) << "AssetScriptingInterface::downloadData url must be of form atp:"; return; } diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h index 2812be65f9..dded2ef21d 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.h +++ b/libraries/script-engine/src/AssetScriptingInterface.h @@ -75,7 +75,24 @@ public: * @param {string} error */ Q_INVOKABLE void setMapping(QString path, QString hash, QScriptValue callback); - + + /**jsdoc + * Look up a path to hash mapping within the connected domain's asset server + * @function Assets.getMapping + * @static + * @param path {string} + * @param callback {Assets~getMappingCallback} + */ + + /**jsdoc + * Called when getMapping is complete. + * @callback Assets~getMappingCallback + * @param assetID {string} hash value if found, else an empty string + * @param success {boolean} false for errors other than "not found", else true + */ + Q_INVOKABLE void getMapping(QString path, QScriptValue callback); + + Q_INVOKABLE void setBakingEnabled(QString path, bool enabled, QScriptValue callback); #if (PR_BUILD || DEV_BUILD) diff --git a/libraries/script-engine/src/ConsoleScriptingInterface.cpp b/libraries/script-engine/src/ConsoleScriptingInterface.cpp index f3ceee63f7..b4ef98938d 100644 --- a/libraries/script-engine/src/ConsoleScriptingInterface.cpp +++ b/libraries/script-engine/src/ConsoleScriptingInterface.cpp @@ -15,6 +15,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include "ConsoleScriptingInterface.h" #include "ScriptEngine.h" diff --git a/libraries/script-engine/src/ModelScriptingInterface.cpp b/libraries/script-engine/src/ModelScriptingInterface.cpp index 3234a079ec..c693083ebf 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.cpp +++ b/libraries/script-engine/src/ModelScriptingInterface.cpp @@ -60,20 +60,20 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { // alloc the resulting mesh gpu::Resource::Size combinedVertexSize = totalVertexCount * sizeof(glm::vec3); - unsigned char* combinedVertexData = new unsigned char[combinedVertexSize]; - unsigned char* combinedVertexDataCursor = combinedVertexData; + std::unique_ptr combinedVertexData{ new unsigned char[combinedVertexSize] }; + unsigned char* combinedVertexDataCursor = combinedVertexData.get(); gpu::Resource::Size combinedColorSize = totalColorCount * sizeof(glm::vec3); - unsigned char* combinedColorData = new unsigned char[combinedColorSize]; - unsigned char* combinedColorDataCursor = combinedColorData; + std::unique_ptr combinedColorData{ new unsigned char[combinedColorSize] }; + unsigned char* combinedColorDataCursor = combinedColorData.get(); gpu::Resource::Size combinedNormalSize = totalNormalCount * sizeof(glm::vec3); - unsigned char* combinedNormalData = new unsigned char[combinedNormalSize]; - unsigned char* combinedNormalDataCursor = combinedNormalData; + std::unique_ptr combinedNormalData{ new unsigned char[combinedNormalSize] }; + unsigned char* combinedNormalDataCursor = combinedNormalData.get(); gpu::Resource::Size combinedIndexSize = totalIndexCount * sizeof(uint32_t); - unsigned char* combinedIndexData = new unsigned char[combinedIndexSize]; - unsigned char* combinedIndexDataCursor = combinedIndexData; + std::unique_ptr combinedIndexData{ new unsigned char[combinedIndexSize] }; + unsigned char* combinedIndexDataCursor = combinedIndexData.get(); uint32_t indexStartOffset { 0 }; @@ -102,40 +102,40 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { indexStartOffset += numVertices; } - model::MeshPointer result(new model::Mesh()); + graphics::MeshPointer result(new graphics::Mesh()); gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); - gpu::Buffer* combinedVertexBuffer = new gpu::Buffer(combinedVertexSize, combinedVertexData); + gpu::Buffer* combinedVertexBuffer = new gpu::Buffer(combinedVertexSize, combinedVertexData.get()); gpu::BufferPointer combinedVertexBufferPointer(combinedVertexBuffer); gpu::BufferView combinedVertexBufferView(combinedVertexBufferPointer, vertexElement); result->setVertexBuffer(combinedVertexBufferView); int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h gpu::Element colorElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); - gpu::Buffer* combinedColorsBuffer = new gpu::Buffer(combinedColorSize, combinedColorData); + gpu::Buffer* combinedColorsBuffer = new gpu::Buffer(combinedColorSize, combinedColorData.get()); gpu::BufferPointer combinedColorsBufferPointer(combinedColorsBuffer); gpu::BufferView combinedColorsBufferView(combinedColorsBufferPointer, colorElement); result->addAttribute(attributeTypeColor, combinedColorsBufferView); int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); - gpu::Buffer* combinedNormalsBuffer = new gpu::Buffer(combinedNormalSize, combinedNormalData); + gpu::Buffer* combinedNormalsBuffer = new gpu::Buffer(combinedNormalSize, combinedNormalData.get()); gpu::BufferPointer combinedNormalsBufferPointer(combinedNormalsBuffer); gpu::BufferView combinedNormalsBufferView(combinedNormalsBufferPointer, normalElement); result->addAttribute(attributeTypeNormal, combinedNormalsBufferView); gpu::Element indexElement = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW); - gpu::Buffer* combinedIndexesBuffer = new gpu::Buffer(combinedIndexSize, combinedIndexData); + gpu::Buffer* combinedIndexesBuffer = new gpu::Buffer(combinedIndexSize, combinedIndexData.get()); gpu::BufferPointer combinedIndexesBufferPointer(combinedIndexesBuffer); gpu::BufferView combinedIndexesBufferView(combinedIndexesBufferPointer, indexElement); result->setIndexBuffer(combinedIndexesBufferView); - std::vector parts; - parts.emplace_back(model::Mesh::Part((model::Index)0, // startIndex - (model::Index)result->getNumIndices(), // numIndices - (model::Index)0, // baseVertex - model::Mesh::TRIANGLES)); // topology - result->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), + std::vector parts; + parts.emplace_back(graphics::Mesh::Part((graphics::Index)0, // startIndex + (graphics::Index)result->getNumIndices(), // numIndices + (graphics::Index)0, // baseVertex + graphics::Mesh::TRIANGLES)); // topology + result->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(graphics::Mesh::Part), (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); @@ -152,9 +152,10 @@ QScriptValue ModelScriptingInterface::transformMesh(glm::mat4 transform, MeshPro return QScriptValue(false); } - model::MeshPointer result = mesh->map([&](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); }, + const auto inverseTransposeTransform = glm::inverse(glm::transpose(transform)); + graphics::MeshPointer result = mesh->map([&](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); }, [&](glm::vec3 color){ return color; }, - [&](glm::vec3 normal){ return glm::vec3(transform * glm::vec4(normal, 0.0f)); }, + [&](glm::vec3 normal){ return glm::vec3(inverseTransposeTransform * glm::vec4(normal, 0.0f)); }, [&](uint32_t index){ return index; }); MeshProxy* resultProxy = new SimpleMeshProxy(result); return meshToScriptValue(_modelScriptEngine, resultProxy); @@ -198,7 +199,7 @@ QScriptValue ModelScriptingInterface::getVertex(MeshProxy* meshProxy, int vertex QScriptValue ModelScriptingInterface::newMesh(const QVector& vertices, const QVector& normals, const QVector& faces) { - model::MeshPointer mesh(new model::Mesh()); + graphics::MeshPointer mesh(new graphics::Mesh()); // vertices auto vertexBuffer = std::make_shared(vertices.size() * sizeof(glm::vec3), (gpu::Byte*)vertices.data()); @@ -235,12 +236,12 @@ QScriptValue ModelScriptingInterface::newMesh(const QVector& vertices mesh->setIndexBuffer(indexBufferView); // parts - std::vector parts; - parts.emplace_back(model::Mesh::Part((model::Index)0, // startIndex - (model::Index)faces.size() * 3, // numIndices - (model::Index)0, // baseVertex - model::Mesh::TRIANGLES)); // topology - mesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part), + std::vector parts; + parts.emplace_back(graphics::Mesh::Part((graphics::Index)0, // startIndex + (graphics::Index)faces.size() * 3, // numIndices + (graphics::Index)0, // baseVertex + graphics::Mesh::TRIANGLES)); // topology + mesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(graphics::Mesh::Part), (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); diff --git a/libraries/script-engine/src/SceneScriptingInterface.cpp b/libraries/script-engine/src/SceneScriptingInterface.cpp index 3883b948df..7b36b815fb 100644 --- a/libraries/script-engine/src/SceneScriptingInterface.cpp +++ b/libraries/script-engine/src/SceneScriptingInterface.cpp @@ -112,17 +112,17 @@ bool SceneScripting::Stage::isSunModelEnabled() const { void SceneScripting::Stage::setBackgroundMode(const QString& mode) { if (mode == QString("inherit")) { - _skyStage->setBackgroundMode(model::SunSkyStage::NO_BACKGROUND); + _skyStage->setBackgroundMode(graphics::SunSkyStage::NO_BACKGROUND); } else if (mode == QString("skybox")) { - _skyStage->setBackgroundMode(model::SunSkyStage::SKY_BOX); + _skyStage->setBackgroundMode(graphics::SunSkyStage::SKY_BOX); } } QString SceneScripting::Stage::getBackgroundMode() const { switch (_skyStage->getBackgroundMode()) { - case model::SunSkyStage::NO_BACKGROUND: + case graphics::SunSkyStage::NO_BACKGROUND: return QString("inherit"); - case model::SunSkyStage::SKY_BOX: + case graphics::SunSkyStage::SKY_BOX: return QString("skybox"); default: return QString("inherit"); @@ -131,7 +131,7 @@ QString SceneScripting::Stage::getBackgroundMode() const { SceneScriptingInterface::SceneScriptingInterface() : _stage{ new SceneScripting::Stage{ _skyStage } } { // Let's make sure the sunSkyStage is using a proceduralSkybox - _skyStage->setSkybox(model::SkyboxPointer(new ProceduralSkybox())); + _skyStage->setSkybox(graphics::SkyboxPointer(new ProceduralSkybox())); } void SceneScriptingInterface::setShouldRenderAvatars(bool shouldRenderAvatars) { @@ -148,6 +148,6 @@ void SceneScriptingInterface::setShouldRenderEntities(bool shouldRenderEntities) } } -model::SunSkyStagePointer SceneScriptingInterface::getSkyStage() const { +graphics::SunSkyStagePointer SceneScriptingInterface::getSkyStage() const { return _skyStage; } diff --git a/libraries/script-engine/src/SceneScriptingInterface.h b/libraries/script-engine/src/SceneScriptingInterface.h index 7bc22eb3e6..07b8c22ca6 100644 --- a/libraries/script-engine/src/SceneScriptingInterface.h +++ b/libraries/script-engine/src/SceneScriptingInterface.h @@ -15,7 +15,7 @@ #include // QObject #include // Dependency -#include "model/Stage.h" +#include "graphics/Stage.h" // TODO: if QT moc ever supports nested classes, subclass these to the interface instead of namespacing namespace SceneScripting { @@ -23,7 +23,7 @@ namespace SceneScripting { Q_OBJECT public: - Location(model::SunSkyStagePointer skyStage) : _skyStage{ skyStage } {} + Location(graphics::SunSkyStagePointer skyStage) : _skyStage{ skyStage } {} Q_PROPERTY(float longitude READ getLongitude WRITE setLongitude) Q_PROPERTY(float latitude READ getLatitude WRITE setLatitude) @@ -37,7 +37,7 @@ namespace SceneScripting { void setAltitude(float altitude); protected: - model::SunSkyStagePointer _skyStage; + graphics::SunSkyStagePointer _skyStage; }; using LocationPointer = std::unique_ptr; @@ -45,7 +45,7 @@ namespace SceneScripting { Q_OBJECT public: - Time(model::SunSkyStagePointer skyStage) : _skyStage{ skyStage } {} + Time(graphics::SunSkyStagePointer skyStage) : _skyStage{ skyStage } {} Q_PROPERTY(float hour READ getHour WRITE setHour) Q_PROPERTY(int day READ getDay WRITE setDay) @@ -56,7 +56,7 @@ namespace SceneScripting { void setDay(int day); protected: - model::SunSkyStagePointer _skyStage; + graphics::SunSkyStagePointer _skyStage; }; using TimePointer = std::unique_ptr