mirror of
https://github.com/overte-org/overte.git
synced 2025-04-11 12:20:26 +02:00
Merging with master
This commit is contained in:
commit
e39a6b5a7d
152 changed files with 6700 additions and 1009 deletions
|
@ -108,8 +108,10 @@ set(PLATFORM_QT_GL OpenGL)
|
|||
|
||||
if (USE_GLES)
|
||||
add_definitions(-DUSE_GLES)
|
||||
add_definitions(-DGPU_POINTER_STORAGE_SHARED)
|
||||
set(PLATFORM_GL_BACKEND gpu-gl-common gpu-gles)
|
||||
else()
|
||||
add_definitions(-DGPU_POINTER_STORAGE_RAW)
|
||||
set(PLATFORM_GL_BACKEND gpu-gl-common gpu-gl)
|
||||
endif()
|
||||
|
||||
|
|
5
android/apps/framePlayer/CMakeLists.txt
Normal file
5
android/apps/framePlayer/CMakeLists.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
set(TARGET_NAME framePlayer)
|
||||
setup_hifi_library(AndroidExtras)
|
||||
link_hifi_libraries(shared ktx shaders qml gpu gl ${PLATFORM_GL_BACKEND})
|
||||
target_link_libraries(${TARGET_NAME} android log m)
|
||||
target_opengl()
|
50
android/apps/framePlayer/build.gradle
Normal file
50
android/apps/framePlayer/build.gradle
Normal file
|
@ -0,0 +1,50 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
android {
|
||||
signingConfigs {
|
||||
release {
|
||||
keyAlias 'key0'
|
||||
keyPassword 'password'
|
||||
storeFile file('C:/android/keystore.jks')
|
||||
storePassword 'password'
|
||||
}
|
||||
}
|
||||
|
||||
compileSdkVersion 28
|
||||
defaultConfig {
|
||||
applicationId "io.highfidelity.frameplayer"
|
||||
minSdkVersion 25
|
||||
targetSdkVersion 28
|
||||
ndk { abiFilters 'arm64-v8a' }
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments '-DHIFI_ANDROID=1',
|
||||
'-DHIFI_ANDROID_APP=framePlayer',
|
||||
'-DANDROID_TOOLCHAIN=clang',
|
||||
'-DANDROID_STL=c++_shared',
|
||||
'-DCMAKE_VERBOSE_MAKEFILE=ON'
|
||||
targets = ['framePlayer']
|
||||
}
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
signingConfig signingConfigs.release
|
||||
}
|
||||
}
|
||||
|
||||
externalNativeBuild.cmake.path '../../../CMakeLists.txt'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: '../../libraries/qt/libs')
|
||||
//implementation project(':oculus')
|
||||
implementation project(':qt')
|
||||
}
|
25
android/apps/framePlayer/proguard-rules.pro
vendored
Normal file
25
android/apps/framePlayer/proguard-rules.pro
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in C:\Android\SDK/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
38
android/apps/framePlayer/src/main/AndroidManifest.xml
Normal file
38
android/apps/framePlayer/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="io.highfidelity.frameplayer"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0"
|
||||
android:installLocation="auto">
|
||||
<uses-feature android:glEsVersion="0x00030002" android:required="true" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<application android:label="Frame Viewer"
|
||||
android:allowBackup="false"
|
||||
android:name="org.qtproject.qt5.android.bindings.QtApplication"
|
||||
tools:ignore="GoogleAppIndexingWarning,MissingApplicationIcon">
|
||||
<meta-data android:name="com.samsung.android.vr.application.mode" android:value="vr_only"/>
|
||||
<activity
|
||||
android:name="org.qtproject.qt5.android.bindings.QtActivity"
|
||||
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
|
||||
android:launchMode="singleTask"
|
||||
android:screenOrientation="landscape"
|
||||
android:excludeFromRecents="false"
|
||||
android:configChanges="screenSize|screenLayout|orientation|keyboardHidden|keyboard|navigation|uiMode">
|
||||
<!-- JNI nonsense -->
|
||||
<meta-data android:name="android.app.lib_name" android:value="framePlayer"/>
|
||||
<!-- Qt nonsense -->
|
||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
||||
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
|
||||
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
|
||||
<meta-data android:name="android.app.load_local_libs" android:value="plugins/platforms/android/libqtforandroid.so:plugins/bearer/libqandroidbearer.so"/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
6
android/apps/framePlayer/src/main/cpp/FramePlayer.qrc
Normal file
6
android/apps/framePlayer/src/main/cpp/FramePlayer.qrc
Normal file
|
@ -0,0 +1,6 @@
|
|||
<!DOCTYPE RCC>
|
||||
<RCC version="1.0">
|
||||
<qresource prefix="/">
|
||||
<file>qml/main.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
91
android/apps/framePlayer/src/main/cpp/PlayerWindow.cpp
Normal file
91
android/apps/framePlayer/src/main/cpp/PlayerWindow.cpp
Normal file
|
@ -0,0 +1,91 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2018/10/21
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "PlayerWindow.h"
|
||||
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtGui/QImageReader>
|
||||
#include <QtQml/QQmlContext>
|
||||
#include <QtQuick/QQuickItem>
|
||||
|
||||
#include <gpu/Frame.h>
|
||||
#include <gpu/FrameIO.h>
|
||||
|
||||
PlayerWindow::PlayerWindow() {
|
||||
setFlags(Qt::MSWindowsOwnDC | Qt::Window | Qt::Dialog | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint);
|
||||
setSurfaceType(QSurface::OpenGLSurface);
|
||||
create();
|
||||
showFullScreen();
|
||||
|
||||
// Make sure the window has been created by processing events
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
// Start the rendering thread
|
||||
_renderThread.initialize(this, &_surface);
|
||||
|
||||
// Start the UI
|
||||
_surface.resize(size());
|
||||
connect(&_surface, &hifi::qml::OffscreenSurface::rootContextCreated, this, [](QQmlContext* context){
|
||||
context->setContextProperty("FRAMES_FOLDER", "file:assets:/frames");
|
||||
});
|
||||
_surface.load("qrc:///qml/main.qml");
|
||||
|
||||
// Connect the UI handler
|
||||
QObject::connect(_surface.getRootItem(), SIGNAL(loadFile(QString)),
|
||||
this, SLOT(loadFile(QString))
|
||||
);
|
||||
|
||||
// Turn on UI input events
|
||||
installEventFilter(&_surface);
|
||||
}
|
||||
|
||||
PlayerWindow::~PlayerWindow() {
|
||||
}
|
||||
|
||||
// static const char* FRAME_FILE = "assets:/frames/20190110_1635.json";
|
||||
|
||||
static void textureLoader(const std::string& filename, const gpu::TexturePointer& texture, uint16_t layer) {
|
||||
QImage image;
|
||||
QImageReader(filename.c_str()).read(&image);
|
||||
if (layer > 0) {
|
||||
return;
|
||||
}
|
||||
texture->assignStoredMip(0, image.byteCount(), image.constBits());
|
||||
}
|
||||
|
||||
void PlayerWindow::loadFile(QString filename) {
|
||||
QString realFilename = QUrl(filename).toLocalFile();
|
||||
if (QFileInfo(realFilename).exists()) {
|
||||
auto frame = gpu::readFrame(realFilename.toStdString(), _renderThread._externalTexture, &textureLoader);
|
||||
_surface.pause();
|
||||
_renderThread.submitFrame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerWindow::touchEvent(QTouchEvent* event) {
|
||||
// Super basic input handling when the 3D scene is active.... tap with two finders to return to the
|
||||
// QML UI
|
||||
static size_t touches = 0;
|
||||
switch (event->type()) {
|
||||
case QEvent::TouchBegin:
|
||||
case QEvent::TouchUpdate:
|
||||
touches = std::max<size_t>(touches, event->touchPoints().size());
|
||||
break;
|
||||
|
||||
case QEvent::TouchEnd:
|
||||
if (touches >= 2) {
|
||||
_renderThread.submitFrame(nullptr);
|
||||
_surface.resume();
|
||||
}
|
||||
touches = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
35
android/apps/framePlayer/src/main/cpp/PlayerWindow.h
Normal file
35
android/apps/framePlayer/src/main/cpp/PlayerWindow.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2018/10/21
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include <QtGui/QWindow>
|
||||
#include <QtCore/QSettings>
|
||||
|
||||
#include <qml/OffscreenSurface.h>
|
||||
#include <gpu/Forward.h>
|
||||
#include "RenderThread.h"
|
||||
|
||||
// Create a simple OpenGL window that renders text in various ways
|
||||
class PlayerWindow : public QWindow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PlayerWindow();
|
||||
virtual ~PlayerWindow();
|
||||
|
||||
protected:
|
||||
void touchEvent(QTouchEvent *ev) override;
|
||||
|
||||
public slots:
|
||||
void loadFile(QString filename);
|
||||
|
||||
private:
|
||||
hifi::qml::OffscreenSurface _surface;
|
||||
QSettings _settings;
|
||||
RenderThread _renderThread;
|
||||
};
|
162
android/apps/framePlayer/src/main/cpp/RenderThread.cpp
Normal file
162
android/apps/framePlayer/src/main/cpp/RenderThread.cpp
Normal file
|
@ -0,0 +1,162 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2018/10/21
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "RenderThread.h"
|
||||
|
||||
#include <QtGui/QWindow>
|
||||
|
||||
void RenderThread::submitFrame(const gpu::FramePointer& frame) {
|
||||
std::unique_lock<std::mutex> lock(_frameLock);
|
||||
_pendingFrames.push(frame);
|
||||
}
|
||||
|
||||
void RenderThread::move(const glm::vec3& v) {
|
||||
std::unique_lock<std::mutex> lock(_frameLock);
|
||||
_correction = glm::inverse(glm::translate(mat4(), v)) * _correction;
|
||||
}
|
||||
|
||||
void RenderThread::setup() {
|
||||
// Wait until the context has been moved to this thread
|
||||
{ std::unique_lock<std::mutex> lock(_frameLock); }
|
||||
|
||||
makeCurrent();
|
||||
// Disable vsync for profiling
|
||||
::gl::setSwapInterval(0);
|
||||
|
||||
glClearColor(1, 1, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
_glContext.swapBuffers();
|
||||
|
||||
// GPU library init
|
||||
gpu::Context::init<gpu::gl::GLBackend>();
|
||||
_gpuContext = std::make_shared<gpu::Context>();
|
||||
_backend = _gpuContext->getBackend();
|
||||
_gpuContext->beginFrame();
|
||||
_gpuContext->endFrame();
|
||||
makeCurrent();
|
||||
|
||||
|
||||
glGenFramebuffers(1, &_uiFbo);
|
||||
glGenTextures(1, &_externalTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, _externalTexture);
|
||||
static const glm::u8vec4 color{ 0 };
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &color);
|
||||
|
||||
glClearColor(0, 1, 1, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
_glContext.swapBuffers();
|
||||
}
|
||||
|
||||
void RenderThread::initialize(QWindow* window, hifi::qml::OffscreenSurface* offscreen) {
|
||||
std::unique_lock<std::mutex> lock(_frameLock);
|
||||
setObjectName("RenderThread");
|
||||
Parent::initialize();
|
||||
|
||||
_offscreen = offscreen;
|
||||
_window = window;
|
||||
_glContext.setWindow(_window);
|
||||
_glContext.create();
|
||||
_glContext.makeCurrent();
|
||||
|
||||
hifi::qml::OffscreenSurface::setSharedContext(_glContext.qglContext());
|
||||
glClearColor(1, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
_glContext.swapBuffers();
|
||||
_glContext.doneCurrent();
|
||||
_glContext.moveToThread(_thread);
|
||||
_thread->setObjectName("RenderThread");
|
||||
}
|
||||
|
||||
void RenderThread::shutdown() {
|
||||
_activeFrame.reset();
|
||||
while (!_pendingFrames.empty()) {
|
||||
_gpuContext->consumeFrameUpdates(_pendingFrames.front());
|
||||
_pendingFrames.pop();
|
||||
}
|
||||
_gpuContext->shutdown();
|
||||
_gpuContext.reset();
|
||||
}
|
||||
|
||||
void RenderThread::renderFrame() {
|
||||
auto windowSize = _window->geometry().size();
|
||||
uvec2 readFboSize;
|
||||
uint32_t readFbo{ 0 };
|
||||
|
||||
if (_activeFrame) {
|
||||
const auto &frame = _activeFrame;
|
||||
_backend->recycle();
|
||||
_backend->syncCache();
|
||||
_gpuContext->enableStereo(frame->stereoState._enable);
|
||||
if (frame && !frame->batches.empty()) {
|
||||
_gpuContext->executeFrame(frame);
|
||||
}
|
||||
auto &glBackend = static_cast<gpu::gl::GLBackend&>(*_backend);
|
||||
readFbo = glBackend.getFramebufferID(frame->framebuffer);
|
||||
readFboSize = frame->framebuffer->getSize();
|
||||
CHECK_GL_ERROR();
|
||||
} else {
|
||||
hifi::qml::OffscreenSurface::TextureAndFence newTextureAndFence;
|
||||
if (_offscreen->fetchTexture(newTextureAndFence)) {
|
||||
if (_uiTexture != 0) {
|
||||
auto readFence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
glFlush();
|
||||
_offscreen->getDiscardLambda()(_uiTexture, readFence);
|
||||
_uiTexture = 0;
|
||||
}
|
||||
|
||||
glWaitSync((GLsync)newTextureAndFence.second, 0, GL_TIMEOUT_IGNORED);
|
||||
glDeleteSync((GLsync)newTextureAndFence.second);
|
||||
_uiTexture = newTextureAndFence.first;
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, _uiFbo);
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _uiTexture, 0);
|
||||
}
|
||||
|
||||
if (_uiTexture != 0) {
|
||||
readFbo = _uiFbo;
|
||||
readFboSize = { windowSize.width(), windowSize.height() };
|
||||
}
|
||||
}
|
||||
|
||||
if (readFbo) {
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, readFbo);
|
||||
glBlitFramebuffer(
|
||||
0, 0, readFboSize.x, readFboSize.y,
|
||||
0, 0, windowSize.width(), windowSize.height(),
|
||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
} else {
|
||||
glClearColor(1, 0, 0, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
||||
_glContext.swapBuffers();
|
||||
}
|
||||
|
||||
void RenderThread::updateFrame() {
|
||||
std::queue<gpu::FramePointer> pendingFrames;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(_frameLock);
|
||||
pendingFrames.swap(_pendingFrames);
|
||||
}
|
||||
|
||||
while (!pendingFrames.empty()) {
|
||||
_activeFrame = pendingFrames.front();
|
||||
pendingFrames.pop();
|
||||
if (_activeFrame) {
|
||||
_gpuContext->consumeFrameUpdates(_activeFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderThread::process() {
|
||||
updateFrame();
|
||||
makeCurrent();
|
||||
renderFrame();
|
||||
return true;
|
||||
}
|
54
android/apps/framePlayer/src/main/cpp/RenderThread.h
Normal file
54
android/apps/framePlayer/src/main/cpp/RenderThread.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2018/10/21
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <GenericThread.h>
|
||||
|
||||
#include <gl/Context.h>
|
||||
#include <gpu/gl/GLBackend.h>
|
||||
#include <qml/OffscreenSurface.h>
|
||||
|
||||
class RenderThread : public GenericThread {
|
||||
using Parent = GenericThread;
|
||||
public:
|
||||
QWindow* _window{ nullptr };
|
||||
std::mutex _mutex;
|
||||
gpu::ContextPointer _gpuContext; // initialized during window creation
|
||||
std::shared_ptr<gpu::Backend> _backend;
|
||||
std::atomic<size_t> _presentCount{ 0 };
|
||||
std::mutex _frameLock;
|
||||
std::queue<gpu::FramePointer> _pendingFrames;
|
||||
gpu::FramePointer _activeFrame;
|
||||
uint32_t _externalTexture{ 0 };
|
||||
glm::mat4 _correction;
|
||||
hifi::qml::OffscreenSurface* _offscreen{ nullptr };
|
||||
|
||||
gl::Context _glContext;
|
||||
uint32_t _uiTexture{ 0 };
|
||||
uint32_t _uiFbo{ 0 };
|
||||
|
||||
void move(const glm::vec3& v);
|
||||
void setup() override;
|
||||
bool process() override;
|
||||
void shutdown() override;
|
||||
|
||||
void initialize(QWindow* window, hifi::qml::OffscreenSurface* offscreen);
|
||||
|
||||
void submitFrame(const gpu::FramePointer& frame);
|
||||
void updateFrame();
|
||||
void renderFrame();
|
||||
|
||||
bool makeCurrent() {
|
||||
return _glContext.makeCurrent();
|
||||
}
|
||||
|
||||
void doneCurrent() {
|
||||
_glContext.doneCurrent();
|
||||
}
|
||||
};
|
54
android/apps/framePlayer/src/main/cpp/main.cpp
Normal file
54
android/apps/framePlayer/src/main/cpp/main.cpp
Normal file
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2018/11/22
|
||||
// Copyright 2014 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QFileInfo>
|
||||
|
||||
#include <Trace.h>
|
||||
|
||||
#include "PlayerWindow.h"
|
||||
|
||||
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
|
||||
if (!message.isEmpty()) {
|
||||
const char * local=message.toStdString().c_str();
|
||||
switch (type) {
|
||||
case QtDebugMsg:
|
||||
__android_log_write(ANDROID_LOG_DEBUG,"Interface",local);
|
||||
break;
|
||||
case QtInfoMsg:
|
||||
__android_log_write(ANDROID_LOG_INFO,"Interface",local);
|
||||
break;
|
||||
case QtWarningMsg:
|
||||
__android_log_write(ANDROID_LOG_WARN,"Interface",local);
|
||||
break;
|
||||
case QtCriticalMsg:
|
||||
__android_log_write(ANDROID_LOG_ERROR,"Interface",local);
|
||||
break;
|
||||
case QtFatalMsg:
|
||||
default:
|
||||
__android_log_write(ANDROID_LOG_FATAL,"Interface",local);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
setupHifiApplication("gpuFramePlayer");
|
||||
QGuiApplication app(argc, argv);
|
||||
auto oldMessageHandler = qInstallMessageHandler(messageHandler);
|
||||
DependencyManager::set<tracing::Tracer>();
|
||||
PlayerWindow window;
|
||||
app.exec();
|
||||
qInstallMessageHandler(oldMessageHandler);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
36
android/apps/framePlayer/src/main/cpp/qml/main.qml
Normal file
36
android/apps/framePlayer/src/main/cpp/qml/main.qml
Normal file
|
@ -0,0 +1,36 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Dialogs 1.1
|
||||
import Qt.labs.folderlistmodel 2.11
|
||||
|
||||
Item {
|
||||
id: root
|
||||
width: 640
|
||||
height: 480
|
||||
|
||||
ListView {
|
||||
anchors.fill: parent
|
||||
|
||||
FolderListModel {
|
||||
id: folderModel
|
||||
folder: FRAMES_FOLDER
|
||||
nameFilters: ["*.json"]
|
||||
}
|
||||
|
||||
Component {
|
||||
id: fileDelegate
|
||||
Text {
|
||||
text: fileName
|
||||
font.pointSize: 36
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: root.loadFile(folderModel.folder + "/" + fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
model: folderModel
|
||||
delegate: fileDelegate
|
||||
}
|
||||
|
||||
signal loadFile(string filename);
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--suppress AndroidUnknownAttribute -->
|
||||
<vector xmlns:api24="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:viewportWidth="192"
|
||||
android:viewportHeight="192"
|
||||
android:width="192dp"
|
||||
android:height="192dp">
|
||||
<path
|
||||
android:pathData="M189.5 96.5A93.5 93.5 0 0 1 96 190 93.5 93.5 0 0 1 2.5 96.5 93.5 93.5 0 0 1 96 3 93.5 93.5 0 0 1 189.5 96.5Z"
|
||||
android:fillColor="#333333" />
|
||||
<path
|
||||
android:pathData="M96.2 173.1c-10.3 0 -20.4 -2.1 -29.8 -6 -9.2 -3.8 -17.3 -9.4 -24.3 -16.4 -7 -7 -12.6 -15.2 -16.4 -24.3 -4.1 -9.6 -6.2 -19.6 -6.2 -30 0 -10.3 2.1 -20.4 6 -29.8 3.8 -9.2 9.4 -17.3 16.4 -24.3 7 -7 15.2 -12.6 24.3 -16.4 9.5 -4 19.5 -6 29.8 -6 10.3 0 20.4 2.1 29.8 6 9.2 3.8 17.3 9.4 24.3 16.4 7 7 12.6 15.2 16.4 24.3 4 9.5 6 19.5 6 29.8 0 10.3 -2.1 20.4 -6 29.8 -3.8 9.2 -9.4 17.3 -16.4 24.3 -7 7 -15.2 12.6 -24.3 16.4 -9.2 4.1 -19.3 6.2 -29.6 6.2zm0 -145.3c-37.8 0 -68.6 30.8 -68.6 68.6 0 37.8 30.8 68.6 68.6 68.6 37.8 0 68.6 -30.8 68.6 -68.6 0 -37.8 -30.8 -68.6 -68.6 -68.6z"
|
||||
android:fillColor="#00b4f0" />
|
||||
<path
|
||||
android:pathData="M119.6 129l0 -53.8c3.4 -1.1 5.8 -4.3 5.8 -8 0 -4.6 -3.8 -8.4 -8.4 -8.4 -4.6 0 -8.4 3.8 -8.4 8.4 0 3.6 2.2 6.6 5.4 7.9l0 25L79 83.8 79 64c3.4 -1.1 5.8 -4.3 5.8 -8 0 -4.6 -3.8 -8.4 -8.4 -8.4 -4.6 0 -8.4 3.8 -8.4 8.4 0 3.6 2.2 6.6 5.4 7.9l0 54.1c-3.1 1.2 -5.4 4.3 -5.4 7.9 0 4.6 3.8 8.4 8.4 8.4 4.6 0 8.4 -3.8 8.4 -8.4 0 -3.7 -2.4 -6.9 -5.8 -8l0 -27.3 35 16.3 0 22.2c-3.1 1.2 -5.4 4.3 -5.4 7.9 0 4.6 3.8 8.4 8.4 8.4 4.6 0 8.4 -3.8 8.4 -8.4 0 -3.8 -2.4 -6.9 -5.8 -8z"
|
||||
android:fillColor="#00b4f0" />
|
||||
</vector>
|
3
android/apps/framePlayer/src/main/res/values/strings.xml
Normal file
3
android/apps/framePlayer/src/main/res/values/strings.xml
Normal file
|
@ -0,0 +1,3 @@
|
|||
<resources>
|
||||
<string name="app_name" translatable="false">GPU Frame Player</string>
|
||||
</resources>
|
|
@ -3,3 +3,6 @@ project(':qt').projectDir = new File(settingsDir, 'libraries/qt')
|
|||
|
||||
include ':interface'
|
||||
project(':interface').projectDir = new File(settingsDir, 'apps/interface')
|
||||
|
||||
//include ':framePlayer'
|
||||
//project(':framePlayer').projectDir = new File(settingsDir, 'apps/framePlayer')
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#
|
||||
macro(TARGET_GLAD)
|
||||
if (ANDROID)
|
||||
include(SelectLibraryConfigurations)
|
||||
set(INSTALL_DIR ${HIFI_ANDROID_PRECOMPILED}/glad)
|
||||
set(GLAD_INCLUDE_DIRS "${INSTALL_DIR}/include")
|
||||
set(GLAD_LIBRARY_DEBUG ${INSTALL_DIR}/lib/libglad_d.a)
|
||||
|
@ -31,8 +32,8 @@ macro(TARGET_GLAD)
|
|||
set(GLAD_INCLUDE_DIRS ${${GLAD_UPPER}_INCLUDE_DIRS})
|
||||
set(GLAD_LIBRARY ${${GLAD_UPPER}_LIBRARY})
|
||||
endif()
|
||||
|
||||
|
||||
target_include_directories(${TARGET_NAME} PUBLIC ${GLAD_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${GLAD_LIBRARY})
|
||||
target_link_libraries(${TARGET_NAME} ${GLAD_EXTRA_LIBRARIES})
|
||||
target_link_libraries(${TARGET_NAME} ${GLAD_EXTRA_LIBRARIES})
|
||||
endmacro()
|
||||
|
|
|
@ -6,8 +6,13 @@
|
|||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
macro(TARGET_ZLIB)
|
||||
# using VCPKG for zlib
|
||||
find_package(ZLIB REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${ZLIB_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES})
|
||||
if (ANDROID)
|
||||
# zlib is part of the NDK
|
||||
target_link_libraries(${TARGET_NAME} z)
|
||||
else()
|
||||
# using VCPKG for zlib
|
||||
find_package(ZLIB REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} SYSTEM PRIVATE ${ZLIB_INCLUDE_DIRS})
|
||||
target_link_libraries(${TARGET_NAME} ${ZLIB_LIBRARIES})
|
||||
endif()
|
||||
endmacro()
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Source: nvtt
|
||||
Version: 8c7e6b40ee5095f227b75880fabd89c99d6f34c0
|
||||
Version: 330c4d56274a0f602a5c70596e2eb670a4ed56c2
|
||||
Description: Texture processing tools with support for Direct3D 10 and 11 formats.
|
|
@ -10,8 +10,8 @@ include(vcpkg_common_functions)
|
|||
vcpkg_from_github(
|
||||
OUT_SOURCE_PATH SOURCE_PATH
|
||||
REPO highfidelity/nvidia-texture-tools
|
||||
REF 8c7e6b40ee5095f227b75880fabd89c99d6f34c0
|
||||
SHA512 f107d19dbbd6651ef2126b1422a5db8db291bf70311ac4fb1dbacb5ceaa8752fee38becbd32964f57596f0b84e1223bb2c3ff9d9c4fdc65c3e77a47836657cef
|
||||
REF 330c4d56274a0f602a5c70596e2eb670a4ed56c2
|
||||
SHA512 4c0bc2f369120d696cc27710b6d33086b27eef55f537ec66b9a5c8b1839bc2426c0413670b0f65be52c5d353468f0126dfe024be1f0690611d4d7e33ac530127
|
||||
HEAD_REF master
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import QtQuick 2.2
|
||||
import QtQuick.Dialogs 1.1
|
||||
import Qt.labs.folderlistmodel 2.11
|
||||
|
||||
Item {
|
||||
width: 640
|
||||
height: 480
|
||||
|
||||
ListView {
|
||||
width: 200; height: 400
|
||||
|
||||
FolderListModel {
|
||||
id: folderModel
|
||||
folder: "assets:/frames/"
|
||||
nameFilters: ["*.json"]
|
||||
}
|
||||
|
||||
Component {
|
||||
id: fileDelegate
|
||||
Text { text: fileName }
|
||||
}
|
||||
|
||||
model: folderModel
|
||||
delegate: fileDelegate
|
||||
}
|
||||
}
|
|
@ -2417,7 +2417,6 @@ void Application::updateVerboseLogging() {
|
|||
bool enable = menu->isOptionChecked(MenuOption::VerboseLogging);
|
||||
|
||||
QString rules =
|
||||
"hifi.*.debug=%1\n"
|
||||
"hifi.*.info=%1\n"
|
||||
"hifi.audio-stream.debug=false\n"
|
||||
"hifi.audio-stream.info=false";
|
||||
|
@ -3544,8 +3543,10 @@ void Application::resizeGL() {
|
|||
auto renderConfig = _graphicsEngine.getRenderEngine()->getConfiguration();
|
||||
assert(renderConfig);
|
||||
auto mainView = renderConfig->getConfig("RenderMainView.RenderDeferredTask");
|
||||
assert(mainView);
|
||||
mainView->setProperty("resolutionScale", renderResolutionScale);
|
||||
// mainView can be null if we're rendering in forward mode
|
||||
if (mainView) {
|
||||
mainView->setProperty("resolutionScale", renderResolutionScale);
|
||||
}
|
||||
displayPlugin->setRenderResolutionScale(renderResolutionScale);
|
||||
}
|
||||
|
||||
|
@ -4093,6 +4094,19 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_G:
|
||||
if (isShifted && isMeta && Menu::getInstance() && Menu::getInstance()->getMenu("Developer")->isVisible()) {
|
||||
static const QString HIFI_FRAMES_FOLDER_VAR = "HIFI_FRAMES_FOLDER";
|
||||
static const QString GPU_FRAME_FOLDER = QProcessEnvironment::systemEnvironment().contains(HIFI_FRAMES_FOLDER_VAR)
|
||||
? QProcessEnvironment::systemEnvironment().value(HIFI_FRAMES_FOLDER_VAR)
|
||||
: "hifiFrames";
|
||||
static QString GPU_FRAME_TEMPLATE = GPU_FRAME_FOLDER + "/{DATE}_{TIME}";
|
||||
QString fullPath = FileUtils::computeDocumentPath(FileUtils::replaceDateTimeTokens(GPU_FRAME_TEMPLATE));
|
||||
if (FileUtils::canCreateFile(fullPath)) {
|
||||
getActiveDisplayPlugin()->captureFrame(fullPath.toStdString());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Qt::Key_X:
|
||||
if (isShifted && isMeta) {
|
||||
auto offscreenUi = getOffscreenUI();
|
||||
|
@ -8246,7 +8260,18 @@ void Application::toggleLogDialog() {
|
|||
return;
|
||||
}
|
||||
if (! _logDialog) {
|
||||
|
||||
bool keepOnTop =_keepLogWindowOnTop.get();
|
||||
#ifdef Q_OS_WIN
|
||||
_logDialog = new LogDialog(keepOnTop ? qApp->getWindow() : nullptr, getLogger());
|
||||
#else
|
||||
_logDialog = new LogDialog(nullptr, getLogger());
|
||||
|
||||
if (keepOnTop) {
|
||||
Qt::WindowFlags flags = _logDialog->windowFlags() | Qt::Tool;
|
||||
_logDialog->setWindowFlags(flags);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (_logDialog->isVisible()) {
|
||||
|
@ -8256,6 +8281,19 @@ void Application::toggleLogDialog() {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::recreateLogWindow(int keepOnTop) {
|
||||
_keepLogWindowOnTop.set(keepOnTop != 0);
|
||||
if (_logDialog) {
|
||||
bool toggle = _logDialog->isVisible();
|
||||
_logDialog->close();
|
||||
_logDialog = nullptr;
|
||||
|
||||
if (toggle) {
|
||||
toggleLogDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Application::toggleEntityScriptServerLogDialog() {
|
||||
if (! _entityScriptServerLogDialog) {
|
||||
_entityScriptServerLogDialog = new EntityScriptServerLogDialog(nullptr);
|
||||
|
|
|
@ -217,6 +217,8 @@ public:
|
|||
void setDesktopTabletScale(float desktopTabletScale);
|
||||
|
||||
bool getDesktopTabletBecomesToolbarSetting() { return _desktopTabletBecomesToolbarSetting.get(); }
|
||||
bool getLogWindowOnTopSetting() { return _keepLogWindowOnTop.get(); }
|
||||
void setLogWindowOnTopSetting(bool keepOnTop) { _keepLogWindowOnTop.set(keepOnTop); }
|
||||
void setDesktopTabletBecomesToolbarSetting(bool value);
|
||||
bool getHmdTabletBecomesToolbarSetting() { return _hmdTabletBecomesToolbarSetting.get(); }
|
||||
void setHmdTabletBecomesToolbarSetting(bool value);
|
||||
|
@ -365,6 +367,7 @@ public slots:
|
|||
Q_INVOKABLE void loadDialog();
|
||||
Q_INVOKABLE void loadScriptURLDialog() const;
|
||||
void toggleLogDialog();
|
||||
void recreateLogWindow(int);
|
||||
void toggleEntityScriptServerLogDialog();
|
||||
Q_INVOKABLE void showAssetServerWidget(QString filePath = "");
|
||||
Q_INVOKABLE void loadAddAvatarBookmarkDialog() const;
|
||||
|
@ -656,6 +659,7 @@ private:
|
|||
Setting::Handle<bool> _constrainToolbarPosition;
|
||||
Setting::Handle<QString> _preferredCursor;
|
||||
Setting::Handle<bool> _miniTabletEnabledSetting;
|
||||
Setting::Handle<bool> _keepLogWindowOnTop { "keepLogWindowOnTop", false };
|
||||
|
||||
float _scaleMirror;
|
||||
float _mirrorYawOffset;
|
||||
|
|
|
@ -288,39 +288,37 @@ void AvatarActionHold::doKinematicUpdate(float deltaTimeStep) {
|
|||
glm::vec3 oneFrameVelocity = (_positionalTarget - _previousPositionalTarget) / deltaTimeStep;
|
||||
|
||||
_measuredLinearVelocities[_measuredLinearVelocitiesIndex++] = oneFrameVelocity;
|
||||
if (_measuredLinearVelocitiesIndex >= AvatarActionHold::velocitySmoothFrames) {
|
||||
_measuredLinearVelocitiesIndex = 0;
|
||||
_measuredLinearVelocitiesIndex %= AvatarActionHold::velocitySmoothFrames;
|
||||
}
|
||||
|
||||
if (_kinematicSetVelocity) {
|
||||
glm::vec3 measuredLinearVelocity = _measuredLinearVelocities[0];
|
||||
for (int i = 1; i < AvatarActionHold::velocitySmoothFrames; i++) {
|
||||
// there is a bit of lag between when someone releases the trigger and when the software reacts to
|
||||
// the release. we calculate the velocity from previous frames but we don't include several
|
||||
// of the most recent.
|
||||
//
|
||||
// if _measuredLinearVelocitiesIndex is
|
||||
// 0 -- ignore i of 3 4 5
|
||||
// 1 -- ignore i of 4 5 0
|
||||
// 2 -- ignore i of 5 0 1
|
||||
// 3 -- ignore i of 0 1 2
|
||||
// 4 -- ignore i of 1 2 3
|
||||
// 5 -- ignore i of 2 3 4
|
||||
|
||||
// This code is now disabled, but I'm leaving it commented-out because I suspect it will come back.
|
||||
// if ((i + 1) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex ||
|
||||
// (i + 2) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex ||
|
||||
// (i + 3) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
measuredLinearVelocity += _measuredLinearVelocities[i];
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 measuredLinearVelocity;
|
||||
for (int i = 0; i < AvatarActionHold::velocitySmoothFrames; i++) {
|
||||
// there is a bit of lag between when someone releases the trigger and when the software reacts to
|
||||
// the release. we calculate the velocity from previous frames but we don't include several
|
||||
// of the most recent.
|
||||
//
|
||||
// if _measuredLinearVelocitiesIndex is
|
||||
// 0 -- ignore i of 3 4 5
|
||||
// 1 -- ignore i of 4 5 0
|
||||
// 2 -- ignore i of 5 0 1
|
||||
// 3 -- ignore i of 0 1 2
|
||||
// 4 -- ignore i of 1 2 3
|
||||
// 5 -- ignore i of 2 3 4
|
||||
|
||||
// This code is now disabled, but I'm leaving it commented-out because I suspect it will come back.
|
||||
// if ((i + 1) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex ||
|
||||
// (i + 2) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex ||
|
||||
// (i + 3) % AvatarActionHold::velocitySmoothFrames == _measuredLinearVelocitiesIndex) {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
measuredLinearVelocity += _measuredLinearVelocities[i];
|
||||
}
|
||||
measuredLinearVelocity /= (float)(AvatarActionHold::velocitySmoothFrames
|
||||
measuredLinearVelocity /= (float)(AvatarActionHold::velocitySmoothFrames
|
||||
// - 3 // 3 because of the 3 we skipped, above
|
||||
);
|
||||
|
||||
if (_kinematicSetVelocity) {
|
||||
rigidBody->setLinearVelocity(glmToBullet(measuredLinearVelocity));
|
||||
rigidBody->setAngularVelocity(glmToBullet(_angularVelocityTarget));
|
||||
}
|
||||
|
|
|
@ -450,6 +450,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
|
|||
_spaceProxiesToDelete.push_back(avatar->getSpaceIndex());
|
||||
}
|
||||
AvatarHashMap::handleRemovedAvatar(avatar, removalReason);
|
||||
avatar->tearDownGrabs();
|
||||
|
||||
avatar->die();
|
||||
queuePhysicsChange(avatar);
|
||||
|
|
|
@ -866,7 +866,7 @@ void MyAvatar::simulate(float deltaTime, bool inView) {
|
|||
// and all of its joints, now update our attachements.
|
||||
Avatar::simulateAttachments(deltaTime);
|
||||
relayJointDataToChildren();
|
||||
if (updateGrabs()) {
|
||||
if (applyGrabChanges()) {
|
||||
_cauterizationNeedsUpdate = true;
|
||||
}
|
||||
|
||||
|
@ -4815,7 +4815,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons
|
|||
}
|
||||
|
||||
bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const {
|
||||
const float CYLINDER_TOP = 0.1f;
|
||||
const float CYLINDER_TOP = 2.0f;
|
||||
const float CYLINDER_BOTTOM = -1.5f;
|
||||
const float SITTING_BOTTOM = -0.02f;
|
||||
|
||||
|
@ -5373,7 +5373,7 @@ void MyAvatar::releaseGrab(const QUuid& grabID) {
|
|||
|
||||
_avatarGrabsLock.withWriteLock([&] {
|
||||
if (_avatarGrabData.remove(grabID)) {
|
||||
_deletedAvatarGrabs.insert(grabID);
|
||||
_grabsToDelete.push_back(grabID);
|
||||
tellHandler = true;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -217,7 +217,7 @@ void OtherAvatar::simulate(float deltaTime, bool inView) {
|
|||
|
||||
{
|
||||
PROFILE_RANGE(simulation, "grabs");
|
||||
updateGrabs();
|
||||
applyGrabChanges();
|
||||
}
|
||||
|
||||
updateFadingStatus();
|
||||
|
|
|
@ -20,9 +20,9 @@
|
|||
#include <PathUtils.h>
|
||||
|
||||
const int TOP_BAR_HEIGHT = 124;
|
||||
const int INITIAL_WIDTH = 720;
|
||||
const int INITIAL_WIDTH = 800;
|
||||
const int INITIAL_HEIGHT = 480;
|
||||
const int MINIMAL_WIDTH = 700;
|
||||
const int MINIMAL_WIDTH = 780;
|
||||
const int SEARCH_BUTTON_LEFT = 25;
|
||||
const int SEARCH_BUTTON_WIDTH = 20;
|
||||
const int SEARCH_TOGGLE_BUTTON_WIDTH = 50;
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
|
||||
#include <shared/AbstractLoggerInterface.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "MainWindow.h"
|
||||
|
||||
const int REVEAL_BUTTON_WIDTH = 122;
|
||||
const int ALL_LOGS_BUTTON_WIDTH = 90;
|
||||
const int MARGIN_LEFT = 25;
|
||||
|
@ -148,6 +151,16 @@ LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : BaseLog
|
|||
_messageCount->setObjectName("messageCount");
|
||||
_messageCount->show();
|
||||
|
||||
_keepOnTopBox = new QCheckBox(" Keep window on top", this);
|
||||
bool isOnTop = qApp-> getLogWindowOnTopSetting();
|
||||
_keepOnTopBox->setCheckState(isOnTop ? Qt::Checked : Qt::Unchecked);
|
||||
#ifdef Q_OS_WIN
|
||||
connect(_keepOnTopBox, &QCheckBox::stateChanged, qApp, &Application::recreateLogWindow);
|
||||
#else
|
||||
connect(_keepOnTopBox, &QCheckBox::stateChanged, this, &LogDialog::handleKeepWindowOnTop);
|
||||
#endif
|
||||
_keepOnTopBox->show();
|
||||
|
||||
_extraDebuggingBox = new QCheckBox("Extra debugging", this);
|
||||
if (_logger->extraDebugging()) {
|
||||
_extraDebuggingBox->setCheckState(Qt::Checked);
|
||||
|
@ -183,6 +196,11 @@ void LogDialog::resizeEvent(QResizeEvent* event) {
|
|||
THIRD_ROW,
|
||||
COMBOBOX_WIDTH,
|
||||
ELEMENT_HEIGHT);
|
||||
|
||||
_keepOnTopBox->setGeometry(width() - ELEMENT_MARGIN - COMBOBOX_WIDTH - ELEMENT_MARGIN - ALL_LOGS_BUTTON_WIDTH - ELEMENT_MARGIN - COMBOBOX_WIDTH - ELEMENT_MARGIN,
|
||||
THIRD_ROW,
|
||||
COMBOBOX_WIDTH,
|
||||
ELEMENT_HEIGHT);
|
||||
_messageCount->setGeometry(_leftPad,
|
||||
THIRD_ROW,
|
||||
COMBOBOX_WIDTH,
|
||||
|
@ -234,6 +252,23 @@ void LogDialog::handleInfoPrintBox(int state) {
|
|||
printLogFile();
|
||||
}
|
||||
|
||||
void LogDialog::handleKeepWindowOnTop(int state) {
|
||||
bool keepOnTop = (state != 0);
|
||||
|
||||
Qt::WindowFlags flags = windowFlags();
|
||||
|
||||
if (keepOnTop) {
|
||||
flags |= Qt::Tool;
|
||||
} else {
|
||||
flags &= ~Qt::Tool;
|
||||
}
|
||||
|
||||
setWindowFlags(flags);
|
||||
qApp->setLogWindowOnTopSetting(keepOnTop);
|
||||
|
||||
show();
|
||||
}
|
||||
|
||||
void LogDialog::handleCriticalPrintBox(int state) {
|
||||
_logger->setCriticalPrint(state != 0);
|
||||
printLogFile();
|
||||
|
|
|
@ -34,6 +34,7 @@ public slots:
|
|||
private slots:
|
||||
void handleRevealButton();
|
||||
void handleExtraDebuggingCheckbox(int);
|
||||
void handleKeepWindowOnTop(int);
|
||||
void handleDebugPrintBox(int);
|
||||
void handleInfoPrintBox(int);
|
||||
void handleCriticalPrintBox(int);
|
||||
|
@ -55,6 +56,7 @@ protected:
|
|||
|
||||
private:
|
||||
QCheckBox* _extraDebuggingBox;
|
||||
QCheckBox* _keepOnTopBox;
|
||||
QPushButton* _revealLogButton;
|
||||
QPushButton* _allLogsButton;
|
||||
QCheckBox* _debugPrintBox;
|
||||
|
|
|
@ -324,88 +324,79 @@ void Avatar::removeAvatarEntitiesFromTree() {
|
|||
}
|
||||
}
|
||||
|
||||
bool Avatar::updateGrabs() {
|
||||
bool Avatar::applyGrabChanges() {
|
||||
if (!_avatarGrabDataChanged && _grabsToChange.empty() && _grabsToDelete.empty()) {
|
||||
// early exit for most common case: nothing to do
|
||||
return false;
|
||||
}
|
||||
|
||||
bool grabAddedOrRemoved = false;
|
||||
// update the Grabs according to any changes in _avatarGrabData
|
||||
_avatarGrabsLock.withWriteLock([&] {
|
||||
if (_avatarGrabDataChanged) {
|
||||
// collect changes in _avatarGrabData
|
||||
foreach (auto grabID, _avatarGrabData.keys()) {
|
||||
AvatarGrabMap::iterator grabItr = _avatarGrabs.find(grabID);
|
||||
if (grabItr == _avatarGrabs.end()) {
|
||||
MapOfGrabs::iterator itr = _avatarGrabs.find(grabID);
|
||||
if (itr == _avatarGrabs.end()) {
|
||||
GrabPointer grab = std::make_shared<Grab>();
|
||||
grab->fromByteArray(_avatarGrabData.value(grabID));
|
||||
_avatarGrabs[grabID] = grab;
|
||||
_changedAvatarGrabs.insert(grabID);
|
||||
_grabsToChange.insert(grabID);
|
||||
} else {
|
||||
GrabPointer grab = grabItr.value();
|
||||
bool changed = grab->fromByteArray(_avatarGrabData.value(grabID));
|
||||
bool changed = itr->second->fromByteArray(_avatarGrabData.value(grabID));
|
||||
if (changed) {
|
||||
_changedAvatarGrabs.insert(grabID);
|
||||
_grabsToChange.insert(grabID);
|
||||
}
|
||||
}
|
||||
}
|
||||
_avatarGrabDataChanged = false;
|
||||
}
|
||||
|
||||
auto treeRenderer = DependencyManager::get<EntityTreeRenderer>();
|
||||
auto entityTree = treeRenderer ? treeRenderer->getTree() : nullptr;
|
||||
EntityEditPacketSender* packetSender = treeRenderer ? treeRenderer->getPacketSender() : nullptr;
|
||||
auto sessionID = DependencyManager::get<NodeList>()->getSessionUUID();
|
||||
|
||||
QMutableSetIterator<QUuid> delItr(_deletedAvatarGrabs);
|
||||
while (delItr.hasNext()) {
|
||||
QUuid grabID = delItr.next();
|
||||
GrabPointer grab = _avatarGrabs[grabID];
|
||||
if (!grab) {
|
||||
delItr.remove();
|
||||
// delete _avatarGrabs
|
||||
VectorOfIDs undeleted;
|
||||
for (const auto& id : _grabsToDelete) {
|
||||
MapOfGrabs::iterator itr = _avatarGrabs.find(id);
|
||||
if (itr == _avatarGrabs.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool success;
|
||||
const GrabPointer& grab = itr->second;
|
||||
SpatiallyNestablePointer target = SpatiallyNestable::findByID(grab->getTargetID(), success);
|
||||
|
||||
// only clear this entry from the _deletedAvatarGrabs if we found the entity.
|
||||
if (success && target) {
|
||||
bool iShouldTellServer = target->getEditSenderID() == sessionID;
|
||||
|
||||
EntityItemPointer entity = std::dynamic_pointer_cast<EntityItem>(target);
|
||||
if (entity && entity->isAvatarEntity() && (entity->getOwningAvatarID() == sessionID ||
|
||||
entity->getOwningAvatarID() == AVATAR_SELF_ID)) {
|
||||
// this is our own avatar-entity, so we always tell the server about the release
|
||||
iShouldTellServer = true;
|
||||
}
|
||||
|
||||
target->removeGrab(grab);
|
||||
delItr.remove();
|
||||
// in case this is the last grab on an entity, we need to shrink the queryAACube and tell the server
|
||||
// about the final position.
|
||||
if (entityTree) {
|
||||
bool force = true;
|
||||
entityTree->withWriteLock([&] {
|
||||
entityTree->updateEntityQueryAACube(target, packetSender, force, iShouldTellServer);
|
||||
});
|
||||
}
|
||||
_avatarGrabs.erase(itr);
|
||||
grabAddedOrRemoved = true;
|
||||
} else {
|
||||
undeleted.push_back(id);
|
||||
}
|
||||
_avatarGrabs.remove(grabID);
|
||||
_changedAvatarGrabs.remove(grabID);
|
||||
}
|
||||
_grabsToDelete = std::move(undeleted);
|
||||
|
||||
QMutableSetIterator<QUuid> changeItr(_changedAvatarGrabs);
|
||||
while (changeItr.hasNext()) {
|
||||
QUuid grabID = changeItr.next();
|
||||
GrabPointer& grab = _avatarGrabs[grabID];
|
||||
// change _avatarGrabs and add Actions to target
|
||||
SetOfIDs unchanged;
|
||||
for (const auto& id : _grabsToChange) {
|
||||
MapOfGrabs::iterator itr = _avatarGrabs.find(id);
|
||||
if (itr == _avatarGrabs.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool success;
|
||||
const GrabPointer& grab = itr->second;
|
||||
SpatiallyNestablePointer target = SpatiallyNestable::findByID(grab->getTargetID(), success);
|
||||
|
||||
if (success && target) {
|
||||
target->addGrab(grab);
|
||||
// only clear this entry from the _changedAvatarGrabs if we found the entity.
|
||||
changeItr.remove();
|
||||
if (isMyAvatar()) {
|
||||
EntityItemPointer entity = std::dynamic_pointer_cast<EntityItem>(target);
|
||||
if (entity) {
|
||||
entity->upgradeScriptSimulationPriority(PERSONAL_SIMULATION_PRIORITY);
|
||||
}
|
||||
}
|
||||
grabAddedOrRemoved = true;
|
||||
} else {
|
||||
unchanged.insert(id);
|
||||
}
|
||||
}
|
||||
_grabsToChange = std::move(unchanged);
|
||||
});
|
||||
return grabAddedOrRemoved;
|
||||
}
|
||||
|
@ -413,8 +404,8 @@ bool Avatar::updateGrabs() {
|
|||
void Avatar::accumulateGrabPositions(std::map<QUuid, GrabLocationAccumulator>& grabAccumulators) {
|
||||
// relay avatar's joint position to grabbed target in a way that allows for averaging
|
||||
_avatarGrabsLock.withReadLock([&] {
|
||||
foreach (auto grabID, _avatarGrabs.keys()) {
|
||||
const GrabPointer& grab = _avatarGrabs.value(grabID);
|
||||
for (const auto& entry : _avatarGrabs) {
|
||||
const GrabPointer& grab = entry.second;
|
||||
|
||||
if (!grab || !grab->getActionID().isNull()) {
|
||||
continue; // the accumulated value isn't used, in this case.
|
||||
|
@ -432,6 +423,20 @@ void Avatar::accumulateGrabPositions(std::map<QUuid, GrabLocationAccumulator>& g
|
|||
});
|
||||
}
|
||||
|
||||
void Avatar::tearDownGrabs() {
|
||||
_avatarGrabsLock.withWriteLock([&] {
|
||||
for (const auto& entry : _avatarGrabs) {
|
||||
_grabsToDelete.push_back(entry.first);
|
||||
}
|
||||
_grabsToChange.clear();
|
||||
});
|
||||
applyGrabChanges();
|
||||
if (!_grabsToDelete.empty()) {
|
||||
// some grabs failed to delete, which is a possible "leak", so we log about it
|
||||
qWarning() << "Failed to tearDown" << _grabsToDelete.size() << "grabs for Avatar" << getID();
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::relayJointDataToChildren() {
|
||||
forEachChild([&](SpatiallyNestablePointer child) {
|
||||
if (child->getNestableType() == NestableType::Entity) {
|
||||
|
@ -1929,3 +1934,12 @@ scriptable::ScriptableModelBase Avatar::getScriptableModel() {
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Avatar::clearAvatarGrabData(const QUuid& id) {
|
||||
AvatarData::clearAvatarGrabData(id);
|
||||
_avatarGrabsLock.withWriteLock([&] {
|
||||
if (_avatarGrabs.find(id) != _avatarGrabs.end()) {
|
||||
_grabsToDelete.push_back(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
#include <functional>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
|
@ -23,10 +26,12 @@
|
|||
#include <graphics-scripting/Forward.h>
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
#include <Grab.h>
|
||||
#include <ThreadSafeValueCache.h>
|
||||
|
||||
#include "Head.h"
|
||||
#include "SkeletonModel.h"
|
||||
#include "Rig.h"
|
||||
#include <ThreadSafeValueCache.h>
|
||||
|
||||
#include "MetaModelPayload.h"
|
||||
|
||||
|
@ -436,6 +441,8 @@ public:
|
|||
|
||||
void accumulateGrabPositions(std::map<QUuid, GrabLocationAccumulator>& grabAccumulators);
|
||||
|
||||
void tearDownGrabs();
|
||||
|
||||
signals:
|
||||
void targetScaleChanged(float targetScale);
|
||||
|
||||
|
@ -538,7 +545,7 @@ protected:
|
|||
|
||||
// protected methods...
|
||||
bool isLookingAtMe(AvatarSharedPointer avatar) const;
|
||||
bool updateGrabs();
|
||||
bool applyGrabChanges();
|
||||
void relayJointDataToChildren();
|
||||
|
||||
void fade(render::Transaction& transaction, render::Transition::Type type);
|
||||
|
@ -625,8 +632,15 @@ protected:
|
|||
|
||||
static void metaBlendshapeOperator(render::ItemID renderItemID, int blendshapeNumber, const QVector<BlendshapeOffset>& blendshapeOffsets,
|
||||
const QVector<int>& blendedMeshSizes, const render::ItemIDs& subItemIDs);
|
||||
void clearAvatarGrabData(const QUuid& grabID) override;
|
||||
|
||||
AvatarGrabMap _avatarGrabs;
|
||||
using SetOfIDs = std::set<QUuid>;
|
||||
using VectorOfIDs = std::vector<QUuid>;
|
||||
using MapOfGrabs = std::map<QUuid, GrabPointer>;
|
||||
|
||||
MapOfGrabs _avatarGrabs;
|
||||
SetOfIDs _grabsToChange; // updated grab IDs -- changes needed to entities or physics
|
||||
VectorOfIDs _grabsToDelete; // deleted grab IDs -- changes needed to entities or physics
|
||||
};
|
||||
|
||||
#endif // hifi_Avatar_h
|
||||
|
|
|
@ -3010,7 +3010,6 @@ void AvatarData::clearAvatarGrabData(const QUuid& grabID) {
|
|||
_avatarGrabsLock.withWriteLock([&] {
|
||||
if (_avatarGrabData.remove(grabID)) {
|
||||
_avatarGrabDataChanged = true;
|
||||
_deletedAvatarGrabs.insert(grabID);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -54,7 +54,6 @@
|
|||
#include "AvatarTraits.h"
|
||||
#include "HeadData.h"
|
||||
#include "PathUtils.h"
|
||||
#include "Grab.h"
|
||||
|
||||
#include <graphics/Material.h>
|
||||
|
||||
|
@ -67,8 +66,6 @@ using PackedAvatarEntityMap = QMap<QUuid, QByteArray>; // similar to AvatarEntit
|
|||
using AvatarEntityIDs = QSet<QUuid>;
|
||||
|
||||
using AvatarGrabDataMap = QMap<QUuid, QByteArray>;
|
||||
using AvatarGrabIDs = QSet<QUuid>;
|
||||
using AvatarGrabMap = QMap<QUuid, GrabPointer>;
|
||||
|
||||
using AvatarDataSequenceNumber = uint16_t;
|
||||
|
||||
|
@ -1475,8 +1472,6 @@ protected:
|
|||
mutable ReadWriteLockable _avatarGrabsLock;
|
||||
AvatarGrabDataMap _avatarGrabData;
|
||||
bool _avatarGrabDataChanged { false }; // by network
|
||||
AvatarGrabIDs _changedAvatarGrabs; // updated grab IDs -- changes needed to entities or physics
|
||||
AvatarGrabIDs _deletedAvatarGrabs; // deleted grab IDs -- changes needed to entities or physics
|
||||
|
||||
// used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers.
|
||||
ThreadSafeValueCache<glm::mat4> _sensorToWorldMatrixCache { glm::mat4() };
|
||||
|
@ -1539,7 +1534,7 @@ protected:
|
|||
}
|
||||
|
||||
bool updateAvatarGrabData(const QUuid& grabID, const QByteArray& grabData);
|
||||
void clearAvatarGrabData(const QUuid& grabID);
|
||||
virtual void clearAvatarGrabData(const QUuid& grabID);
|
||||
|
||||
private:
|
||||
friend void avatarStateFromFrame(const QByteArray& frameData, AvatarData* _avatar);
|
||||
|
|
|
@ -15,8 +15,10 @@
|
|||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QFileInfo>
|
||||
|
||||
#include <QtGui/QImage>
|
||||
#include <QtGui/QImageWriter>
|
||||
#include <QtGui/QOpenGLFramebufferObject>
|
||||
|
||||
#include <NumericalConstants.h>
|
||||
|
@ -30,6 +32,7 @@
|
|||
#include <gl/OffscreenGLCanvas.h>
|
||||
|
||||
#include <gpu/Texture.h>
|
||||
#include <gpu/FrameIO.h>
|
||||
#include <shaders/Shaders.h>
|
||||
#include <gpu/gl/GLShared.h>
|
||||
#include <gpu/gl/GLBackend.h>
|
||||
|
@ -466,11 +469,43 @@ void OpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) {
|
|||
});
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor) {
|
||||
renderFromTexture(batch, texture, viewport, scissor, gpu::FramebufferPointer());
|
||||
void OpenGLDisplayPlugin::captureFrame(const std::string& filename) const {
|
||||
withOtherThreadContext([&] {
|
||||
using namespace gpu;
|
||||
auto glBackend = const_cast<OpenGLDisplayPlugin&>(*this).getGLBackend();
|
||||
FramebufferPointer framebuffer{ Framebuffer::create("captureFramebuffer") };
|
||||
TextureCapturer captureLambda = [&](const std::string& filename, const gpu::TexturePointer& texture, uint16 layer) {
|
||||
QImage image;
|
||||
if (texture->getUsageType() == TextureUsageType::STRICT_RESOURCE) {
|
||||
image = QImage{ 1, 1, QImage::Format_ARGB32 };
|
||||
auto storedImage = texture->accessStoredMipFace(0, 0);
|
||||
memcpy(image.bits(), storedImage->data(), image.sizeInBytes());
|
||||
//if (texture == textureCache->getWhiteTexture()) {
|
||||
//} else if (texture == textureCache->getBlackTexture()) {
|
||||
//} else if (texture == textureCache->getBlueTexture()) {
|
||||
//} else if (texture == textureCache->getGrayTexture()) {
|
||||
} else {
|
||||
ivec4 rect = { 0, 0, texture->getWidth(), texture->getHeight() };
|
||||
framebuffer->setRenderBuffer(0, texture, layer);
|
||||
glBackend->syncGPUObject(*framebuffer);
|
||||
|
||||
image = QImage{ rect.z, rect.w, QImage::Format_ARGB32 };
|
||||
glBackend->downloadFramebuffer(framebuffer, rect, image);
|
||||
}
|
||||
QImageWriter(filename.c_str()).write(image);
|
||||
};
|
||||
|
||||
if (_currentFrame) {
|
||||
gpu::writeFrame(filename, _currentFrame, captureLambda);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor, gpu::FramebufferPointer copyFbo /*=gpu::FramebufferPointer()*/) {
|
||||
void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor) {
|
||||
renderFromTexture(batch, texture, viewport, scissor, nullptr);
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& copyFbo /*=gpu::FramebufferPointer()*/) {
|
||||
auto fbo = gpu::FramebufferPointer();
|
||||
batch.enableStereo(false);
|
||||
batch.resetViewTransform();
|
||||
|
|
|
@ -47,7 +47,7 @@ public:
|
|||
void endSession() override final;
|
||||
bool eventFilter(QObject* receiver, QEvent* event) override;
|
||||
bool isDisplayVisible() const override { return true; }
|
||||
|
||||
void captureFrame(const std::string& outputName) const override;
|
||||
void submitFrame(const gpu::FramePointer& newFrame) override;
|
||||
|
||||
glm::uvec2 getRecommendedRenderSize() const override {
|
||||
|
@ -113,8 +113,8 @@ protected:
|
|||
// Plugin specific functionality to send the composed scene to the output window or device
|
||||
virtual void internalPresent();
|
||||
|
||||
void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor, gpu::FramebufferPointer fbo);
|
||||
void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor);
|
||||
void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& fbo);
|
||||
void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor);
|
||||
virtual void updateFrameData();
|
||||
virtual glm::mat4 getViewCorrection() { return glm::mat4(); }
|
||||
|
||||
|
|
|
@ -69,7 +69,10 @@ bool DebugHmdDisplayPlugin::internalActivate() {
|
|||
_eyeInverseProjections[1] = glm::inverse(_eyeProjections[1]);
|
||||
_eyeOffsets[0][3] = vec4{ -0.0327499993, 0.0, -0.0149999997, 1.0 };
|
||||
_eyeOffsets[1][3] = vec4{ 0.0327499993, 0.0, -0.0149999997, 1.0 };
|
||||
_renderTargetSize = { 3024, 1680 };
|
||||
// Test HMD per-eye resolution
|
||||
_renderTargetSize = uvec2{ 1214 * 2 , 1344 };
|
||||
// uncomment to capture a quarter size frame
|
||||
//_renderTargetSize /= 2;
|
||||
_cullingProjection = _eyeProjections[0];
|
||||
// This must come after the initialization, so that the values calculated
|
||||
// above are available during the customizeContext call (when not running
|
||||
|
|
|
@ -141,7 +141,7 @@ std::shared_ptr<T> make_renderer(const EntityItemPointer& entity) {
|
|||
return std::shared_ptr<T>(new T(entity), [](T* ptr) { ptr->deleteLater(); });
|
||||
}
|
||||
|
||||
EntityRenderer::EntityRenderer(const EntityItemPointer& entity) : _entity(entity) {
|
||||
EntityRenderer::EntityRenderer(const EntityItemPointer& entity) : _created(entity->getCreated()), _entity(entity) {
|
||||
connect(entity.get(), &EntityItem::requestRenderUpdate, this, [&] {
|
||||
_needsRenderUpdate = true;
|
||||
emit requestRenderUpdate();
|
||||
|
@ -468,3 +468,32 @@ void EntityRenderer::removeMaterial(graphics::MaterialPointer material, const st
|
|||
std::lock_guard<std::mutex> lock(_materialsLock);
|
||||
_materials[parentMaterialName].remove(material);
|
||||
}
|
||||
|
||||
glm::vec4 EntityRenderer::calculatePulseColor(const glm::vec4& color, const PulsePropertyGroup& pulseProperties, quint64 start) {
|
||||
if (pulseProperties.getPeriod() == 0.0f || (pulseProperties.getColorMode() == PulseMode::NONE && pulseProperties.getAlphaMode() == PulseMode::NONE)) {
|
||||
return color;
|
||||
}
|
||||
|
||||
float t = ((float)(usecTimestampNow() - start)) / ((float)USECS_PER_SECOND);
|
||||
float pulse = 0.5f * (cosf(t * (2.0f * (float)M_PI) / pulseProperties.getPeriod()) + 1.0f) * (pulseProperties.getMax() - pulseProperties.getMin()) + pulseProperties.getMin();
|
||||
float outPulse = (1.0f - pulse);
|
||||
|
||||
glm::vec4 result = color;
|
||||
if (pulseProperties.getColorMode() == PulseMode::IN_PHASE) {
|
||||
result.r *= pulse;
|
||||
result.g *= pulse;
|
||||
result.b *= pulse;
|
||||
} else if (pulseProperties.getColorMode() == PulseMode::OUT_PHASE) {
|
||||
result.r *= outPulse;
|
||||
result.g *= outPulse;
|
||||
result.b *= outPulse;
|
||||
}
|
||||
|
||||
if (pulseProperties.getAlphaMode() == PulseMode::IN_PHASE) {
|
||||
result.a *= pulse;
|
||||
} else if (pulseProperties.getAlphaMode() == PulseMode::OUT_PHASE) {
|
||||
result.a *= outPulse;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
|
@ -62,6 +62,8 @@ public:
|
|||
|
||||
virtual scriptable::ScriptableModelBase getScriptableModel() override { return scriptable::ScriptableModelBase(); }
|
||||
|
||||
static glm::vec4 calculatePulseColor(const glm::vec4& color, const PulsePropertyGroup& pulseProperties, quint64 start);
|
||||
|
||||
protected:
|
||||
virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); }
|
||||
virtual void onAddToScene(const EntityItemPointer& entity);
|
||||
|
@ -151,6 +153,8 @@ protected:
|
|||
std::unordered_map<std::string, graphics::MultiMaterial> _materials;
|
||||
std::mutex _materialsLock;
|
||||
|
||||
quint64 _created;
|
||||
|
||||
private:
|
||||
// The base class relies on comparing the model transform to the entity transform in order
|
||||
// to trigger an update, so the member must not be visible to derived classes as a modifiable
|
||||
|
|
|
@ -26,7 +26,7 @@ GridEntityRenderer::~GridEntityRenderer() {
|
|||
}
|
||||
|
||||
bool GridEntityRenderer::isTransparent() const {
|
||||
return Parent::isTransparent() || _alpha < 1.0f;
|
||||
return Parent::isTransparent() || _alpha < 1.0f || _pulseProperties.getAlphaMode() != PulseMode::NONE;
|
||||
}
|
||||
|
||||
bool GridEntityRenderer::needsRenderUpdate() const {
|
||||
|
@ -55,6 +55,10 @@ bool GridEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
|
|||
return true;
|
||||
}
|
||||
|
||||
if (_pulseProperties != entity->getPulseProperties()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
|
@ -65,6 +69,7 @@ void GridEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
|
|||
withWriteLock([&] {
|
||||
_color = entity->getColor();
|
||||
_alpha = entity->getAlpha();
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
|
||||
_followCamera = entity->getFollowCamera();
|
||||
_majorGridEvery = entity->getMajorGridEvery();
|
||||
|
@ -105,11 +110,12 @@ ShapeKey GridEntityRenderer::getShapeKey() {
|
|||
}
|
||||
|
||||
void GridEntityRenderer::doRender(RenderArgs* args) {
|
||||
glm::u8vec3 color;
|
||||
glm::vec4 color;
|
||||
glm::vec3 dimensions;
|
||||
Transform renderTransform;
|
||||
withReadLock([&] {
|
||||
color = _color;
|
||||
color = glm::vec4(toGlm(_color), _alpha);
|
||||
color = EntityRenderer::calculatePulseColor(color, _pulseProperties, _created);
|
||||
dimensions = _dimensions;
|
||||
renderTransform = _renderTransform;
|
||||
});
|
||||
|
@ -141,12 +147,11 @@ void GridEntityRenderer::doRender(RenderArgs* args) {
|
|||
float majorGridColDivisions = dimensions.y / _majorGridEvery;
|
||||
float minorGridRowDivisions = dimensions.x / _minorGridEvery;
|
||||
float minorGridColDivisions = dimensions.y / _minorGridEvery;
|
||||
glm::vec4 gridColor(toGlm(color), _alpha);
|
||||
|
||||
const float MINOR_GRID_EDGE = 0.0025f;
|
||||
const float MAJOR_GRID_EDGE = 0.005f;
|
||||
DependencyManager::get<GeometryCache>()->renderGrid(*batch, minCorner, maxCorner,
|
||||
minorGridRowDivisions, minorGridColDivisions, MINOR_GRID_EDGE,
|
||||
majorGridRowDivisions, majorGridColDivisions, MAJOR_GRID_EDGE,
|
||||
gridColor, _geometryId);
|
||||
color, _geometryId);
|
||||
}
|
|
@ -36,6 +36,7 @@ private:
|
|||
|
||||
glm::u8vec3 _color;
|
||||
float _alpha;
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
|
||||
bool _followCamera;
|
||||
uint32_t _majorGridEvery;
|
||||
|
|
|
@ -26,7 +26,7 @@ ImageEntityRenderer::~ImageEntityRenderer() {
|
|||
}
|
||||
|
||||
bool ImageEntityRenderer::isTransparent() const {
|
||||
return Parent::isTransparent() || (_textureIsLoaded && _texture->getGPUTexture() && _texture->getGPUTexture()->getUsage().isAlpha()) || _alpha < 1.0f;
|
||||
return Parent::isTransparent() || (_textureIsLoaded && _texture->getGPUTexture() && _texture->getGPUTexture()->getUsage().isAlpha()) || _alpha < 1.0f || _pulseProperties.getAlphaMode() != PulseMode::NONE;
|
||||
}
|
||||
|
||||
bool ImageEntityRenderer::needsRenderUpdate() const {
|
||||
|
@ -71,6 +71,10 @@ bool ImageEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin
|
|||
return true;
|
||||
}
|
||||
|
||||
if (_pulseProperties != entity->getPulseProperties()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
|
@ -97,6 +101,7 @@ void ImageEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
|
||||
_color = entity->getColor();
|
||||
_alpha = entity->getAlpha();
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
|
||||
if (!_textureIsLoaded && _texture && _texture->isLoaded()) {
|
||||
_textureIsLoaded = true;
|
||||
|
@ -135,13 +140,14 @@ ShapeKey ImageEntityRenderer::getShapeKey() {
|
|||
void ImageEntityRenderer::doRender(RenderArgs* args) {
|
||||
NetworkTexturePointer texture;
|
||||
QRect subImage;
|
||||
glm::u8vec3 color;
|
||||
glm::vec4 color;
|
||||
glm::vec3 dimensions;
|
||||
Transform transform;
|
||||
withReadLock([&] {
|
||||
texture = _texture;
|
||||
subImage = _subImage;
|
||||
color = _color;
|
||||
color = glm::vec4(toGlm(_color), _alpha);
|
||||
color = EntityRenderer::calculatePulseColor(color, _pulseProperties, _created);
|
||||
dimensions = _dimensions;
|
||||
transform = _renderTransform;
|
||||
});
|
||||
|
@ -211,11 +217,9 @@ void ImageEntityRenderer::doRender(RenderArgs* args) {
|
|||
glm::vec2 texCoordBottomRight((fromImage.x() + fromImage.width() - 0.5f) / imageWidth,
|
||||
(fromImage.y() + fromImage.height() - 0.5f) / imageHeight);
|
||||
|
||||
glm::vec4 imageColor(toGlm(color), _alpha);
|
||||
|
||||
DependencyManager::get<GeometryCache>()->renderQuad(
|
||||
*batch, topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
|
||||
imageColor, _geometryId
|
||||
color, _geometryId
|
||||
);
|
||||
|
||||
batch->setResourceTexture(0, nullptr);
|
||||
|
|
|
@ -44,6 +44,7 @@ private:
|
|||
|
||||
glm::u8vec3 _color;
|
||||
float _alpha;
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
|
||||
glm::vec3 _dimensions;
|
||||
|
||||
|
|
|
@ -71,8 +71,11 @@ bool ParticleEffectEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedE
|
|||
return true;
|
||||
}
|
||||
|
||||
auto particleProperties = entity->getParticleProperties();
|
||||
if (particleProperties != _particleProperties) {
|
||||
if (_particleProperties != entity->getParticleProperties()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_pulseProperties != entity->getPulseProperties()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -95,6 +98,10 @@ void ParticleEffectEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePoi
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
withWriteLock([&] {
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
});
|
||||
_emitting = entity->getIsEmitting();
|
||||
|
||||
bool hasTexture = resultWithReadLock<bool>([&]{ return _particleProperties.textures.isEmpty(); });
|
||||
|
@ -142,10 +149,6 @@ void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEn
|
|||
particleUniforms.radius.middle = _particleProperties.radius.gradient.target;
|
||||
particleUniforms.radius.finish = _particleProperties.radius.range.finish;
|
||||
particleUniforms.radius.spread = _particleProperties.radius.gradient.spread;
|
||||
particleUniforms.color.start = _particleProperties.getColorStart();
|
||||
particleUniforms.color.middle = _particleProperties.getColorMiddle();
|
||||
particleUniforms.color.finish = _particleProperties.getColorFinish();
|
||||
particleUniforms.color.spread = _particleProperties.getColorSpread();
|
||||
particleUniforms.spin.start = _particleProperties.spin.range.start;
|
||||
particleUniforms.spin.middle = _particleProperties.spin.gradient.target;
|
||||
particleUniforms.spin.finish = _particleProperties.spin.range.finish;
|
||||
|
@ -158,6 +161,7 @@ void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEn
|
|||
}
|
||||
|
||||
ItemKey ParticleEffectEntityRenderer::getKey() {
|
||||
// FIXME: implement isTransparent() for particles and an opaque pipeline
|
||||
if (_visible) {
|
||||
return ItemKey::Builder::transparentShape().withTagBits(getTagMask()).withLayer(getHifiRenderLayer());
|
||||
} else {
|
||||
|
@ -334,12 +338,18 @@ void ParticleEffectEntityRenderer::doRender(RenderArgs* args) {
|
|||
gpu::Batch& batch = *args->_batch;
|
||||
batch.setResourceTexture(0, _networkTexture->getGPUTexture());
|
||||
|
||||
Transform transform;
|
||||
Transform transform;
|
||||
// The particles are in world space, so the transform is unused, except for the rotation, which we use
|
||||
// if the particles are marked rotateWithEntity
|
||||
withReadLock([&] {
|
||||
transform.setRotation(_renderTransform.getRotation());
|
||||
auto& color = _uniformBuffer.edit<ParticleUniforms>().color;
|
||||
color.start = EntityRenderer::calculatePulseColor(_particleProperties.getColorStart(), _pulseProperties, _created);
|
||||
color.middle = EntityRenderer::calculatePulseColor(_particleProperties.getColorMiddle(), _pulseProperties, _created);
|
||||
color.finish = EntityRenderer::calculatePulseColor(_particleProperties.getColorFinish(), _pulseProperties, _created);
|
||||
color.spread = EntityRenderer::calculatePulseColor(_particleProperties.getColorSpread(), _pulseProperties, _created);
|
||||
});
|
||||
|
||||
batch.setModelTransform(transform);
|
||||
batch.setUniformBuffer(0, _uniformBuffer);
|
||||
batch.setInputFormat(_vertexFormat);
|
||||
|
|
|
@ -94,6 +94,8 @@ private:
|
|||
BufferView _uniformBuffer;
|
||||
quint64 _lastSimulated { 0 };
|
||||
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
|
||||
NetworkTexturePointer _networkTexture;
|
||||
ScenePointer _scene;
|
||||
};
|
||||
|
|
|
@ -85,6 +85,10 @@ bool ShapeEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin
|
|||
return true;
|
||||
}
|
||||
|
||||
if (_pulseProperties != entity->getPulseProperties()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -97,6 +101,7 @@ void ShapeEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
}
|
||||
|
||||
_shape = entity->getShape();
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
});
|
||||
|
||||
void* key = (void*)this;
|
||||
|
@ -141,6 +146,10 @@ void ShapeEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPoint
|
|||
}
|
||||
|
||||
bool ShapeEntityRenderer::isTransparent() const {
|
||||
if (_pulseProperties.getAlphaMode() != PulseMode::NONE) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_procedural.isEnabled() && _procedural.isFading()) {
|
||||
return Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) < 1.0f;
|
||||
}
|
||||
|
@ -248,6 +257,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
|
|||
materials = _materials["0"];
|
||||
auto& schema = materials.getSchemaBuffer().get<graphics::MultiMaterial::Schema>();
|
||||
outColor = glm::vec4(schema._albedo, schema._opacity);
|
||||
outColor = EntityRenderer::calculatePulseColor(outColor, _pulseProperties, _created);
|
||||
if (_procedural.isReady()) {
|
||||
outColor = _procedural.getColor(outColor);
|
||||
outColor.a *= _procedural.isFading() ? Interpolate::calculateFadeRatio(_procedural.getFadeStartTime()) : 1.0f;
|
||||
|
|
|
@ -40,9 +40,12 @@ private:
|
|||
Procedural _procedural;
|
||||
QString _lastUserData;
|
||||
entity::Shape _shape { entity::Sphere };
|
||||
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
std::shared_ptr<graphics::Material> _material { std::make_shared<graphics::Material>() };
|
||||
glm::u8vec3 _color;
|
||||
float _alpha;
|
||||
|
||||
glm::vec3 _position;
|
||||
glm::vec3 _dimensions;
|
||||
glm::quat _orientation;
|
||||
|
|
|
@ -41,7 +41,7 @@ TextEntityRenderer::~TextEntityRenderer() {
|
|||
}
|
||||
|
||||
bool TextEntityRenderer::isTransparent() const {
|
||||
return Parent::isTransparent() || _textAlpha < 1.0f || _backgroundAlpha < 1.0f;
|
||||
return Parent::isTransparent() || _textAlpha < 1.0f || _backgroundAlpha < 1.0f || _pulseProperties.getAlphaMode() != PulseMode::NONE;
|
||||
}
|
||||
|
||||
ShapeKey TextEntityRenderer::getShapeKey() {
|
||||
|
@ -104,6 +104,10 @@ bool TextEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint
|
|||
return true;
|
||||
}
|
||||
|
||||
if (_pulseProperties != entity->getPulseProperties()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -119,32 +123,39 @@ void TextEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen
|
|||
}
|
||||
|
||||
void TextEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
||||
_text = entity->getText();
|
||||
_lineHeight = entity->getLineHeight();
|
||||
_textColor = toGlm(entity->getTextColor());
|
||||
_textAlpha = entity->getTextAlpha();
|
||||
_backgroundColor = toGlm(entity->getBackgroundColor());
|
||||
_backgroundAlpha = entity->getBackgroundAlpha();
|
||||
_billboardMode = entity->getBillboardMode();
|
||||
_leftMargin = entity->getLeftMargin();
|
||||
_rightMargin = entity->getRightMargin();
|
||||
_topMargin = entity->getTopMargin();
|
||||
_bottomMargin = entity->getBottomMargin();
|
||||
withWriteLock([&] {
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
_text = entity->getText();
|
||||
_lineHeight = entity->getLineHeight();
|
||||
_textColor = toGlm(entity->getTextColor());
|
||||
_textAlpha = entity->getTextAlpha();
|
||||
_backgroundColor = toGlm(entity->getBackgroundColor());
|
||||
_backgroundAlpha = entity->getBackgroundAlpha();
|
||||
_billboardMode = entity->getBillboardMode();
|
||||
_leftMargin = entity->getLeftMargin();
|
||||
_rightMargin = entity->getRightMargin();
|
||||
_topMargin = entity->getTopMargin();
|
||||
_bottomMargin = entity->getBottomMargin();
|
||||
});
|
||||
}
|
||||
|
||||
void TextEntityRenderer::doRender(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RenderableTextEntityItem::render");
|
||||
|
||||
glm::vec4 textColor;
|
||||
glm::vec4 backgroundColor;
|
||||
Transform modelTransform;
|
||||
glm::vec3 dimensions;
|
||||
withReadLock([&] {
|
||||
modelTransform = _renderTransform;
|
||||
dimensions = _dimensions;
|
||||
});
|
||||
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
glm::vec4 textColor = glm::vec4(_textColor, fadeRatio * _textAlpha);
|
||||
glm::vec4 backgroundColor = glm::vec4(_backgroundColor, fadeRatio * _backgroundAlpha);
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
textColor = glm::vec4(_textColor, fadeRatio * _textAlpha);
|
||||
textColor = EntityRenderer::calculatePulseColor(textColor, _pulseProperties, _created);
|
||||
backgroundColor = glm::vec4(_backgroundColor, fadeRatio * _backgroundAlpha);
|
||||
backgroundColor = EntityRenderer::calculatePulseColor(backgroundColor, _pulseProperties, _created);
|
||||
});
|
||||
|
||||
// Render background
|
||||
static const float SLIGHTLY_BEHIND = -0.005f;
|
||||
|
|
|
@ -38,6 +38,8 @@ private:
|
|||
int _geometryID{ 0 };
|
||||
std::shared_ptr<TextRenderer3D> _textRenderer;
|
||||
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
|
||||
QString _text;
|
||||
float _lineHeight;
|
||||
glm::vec3 _textColor;
|
||||
|
|
|
@ -97,7 +97,7 @@ WebEntityRenderer::~WebEntityRenderer() {
|
|||
|
||||
bool WebEntityRenderer::isTransparent() const {
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
return fadeRatio < OPAQUE_ALPHA_THRESHOLD || _alpha < 1.0f;
|
||||
return fadeRatio < OPAQUE_ALPHA_THRESHOLD || _alpha < 1.0f || _pulseProperties.getAlphaMode() != PulseMode::NONE;
|
||||
}
|
||||
|
||||
bool WebEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
|
||||
|
@ -143,6 +143,10 @@ bool WebEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointe
|
|||
return true;
|
||||
}
|
||||
|
||||
if (_pulseProperties != entity->getPulseProperties()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -201,6 +205,7 @@ void WebEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scene
|
|||
_dpi = entity->getDPI();
|
||||
_color = entity->getColor();
|
||||
_alpha = entity->getAlpha();
|
||||
_pulseProperties = entity->getPulseProperties();
|
||||
|
||||
if (_contentType == ContentType::NoContent) {
|
||||
return;
|
||||
|
@ -293,6 +298,7 @@ void WebEntityRenderer::doRender(RenderArgs* args) {
|
|||
withReadLock([&] {
|
||||
float fadeRatio = _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) : 1.0f;
|
||||
color = glm::vec4(toGlm(_color), _alpha * fadeRatio);
|
||||
color = EntityRenderer::calculatePulseColor(color, _pulseProperties, _created);
|
||||
batch.setModelTransform(_renderTransform);
|
||||
});
|
||||
batch.setResourceTexture(0, _texture);
|
||||
|
|
|
@ -84,6 +84,7 @@ private:
|
|||
|
||||
glm::u8vec3 _color;
|
||||
float _alpha { 1.0f };
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
|
||||
QString _sourceURL;
|
||||
uint16_t _dpi;
|
||||
|
|
|
@ -98,9 +98,7 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
|
|||
requestedProperties += PROP_RENDER_LAYER;
|
||||
requestedProperties += PROP_PRIMITIVE_MODE;
|
||||
requestedProperties += PROP_IGNORE_PICK_INTERSECTION;
|
||||
withReadLock([&] {
|
||||
requestedProperties += _grabProperties.getEntityProperties(params);
|
||||
});
|
||||
requestedProperties += _grabProperties.getEntityProperties(params);
|
||||
|
||||
// Physics
|
||||
requestedProperties += PROP_DENSITY;
|
||||
|
@ -3427,7 +3425,19 @@ void EntityItem::addGrab(GrabPointer grab) {
|
|||
enableNoBootstrap();
|
||||
SpatiallyNestable::addGrab(grab);
|
||||
|
||||
if (getDynamic() && getParentID().isNull()) {
|
||||
if (!getParentID().isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int jointIndex = grab->getParentJointIndex();
|
||||
bool isFarGrab = jointIndex == FARGRAB_RIGHTHAND_INDEX
|
||||
|| jointIndex == FARGRAB_LEFTHAND_INDEX
|
||||
|| jointIndex == FARGRAB_MOUSE_INDEX;
|
||||
|
||||
// GRAB HACK: FarGrab doesn't work on non-dynamic things yet, but we really really want NearGrab
|
||||
// (aka Hold) to work for such ojects, hence we filter the useAction case like so:
|
||||
bool useAction = getDynamic() || (_physicsInfo && !isFarGrab);
|
||||
if (useAction) {
|
||||
EntityTreePointer entityTree = getTree();
|
||||
assert(entityTree);
|
||||
EntitySimulationPointer simulation = entityTree ? entityTree->getSimulation() : nullptr;
|
||||
|
@ -3438,13 +3448,11 @@ void EntityItem::addGrab(GrabPointer grab) {
|
|||
|
||||
EntityDynamicType dynamicType;
|
||||
QVariantMap arguments;
|
||||
int grabParentJointIndex =grab->getParentJointIndex();
|
||||
if (grabParentJointIndex == FARGRAB_RIGHTHAND_INDEX || grabParentJointIndex == FARGRAB_LEFTHAND_INDEX ||
|
||||
grabParentJointIndex == FARGRAB_MOUSE_INDEX) {
|
||||
if (isFarGrab) {
|
||||
// add a far-grab action
|
||||
dynamicType = DYNAMIC_TYPE_FAR_GRAB;
|
||||
arguments["otherID"] = grab->getOwnerID();
|
||||
arguments["otherJointIndex"] = grabParentJointIndex;
|
||||
arguments["otherJointIndex"] = jointIndex;
|
||||
arguments["targetPosition"] = vec3ToQMap(grab->getPositionalOffset());
|
||||
arguments["targetRotation"] = quatToQMap(grab->getRotationalOffset());
|
||||
arguments["linearTimeScale"] = 0.05;
|
||||
|
@ -3465,11 +3473,23 @@ void EntityItem::addGrab(GrabPointer grab) {
|
|||
grab->setActionID(actionID);
|
||||
_grabActions[actionID] = action;
|
||||
simulation->addDynamic(action);
|
||||
markDirtyFlags(Simulation::DIRTY_MOTION_TYPE);
|
||||
simulation->changeEntity(getThisPointer());
|
||||
}
|
||||
}
|
||||
|
||||
void EntityItem::removeGrab(GrabPointer grab) {
|
||||
int oldNumGrabs = _grabs.size();
|
||||
SpatiallyNestable::removeGrab(grab);
|
||||
if (!getDynamic() && _grabs.size() != oldNumGrabs) {
|
||||
// GRAB HACK: the expected behavior is for non-dynamic grabbed things to NOT be throwable
|
||||
// so we slam the velocities to zero here whenever the number of grabs change.
|
||||
// NOTE: if there is still another grab in effect this shouldn't interfere with the object's motion
|
||||
// because that grab will slam the position+velocities next frame.
|
||||
setLocalVelocity(glm::vec3(0.0f));
|
||||
setAngularVelocity(glm::vec3(0.0f));
|
||||
}
|
||||
markDirtyFlags(Simulation::DIRTY_MOTION_TYPE);
|
||||
|
||||
QUuid actionID = grab->getActionID();
|
||||
if (!actionID.isNull()) {
|
||||
|
|
|
@ -42,6 +42,7 @@ BloomPropertyGroup EntityItemProperties::_staticBloom;
|
|||
KeyLightPropertyGroup EntityItemProperties::_staticKeyLight;
|
||||
AmbientLightPropertyGroup EntityItemProperties::_staticAmbientLight;
|
||||
GrabPropertyGroup EntityItemProperties::_staticGrab;
|
||||
PulsePropertyGroup EntityItemProperties::_staticPulse;
|
||||
|
||||
EntityPropertyList PROP_LAST_ITEM = (EntityPropertyList)(PROP_AFTER_LAST_ITEM - 1);
|
||||
|
||||
|
@ -514,6 +515,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL);
|
||||
CHECK_PROPERTY_CHANGE(PROP_COLOR, color);
|
||||
CHECK_PROPERTY_CHANGE(PROP_ALPHA, alpha);
|
||||
changedProperties += _pulse.getChangedProperties();
|
||||
CHECK_PROPERTY_CHANGE(PROP_TEXTURES, textures);
|
||||
|
||||
// Particles
|
||||
|
@ -1115,6 +1117,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* and <code>spinSpread == PI/2</code>, each particle will have a spin in the range <code>PI/2</code> – <code>3*PI/2</code>.
|
||||
* @property {boolean} rotateWithEntity=false - Whether or not the particles' spin will rotate with the entity. If false, when <code>particleSpin == 0</code>, the particles will point
|
||||
* up in the world. If true, they will point towards the entity's up vector, based on its orientation.
|
||||
* @property {Entities.Pulse} pulse - The pulse-related properties. Deprecated.
|
||||
*
|
||||
* @property {ShapeType} shapeType="none" - <em>Currently not used.</em> <em>Read-only.</em>
|
||||
*
|
||||
|
@ -1242,6 +1245,8 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {Entities.Shape} shape="Sphere" - The shape of the entity.
|
||||
* @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity.
|
||||
* @property {Color} color=255,255,255 - The color of the entity.
|
||||
* @property {number} alpha=1 - The alpha of the shape.
|
||||
* @property {Entities.Pulse} pulse - The pulse-related properties. Deprecated.
|
||||
* @example <caption>Create a cylinder.</caption>
|
||||
* var shape = Entities.addEntity({
|
||||
* type: "Shape",
|
||||
|
@ -1281,6 +1286,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {number} rightMargin=0.0 - The right margin, in meters.
|
||||
* @property {number} topMargin=0.0 - The top margin, in meters.
|
||||
* @property {number} bottomMargin=0.0 - The bottom margin, in meters.
|
||||
* @property {Entities.Pulse} pulse - The pulse-related properties. Deprecated.
|
||||
* @example <caption>Create a text entity.</caption>
|
||||
* var text = Entities.addEntity({
|
||||
* type: "Text",
|
||||
|
@ -1310,6 +1316,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @property {string} scriptURL="" - The URL of a JavaScript file to inject into the Web page.
|
||||
* @property {number} maxFPS=10 - The maximum update rate for the Web content, in frames/second.
|
||||
* @property {WebInputMode} inputMode="touch" - The user input mode to use.
|
||||
* @property {Entities.Pulse} pulse - The pulse-related properties. Deprecated.
|
||||
* @example <caption>Create a Web entity displaying at 1920 x 1080 resolution.</caption>
|
||||
* var METERS_TO_INCHES = 39.3701;
|
||||
* var entity = Entities.addEntity({
|
||||
|
@ -1419,6 +1426,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* the full image in that dimension.
|
||||
* @property {Color} color=255,255,255 - The color of the image.
|
||||
* @property {number} alpha=1 - The alpha of the image.
|
||||
* @property {Entities.Pulse} pulse - The pulse-related properties. Deprecated.
|
||||
* @example <caption>Create a image entity.</caption>
|
||||
* var image = Entities.addEntity({
|
||||
* type: "Image",
|
||||
|
@ -1442,6 +1450,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* line. Minimum value = <code>1</code>.
|
||||
* @property {number} minorGridEvery=1 - Real number of meters at which to draw thin grid lines. Minimum value =
|
||||
* <code>0.001</code>.
|
||||
* @property {Entities.Pulse} pulse - The pulse-related properties. Deprecated.
|
||||
* @example <caption>Create a grid entity.</caption>
|
||||
* var grid = Entities.addEntity({
|
||||
* type: "Grid",
|
||||
|
@ -1579,6 +1588,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, getShapeTypeAsString());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha);
|
||||
_pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures);
|
||||
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MAX_PARTICLES, maxParticles);
|
||||
|
@ -1652,6 +1662,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
if (_type == EntityTypes::Box || _type == EntityTypes::Sphere || _type == EntityTypes::Shape) {
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha);
|
||||
_pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SHAPE, shape);
|
||||
}
|
||||
|
||||
|
@ -1667,6 +1678,8 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
|
||||
// Text only
|
||||
if (_type == EntityTypes::Text) {
|
||||
_pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
|
||||
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXT, text);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_HEIGHT, lineHeight);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_TYPED(PROP_TEXT_COLOR, textColor, getTextColor(), u8vec3Color);
|
||||
|
@ -1708,6 +1721,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
if (_type == EntityTypes::Web) {
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha);
|
||||
_pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
|
||||
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SOURCE_URL, sourceUrl);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DPI, dpi);
|
||||
|
@ -1772,6 +1786,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
if (_type == EntityTypes::Image) {
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha);
|
||||
_pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
|
||||
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IMAGE_URL, imageURL);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_EMISSIVE, emissive);
|
||||
|
@ -1792,6 +1807,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
if (_type == EntityTypes::Grid) {
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_COLOR, color, u8vec3Color);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha);
|
||||
_pulse.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
|
||||
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_GRID_FOLLOW_CAMERA, followCamera);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MAJOR_GRID_EVERY, majorGridEvery);
|
||||
|
@ -1972,6 +1988,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
|||
COPY_PROPERTY_FROM_QSCRIPTVALUE(compoundShapeURL, QString, setCompoundShapeURL);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(color, u8vec3Color, setColor);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(alpha, float, setAlpha);
|
||||
_pulse.copyFromScriptValue(object, _defaultSettings);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(textures, QString, setTextures);
|
||||
|
||||
// Particles
|
||||
|
@ -2245,6 +2262,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
|
|||
COPY_PROPERTY_IF_CHANGED(compoundShapeURL);
|
||||
COPY_PROPERTY_IF_CHANGED(color);
|
||||
COPY_PROPERTY_IF_CHANGED(alpha);
|
||||
_pulse.merge(other._pulse);
|
||||
COPY_PROPERTY_IF_CHANGED(textures);
|
||||
|
||||
// Particles
|
||||
|
@ -2552,6 +2570,13 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr
|
|||
ADD_PROPERTY_TO_MAP(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString);
|
||||
ADD_PROPERTY_TO_MAP(PROP_COLOR, Color, color, u8vec3Color);
|
||||
ADD_PROPERTY_TO_MAP_WITH_RANGE(PROP_ALPHA, Alpha, alpha, float, particle::MINIMUM_ALPHA, particle::MAXIMUM_ALPHA);
|
||||
{ // Pulse
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_PULSE_MIN, Pulse, pulse, Min, min);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_PULSE_MAX, Pulse, pulse, Max, max);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_PULSE_PERIOD, Pulse, pulse, Period, period);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_PULSE_COLOR_MODE, Pulse, pulse, ColorMode, colorMode);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_PULSE_ALPHA_MODE, Pulse, pulse, AlphaMode, alphaMode);
|
||||
}
|
||||
ADD_PROPERTY_TO_MAP(PROP_TEXTURES, Textures, textures, QString);
|
||||
|
||||
// Particles
|
||||
|
@ -2952,6 +2977,9 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)(properties.getShapeType()));
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha());
|
||||
_staticPulse.setProperties(properties);
|
||||
_staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags,
|
||||
propertiesDidntFit, propertyCount, appendState);
|
||||
APPEND_ENTITY_PROPERTY(PROP_TEXTURES, properties.getTextures());
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_MAX_PARTICLES, properties.getMaxParticles());
|
||||
|
@ -3023,6 +3051,10 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::Text) {
|
||||
_staticPulse.setProperties(properties);
|
||||
_staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags,
|
||||
propertiesDidntFit, propertyCount, appendState);
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_TEXT, properties.getText());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, properties.getLineHeight());
|
||||
APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, properties.getTextColor());
|
||||
|
@ -3084,6 +3116,9 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
if (properties.getType() == EntityTypes::Web) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha());
|
||||
_staticPulse.setProperties(properties);
|
||||
_staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags,
|
||||
propertiesDidntFit, propertyCount, appendState);
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, properties.getSourceUrl());
|
||||
APPEND_ENTITY_PROPERTY(PROP_DPI, properties.getDPI());
|
||||
|
@ -3118,6 +3153,9 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
properties.getType() == EntityTypes::Sphere) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha());
|
||||
_staticPulse.setProperties(properties);
|
||||
_staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags,
|
||||
propertiesDidntFit, propertyCount, appendState);
|
||||
APPEND_ENTITY_PROPERTY(PROP_SHAPE, properties.getShape());
|
||||
}
|
||||
|
||||
|
@ -3138,6 +3176,9 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
if (properties.getType() == EntityTypes::Image) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha());
|
||||
_staticPulse.setProperties(properties);
|
||||
_staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags,
|
||||
propertiesDidntFit, propertyCount, appendState);
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_IMAGE_URL, properties.getImageURL());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMISSIVE, properties.getEmissive());
|
||||
|
@ -3150,6 +3191,9 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy
|
|||
if (properties.getType() == EntityTypes::Grid) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, properties.getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, properties.getAlpha());
|
||||
_staticPulse.setProperties(properties);
|
||||
_staticPulse.appendToEditPacket(packetData, requestedProperties, propertyFlags,
|
||||
propertiesDidntFit, propertyCount, appendState);
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_GRID_FOLLOW_CAMERA, properties.getFollowCamera());
|
||||
APPEND_ENTITY_PROPERTY(PROP_MAJOR_GRID_EVERY, properties.getMajorGridEvery());
|
||||
|
@ -3401,6 +3445,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha);
|
||||
properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXTURES, QString, setTextures);
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MAX_PARTICLES, quint32, setMaxParticles);
|
||||
|
@ -3472,6 +3517,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::Text) {
|
||||
properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT, QString, setText);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LINE_HEIGHT, float, setLineHeight);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_TEXT_COLOR, u8vec3Color, setTextColor);
|
||||
|
@ -3524,6 +3571,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
if (properties.getType() == EntityTypes::Web) {
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha);
|
||||
properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SOURCE_URL, QString, setSourceUrl);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_DPI, uint16_t, setDPI);
|
||||
|
@ -3558,6 +3606,8 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
properties.getType() == EntityTypes::Sphere) {
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha);
|
||||
properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE, QString, setShape);
|
||||
}
|
||||
|
||||
|
@ -3578,6 +3628,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
if (properties.getType() == EntityTypes::Image) {
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha);
|
||||
properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_IMAGE_URL, QString, setImageURL);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_EMISSIVE, bool, setEmissive);
|
||||
|
@ -3590,6 +3641,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
|
|||
if (properties.getType() == EntityTypes::Grid) {
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_COLOR, u8vec3Color, setColor);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ALPHA, float, setAlpha);
|
||||
properties.getPulse().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
|
||||
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GRID_FOLLOW_CAMERA, bool, setFollowCamera);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MAJOR_GRID_EVERY, uint32_t, setMajorGridEvery);
|
||||
|
@ -3793,6 +3845,7 @@ void EntityItemProperties::markAllChanged() {
|
|||
_shapeTypeChanged = true;
|
||||
_colorChanged = true;
|
||||
_alphaChanged = true;
|
||||
_pulse.markAllChanged();
|
||||
_texturesChanged = true;
|
||||
_compoundShapeURLChanged = true;
|
||||
|
||||
|
@ -4257,6 +4310,7 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
|||
if (alphaChanged()) {
|
||||
out += "alpha";
|
||||
}
|
||||
getPulse().listChangedProperties(out);
|
||||
if (texturesChanged()) {
|
||||
out += "textures";
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
#include "SkyboxPropertyGroup.h"
|
||||
#include "HazePropertyGroup.h"
|
||||
#include "BloomPropertyGroup.h"
|
||||
#include "PulsePropertyGroup.h"
|
||||
|
||||
#include "MaterialMappingMode.h"
|
||||
#include "BillboardMode.h"
|
||||
|
@ -235,6 +236,7 @@ public:
|
|||
DEFINE_PROPERTY_REF(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString, "");
|
||||
DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, u8vec3Color, particle::DEFAULT_COLOR);
|
||||
DEFINE_PROPERTY(PROP_ALPHA, Alpha, alpha, float, particle::DEFAULT_ALPHA);
|
||||
DEFINE_PROPERTY_GROUP(Pulse, pulse, PulsePropertyGroup);
|
||||
DEFINE_PROPERTY_REF(PROP_TEXTURES, Textures, textures, QString, "");
|
||||
|
||||
// Particles
|
||||
|
|
|
@ -383,13 +383,29 @@ inline QRect QRect_convertFromScriptValue(const QScriptValue& v, bool& isValid)
|
|||
} \
|
||||
}
|
||||
|
||||
#define COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(P, S) \
|
||||
QScriptValue P = object.property(#P); \
|
||||
if (P.isValid()) { \
|
||||
QString newValue = P.toVariant().toString(); \
|
||||
if (_defaultSettings || newValue != get##S##AsString()) { \
|
||||
set##S##FromString(newValue); \
|
||||
} \
|
||||
#define COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(P, S) \
|
||||
{ \
|
||||
QScriptValue P = object.property(#P); \
|
||||
if (P.isValid()) { \
|
||||
QString newValue = P.toVariant().toString(); \
|
||||
if (_defaultSettings || newValue != get##S##AsString()) { \
|
||||
set##S##FromString(newValue); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_ENUM(G, P, S) \
|
||||
{ \
|
||||
QScriptValue G = object.property(#G); \
|
||||
if (G.isValid()) { \
|
||||
QScriptValue P = G.property(#P); \
|
||||
if (P.isValid()) { \
|
||||
QString newValue = P.toVariant().toString(); \
|
||||
if (_defaultSettings || newValue != get##S##AsString()) { \
|
||||
set##S##FromString(newValue); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define DEFINE_PROPERTY_GROUP(N, n, T) \
|
||||
|
|
|
@ -106,12 +106,17 @@ enum EntityPropertyList {
|
|||
PROP_LOCAL_VELOCITY,
|
||||
PROP_LOCAL_ANGULAR_VELOCITY,
|
||||
PROP_LOCAL_DIMENSIONS,
|
||||
|
||||
|
||||
// These properties are used by multiple subtypes but aren't in the base EntityItem
|
||||
PROP_SHAPE_TYPE,
|
||||
PROP_COMPOUND_SHAPE_URL,
|
||||
PROP_COLOR,
|
||||
PROP_ALPHA,
|
||||
PROP_PULSE_MIN,
|
||||
PROP_PULSE_MAX,
|
||||
PROP_PULSE_PERIOD,
|
||||
PROP_PULSE_COLOR_MODE,
|
||||
PROP_PULSE_ALPHA_MODE,
|
||||
PROP_TEXTURES,
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -35,6 +35,9 @@ EntityItemProperties GridEntityItem::getProperties(const EntityPropertyFlags& de
|
|||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha);
|
||||
withReadLock([&] {
|
||||
_pulseProperties.getProperties(properties);
|
||||
});
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(followCamera, getFollowCamera);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(majorGridEvery, getMajorGridEvery);
|
||||
|
@ -48,6 +51,10 @@ bool GridEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha);
|
||||
withWriteLock([&] {
|
||||
bool pulsePropertiesChanged = _pulseProperties.setProperties(properties);
|
||||
somethingChanged |= pulsePropertiesChanged;
|
||||
});
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(followCamera, setFollowCamera);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(majorGridEvery, setMajorGridEvery);
|
||||
|
@ -76,6 +83,13 @@ int GridEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
|
||||
READ_ENTITY_PROPERTY(PROP_COLOR, u8vec3Color, setColor);
|
||||
READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha);
|
||||
withWriteLock([&] {
|
||||
int bytesFromPulse = _pulseProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData,
|
||||
somethingChanged);
|
||||
bytesRead += bytesFromPulse;
|
||||
dataAt += bytesFromPulse;
|
||||
});
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_GRID_FOLLOW_CAMERA, bool, setFollowCamera);
|
||||
READ_ENTITY_PROPERTY(PROP_MAJOR_GRID_EVERY, uint32_t, setMajorGridEvery);
|
||||
|
@ -89,6 +103,7 @@ EntityPropertyFlags GridEntityItem::getEntityProperties(EncodeBitstreamParams& p
|
|||
|
||||
requestedProperties += PROP_COLOR;
|
||||
requestedProperties += PROP_ALPHA;
|
||||
requestedProperties += _pulseProperties.getEntityProperties(params);
|
||||
|
||||
requestedProperties += PROP_GRID_FOLLOW_CAMERA;
|
||||
requestedProperties += PROP_MAJOR_GRID_EVERY;
|
||||
|
@ -98,7 +113,7 @@ EntityPropertyFlags GridEntityItem::getEntityProperties(EncodeBitstreamParams& p
|
|||
}
|
||||
|
||||
void GridEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
|
@ -109,6 +124,10 @@ void GridEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
|
|||
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha());
|
||||
withReadLock([&] {
|
||||
_pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties,
|
||||
propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
});
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_GRID_FOLLOW_CAMERA, getFollowCamera());
|
||||
APPEND_ENTITY_PROPERTY(PROP_MAJOR_GRID_EVERY, getMajorGridEvery());
|
||||
|
@ -175,4 +194,10 @@ float GridEntityItem::getMinorGridEvery() const {
|
|||
return resultWithReadLock<float>([&] {
|
||||
return _minorGridEvery;
|
||||
});
|
||||
}
|
||||
|
||||
PulsePropertyGroup GridEntityItem::getPulseProperties() const {
|
||||
return resultWithReadLock<PulsePropertyGroup>([&] {
|
||||
return _pulseProperties;
|
||||
});
|
||||
}
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "EntityItem.h"
|
||||
|
||||
#include "PulsePropertyGroup.h"
|
||||
|
||||
class GridEntityItem : public EntityItem {
|
||||
using Pointer = std::shared_ptr<GridEntityItem>;
|
||||
public:
|
||||
|
@ -29,7 +31,7 @@ public:
|
|||
EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
|
@ -59,9 +61,13 @@ public:
|
|||
void setMinorGridEvery(float minorGridEvery);
|
||||
float getMinorGridEvery() const;
|
||||
|
||||
PulsePropertyGroup getPulseProperties() const;
|
||||
|
||||
protected:
|
||||
glm::u8vec3 _color;
|
||||
float _alpha;
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
|
||||
bool _followCamera { true };
|
||||
uint32_t _majorGridEvery { DEFAULT_MAJOR_GRID_EVERY };
|
||||
float _minorGridEvery { DEFAULT_MINOR_GRID_EVERY };
|
||||
|
|
|
@ -32,6 +32,9 @@ EntityItemProperties ImageEntityItem::getProperties(const EntityPropertyFlags& d
|
|||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha);
|
||||
withReadLock([&] {
|
||||
_pulseProperties.getProperties(properties);
|
||||
});
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(imageURL, getImageURL);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(emissive, getEmissive);
|
||||
|
@ -47,6 +50,10 @@ bool ImageEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha);
|
||||
withWriteLock([&] {
|
||||
bool pulsePropertiesChanged = _pulseProperties.setProperties(properties);
|
||||
somethingChanged |= pulsePropertiesChanged;
|
||||
});
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(imageURL, setImageURL);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(emissive, setEmissive);
|
||||
|
@ -77,6 +84,13 @@ int ImageEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
|
||||
READ_ENTITY_PROPERTY(PROP_COLOR, u8vec3Color, setColor);
|
||||
READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha);
|
||||
withWriteLock([&] {
|
||||
int bytesFromPulse = _pulseProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData,
|
||||
somethingChanged);
|
||||
bytesRead += bytesFromPulse;
|
||||
dataAt += bytesFromPulse;
|
||||
});
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_IMAGE_URL, QString, setImageURL);
|
||||
READ_ENTITY_PROPERTY(PROP_EMISSIVE, bool, setEmissive);
|
||||
|
@ -92,6 +106,7 @@ EntityPropertyFlags ImageEntityItem::getEntityProperties(EncodeBitstreamParams&
|
|||
|
||||
requestedProperties += PROP_COLOR;
|
||||
requestedProperties += PROP_ALPHA;
|
||||
requestedProperties += _pulseProperties.getEntityProperties(params);
|
||||
|
||||
requestedProperties += PROP_IMAGE_URL;
|
||||
requestedProperties += PROP_EMISSIVE;
|
||||
|
@ -103,7 +118,7 @@ EntityPropertyFlags ImageEntityItem::getEntityProperties(EncodeBitstreamParams&
|
|||
}
|
||||
|
||||
void ImageEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
|
@ -114,6 +129,10 @@ void ImageEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
|
|||
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha());
|
||||
withReadLock([&] {
|
||||
_pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties,
|
||||
propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
});
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_IMAGE_URL, getImageURL());
|
||||
APPEND_ENTITY_PROPERTY(PROP_EMISSIVE, getEmissive());
|
||||
|
@ -266,4 +285,10 @@ float ImageEntityItem::getAlpha() const {
|
|||
return resultWithReadLock<float>([&] {
|
||||
return _alpha;
|
||||
});
|
||||
}
|
||||
|
||||
PulsePropertyGroup ImageEntityItem::getPulseProperties() const {
|
||||
return resultWithReadLock<PulsePropertyGroup>([&] {
|
||||
return _pulseProperties;
|
||||
});
|
||||
}
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "EntityItem.h"
|
||||
|
||||
#include "PulsePropertyGroup.h"
|
||||
|
||||
class ImageEntityItem : public EntityItem {
|
||||
using Pointer = std::shared_ptr<ImageEntityItem>;
|
||||
public:
|
||||
|
@ -29,7 +31,7 @@ public:
|
|||
EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
|
@ -72,6 +74,8 @@ public:
|
|||
void setAlpha(float alpha);
|
||||
float getAlpha() const;
|
||||
|
||||
PulsePropertyGroup getPulseProperties() const;
|
||||
|
||||
protected:
|
||||
QString _imageURL;
|
||||
bool _emissive { false };
|
||||
|
@ -81,6 +85,7 @@ protected:
|
|||
|
||||
glm::u8vec3 _color;
|
||||
float _alpha;
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
};
|
||||
|
||||
#endif // hifi_ImageEntityItem_h
|
||||
|
|
|
@ -412,6 +412,9 @@ EntityItemProperties ParticleEffectEntityItem::getProperties(const EntityPropert
|
|||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha);
|
||||
withReadLock([&] {
|
||||
_pulseProperties.getProperties(properties);
|
||||
});
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textures, getTextures);
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(maxParticles, getMaxParticles);
|
||||
|
@ -463,6 +466,10 @@ bool ParticleEffectEntityItem::setProperties(const EntityItemProperties& propert
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha);
|
||||
withWriteLock([&] {
|
||||
bool pulsePropertiesChanged = _pulseProperties.setProperties(properties);
|
||||
somethingChanged |= pulsePropertiesChanged;
|
||||
});
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textures, setTextures);
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(maxParticles, setMaxParticles);
|
||||
|
@ -535,6 +542,13 @@ int ParticleEffectEntityItem::readEntitySubclassDataFromBuffer(const unsigned ch
|
|||
READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType);
|
||||
READ_ENTITY_PROPERTY(PROP_COLOR, u8vec3Color, setColor);
|
||||
READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha);
|
||||
withWriteLock([&] {
|
||||
int bytesFromPulse = _pulseProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData,
|
||||
somethingChanged);
|
||||
bytesRead += bytesFromPulse;
|
||||
dataAt += bytesFromPulse;
|
||||
});
|
||||
READ_ENTITY_PROPERTY(PROP_TEXTURES, QString, setTextures);
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_MAX_PARTICLES, quint32, setMaxParticles);
|
||||
|
@ -586,6 +600,7 @@ EntityPropertyFlags ParticleEffectEntityItem::getEntityProperties(EncodeBitstrea
|
|||
requestedProperties += PROP_SHAPE_TYPE;
|
||||
requestedProperties += PROP_COLOR;
|
||||
requestedProperties += PROP_ALPHA;
|
||||
requestedProperties += _pulseProperties.getEntityProperties(params);
|
||||
requestedProperties += PROP_TEXTURES;
|
||||
|
||||
requestedProperties += PROP_MAX_PARTICLES;
|
||||
|
@ -643,6 +658,10 @@ void ParticleEffectEntityItem::appendSubclassData(OctreePacketData* packetData,
|
|||
APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)getShapeType());
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha());
|
||||
withReadLock([&] {
|
||||
_pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties,
|
||||
propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
});
|
||||
APPEND_ENTITY_PROPERTY(PROP_TEXTURES, getTextures());
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_MAX_PARTICLES, getMaxParticles());
|
||||
|
@ -786,4 +805,10 @@ particle::Properties ParticleEffectEntityItem::getParticleProperties() const {
|
|||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
PulsePropertyGroup ParticleEffectEntityItem::getPulseProperties() const {
|
||||
return resultWithReadLock<PulsePropertyGroup>([&] {
|
||||
return _pulseProperties;
|
||||
});
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
#include "EntityItem.h"
|
||||
|
||||
#include "ColorUtils.h"
|
||||
#include "PulsePropertyGroup.h"
|
||||
|
||||
namespace particle {
|
||||
static const float SCRIPT_MAXIMUM_PI = 3.1416f; // Round up so that reasonable property values work
|
||||
|
@ -341,9 +342,11 @@ public:
|
|||
virtual bool supportsDetailedIntersection() const override { return false; }
|
||||
|
||||
particle::Properties getParticleProperties() const;
|
||||
PulsePropertyGroup getPulseProperties() const;
|
||||
|
||||
protected:
|
||||
particle::Properties _particleProperties;
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
bool _isEmitting { true };
|
||||
|
||||
ShapeType _shapeType { SHAPE_TYPE_NONE };
|
||||
|
|
249
libraries/entities/src/PulsePropertyGroup.cpp
Normal file
249
libraries/entities/src/PulsePropertyGroup.cpp
Normal file
|
@ -0,0 +1,249 @@
|
|||
//
|
||||
// PulsePropertyGroup.cpp
|
||||
//
|
||||
// Created by Sam Gondelman on 1/15/19
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "PulsePropertyGroup.h"
|
||||
|
||||
#include <OctreePacketData.h>
|
||||
|
||||
#include "EntityItemProperties.h"
|
||||
#include "EntityItemPropertiesMacros.h"
|
||||
|
||||
QHash<QString, PulseMode> stringToPulseModeLookup;
|
||||
|
||||
void addPulseMode(PulseMode mode) {
|
||||
stringToPulseModeLookup[PulseModeHelpers::getNameForPulseMode(mode)] = mode;
|
||||
}
|
||||
|
||||
void buildStringToPulseModeLookup() {
|
||||
addPulseMode(PulseMode::NONE);
|
||||
addPulseMode(PulseMode::IN_PHASE);
|
||||
addPulseMode(PulseMode::OUT_PHASE);
|
||||
}
|
||||
|
||||
QString PulsePropertyGroup::getColorModeAsString() const {
|
||||
return PulseModeHelpers::getNameForPulseMode(_colorMode);
|
||||
}
|
||||
|
||||
void PulsePropertyGroup::setColorModeFromString(const QString& pulseMode) {
|
||||
if (stringToPulseModeLookup.empty()) {
|
||||
buildStringToPulseModeLookup();
|
||||
}
|
||||
auto pulseModeItr = stringToPulseModeLookup.find(pulseMode.toLower());
|
||||
if (pulseModeItr != stringToPulseModeLookup.end()) {
|
||||
_colorMode = pulseModeItr.value();
|
||||
_colorModeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
QString PulsePropertyGroup::getAlphaModeAsString() const {
|
||||
return PulseModeHelpers::getNameForPulseMode(_alphaMode);
|
||||
}
|
||||
|
||||
void PulsePropertyGroup::setAlphaModeFromString(const QString& pulseMode) {
|
||||
if (stringToPulseModeLookup.empty()) {
|
||||
buildStringToPulseModeLookup();
|
||||
}
|
||||
auto pulseModeItr = stringToPulseModeLookup.find(pulseMode.toLower());
|
||||
if (pulseModeItr != stringToPulseModeLookup.end()) {
|
||||
_alphaMode = pulseModeItr.value();
|
||||
_alphaModeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
void PulsePropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties,
|
||||
QScriptEngine* engine, bool skipDefaults,
|
||||
EntityItemProperties& defaultEntityProperties) const {
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_PULSE_MIN, Pulse, pulse, Min, min);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_PULSE_MAX, Pulse, pulse, Max, max);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_PULSE_PERIOD, Pulse, pulse, Period, period);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_PULSE_COLOR_MODE, Pulse, pulse, ColorMode, colorMode, getColorModeAsString);
|
||||
COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_PULSE_ALPHA_MODE, Pulse, pulse, AlphaMode, alphaMode, getAlphaModeAsString);
|
||||
}
|
||||
|
||||
void PulsePropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) {
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(pulse, min, float, setMin);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(pulse, max, float, setMax);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(pulse, period, float, setPeriod);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_ENUM(pulse, colorMode, ColorMode);
|
||||
COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE_ENUM(pulse, alphaMode, AlphaMode);
|
||||
}
|
||||
|
||||
void PulsePropertyGroup::merge(const PulsePropertyGroup& other) {
|
||||
COPY_PROPERTY_IF_CHANGED(min);
|
||||
COPY_PROPERTY_IF_CHANGED(max);
|
||||
COPY_PROPERTY_IF_CHANGED(period);
|
||||
COPY_PROPERTY_IF_CHANGED(colorMode);
|
||||
COPY_PROPERTY_IF_CHANGED(alphaMode);
|
||||
}
|
||||
|
||||
void PulsePropertyGroup::debugDump() const {
|
||||
qCDebug(entities) << " PulsePropertyGroup: ---------------------------------------------";
|
||||
qCDebug(entities) << " _min:" << _min;
|
||||
qCDebug(entities) << " _max:" << _max;
|
||||
qCDebug(entities) << " _period:" << _period;
|
||||
qCDebug(entities) << " _colorMode:" << getColorModeAsString();
|
||||
qCDebug(entities) << " _alphaMode:" << getAlphaModeAsString();
|
||||
}
|
||||
|
||||
void PulsePropertyGroup::listChangedProperties(QList<QString>& out) {
|
||||
if (minChanged()) {
|
||||
out << "pulse-min";
|
||||
}
|
||||
if (maxChanged()) {
|
||||
out << "pulse-max";
|
||||
}
|
||||
if (periodChanged()) {
|
||||
out << "pulse-period";
|
||||
}
|
||||
if (colorModeChanged()) {
|
||||
out << "pulse-colorMode";
|
||||
}
|
||||
if (alphaModeChanged()) {
|
||||
out << "pulse-alphaMode";
|
||||
}
|
||||
}
|
||||
|
||||
bool PulsePropertyGroup::appendToEditPacket(OctreePacketData* packetData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
int& propertyCount,
|
||||
OctreeElement::AppendState& appendState) const {
|
||||
|
||||
bool successPropertyFits = true;
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_PULSE_MIN, getMin());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PULSE_MAX, getMax());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PULSE_PERIOD, getPeriod());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PULSE_COLOR_MODE, (uint32_t)getColorMode());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PULSE_ALPHA_MODE, (uint32_t)getAlphaMode());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PulsePropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags,
|
||||
const unsigned char*& dataAt , int& processedBytes) {
|
||||
|
||||
int bytesRead = 0;
|
||||
bool overwriteLocalData = true;
|
||||
bool somethingChanged = false;
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_PULSE_MIN, float, setMin);
|
||||
READ_ENTITY_PROPERTY(PROP_PULSE_MAX, float, setMax);
|
||||
READ_ENTITY_PROPERTY(PROP_PULSE_PERIOD, float, setPeriod);
|
||||
READ_ENTITY_PROPERTY(PROP_PULSE_COLOR_MODE, PulseMode, setColorMode);
|
||||
READ_ENTITY_PROPERTY(PROP_PULSE_ALPHA_MODE, PulseMode, setAlphaMode);
|
||||
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_PULSE_MIN, Min);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_PULSE_MAX, Max);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_PULSE_PERIOD, Period);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_PULSE_COLOR_MODE, ColorMode);
|
||||
DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_PULSE_ALPHA_MODE, AlphaMode);
|
||||
|
||||
processedBytes += bytesRead;
|
||||
|
||||
Q_UNUSED(somethingChanged);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PulsePropertyGroup::markAllChanged() {
|
||||
_minChanged = true;
|
||||
_maxChanged = true;
|
||||
_periodChanged = true;
|
||||
_colorModeChanged = true;
|
||||
_alphaModeChanged = true;
|
||||
}
|
||||
|
||||
EntityPropertyFlags PulsePropertyGroup::getChangedProperties() const {
|
||||
EntityPropertyFlags changedProperties;
|
||||
|
||||
CHECK_PROPERTY_CHANGE(PROP_PULSE_MIN, min);
|
||||
CHECK_PROPERTY_CHANGE(PROP_PULSE_MAX, max);
|
||||
CHECK_PROPERTY_CHANGE(PROP_PULSE_PERIOD, period);
|
||||
CHECK_PROPERTY_CHANGE(PROP_PULSE_COLOR_MODE, colorMode);
|
||||
CHECK_PROPERTY_CHANGE(PROP_PULSE_ALPHA_MODE, alphaMode);
|
||||
|
||||
return changedProperties;
|
||||
}
|
||||
|
||||
void PulsePropertyGroup::getProperties(EntityItemProperties& properties) const {
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Pulse, Min, getMin);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Pulse, Max, getMax);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Pulse, Period, getPeriod);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Pulse, ColorMode, getColorMode);
|
||||
COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Pulse, AlphaMode, getAlphaMode);
|
||||
}
|
||||
|
||||
bool PulsePropertyGroup::setProperties(const EntityItemProperties& properties) {
|
||||
bool somethingChanged = false;
|
||||
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Pulse, Min, min, setMin);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Pulse, Max, max, setMax);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Pulse, Period, period, setPeriod);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Pulse, ColorMode, colorMode, setColorMode);
|
||||
SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Pulse, AlphaMode, alphaMode, setAlphaMode);
|
||||
|
||||
return somethingChanged;
|
||||
}
|
||||
|
||||
EntityPropertyFlags PulsePropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties;
|
||||
|
||||
requestedProperties += PROP_PULSE_MIN;
|
||||
requestedProperties += PROP_PULSE_MAX;
|
||||
requestedProperties += PROP_PULSE_PERIOD;
|
||||
requestedProperties += PROP_PULSE_COLOR_MODE;
|
||||
requestedProperties += PROP_PULSE_ALPHA_MODE;
|
||||
|
||||
return requestedProperties;
|
||||
}
|
||||
|
||||
void PulsePropertyGroup::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_PULSE_MIN, getMin());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PULSE_MAX, getMax());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PULSE_PERIOD, getPeriod());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PULSE_COLOR_MODE, (uint32_t)getColorMode());
|
||||
APPEND_ENTITY_PROPERTY(PROP_PULSE_ALPHA_MODE, (uint32_t)getAlphaMode());
|
||||
}
|
||||
|
||||
int PulsePropertyGroup::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_PULSE_MIN, float, setMin);
|
||||
READ_ENTITY_PROPERTY(PROP_PULSE_MAX, float, setMax);
|
||||
READ_ENTITY_PROPERTY(PROP_PULSE_PERIOD, float, setPeriod);
|
||||
READ_ENTITY_PROPERTY(PROP_PULSE_COLOR_MODE, PulseMode, setColorMode);
|
||||
READ_ENTITY_PROPERTY(PROP_PULSE_ALPHA_MODE, PulseMode, setAlphaMode);
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
bool PulsePropertyGroup::operator==(const PulsePropertyGroup& a) const {
|
||||
return (a._min == _min) &&
|
||||
(a._max == _max) &&
|
||||
(a._period == _period) &&
|
||||
(a._colorMode == _colorMode) &&
|
||||
(a._alphaMode == _alphaMode);
|
||||
}
|
99
libraries/entities/src/PulsePropertyGroup.h
Normal file
99
libraries/entities/src/PulsePropertyGroup.h
Normal file
|
@ -0,0 +1,99 @@
|
|||
//
|
||||
// PulsePropertyGroup.h
|
||||
//
|
||||
// Created by Sam Gondelman on 1/15/19
|
||||
// Copyright 2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_PulsePropertyGroup_h
|
||||
#define hifi_PulsePropertyGroup_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <QtScript/QScriptEngine>
|
||||
|
||||
#include <PulseMode.h>
|
||||
|
||||
#include "PropertyGroup.h"
|
||||
#include "EntityItemPropertiesMacros.h"
|
||||
|
||||
class EntityItemProperties;
|
||||
class EncodeBitstreamParams;
|
||||
class OctreePacketData;
|
||||
class ReadBitstreamToTreeParams;
|
||||
|
||||
/**jsdoc
|
||||
* Pulse is defined by the following properties.
|
||||
* @typedef {object} Entities.Pulse
|
||||
*
|
||||
* @property {number} min=0 - The minimum value of the pulse multiplier.
|
||||
* @property {number} max=1 - The maximum value of the pulse multiplier.
|
||||
* @property {number} period=1 - The duration of the color and alpha pulse, in seconds. A pulse multiplier value goes from
|
||||
* <code>min</code> to <code>max</code>, then <code>max</code> to <code>min</code> in one period.
|
||||
* @property {PulseMode} colorMode="none" - If "in", the color is pulsed in phase with the pulse period; if "out"
|
||||
* the color is pulsed out of phase with the pulse period.
|
||||
* @property {PulseMode} alphaMode="none" - If "in", the alpha is pulsed in phase with the pulse period; if "out"
|
||||
* the alpha is pulsed out of phase with the pulse period.
|
||||
*/
|
||||
|
||||
class PulsePropertyGroup : public PropertyGroup {
|
||||
public:
|
||||
// EntityItemProperty related helpers
|
||||
virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties,
|
||||
QScriptEngine* engine, bool skipDefaults,
|
||||
EntityItemProperties& defaultEntityProperties) const override;
|
||||
virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override;
|
||||
|
||||
void merge(const PulsePropertyGroup& other);
|
||||
|
||||
virtual void debugDump() const override;
|
||||
virtual void listChangedProperties(QList<QString>& out) override;
|
||||
|
||||
virtual bool appendToEditPacket(OctreePacketData* packetData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
int& propertyCount,
|
||||
OctreeElement::AppendState& appendState) const override;
|
||||
|
||||
virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags,
|
||||
const unsigned char*& dataAt, int& processedBytes) override;
|
||||
virtual void markAllChanged() override;
|
||||
virtual EntityPropertyFlags getChangedProperties() const override;
|
||||
|
||||
// EntityItem related helpers
|
||||
// methods for getting/setting all properties of an entity
|
||||
virtual void getProperties(EntityItemProperties& propertiesOut) const override;
|
||||
|
||||
// returns true if something changed
|
||||
virtual bool setProperties(const EntityItemProperties& properties) override;
|
||||
|
||||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
int& propertyCount,
|
||||
OctreeElement::AppendState& appendState) const override;
|
||||
|
||||
virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args,
|
||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||
bool& somethingChanged) override;
|
||||
|
||||
bool operator==(const PulsePropertyGroup& a) const;
|
||||
bool operator!=(const PulsePropertyGroup& a) const { return !(*this == a); }
|
||||
|
||||
DEFINE_PROPERTY(PROP_PULSE_MIN, Min, min, float, 0.0f);
|
||||
DEFINE_PROPERTY(PROP_PULSE_MAX, Max, max, float, 1.0f);
|
||||
DEFINE_PROPERTY(PROP_PULSE_PERIOD, Period, period, float, 1.0f);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_PULSE_COLOR_MODE, ColorMode, colorMode, PulseMode, PulseMode::NONE);
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_PULSE_ALPHA_MODE, AlphaMode, alphaMode, PulseMode, PulseMode::NONE);
|
||||
};
|
||||
|
||||
#endif // hifi_PulsePropertyGroup_h
|
|
@ -119,6 +119,9 @@ EntityItemProperties ShapeEntityItem::getProperties(const EntityPropertyFlags& d
|
|||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha);
|
||||
withReadLock([&] {
|
||||
_pulseProperties.getProperties(properties);
|
||||
});
|
||||
properties.setShape(entity::stringFromShape(getShape()));
|
||||
properties._shapeChanged = false;
|
||||
|
||||
|
@ -159,6 +162,10 @@ bool ShapeEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha);
|
||||
withWriteLock([&] {
|
||||
bool pulsePropertiesChanged = _pulseProperties.setProperties(properties);
|
||||
somethingChanged |= pulsePropertiesChanged;
|
||||
});
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(shape, setShape);
|
||||
|
||||
if (somethingChanged) {
|
||||
|
@ -184,6 +191,13 @@ int ShapeEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
|
||||
READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor);
|
||||
READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha);
|
||||
withWriteLock([&] {
|
||||
int bytesFromPulse = _pulseProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData,
|
||||
somethingChanged);
|
||||
bytesRead += bytesFromPulse;
|
||||
dataAt += bytesFromPulse;
|
||||
});
|
||||
READ_ENTITY_PROPERTY(PROP_SHAPE, QString, setShape);
|
||||
|
||||
return bytesRead;
|
||||
|
@ -193,12 +207,13 @@ EntityPropertyFlags ShapeEntityItem::getEntityProperties(EncodeBitstreamParams&
|
|||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
requestedProperties += PROP_COLOR;
|
||||
requestedProperties += PROP_ALPHA;
|
||||
requestedProperties += _pulseProperties.getEntityProperties(params);
|
||||
requestedProperties += PROP_SHAPE;
|
||||
return requestedProperties;
|
||||
}
|
||||
|
||||
void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
|
@ -208,6 +223,10 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
|
|||
bool successPropertyFits = true;
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha());
|
||||
withReadLock([&] {
|
||||
_pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties,
|
||||
propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
});
|
||||
APPEND_ENTITY_PROPERTY(PROP_SHAPE, entity::stringFromShape(getShape()));
|
||||
}
|
||||
|
||||
|
@ -416,4 +435,10 @@ void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) {
|
|||
// This value specifies how the shape should be treated by physics calculations.
|
||||
ShapeType ShapeEntityItem::getShapeType() const {
|
||||
return _collisionShapeType;
|
||||
}
|
||||
|
||||
PulsePropertyGroup ShapeEntityItem::getPulseProperties() const {
|
||||
return resultWithReadLock<PulsePropertyGroup>([&] {
|
||||
return _pulseProperties;
|
||||
});
|
||||
}
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "EntityItem.h"
|
||||
|
||||
#include "PulsePropertyGroup.h"
|
||||
|
||||
namespace entity {
|
||||
enum Shape {
|
||||
Triangle,
|
||||
|
@ -58,7 +60,7 @@ public:
|
|||
EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
|
@ -99,9 +101,12 @@ public:
|
|||
virtual void computeShapeInfo(ShapeInfo& info) override;
|
||||
virtual ShapeType getShapeType() const override;
|
||||
|
||||
PulsePropertyGroup getPulseProperties() const;
|
||||
|
||||
protected:
|
||||
glm::u8vec3 _color;
|
||||
float _alpha { 1.0f };
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
entity::Shape _shape { entity::Shape::Sphere };
|
||||
|
||||
//! This is SHAPE_TYPE_ELLIPSOID rather than SHAPE_TYPE_NONE to maintain
|
||||
|
|
|
@ -96,11 +96,11 @@ const uint8_t RECRUIT_SIMULATION_PRIORITY = VOLUNTEER_SIMULATION_PRIORITY + 1;
|
|||
// When poking objects with scripts an observer will bid at SCRIPT_EDIT priority.
|
||||
const uint8_t SCRIPT_GRAB_SIMULATION_PRIORITY = 128;
|
||||
const uint8_t SCRIPT_POKE_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY - 1;
|
||||
const uint8_t AVATAR_ENTITY_SIMULATION_PRIORITY = 255;
|
||||
|
||||
// PERSONAL priority (needs a better name) is the level at which a simulation observer owns its own avatar
|
||||
// which really just means: things that collide with it will be bid at a priority level one lower
|
||||
const uint8_t PERSONAL_SIMULATION_PRIORITY = SCRIPT_GRAB_SIMULATION_PRIORITY;
|
||||
const uint8_t AVATAR_ENTITY_SIMULATION_PRIORITY = PERSONAL_SIMULATION_PRIORITY;
|
||||
|
||||
|
||||
class SimulationOwner {
|
||||
|
|
|
@ -49,6 +49,10 @@ void TextEntityItem::setUnscaledDimensions(const glm::vec3& value) {
|
|||
EntityItemProperties TextEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const {
|
||||
EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class
|
||||
|
||||
withReadLock([&] {
|
||||
_pulseProperties.getProperties(properties);
|
||||
});
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(text, getText);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(lineHeight, getLineHeight);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(textColor, getTextColor);
|
||||
|
@ -67,6 +71,11 @@ bool TextEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
bool somethingChanged = false;
|
||||
somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
|
||||
|
||||
withWriteLock([&] {
|
||||
bool pulsePropertiesChanged = _pulseProperties.setProperties(properties);
|
||||
somethingChanged |= pulsePropertiesChanged;
|
||||
});
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(text, setText);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(lineHeight, setLineHeight);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(textColor, setTextColor);
|
||||
|
@ -101,6 +110,14 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
int bytesRead = 0;
|
||||
const unsigned char* dataAt = data;
|
||||
|
||||
withWriteLock([&] {
|
||||
int bytesFromPulse = _pulseProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData,
|
||||
somethingChanged);
|
||||
bytesRead += bytesFromPulse;
|
||||
dataAt += bytesFromPulse;
|
||||
});
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_TEXT, QString, setText);
|
||||
READ_ENTITY_PROPERTY(PROP_LINE_HEIGHT, float, setLineHeight);
|
||||
READ_ENTITY_PROPERTY(PROP_TEXT_COLOR, glm::u8vec3, setTextColor);
|
||||
|
@ -118,6 +135,8 @@ int TextEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
|
|||
|
||||
EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
|
||||
requestedProperties += _pulseProperties.getEntityProperties(params);
|
||||
requestedProperties += PROP_TEXT;
|
||||
requestedProperties += PROP_LINE_HEIGHT;
|
||||
requestedProperties += PROP_TEXT_COLOR;
|
||||
|
@ -129,11 +148,12 @@ EntityPropertyFlags TextEntityItem::getEntityProperties(EncodeBitstreamParams& p
|
|||
requestedProperties += PROP_RIGHT_MARGIN;
|
||||
requestedProperties += PROP_TOP_MARGIN;
|
||||
requestedProperties += PROP_BOTTOM_MARGIN;
|
||||
|
||||
return requestedProperties;
|
||||
}
|
||||
|
||||
void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
|
@ -142,6 +162,11 @@ void TextEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits
|
|||
|
||||
bool successPropertyFits = true;
|
||||
|
||||
withReadLock([&] {
|
||||
_pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties,
|
||||
propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
});
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_TEXT, getText());
|
||||
APPEND_ENTITY_PROPERTY(PROP_LINE_HEIGHT, getLineHeight());
|
||||
APPEND_ENTITY_PROPERTY(PROP_TEXT_COLOR, getTextColor());
|
||||
|
@ -345,3 +370,9 @@ float TextEntityItem::getBottomMargin() const {
|
|||
return _bottomMargin;
|
||||
});
|
||||
}
|
||||
|
||||
PulsePropertyGroup TextEntityItem::getPulseProperties() const {
|
||||
return resultWithReadLock<PulsePropertyGroup>([&] {
|
||||
return _pulseProperties;
|
||||
});
|
||||
}
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
#include "EntityItem.h"
|
||||
|
||||
#include "PulsePropertyGroup.h"
|
||||
|
||||
class TextEntityItem : public EntityItem {
|
||||
public:
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
@ -33,7 +35,7 @@ public:
|
|||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
|
@ -94,6 +96,8 @@ public:
|
|||
float getBottomMargin() const;
|
||||
void setBottomMargin(float value);
|
||||
|
||||
PulsePropertyGroup getPulseProperties() const;
|
||||
|
||||
private:
|
||||
QString _text;
|
||||
float _lineHeight;
|
||||
|
@ -101,6 +105,7 @@ private:
|
|||
float _textAlpha;
|
||||
glm::u8vec3 _backgroundColor;
|
||||
float _backgroundAlpha;
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
BillboardMode _billboardMode;
|
||||
float _leftMargin;
|
||||
float _rightMargin;
|
||||
|
|
|
@ -45,6 +45,9 @@ EntityItemProperties WebEntityItem::getProperties(const EntityPropertyFlags& des
|
|||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getColor);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(alpha, getAlpha);
|
||||
withReadLock([&] {
|
||||
_pulseProperties.getProperties(properties);
|
||||
});
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(sourceUrl, getSourceUrl);
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(dpi, getDPI);
|
||||
|
@ -60,6 +63,10 @@ bool WebEntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(alpha, setAlpha);
|
||||
withWriteLock([&] {
|
||||
bool pulsePropertiesChanged = _pulseProperties.setProperties(properties);
|
||||
somethingChanged |= pulsePropertiesChanged;
|
||||
});
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(sourceUrl, setSourceUrl);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(dpi, setDPI);
|
||||
|
@ -91,6 +98,13 @@ int WebEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, i
|
|||
|
||||
READ_ENTITY_PROPERTY(PROP_COLOR, glm::u8vec3, setColor);
|
||||
READ_ENTITY_PROPERTY(PROP_ALPHA, float, setAlpha);
|
||||
withWriteLock([&] {
|
||||
int bytesFromPulse = _pulseProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData,
|
||||
somethingChanged);
|
||||
bytesRead += bytesFromPulse;
|
||||
dataAt += bytesFromPulse;
|
||||
});
|
||||
|
||||
READ_ENTITY_PROPERTY(PROP_SOURCE_URL, QString, setSourceUrl);
|
||||
READ_ENTITY_PROPERTY(PROP_DPI, uint16_t, setDPI);
|
||||
|
@ -105,6 +119,7 @@ EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& pa
|
|||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
requestedProperties += PROP_COLOR;
|
||||
requestedProperties += PROP_ALPHA;
|
||||
requestedProperties += _pulseProperties.getEntityProperties(params);
|
||||
|
||||
requestedProperties += PROP_SOURCE_URL;
|
||||
requestedProperties += PROP_DPI;
|
||||
|
@ -115,7 +130,7 @@ EntityPropertyFlags WebEntityItem::getEntityProperties(EncodeBitstreamParams& pa
|
|||
}
|
||||
|
||||
void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
|
@ -125,6 +140,10 @@ void WebEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitst
|
|||
bool successPropertyFits = true;
|
||||
APPEND_ENTITY_PROPERTY(PROP_COLOR, getColor());
|
||||
APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha());
|
||||
withReadLock([&] {
|
||||
_pulseProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties,
|
||||
propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
});
|
||||
|
||||
APPEND_ENTITY_PROPERTY(PROP_SOURCE_URL, getSourceUrl());
|
||||
APPEND_ENTITY_PROPERTY(PROP_DPI, getDPI());
|
||||
|
@ -285,4 +304,10 @@ WebInputMode WebEntityItem::getInputMode() const {
|
|||
return resultWithReadLock<WebInputMode>([&] {
|
||||
return _inputMode;
|
||||
});
|
||||
}
|
||||
|
||||
PulsePropertyGroup WebEntityItem::getPulseProperties() const {
|
||||
return resultWithReadLock<PulsePropertyGroup>([&] {
|
||||
return _pulseProperties;
|
||||
});
|
||||
}
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
#include "EntityItem.h"
|
||||
|
||||
#include "PulsePropertyGroup.h"
|
||||
|
||||
class WebEntityItem : public EntityItem {
|
||||
public:
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
@ -30,7 +32,7 @@ public:
|
|||
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer modelTreeElementExtraEncodeData,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
|
@ -75,9 +77,12 @@ public:
|
|||
void setInputMode(const WebInputMode& value);
|
||||
WebInputMode getInputMode() const;
|
||||
|
||||
PulsePropertyGroup getPulseProperties() const;
|
||||
|
||||
protected:
|
||||
glm::u8vec3 _color;
|
||||
float _alpha { 1.0f };
|
||||
PulsePropertyGroup _pulseProperties;
|
||||
|
||||
QString _sourceUrl;
|
||||
uint16_t _dpi;
|
||||
|
|
|
@ -1480,9 +1480,6 @@ HFMModel* FBXSerializer::extractHFMModel(const QVariantHash& mapping, const QStr
|
|||
}
|
||||
}
|
||||
|
||||
extracted.mesh.createMeshTangents(generateTangents);
|
||||
extracted.mesh.createBlendShapeTangents(generateTangents);
|
||||
|
||||
// find the clusters with which the mesh is associated
|
||||
QVector<QString> clusterIDs;
|
||||
foreach (const QString& childID, _connectionChildMap.values(it.key())) {
|
||||
|
|
|
@ -118,6 +118,8 @@ void gl::setSwapInterval(int interval) {
|
|||
wglSwapIntervalEXT(interval);
|
||||
#elif defined(Q_OS_MAC)
|
||||
CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &interval);
|
||||
#elif defined(Q_OS_ANDROID)
|
||||
eglSwapInterval(eglGetCurrentDisplay(), interval);
|
||||
#else
|
||||
Q_UNUSED(interval);
|
||||
#endif
|
||||
|
|
|
@ -34,22 +34,9 @@ bool Context::USE_CUSTOM_CONTEXT { true };
|
|||
#endif
|
||||
|
||||
bool Context::enableDebugLogger() {
|
||||
#if defined(Q_OS_MAC)
|
||||
// OSX does not support GL_KHR_debug or GL_ARB_debug_output
|
||||
return false;
|
||||
#else
|
||||
#if defined(DEBUG) || defined(USE_GLES)
|
||||
static bool enableDebugLogger = true;
|
||||
#else
|
||||
static const QString DEBUG_FLAG("HIFI_DEBUG_OPENGL");
|
||||
static bool enableDebugLogger = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
|
||||
#endif
|
||||
return enableDebugLogger;
|
||||
#endif
|
||||
return gl::debugContextEnabled();
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::atomic<size_t> Context::_totalSwapchainMemoryUsage { 0 };
|
||||
|
||||
size_t Context::getSwapchainMemoryUsage() { return _totalSwapchainMemoryUsage.load(); }
|
||||
|
|
|
@ -52,6 +52,11 @@ void Context::moveToThread(QThread* thread) {
|
|||
}
|
||||
|
||||
void Context::debugMessageHandler(const QOpenGLDebugMessage& debugMessage) {
|
||||
auto type = debugMessage.type();
|
||||
if (type == QOpenGLDebugMessage::PerformanceType) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto severity = debugMessage.severity();
|
||||
switch (severity) {
|
||||
case QOpenGLDebugMessage::NotificationSeverity:
|
||||
|
@ -60,13 +65,13 @@ void Context::debugMessageHandler(const QOpenGLDebugMessage& debugMessage) {
|
|||
default:
|
||||
break;
|
||||
}
|
||||
qDebug(glLogging) << debugMessage;
|
||||
qWarning(glLogging) << debugMessage;
|
||||
return;
|
||||
}
|
||||
|
||||
void Context::setupDebugLogging(QOpenGLContext *context) {
|
||||
QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(context);
|
||||
QObject::connect(logger, &QOpenGLDebugLogger::messageLogged, nullptr, [](const QOpenGLDebugMessage& message){
|
||||
QObject::connect(logger, &QOpenGLDebugLogger::messageLogged, context, [](const QOpenGLDebugMessage& message){
|
||||
Context::debugMessageHandler(message);
|
||||
});
|
||||
if (logger->initialize()) {
|
||||
|
|
|
@ -200,11 +200,48 @@ namespace gl {
|
|||
|
||||
|
||||
bool checkGLErrorDebug(const char* name) {
|
||||
#ifdef DEBUG
|
||||
// Disabling error checking macro on Android debug builds for now,
|
||||
// as it throws off performance testing, which must be done on
|
||||
// Debug builds
|
||||
#if defined(DEBUG) && !defined(Q_OS_ANDROID)
|
||||
return checkGLError(name);
|
||||
#else
|
||||
Q_UNUSED(name);
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Enables annotation of captures made by tools like renderdoc
|
||||
bool khrDebugEnabled() {
|
||||
static std::once_flag once;
|
||||
static bool khrDebug = false;
|
||||
std::call_once(once, [&] {
|
||||
khrDebug = nullptr != glPushDebugGroupKHR;
|
||||
});
|
||||
return khrDebug;
|
||||
}
|
||||
|
||||
// Enables annotation of captures made by tools like renderdoc
|
||||
bool extDebugMarkerEnabled() {
|
||||
static std::once_flag once;
|
||||
static bool extMarker = false;
|
||||
std::call_once(once, [&] {
|
||||
extMarker = nullptr != glPushGroupMarkerEXT;
|
||||
});
|
||||
return extMarker;
|
||||
}
|
||||
|
||||
bool debugContextEnabled() {
|
||||
#if defined(Q_OS_MAC)
|
||||
// OSX does not support GL_KHR_debug or GL_ARB_debug_output
|
||||
static bool enableDebugLogger = false;
|
||||
#elif defined(DEBUG) || defined(USE_GLES)
|
||||
//static bool enableDebugLogger = true;
|
||||
static bool enableDebugLogger = false;
|
||||
#else
|
||||
static const QString DEBUG_FLAG("HIFI_DEBUG_OPENGL");
|
||||
static bool enableDebugLogger = QProcessEnvironment::systemEnvironment().contains(DEBUG_FLAG);
|
||||
#endif
|
||||
return enableDebugLogger;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,13 @@ bool isRenderThread();
|
|||
namespace gl {
|
||||
void globalLock();
|
||||
void globalRelease(bool finish = true);
|
||||
|
||||
|
||||
bool debugContextEnabled();
|
||||
|
||||
bool khrDebugEnabled();
|
||||
|
||||
bool extDebugMarkerEnabled();
|
||||
|
||||
void withSavedContext(const std::function<void()>& f);
|
||||
|
||||
bool checkGLError(const char* name);
|
||||
|
|
|
@ -392,8 +392,38 @@ void GLBackend::renderPassDraw(const Batch& batch) {
|
|||
}
|
||||
}
|
||||
|
||||
// Support annotating captures in tools like Renderdoc
|
||||
class GlDuration {
|
||||
public:
|
||||
#ifdef USE_GLES
|
||||
GlDuration(const char* name) {
|
||||
// We need to use strlen here instead of -1, because the Snapdragon profiler
|
||||
// will crash otherwise
|
||||
glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, strlen(name), name);
|
||||
}
|
||||
~GlDuration() {
|
||||
glPopDebugGroup();
|
||||
}
|
||||
#else
|
||||
GlDuration(const char* name) {
|
||||
if (::gl::khrDebugEnabled()) {
|
||||
glPushDebugGroupKHR(GL_DEBUG_SOURCE_APPLICATION_KHR, 0, -1, name);
|
||||
}
|
||||
}
|
||||
~GlDuration() {
|
||||
if (::gl::khrDebugEnabled()) {
|
||||
glPopDebugGroupKHR();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
#define GL_PROFILE_RANGE(category, name) \
|
||||
PROFILE_RANGE(category, name); \
|
||||
GlDuration glProfileRangeThis(name);
|
||||
|
||||
void GLBackend::render(const Batch& batch) {
|
||||
PROFILE_RANGE(render_gpu_gl, batch.getName());
|
||||
GL_PROFILE_RANGE(render_gpu_gl, batch.getName().c_str());
|
||||
|
||||
_transform._skybox = _stereo._skybox = batch.isSkyboxEnabled();
|
||||
// Allow the batch to override the rendering stereo settings
|
||||
|
@ -406,7 +436,7 @@ void GLBackend::render(const Batch& batch) {
|
|||
_transform._projectionJitter = Vec2(0.0f, 0.0f);
|
||||
|
||||
{
|
||||
PROFILE_RANGE(render_gpu_gl_detail, "Transfer");
|
||||
GL_PROFILE_RANGE(render_gpu_gl_detail, "Transfer");
|
||||
renderPassTransfer(batch);
|
||||
}
|
||||
|
||||
|
@ -416,7 +446,7 @@ void GLBackend::render(const Batch& batch) {
|
|||
}
|
||||
#endif
|
||||
{
|
||||
PROFILE_RANGE(render_gpu_gl_detail, _stereo.isStereo() ? "Render Stereo" : "Render");
|
||||
GL_PROFILE_RANGE(render_gpu_gl_detail, _stereo.isStereo() ? "Render Stereo" : "Render");
|
||||
renderPassDraw(batch);
|
||||
}
|
||||
#ifdef GPU_STEREO_DRAWCALL_INSTANCED
|
||||
|
|
|
@ -54,153 +54,8 @@
|
|||
#define GPU_STEREO_CAMERA_BUFFER
|
||||
#endif
|
||||
|
||||
//
|
||||
// GL Backend pointer storage mechanism
|
||||
// One of the following three defines must be defined.
|
||||
// GPU_POINTER_STORAGE_SHARED
|
||||
|
||||
// The platonic ideal, use references to smart pointers.
|
||||
// However, this produces artifacts because there are too many places in the code right now that
|
||||
// create temporary values (undesirable smart pointer duplications) and then those temp variables
|
||||
// get passed on and have their reference taken, and then invalidated
|
||||
// GPU_POINTER_STORAGE_REF
|
||||
|
||||
// Raw pointer manipulation. Seems more dangerous than the reference wrappers,
|
||||
// but in practice, the danger of grabbing a reference to a temporary variable
|
||||
// is causing issues
|
||||
// GPU_POINTER_STORAGE_RAW
|
||||
|
||||
#if defined(USE_GLES)
|
||||
#define GPU_POINTER_STORAGE_SHARED
|
||||
#else
|
||||
#define GPU_POINTER_STORAGE_RAW
|
||||
#endif
|
||||
|
||||
namespace gpu { namespace gl {
|
||||
|
||||
#if defined(GPU_POINTER_STORAGE_SHARED)
|
||||
template <typename T>
|
||||
static inline bool compare(const std::shared_ptr<T>& a, const std::shared_ptr<T>& b) {
|
||||
return a == b;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T* acquire(const std::shared_ptr<T>& pointer) {
|
||||
return pointer.get();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void reset(std::shared_ptr<T>& pointer) {
|
||||
return pointer.reset();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline bool valid(const std::shared_ptr<T>& pointer) {
|
||||
return pointer.operator bool();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void assign(std::shared_ptr<T>& pointer, const std::shared_ptr<T>& source) {
|
||||
pointer = source;
|
||||
}
|
||||
|
||||
using BufferReference = BufferPointer;
|
||||
using TextureReference = TexturePointer;
|
||||
using FramebufferReference = FramebufferPointer;
|
||||
using FormatReference = Stream::FormatPointer;
|
||||
using PipelineReference = PipelinePointer;
|
||||
|
||||
#define GPU_REFERENCE_INIT_VALUE nullptr
|
||||
|
||||
#elif defined(GPU_POINTER_STORAGE_REF)
|
||||
|
||||
template <typename T>
|
||||
class PointerReferenceWrapper : public std::reference_wrapper<const std::shared_ptr<T>> {
|
||||
using Parent = std::reference_wrapper<const std::shared_ptr<T>>;
|
||||
|
||||
public:
|
||||
using Pointer = std::shared_ptr<T>;
|
||||
PointerReferenceWrapper() : Parent(EMPTY()) {}
|
||||
PointerReferenceWrapper(const Pointer& pointer) : Parent(pointer) {}
|
||||
void clear() { *this = EMPTY(); }
|
||||
|
||||
private:
|
||||
static const Pointer& EMPTY() {
|
||||
static const Pointer EMPTY_VALUE;
|
||||
return EMPTY_VALUE;
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static bool compare(const PointerReferenceWrapper<T>& reference, const std::shared_ptr<T>& pointer) {
|
||||
return reference.get() == pointer;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T* acquire(const PointerReferenceWrapper<T>& reference) {
|
||||
return reference.get().get();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void assign(PointerReferenceWrapper<T>& reference, const std::shared_ptr<T>& pointer) {
|
||||
reference = pointer;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool valid(const PointerReferenceWrapper<T>& reference) {
|
||||
return reference.get().operator bool();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void reset(PointerReferenceWrapper<T>& reference) {
|
||||
return reference.clear();
|
||||
}
|
||||
|
||||
using BufferReference = PointerReferenceWrapper<Buffer>;
|
||||
using TextureReference = PointerReferenceWrapper<Texture>;
|
||||
using FramebufferReference = PointerReferenceWrapper<Framebuffer>;
|
||||
using FormatReference = PointerReferenceWrapper<Stream::Format>;
|
||||
using PipelineReference = PointerReferenceWrapper<Pipeline>;
|
||||
|
||||
#define GPU_REFERENCE_INIT_VALUE
|
||||
|
||||
#elif defined(GPU_POINTER_STORAGE_RAW)
|
||||
|
||||
template <typename T>
|
||||
static bool compare(const T* const& rawPointer, const std::shared_ptr<T>& pointer) {
|
||||
return rawPointer == pointer.get();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T* acquire(T*& rawPointer) {
|
||||
return rawPointer;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline bool valid(const T* const& rawPointer) {
|
||||
return rawPointer;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void reset(T*& rawPointer) {
|
||||
rawPointer = nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void assign(T*& rawPointer, const std::shared_ptr<T>& pointer) {
|
||||
rawPointer = pointer.get();
|
||||
}
|
||||
|
||||
using BufferReference = Buffer*;
|
||||
using TextureReference = Texture*;
|
||||
using FramebufferReference = Framebuffer*;
|
||||
using FormatReference = Stream::Format*;
|
||||
using PipelineReference = Pipeline*;
|
||||
|
||||
#define GPU_REFERENCE_INIT_VALUE nullptr
|
||||
|
||||
#endif
|
||||
|
||||
class GLBackend : public Backend, public std::enable_shared_from_this<GLBackend> {
|
||||
// Context Backend static interface required
|
||||
friend class gpu::Context;
|
||||
|
@ -583,13 +438,13 @@ protected:
|
|||
|
||||
BufferState& operator=(const BufferState& other) = delete;
|
||||
void reset() {
|
||||
gpu::gl::reset(buffer);
|
||||
gpu::reset(buffer);
|
||||
offset = 0;
|
||||
size = 0;
|
||||
}
|
||||
bool compare(const BufferPointer& buffer, GLintptr offset, GLsizeiptr size) {
|
||||
const auto& self = *this;
|
||||
return (self.offset == offset && self.size == size && gpu::gl::compare(self.buffer, buffer));
|
||||
return (self.offset == offset && self.size == size && gpu::compare(self.buffer, buffer));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ void GLBackend::do_advance(const Batch& batch, size_t paramOffset) {
|
|||
}
|
||||
|
||||
void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) {
|
||||
if (_stereo.isStereo() && !_pipeline._stateCache.scissorEnable) {
|
||||
if (_stereo.isStereo() && !_pipeline._stateCache.flags.scissorEnable) {
|
||||
qWarning("Clear without scissor in stereo mode");
|
||||
}
|
||||
|
||||
|
@ -140,7 +140,7 @@ void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) {
|
|||
}
|
||||
|
||||
// Apply scissor if needed and if not already on
|
||||
bool doEnableScissor = (useScissor && (!_pipeline._stateCache.scissorEnable));
|
||||
bool doEnableScissor = (useScissor && (!_pipeline._stateCache.flags.scissorEnable));
|
||||
if (doEnableScissor) {
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
}
|
||||
|
|
|
@ -100,17 +100,17 @@ void GLBackend::do_setStateCullMode(int32 mode) {
|
|||
}
|
||||
|
||||
void GLBackend::do_setStateFrontFaceClockwise(bool isClockwise) {
|
||||
if (_pipeline._stateCache.frontFaceClockwise != isClockwise) {
|
||||
if (_pipeline._stateCache.flags.frontFaceClockwise != isClockwise) {
|
||||
static GLenum GL_FRONT_FACES[] = { GL_CCW, GL_CW };
|
||||
glFrontFace(GL_FRONT_FACES[isClockwise]);
|
||||
(void)CHECK_GL_ERROR();
|
||||
|
||||
_pipeline._stateCache.frontFaceClockwise = isClockwise;
|
||||
_pipeline._stateCache.flags.frontFaceClockwise = isClockwise;
|
||||
}
|
||||
}
|
||||
|
||||
void GLBackend::do_setStateDepthClampEnable(bool enable) {
|
||||
if (_pipeline._stateCache.depthClampEnable != enable) {
|
||||
if (_pipeline._stateCache.flags.depthClampEnable != enable) {
|
||||
#if !defined(USE_GLES)
|
||||
if (enable) {
|
||||
glEnable(GL_DEPTH_CLAMP);
|
||||
|
@ -118,13 +118,13 @@ void GLBackend::do_setStateDepthClampEnable(bool enable) {
|
|||
glDisable(GL_DEPTH_CLAMP);
|
||||
}
|
||||
(void)CHECK_GL_ERROR();
|
||||
_pipeline._stateCache.depthClampEnable = enable;
|
||||
_pipeline._stateCache.flags.depthClampEnable = enable;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void GLBackend::do_setStateScissorEnable(bool enable) {
|
||||
if (_pipeline._stateCache.scissorEnable != enable) {
|
||||
if (_pipeline._stateCache.flags.scissorEnable != enable) {
|
||||
if (enable) {
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
} else {
|
||||
|
@ -132,12 +132,12 @@ void GLBackend::do_setStateScissorEnable(bool enable) {
|
|||
}
|
||||
(void)CHECK_GL_ERROR();
|
||||
|
||||
_pipeline._stateCache.scissorEnable = enable;
|
||||
_pipeline._stateCache.flags.scissorEnable = enable;
|
||||
}
|
||||
}
|
||||
|
||||
void GLBackend::do_setStateMultisampleEnable(bool enable) {
|
||||
if (_pipeline._stateCache.multisampleEnable != enable) {
|
||||
if (_pipeline._stateCache.flags.multisampleEnable != enable) {
|
||||
#if !defined(USE_GLES)
|
||||
if (enable) {
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
|
@ -146,13 +146,13 @@ void GLBackend::do_setStateMultisampleEnable(bool enable) {
|
|||
}
|
||||
(void)CHECK_GL_ERROR();
|
||||
|
||||
_pipeline._stateCache.multisampleEnable = enable;
|
||||
_pipeline._stateCache.flags.multisampleEnable = enable;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void GLBackend::do_setStateAntialiasedLineEnable(bool enable) {
|
||||
if (_pipeline._stateCache.antialisedLineEnable != enable) {
|
||||
if (_pipeline._stateCache.flags.antialisedLineEnable != enable) {
|
||||
#if !defined(USE_GLES)
|
||||
if (enable) {
|
||||
glEnable(GL_LINE_SMOOTH);
|
||||
|
@ -161,7 +161,7 @@ void GLBackend::do_setStateAntialiasedLineEnable(bool enable) {
|
|||
}
|
||||
(void)CHECK_GL_ERROR();
|
||||
|
||||
_pipeline._stateCache.antialisedLineEnable = enable;
|
||||
_pipeline._stateCache.flags.antialisedLineEnable = enable;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -206,7 +206,7 @@ void GLBackend::do_setStateDepthTest(State::DepthTest test) {
|
|||
if (CHECK_GL_ERROR()) {
|
||||
qCDebug(gpulogging) << "DepthTest" << (test.isEnabled() ? "Enabled" : "Disabled")
|
||||
<< "Mask=" << (test.getWriteMask() ? "Write" : "no Write")
|
||||
<< "Func=" << test.getFunction()
|
||||
<< "Func=" << (uint16_t)test.getFunction()
|
||||
<< "Raw=" << test.getRaw();
|
||||
}
|
||||
_pipeline._stateCache.depthTest = test;
|
||||
|
@ -264,7 +264,7 @@ void GLBackend::do_setStateStencil(State::StencilActivation activation, State::S
|
|||
}
|
||||
|
||||
void GLBackend::do_setStateAlphaToCoverageEnable(bool enable) {
|
||||
if (_pipeline._stateCache.alphaToCoverageEnable != enable) {
|
||||
if (_pipeline._stateCache.flags.alphaToCoverageEnable != enable) {
|
||||
if (enable) {
|
||||
glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE);
|
||||
} else {
|
||||
|
@ -272,7 +272,7 @@ void GLBackend::do_setStateAlphaToCoverageEnable(bool enable) {
|
|||
}
|
||||
(void)CHECK_GL_ERROR();
|
||||
|
||||
_pipeline._stateCache.alphaToCoverageEnable = enable;
|
||||
_pipeline._stateCache.flags.alphaToCoverageEnable = enable;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -317,7 +317,7 @@ void GLBackend::do_setStateColorWriteMask(uint32 mask) {
|
|||
mask & State::ColorMask::WRITE_ALPHA);
|
||||
(void)CHECK_GL_ERROR();
|
||||
|
||||
_pipeline._stateCache.colorWriteMask = mask;
|
||||
_pipeline._stateCache.colorWriteMask = (State::ColorMask)mask;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -174,17 +174,17 @@ void getCurrentGLState(State::Data& state) {
|
|||
{
|
||||
GLint winding;
|
||||
glGetIntegerv(GL_FRONT_FACE, &winding);
|
||||
state.frontFaceClockwise = (winding == GL_CW);
|
||||
state.flags.frontFaceClockwise = (winding == GL_CW);
|
||||
#if defined(USE_GLES)
|
||||
state.multisampleEnable = glIsEnabled(GL_MULTISAMPLE_EXT);
|
||||
state.antialisedLineEnable = false;
|
||||
state.depthClampEnable = false;
|
||||
state.flags.multisampleEnable = glIsEnabled(GL_MULTISAMPLE_EXT);
|
||||
state.flags.antialisedLineEnable = false;
|
||||
state.flags.depthClampEnable = false;
|
||||
#else
|
||||
state.multisampleEnable = glIsEnabled(GL_MULTISAMPLE);
|
||||
state.antialisedLineEnable = glIsEnabled(GL_LINE_SMOOTH);
|
||||
state.depthClampEnable = glIsEnabled(GL_DEPTH_CLAMP);
|
||||
state.flags.multisampleEnable = glIsEnabled(GL_MULTISAMPLE);
|
||||
state.flags.antialisedLineEnable = glIsEnabled(GL_LINE_SMOOTH);
|
||||
state.flags.depthClampEnable = glIsEnabled(GL_DEPTH_CLAMP);
|
||||
#endif
|
||||
state.scissorEnable = glIsEnabled(GL_SCISSOR_TEST);
|
||||
state.flags.scissorEnable = glIsEnabled(GL_SCISSOR_TEST);
|
||||
}
|
||||
{
|
||||
if (glIsEnabled(GL_POLYGON_OFFSET_FILL)) {
|
||||
|
@ -247,7 +247,7 @@ void getCurrentGLState(State::Data& state) {
|
|||
state.sampleMask = mask;
|
||||
}
|
||||
{
|
||||
state.alphaToCoverageEnable = glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE);
|
||||
state.flags.alphaToCoverageEnable = glIsEnabled(GL_SAMPLE_ALPHA_TO_COVERAGE);
|
||||
}
|
||||
{
|
||||
GLboolean isEnabled = glIsEnabled(GL_BLEND);
|
||||
|
@ -272,10 +272,10 @@ void getCurrentGLState(State::Data& state) {
|
|||
{
|
||||
GLboolean mask[4];
|
||||
glGetBooleanv(GL_COLOR_WRITEMASK, mask);
|
||||
state.colorWriteMask = (mask[0] ? State::WRITE_RED : 0)
|
||||
state.colorWriteMask = (State::ColorMask)((mask[0] ? State::WRITE_RED : 0)
|
||||
| (mask[1] ? State::WRITE_GREEN : 0)
|
||||
| (mask[2] ? State::WRITE_BLUE : 0)
|
||||
| (mask[3] ? State::WRITE_ALPHA : 0);
|
||||
| (mask[3] ? State::WRITE_ALPHA : 0));
|
||||
}
|
||||
|
||||
(void)CHECK_GL_ERROR();
|
||||
|
|
|
@ -56,11 +56,11 @@ const GLState::Commands makeResetStateCommands() {
|
|||
return {
|
||||
std::make_shared<Command1I>(&GLBackend::do_setStateFillMode, DEFAULT.fillMode),
|
||||
std::make_shared<Command1I>(&GLBackend::do_setStateCullMode, DEFAULT.cullMode),
|
||||
std::make_shared<Command1B>(&GLBackend::do_setStateFrontFaceClockwise, DEFAULT.frontFaceClockwise),
|
||||
std::make_shared<Command1B>(&GLBackend::do_setStateDepthClampEnable, DEFAULT.depthClampEnable),
|
||||
std::make_shared<Command1B>(&GLBackend::do_setStateScissorEnable, DEFAULT.scissorEnable),
|
||||
std::make_shared<Command1B>(&GLBackend::do_setStateMultisampleEnable, DEFAULT.multisampleEnable),
|
||||
std::make_shared<Command1B>(&GLBackend::do_setStateAntialiasedLineEnable, DEFAULT.antialisedLineEnable),
|
||||
std::make_shared<Command1B>(&GLBackend::do_setStateFrontFaceClockwise, DEFAULT.flags.frontFaceClockwise),
|
||||
std::make_shared<Command1B>(&GLBackend::do_setStateDepthClampEnable, DEFAULT.flags.depthClampEnable),
|
||||
std::make_shared<Command1B>(&GLBackend::do_setStateScissorEnable, DEFAULT.flags.scissorEnable),
|
||||
std::make_shared<Command1B>(&GLBackend::do_setStateMultisampleEnable, DEFAULT.flags.multisampleEnable),
|
||||
std::make_shared<Command1B>(&GLBackend::do_setStateAntialiasedLineEnable, DEFAULT.flags.antialisedLineEnable),
|
||||
|
||||
// Depth bias has 2 fields in State but really one call in GLBackend
|
||||
CommandPointer(depthBiasCommand),
|
||||
|
@ -75,7 +75,7 @@ const GLState::Commands makeResetStateCommands() {
|
|||
|
||||
std::make_shared<Command1U>(&GLBackend::do_setStateSampleMask, DEFAULT.sampleMask),
|
||||
|
||||
std::make_shared<Command1B>(&GLBackend::do_setStateAlphaToCoverageEnable, DEFAULT.alphaToCoverageEnable),
|
||||
std::make_shared<Command1B>(&GLBackend::do_setStateAlphaToCoverageEnable, DEFAULT.flags.alphaToCoverageEnable),
|
||||
|
||||
std::make_shared<CommandBlend>(&GLBackend::do_setStateBlend, DEFAULT.blendFunction),
|
||||
|
||||
|
|
|
@ -4,3 +4,4 @@ setup_hifi_library()
|
|||
link_hifi_libraries(shared ktx shaders)
|
||||
|
||||
target_nsight()
|
||||
target_json()
|
||||
|
|
|
@ -45,7 +45,7 @@ size_t Batch::_dataMax{ BATCH_PREALLOCATE_MIN };
|
|||
size_t Batch::_objectsMax{ BATCH_PREALLOCATE_MIN };
|
||||
size_t Batch::_drawCallInfosMax{ BATCH_PREALLOCATE_MIN };
|
||||
|
||||
Batch::Batch(const char* name) {
|
||||
Batch::Batch(const std::string& name) {
|
||||
_name = name;
|
||||
_commands.reserve(_commandsMax);
|
||||
_commandOffsets.reserve(_commandOffsetsMax);
|
||||
|
@ -64,7 +64,7 @@ Batch::~Batch() {
|
|||
_drawCallInfosMax = std::max(_drawCallInfos.size(), _drawCallInfosMax);
|
||||
}
|
||||
|
||||
void Batch::setName(const char* name) {
|
||||
void Batch::setName(const std::string& name) {
|
||||
_name = name;
|
||||
}
|
||||
|
||||
|
@ -96,7 +96,7 @@ void Batch::clear() {
|
|||
_textureTables.clear();
|
||||
_transforms.clear();
|
||||
|
||||
_name = nullptr;
|
||||
_name.clear();
|
||||
_invalidModel = true;
|
||||
_currentModel = Transform();
|
||||
_drawcallUniform = 0;
|
||||
|
|
|
@ -89,14 +89,14 @@ public:
|
|||
void captureDrawCallInfo();
|
||||
void captureNamedDrawCallInfo(std::string name);
|
||||
|
||||
Batch(const char* name = nullptr);
|
||||
Batch(const std::string& name = "");
|
||||
// Disallow copy construction and assignement of batches
|
||||
Batch(const Batch& batch) = delete;
|
||||
Batch& operator=(const Batch& batch) = delete;
|
||||
~Batch();
|
||||
|
||||
void setName(const char* name);
|
||||
const char* getName() const { return _name; }
|
||||
void setName(const std::string& name);
|
||||
const std::string& getName() const { return _name; }
|
||||
void clear();
|
||||
|
||||
// Batches may need to override the context level stereo settings
|
||||
|
@ -440,6 +440,18 @@ public:
|
|||
};
|
||||
};
|
||||
|
||||
using CommandHandler = std::function<void(Command, const Param*)>;
|
||||
|
||||
void forEachCommand(const CommandHandler& handler) const {
|
||||
size_t count = _commands.size();
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
const auto command = _commands[i];
|
||||
const auto offset = _commandOffsets[i];
|
||||
const Param* params = _params.data() + offset;
|
||||
handler(command, params);
|
||||
}
|
||||
}
|
||||
|
||||
typedef Cache<BufferPointer>::Vector BufferCaches;
|
||||
typedef Cache<TexturePointer>::Vector TextureCaches;
|
||||
typedef Cache<TextureTablePointer>::Vector TextureTableCaches;
|
||||
|
@ -519,7 +531,7 @@ public:
|
|||
bool _enableSkybox { false };
|
||||
|
||||
protected:
|
||||
const char* _name;
|
||||
std::string _name;
|
||||
|
||||
friend class Context;
|
||||
friend class Frame;
|
||||
|
|
|
@ -149,7 +149,8 @@ protected:
|
|||
Size _end{ 0 };
|
||||
Sysmem _sysmem;
|
||||
|
||||
|
||||
friend class Serializer;
|
||||
friend class Deserializer;
|
||||
friend class BufferView;
|
||||
friend class Frame;
|
||||
friend class Batch;
|
||||
|
|
|
@ -47,10 +47,7 @@ Context::Context(const Context& context) {
|
|||
}
|
||||
|
||||
Context::~Context() {
|
||||
for (auto batch : _batchPool) {
|
||||
delete batch;
|
||||
}
|
||||
_batchPool.clear();
|
||||
clearBatches();
|
||||
_syncedPrograms.clear();
|
||||
}
|
||||
|
||||
|
@ -97,6 +94,12 @@ FramePointer Context::endFrame() {
|
|||
return result;
|
||||
}
|
||||
|
||||
void Context::executeBatch(const char* name, std::function<void(Batch&)> lambda) const {
|
||||
auto batch = acquireBatch(name);
|
||||
lambda(*batch);
|
||||
executeBatch(*batch);
|
||||
}
|
||||
|
||||
void Context::executeBatch(Batch& batch) const {
|
||||
PROFILE_RANGE(render_gpu, __FUNCTION__);
|
||||
batch.flush();
|
||||
|
@ -117,28 +120,27 @@ void Context::executeFrame(const FramePointer& frame) const {
|
|||
PROFILE_RANGE(render_gpu, __FUNCTION__);
|
||||
|
||||
// Grab the stats at the around the frame and delta to have a consistent sampling
|
||||
ContextStats beginStats;
|
||||
static ContextStats beginStats;
|
||||
getStats(beginStats);
|
||||
|
||||
// FIXME? probably not necessary, but safe
|
||||
consumeFrameUpdates(frame);
|
||||
_backend->setStereoState(frame->stereoState);
|
||||
{
|
||||
Batch beginBatch("Context::executeFrame::begin");
|
||||
_frameRangeTimer->begin(beginBatch);
|
||||
_backend->render(beginBatch);
|
||||
|
||||
// Execute the frame rendering commands
|
||||
for (auto& batch : frame->batches) {
|
||||
_backend->render(*batch);
|
||||
}
|
||||
|
||||
Batch endBatch("Context::executeFrame::end");
|
||||
_frameRangeTimer->end(endBatch);
|
||||
_backend->render(endBatch);
|
||||
executeBatch("Context::executeFrame::begin", [&](Batch& batch){
|
||||
batch.pushProfileRange("Frame");
|
||||
_frameRangeTimer->begin(batch);
|
||||
});
|
||||
// Execute the frame rendering commands
|
||||
for (auto& batch : frame->batches) {
|
||||
_backend->render(*batch);
|
||||
}
|
||||
executeBatch("Context::executeFrame::end", [&](Batch& batch){
|
||||
batch.popProfileRange();
|
||||
_frameRangeTimer->end(batch);
|
||||
});
|
||||
|
||||
ContextStats endStats;
|
||||
static ContextStats endStats;
|
||||
getStats(endStats);
|
||||
_frameStats.evalDelta(beginStats, endStats);
|
||||
}
|
||||
|
@ -381,6 +383,16 @@ void Context::processProgramsToSync() {
|
|||
}
|
||||
}
|
||||
|
||||
std::mutex Context::_batchPoolMutex;
|
||||
std::list<Batch*> Context::_batchPool;
|
||||
|
||||
void Context::clearBatches() {
|
||||
for (auto batch : _batchPool) {
|
||||
delete batch;
|
||||
}
|
||||
_batchPool.clear();
|
||||
}
|
||||
|
||||
BatchPointer Context::acquireBatch(const char* name) {
|
||||
Batch* rawBatch = nullptr;
|
||||
{
|
||||
|
@ -393,8 +405,10 @@ BatchPointer Context::acquireBatch(const char* name) {
|
|||
if (!rawBatch) {
|
||||
rawBatch = new Batch();
|
||||
}
|
||||
rawBatch->setName(name);
|
||||
return BatchPointer(rawBatch, [this](Batch* batch) { releaseBatch(batch); });
|
||||
if (name) {
|
||||
rawBatch->setName(name);
|
||||
}
|
||||
return BatchPointer(rawBatch, [](Batch* batch) { releaseBatch(batch); });
|
||||
}
|
||||
|
||||
void Context::releaseBatch(Batch* batch) {
|
||||
|
@ -406,7 +420,7 @@ void Context::releaseBatch(Batch* batch) {
|
|||
void gpu::doInBatch(const char* name,
|
||||
const std::shared_ptr<gpu::Context>& context,
|
||||
const std::function<void(Batch& batch)>& f) {
|
||||
auto batch = context->acquireBatch(name);
|
||||
auto batch = Context::acquireBatch(name);
|
||||
f(*batch);
|
||||
context->appendFrameBatch(batch);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "Pipeline.h"
|
||||
#include "Framebuffer.h"
|
||||
#include "Frame.h"
|
||||
#include "PointerStorage.h"
|
||||
|
||||
class QImage;
|
||||
|
||||
|
@ -162,8 +163,8 @@ public:
|
|||
void appendFrameBatch(const BatchPointer& batch);
|
||||
FramePointer endFrame();
|
||||
|
||||
BatchPointer acquireBatch(const char* name = nullptr);
|
||||
void releaseBatch(Batch* batch);
|
||||
static BatchPointer acquireBatch(const char* name = nullptr);
|
||||
static void releaseBatch(Batch* batch);
|
||||
|
||||
// MUST only be called on the rendering thread
|
||||
//
|
||||
|
@ -175,6 +176,11 @@ public:
|
|||
// Execute a batch immediately, rather than as part of a frame
|
||||
void executeBatch(Batch& batch) const;
|
||||
|
||||
// MUST only be called on the rendering thread
|
||||
//
|
||||
// Execute a batch immediately, rather than as part of a frame
|
||||
void executeBatch(const char* name, std::function<void(Batch&)> lambda) const;
|
||||
|
||||
// MUST only be called on the rendering thread
|
||||
//
|
||||
// Executes a frame, applying any updates contained in the frame batches to the rendering
|
||||
|
@ -267,8 +273,6 @@ protected:
|
|||
Context(const Context& context);
|
||||
|
||||
std::shared_ptr<Backend> _backend;
|
||||
std::mutex _batchPoolMutex;
|
||||
std::list<Batch*> _batchPool;
|
||||
bool _frameActive{ false };
|
||||
FramePointer _currentFrame;
|
||||
RangeTimerPointer _frameRangeTimer;
|
||||
|
@ -285,6 +289,11 @@ protected:
|
|||
static CreateBackend _createBackendCallback;
|
||||
static std::once_flag _initialized;
|
||||
|
||||
// Should probably move this functionality to Batch
|
||||
static void clearBatches();
|
||||
static std::mutex _batchPoolMutex;
|
||||
static std::list<Batch*> _batchPool;
|
||||
|
||||
friend class Shader;
|
||||
friend class Backend;
|
||||
};
|
||||
|
|
|
@ -322,7 +322,7 @@ public:
|
|||
uint8 getLocationScalarCount() const { return DIMENSION_SCALAR_COUNT_PER_LOCATION[(Dimension)_dimension]; }
|
||||
uint32 getLocationSize() const { return DIMENSION_SCALAR_COUNT_PER_LOCATION[_dimension] * TYPE_SIZE[_type]; }
|
||||
|
||||
uint16 getRaw() const { return *((uint16*) (this)); }
|
||||
uint16 getRaw() const { return *((const uint16*) (this)); }
|
||||
|
||||
|
||||
bool operator ==(const Element& right) const {
|
||||
|
@ -376,7 +376,7 @@ public:
|
|||
};
|
||||
|
||||
|
||||
enum ComparisonFunction {
|
||||
enum ComparisonFunction : uint16 {
|
||||
NEVER = 0,
|
||||
LESS,
|
||||
EQUAL,
|
||||
|
|
|
@ -96,6 +96,7 @@ namespace gpu {
|
|||
using TextureTablePointer = std::shared_ptr<TextureTable>;
|
||||
|
||||
struct StereoState {
|
||||
StereoState() {}
|
||||
bool isStereo() const {
|
||||
return _enable && !_contextDisable;
|
||||
}
|
||||
|
@ -108,6 +109,9 @@ namespace gpu {
|
|||
Mat4 _eyeProjections[2];
|
||||
};
|
||||
|
||||
class Serializer;
|
||||
class Deserializer;
|
||||
|
||||
class GPUObject {
|
||||
public:
|
||||
virtual ~GPUObject() = default;
|
||||
|
|
|
@ -42,6 +42,8 @@ namespace gpu {
|
|||
FramebufferRecycler framebufferRecycler;
|
||||
|
||||
protected:
|
||||
friend class Deserializer;
|
||||
|
||||
// Should be called once per frame, on the recording thred
|
||||
void finish();
|
||||
void preRender();
|
||||
|
|
29
libraries/gpu/src/gpu/FrameIO.h
Normal file
29
libraries/gpu/src/gpu/FrameIO.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2018/10/14
|
||||
// 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
|
||||
//
|
||||
#pragma once
|
||||
#ifndef hifi_gpu_FrameIO_h
|
||||
#define hifi_gpu_FrameIO_h
|
||||
|
||||
#include "Forward.h"
|
||||
#include "Format.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace gpu {
|
||||
|
||||
using TextureCapturer = std::function<void(const std::string&, const TexturePointer&, uint16 layer)>;
|
||||
using TextureLoader = std::function<void(const std::string&, const TexturePointer&, uint16 layer)>;
|
||||
void writeFrame(const std::string& filename, const FramePointer& frame, const TextureCapturer& capturer = nullptr);
|
||||
FramePointer readFrame(const std::string& filename, uint32_t externalTexture, const TextureLoader& loader = nullptr);
|
||||
|
||||
using IndexOptimizer = std::function<void(Primitive, uint32_t faceCount, uint32_t indexCount, uint32_t* indices )>;
|
||||
void optimizeFrame(const std::string& filename, const IndexOptimizer& optimizer);
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
#endif
|
214
libraries/gpu/src/gpu/FrameIOKeys.h
Normal file
214
libraries/gpu/src/gpu/FrameIOKeys.h
Normal file
|
@ -0,0 +1,214 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2018/10/14
|
||||
// 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
|
||||
//
|
||||
#pragma once
|
||||
#ifndef hifi_gpu_FrameIOKeys_h
|
||||
#define hifi_gpu_FrameIOKeys_h
|
||||
|
||||
namespace gpu { namespace keys {
|
||||
|
||||
static const char* binary = "binary";
|
||||
static const char* L00 = "L00";
|
||||
static const char* L1m1 = "L1m1";
|
||||
static const char* L10 = "L10";
|
||||
static const char* L11 = "L11";
|
||||
static const char* L2m2 = "L2m2";
|
||||
static const char* L2m1 = "L2m1";
|
||||
static const char* L20 = "L20";
|
||||
static const char* L21 = "L21";
|
||||
static const char* L22 = "L22";
|
||||
|
||||
static const char* eyeProjections = "eyeProjections";
|
||||
static const char* eyeViews = "eyeViews";
|
||||
static const char* alphaToCoverageEnable = "alphaToCoverageEnable";
|
||||
static const char* antialisedLineEnable = "antialisedLineEnable";
|
||||
static const char* attributes = "attributes";
|
||||
static const char* batches = "batches";
|
||||
static const char* blendFunction = "blendFunction";
|
||||
static const char* borderColor = "borderColor";
|
||||
static const char* bufferMask = "bufferMask";
|
||||
static const char* buffers = "buffers";
|
||||
static const char* capturedTextures = "capturedTextures";
|
||||
static const char* channel = "channel";
|
||||
static const char* colorAttachments = "colorAttachments";
|
||||
static const char* colorWriteMask = "colorWriteMask";
|
||||
static const char* commands = "commands";
|
||||
static const char* comparisonFunction = "comparisonFunction";
|
||||
static const char* cullMode = "cullMode";
|
||||
static const char* data = "data";
|
||||
static const char* depth = "depth";
|
||||
static const char* depthBias = "depthBias";
|
||||
static const char* depthBiasSlopeScale = "depthBiasSlopeScale";
|
||||
static const char* depthClampEnable = "depthClampEnable";
|
||||
static const char* depthStencilAttachment = "depthStencilAttachment";
|
||||
static const char* depthTest = "depthTest";
|
||||
static const char* drawCallInfos = "drawCallInfos";
|
||||
static const char* drawcallUniform = "drawcallUniform";
|
||||
static const char* drawcallUniformReset = "drawcallUniformReset";
|
||||
static const char* element = "element";
|
||||
static const char* fillMode = "fillMode";
|
||||
static const char* filter = "filter";
|
||||
static const char* formats = "formats";
|
||||
static const char* frameIndex = "frameIndex";
|
||||
static const char* framebuffer = "framebuffer";
|
||||
static const char* framebuffers = "framebuffers";
|
||||
static const char* frequency = "frequency";
|
||||
static const char* frontFaceClockwise = "frontFaceClockwise";
|
||||
static const char* height = "height";
|
||||
static const char* id = "id";
|
||||
static const char* ktxFile = "ktxFile";
|
||||
static const char* layers = "layers";
|
||||
static const char* maxAnisotropy = "maxAnisotropy";
|
||||
static const char* maxMip = "maxMip";
|
||||
static const char* minMip = "minMip";
|
||||
static const char* mipOffset = "mipOffset";
|
||||
static const char* mips = "mips";
|
||||
static const char* multisampleEnable = "multisampleEnable";
|
||||
static const char* name = "name";
|
||||
static const char* namedData = "namedData";
|
||||
static const char* names = "names";
|
||||
static const char* objects = "objects";
|
||||
static const char* offset = "offset";
|
||||
static const char* pipelines = "pipelines";
|
||||
static const char* pose = "pose";
|
||||
static const char* profileRanges = "profileRanges";
|
||||
static const char* program = "program";
|
||||
static const char* programs = "programs";
|
||||
static const char* projectionJitter = "projectionJitter";
|
||||
static const char* queries = "queries";
|
||||
static const char* sampleCount = "sampleCount";
|
||||
static const char* sampleMask = "sampleMask";
|
||||
static const char* sampler = "sampler";
|
||||
static const char* samples = "samples";
|
||||
static const char* scissorEnable = "scissorEnable";
|
||||
static const char* shaders = "shaders";
|
||||
static const char* size = "size";
|
||||
static const char* skybox = "skybox";
|
||||
static const char* slot = "slot";
|
||||
static const char* source = "source";
|
||||
static const char* state = "state";
|
||||
static const char* stencilActivation = "stencilActivation";
|
||||
static const char* stencilTestBack = "stencilTestBack";
|
||||
static const char* stencilTestFront = "stencilTestFront";
|
||||
static const char* stereo = "stereo";
|
||||
static const char* subresource = "subresource";
|
||||
static const char* swapchains = "swapchains";
|
||||
static const char* texelFormat = "texelFormat";
|
||||
static const char* texture = "texture";
|
||||
static const char* textureTables = "textureTables";
|
||||
static const char* textures = "textures";
|
||||
static const char* transforms = "transforms";
|
||||
static const char* type = "type";
|
||||
static const char* usageType = "usageType";
|
||||
static const char* view = "view";
|
||||
static const char* width = "width";
|
||||
static const char* wrapModeU = "wrapModeU";
|
||||
static const char* wrapModeV = "wrapModeV";
|
||||
static const char* wrapModeW = "wrapModeW";
|
||||
|
||||
|
||||
static const char* backWriteMask = "backWriteMask";
|
||||
static const char* frontWriteMask = "frontWriteMask";
|
||||
static const char* reference = "reference";
|
||||
static const char* readMask = "readMask";
|
||||
static const char* failOp = "failOp";
|
||||
static const char* depthFailOp = "depthFailOp";
|
||||
static const char* passOp = "passOp";
|
||||
static const char* enabled = "enabled";
|
||||
static const char* blend = "blend";
|
||||
static const char* flags = "flags";
|
||||
static const char* writeMask = "writeMask";
|
||||
static const char* function = "function";
|
||||
static const char* sourceColor = "sourceColor";
|
||||
static const char* sourceAlpha = "sourceAlpha";
|
||||
static const char* destColor = "destColor";
|
||||
static const char* destAlpha = "destAlpha";
|
||||
static const char* opColor = "opColor";
|
||||
static const char* opAlpha = "opAlpha";
|
||||
static const char* enable = "enable";
|
||||
static const char* contextDisable = "contextDisable";
|
||||
|
||||
static const char* COMMAND_NAMES[] = {
|
||||
"draw",
|
||||
"drawIndexed",
|
||||
"drawInstanced",
|
||||
"drawIndexedInstanced",
|
||||
"multiDrawIndirect",
|
||||
"multiDrawIndexedIndirect",
|
||||
|
||||
"setInputFormat",
|
||||
"setInputBuffer",
|
||||
"setIndexBuffer",
|
||||
"setIndirectBuffer",
|
||||
|
||||
"setModelTransform",
|
||||
"setViewTransform",
|
||||
"setProjectionTransform",
|
||||
"setProjectionJitter",
|
||||
"setViewportTransform",
|
||||
"setDepthRangeTransform",
|
||||
|
||||
"setPipeline",
|
||||
"setStateBlendFactor",
|
||||
"setStateScissorRect",
|
||||
|
||||
"setUniformBuffer",
|
||||
"setResourceBuffer",
|
||||
"setResourceTexture",
|
||||
"setResourceTextureTable",
|
||||
"setResourceFramebufferSwapChainTexture",
|
||||
|
||||
"setFramebuffer",
|
||||
"setFramebufferSwapChain",
|
||||
"clearFramebuffer",
|
||||
"blit",
|
||||
"generateTextureMips",
|
||||
"generateTextureMipsWithPipeline",
|
||||
|
||||
"advance",
|
||||
|
||||
"beginQuery",
|
||||
"endQuery",
|
||||
"getQuery",
|
||||
|
||||
"resetStages",
|
||||
|
||||
"disableContextViewCorrection",
|
||||
"restoreContextViewCorrection",
|
||||
|
||||
"disableContextStereo",
|
||||
"restoreContextStereo",
|
||||
|
||||
"runLambda",
|
||||
|
||||
"startNamedCall",
|
||||
"stopNamedCall",
|
||||
|
||||
"glUniform1i",
|
||||
"glUniform1f",
|
||||
"glUniform2f",
|
||||
"glUniform3f",
|
||||
"glUniform4f",
|
||||
"glUniform3fv",
|
||||
"glUniform4fv",
|
||||
"glUniform4iv",
|
||||
"glUniformMatrix3fv",
|
||||
"glUniformMatrix4fv",
|
||||
|
||||
"glColor4f",
|
||||
|
||||
"pushProfileRange",
|
||||
"popProfileRange",
|
||||
};
|
||||
|
||||
template<class T, size_t N>
|
||||
constexpr size_t array_size(T (&)[N]) { return N; }
|
||||
|
||||
static_assert(array_size(COMMAND_NAMES) == Batch::Command::NUM_COMMANDS, "Command array sizes must match");
|
||||
}} // namespace gpu::keys
|
||||
|
||||
#endif
|
906
libraries/gpu/src/gpu/FrameReader.cpp
Normal file
906
libraries/gpu/src/gpu/FrameReader.cpp
Normal file
|
@ -0,0 +1,906 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2018/10/14
|
||||
// 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 "FrameIO.h"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QDir>
|
||||
|
||||
#include <ktx/KTX.h>
|
||||
#include "Frame.h"
|
||||
#include "Batch.h"
|
||||
#include "TextureTable.h"
|
||||
|
||||
|
||||
#include "FrameIOKeys.h"
|
||||
|
||||
namespace gpu {
|
||||
using json = nlohmann::json;
|
||||
|
||||
class Deserializer {
|
||||
public:
|
||||
static std::string getBaseName(const std::string& filename) {
|
||||
static const std::string ext{ ".json" };
|
||||
if (std::string::npos != filename.rfind(ext)) {
|
||||
return filename.substr(0, filename.size() - ext.size());
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
Deserializer(const std::string& filename, uint32_t externalTexture, const TextureLoader& loader) :
|
||||
basename(getBaseName(filename)), externalTexture(externalTexture), textureLoader(loader) {}
|
||||
|
||||
const std::string basename;
|
||||
std::string basedir;
|
||||
std::string binaryFile;
|
||||
const uint32_t externalTexture;
|
||||
TextureLoader textureLoader;
|
||||
std::vector<ShaderPointer> shaders;
|
||||
std::vector<ShaderPointer> programs;
|
||||
std::vector<TexturePointer> textures;
|
||||
std::vector<TextureTablePointer> textureTables;
|
||||
std::vector<BufferPointer> buffers;
|
||||
std::unordered_map<BufferPointer, size_t> bufferOffsets;
|
||||
std::vector<Stream::FormatPointer> formats;
|
||||
std::vector<PipelinePointer> pipelines;
|
||||
std::vector<FramebufferPointer> framebuffers;
|
||||
std::vector<SwapChainPointer> swapchains;
|
||||
std::vector<QueryPointer> queries;
|
||||
json frameNode;
|
||||
FramePointer readFrame();
|
||||
void optimizeFrame(const IndexOptimizer& optimizer);
|
||||
|
||||
FramePointer deserializeFrame();
|
||||
|
||||
|
||||
void readBuffers(const json& node);
|
||||
|
||||
template <typename T>
|
||||
static std::vector<T> readArray(const json& node, const std::string& name, std::function<T(const json& node)> parser) {
|
||||
std::vector<T> result;
|
||||
if (node.count(name)) {
|
||||
const auto& sourceArrayNode = node[name];
|
||||
result.reserve(sourceArrayNode.size());
|
||||
for (const auto& sourceNode : sourceArrayNode) {
|
||||
if (sourceNode.is_null()) {
|
||||
result.push_back(nullptr);
|
||||
continue;
|
||||
}
|
||||
result.push_back(parser(sourceNode));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::vector<T> readNumericVector(const json& node) {
|
||||
auto count = node.size();
|
||||
std::vector<T> result;
|
||||
result.resize(count);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
result[i] = node[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
static void readFloatArray(const json& node, float* out) {
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
out[i] = node[i].operator float();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool readOptionalTransformed(T& dest, const json& node, const std::string& name, std::function<T(const json&)> f) {
|
||||
if (node.count(name)) {
|
||||
dest = f(node[name]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool readOptionalVectorTransformed(std::vector<T>& dest,
|
||||
const json& node,
|
||||
const std::string& name,
|
||||
std::function<T(const json&)> f) {
|
||||
if (node.count(name)) {
|
||||
const auto& arrayNode = node[name];
|
||||
const auto count = arrayNode.size();
|
||||
dest.reserve(count);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
dest.emplace_back(f(arrayNode[i]));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool readOptionalVector(std::vector<T>& dest, const json& node, const std::string& name) {
|
||||
return readOptionalVectorTransformed(dest, node, name, [](const json& node) { return node.get<T>(); });
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static T defaultNodeTransform(const json& node) {
|
||||
return node.get<T>();
|
||||
}
|
||||
|
||||
template <typename T, typename TT = T>
|
||||
static bool readBatchCacheTransformed(typename Batch::Cache<T>::Vector& dest,
|
||||
const json& node,
|
||||
const std::string& name,
|
||||
std::function<TT(const json&)> f = [](const json& node) -> TT {
|
||||
return node.get<TT>();
|
||||
}) {
|
||||
if (node.count(name)) {
|
||||
const auto& arrayNode = node[name];
|
||||
for (const auto& entry : arrayNode) {
|
||||
dest.cache(f(entry));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool readPointerCache(typename Batch::Cache<T>::Vector& dest,
|
||||
const json& node,
|
||||
const std::string& name,
|
||||
std::vector<T>& global) {
|
||||
auto transform = [&](const json& node) -> const T& { return global[node.get<uint32_t>()]; };
|
||||
return readBatchCacheTransformed<T, const T&>(dest, node, name, transform);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool readOptional(T& dest, const json& node, const std::string& name) {
|
||||
return readOptionalTransformed<T>(dest, node, name, [](const json& child) {
|
||||
T result = child;
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
SwapChainPointer readSwapchain(const json& node);
|
||||
ShaderPointer readProgram(const json& node);
|
||||
PipelinePointer readPipeline(const json& node);
|
||||
TextureTablePointer readTextureTable(const json& node);
|
||||
TextureView readTextureView(const json& node);
|
||||
FramebufferPointer readFramebuffer(const json& node);
|
||||
BatchPointer readBatch(const json& node);
|
||||
Batch::NamedBatchData readNamedBatchData(const json& node);
|
||||
|
||||
//static StatePointer readState(const json& node);
|
||||
static QueryPointer readQuery(const json& node);
|
||||
TexturePointer readTexture(const json& node, uint32_t externalTexture);
|
||||
static ShaderPointer readShader(const json& node);
|
||||
static Stream::FormatPointer readFormat(const json& node);
|
||||
static Element readElement(const json& node);
|
||||
static Sampler readSampler(const json& node);
|
||||
static glm::mat4 readMat4(const json& node) {
|
||||
glm::mat4 m;
|
||||
if (!node.is_null()) {
|
||||
readFloatArray<16>(node, &m[0][0]);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
static glm::vec4 readVec4(const json& node) {
|
||||
glm::vec4 v;
|
||||
if (!node.is_null()) {
|
||||
readFloatArray<4>(node, &v[0]);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
static glm::vec3 readVec3(const json& node) {
|
||||
glm::vec3 v;
|
||||
if (!node.is_null()) {
|
||||
readFloatArray<3>(node, &v[0]);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
static glm::vec2 readVec2(const json& node) {
|
||||
glm::vec2 v;
|
||||
if (!node.is_null()) {
|
||||
readFloatArray<2>(node, &v[0]);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
static Transform readTransform(const json& node) { return Transform{ readMat4(node) }; }
|
||||
static std::vector<uint8_t> fromBase64(const json& node);
|
||||
static void readCommand(const json& node, Batch& batch);
|
||||
};
|
||||
|
||||
FramePointer readFrame(const std::string& filename, uint32_t externalTexture, const TextureLoader& loader) {
|
||||
return Deserializer(filename, externalTexture, loader).readFrame();
|
||||
}
|
||||
|
||||
void optimizeFrame(const std::string& filename, const IndexOptimizer& optimizer) {
|
||||
return Deserializer(filename, 0, {}).optimizeFrame(optimizer);
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
using namespace gpu;
|
||||
|
||||
void Deserializer::readBuffers(const json& buffersNode) {
|
||||
storage::FileStorage mappedFile(binaryFile.c_str());
|
||||
const auto mappedSize = mappedFile.size();
|
||||
const auto* mapped = mappedFile.data();
|
||||
size_t bufferCount = buffersNode.size();
|
||||
buffers.reserve(buffersNode.size());
|
||||
size_t offset = 0;
|
||||
for (size_t i = 0; i < bufferCount; ++i) {
|
||||
const auto& bufferNode = buffersNode[i];
|
||||
if (bufferNode.is_null()) {
|
||||
buffers.push_back(nullptr);
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t size = bufferNode;
|
||||
if (offset + size > mappedSize) {
|
||||
throw std::runtime_error("read buffer error");
|
||||
}
|
||||
buffers.push_back(std::make_shared<Buffer>(size, mapped + offset));
|
||||
bufferOffsets[buffers.back()] = offset;
|
||||
offset += size;
|
||||
}
|
||||
}
|
||||
|
||||
Element Deserializer::readElement(const json& node) {
|
||||
Element result;
|
||||
if (!node.is_null()) {
|
||||
*((uint16*)&result) = node;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Sampler Deserializer::readSampler(const json& node) {
|
||||
Sampler result;
|
||||
if (!node.is_null()) {
|
||||
if (node.count(keys::borderColor)) {
|
||||
result._desc._borderColor = readVec4(node[keys::borderColor]);
|
||||
}
|
||||
if (node.count(keys::maxAnisotropy)) {
|
||||
result._desc._maxAnisotropy = node[keys::maxAnisotropy];
|
||||
}
|
||||
if (node.count(keys::wrapModeU)) {
|
||||
result._desc._wrapModeU = node[keys::wrapModeU];
|
||||
}
|
||||
if (node.count(keys::wrapModeV)) {
|
||||
result._desc._wrapModeV = node[keys::wrapModeV];
|
||||
}
|
||||
if (node.count(keys::wrapModeW)) {
|
||||
result._desc._wrapModeW = node[keys::wrapModeW];
|
||||
}
|
||||
if (node.count(keys::filter)) {
|
||||
result._desc._filter = node[keys::filter];
|
||||
}
|
||||
if (node.count(keys::comparisonFunction)) {
|
||||
result._desc._comparisonFunc = node[keys::comparisonFunction];
|
||||
}
|
||||
if (node.count(keys::minMip)) {
|
||||
result._desc._minMip = node[keys::minMip];
|
||||
}
|
||||
if (node.count(keys::maxMip)) {
|
||||
result._desc._maxMip = node[keys::maxMip];
|
||||
}
|
||||
if (node.count(keys::mipOffset)) {
|
||||
result._desc._mipOffset = node[keys::mipOffset];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
TexturePointer Deserializer::readTexture(const json& node, uint32_t external) {
|
||||
if (node.is_null()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TextureUsageType usageType = node[keys::usageType];
|
||||
Texture::Type type = node[keys::type];
|
||||
glm::u16vec4 dims;
|
||||
dims.x = node[keys::width];
|
||||
dims.y = node[keys::height];
|
||||
dims.z = node[keys::depth];
|
||||
dims.w = node[keys::layers];
|
||||
uint16 mips = node[keys::mips];
|
||||
uint16 samples = node[keys::samples];
|
||||
Element texelFormat = readElement(node[keys::texelFormat]);
|
||||
Sampler sampler;
|
||||
readOptionalTransformed<Sampler>(sampler, node, keys::sampler, [](const json& node) { return readSampler(node); });
|
||||
TexturePointer result;
|
||||
if (usageType == TextureUsageType::EXTERNAL) {
|
||||
result = Texture::createExternal([](uint32_t, void*) {});
|
||||
result->setExternalTexture(external, nullptr);
|
||||
} else {
|
||||
result = Texture::create(usageType, type, texelFormat, dims.x, dims.y, dims.z, samples, dims.w, mips, sampler);
|
||||
}
|
||||
|
||||
auto& texture = *result;
|
||||
readOptional(texture._source, node, keys::source);
|
||||
|
||||
std::string ktxFile;
|
||||
readOptional(ktxFile, node, keys::ktxFile);
|
||||
if (!ktxFile.empty()) {
|
||||
if (QFileInfo(ktxFile.c_str()).isRelative()) {
|
||||
ktxFile = basedir + "/" + ktxFile;
|
||||
}
|
||||
texture.setKtxBacking(ktxFile);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
SwapChainPointer Deserializer::readSwapchain(const json& node) {
|
||||
if (node.is_null()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint8_t swapChainSize = node[keys::size];
|
||||
std::vector<FramebufferPointer> swapChainFramebuffers;
|
||||
const auto& framebuffersNode = node[keys::framebuffers];
|
||||
swapChainFramebuffers.resize(swapChainSize);
|
||||
for (uint8_t i = 0; i < swapChainSize; ++i) {
|
||||
auto index = framebuffersNode[i].get<uint32_t>();
|
||||
swapChainFramebuffers[i] = framebuffers[index];
|
||||
}
|
||||
return std::make_shared<FramebufferSwapChain>(swapChainFramebuffers);
|
||||
}
|
||||
|
||||
ShaderPointer Deserializer::readShader(const json& node) {
|
||||
if (node.is_null()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// FIXME support procedural shaders
|
||||
Shader::Type type = node[keys::type];
|
||||
uint32_t id = node[keys::id];
|
||||
ShaderPointer result;
|
||||
switch (type) {
|
||||
//case Shader::Type::GEOMETRY:
|
||||
// result = Shader::createGeometry(id);
|
||||
// break;
|
||||
case Shader::Type::VERTEX:
|
||||
result = Shader::createVertex(id);
|
||||
break;
|
||||
case Shader::Type::FRAGMENT:
|
||||
result = Shader::createPixel(id);
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("not implemented");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ShaderPointer Deserializer::readProgram(const json& node) {
|
||||
if (node.is_null()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<ShaderPointer> programShaders;
|
||||
programShaders.reserve(node.size());
|
||||
for (const auto& shaderRef : node) {
|
||||
uint32_t shaderIndex = shaderRef;
|
||||
programShaders.push_back(this->shaders[shaderIndex]);
|
||||
}
|
||||
|
||||
// FIXME account for geometry and compute shaders?
|
||||
return Shader::createProgram(programShaders[0], programShaders[1]);
|
||||
}
|
||||
|
||||
static State::Flags readStateFlags(const json& node) {
|
||||
State::Flags result;
|
||||
// Hacky implementation because you can't pass boolean bitfields as references
|
||||
bool value;
|
||||
if (Deserializer::readOptional(value, node, keys::alphaToCoverageEnable)) {
|
||||
result.alphaToCoverageEnable = value;
|
||||
}
|
||||
if (Deserializer::readOptional(value, node, keys::frontFaceClockwise)) {
|
||||
result.frontFaceClockwise = value;
|
||||
}
|
||||
if (Deserializer::readOptional(value, node, keys::depthClampEnable)) {
|
||||
result.depthClampEnable = value;
|
||||
}
|
||||
if (Deserializer::readOptional(value, node, keys::scissorEnable)) {
|
||||
result.scissorEnable = value;
|
||||
}
|
||||
if (Deserializer::readOptional(value, node, keys::multisampleEnable)) {
|
||||
result.multisampleEnable = value;
|
||||
}
|
||||
if (Deserializer::readOptional(value, node, keys::antialisedLineEnable)) {
|
||||
result.antialisedLineEnable = value;
|
||||
}
|
||||
if (Deserializer::readOptional(value, node, keys::alphaToCoverageEnable)) {
|
||||
result.alphaToCoverageEnable = value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static State::BlendFunction readBlendFunction(const json& node) {
|
||||
State::BlendFunction result;
|
||||
uint16 enabled;
|
||||
State::BlendArg blendArg;
|
||||
State::BlendOp blendOp;
|
||||
if (Deserializer::readOptional(enabled, node, keys::enabled)) {
|
||||
result.enabled = enabled;
|
||||
}
|
||||
if (Deserializer::readOptional(blendArg, node, keys::sourceColor)) {
|
||||
result.sourceColor = blendArg;
|
||||
}
|
||||
if (Deserializer::readOptional(blendArg, node, keys::sourceAlpha)) {
|
||||
result.sourceAlpha = blendArg;
|
||||
}
|
||||
if (Deserializer::readOptional(blendArg, node, keys::destColor)) {
|
||||
result.destColor = blendArg;
|
||||
}
|
||||
if (Deserializer::readOptional(blendArg, node, keys::destAlpha)) {
|
||||
result.destAlpha = blendArg;
|
||||
}
|
||||
if (Deserializer::readOptional(blendOp, node, keys::opAlpha)) {
|
||||
result.opAlpha = blendOp;
|
||||
}
|
||||
if (Deserializer::readOptional(blendOp, node, keys::opColor)) {
|
||||
result.opColor = blendOp;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static State::DepthTest readDepthTest(const json& node) {
|
||||
State::DepthTest result;
|
||||
Deserializer::readOptional(result.writeMask, node, keys::writeMask);
|
||||
Deserializer::readOptional(result.enabled, node, keys::enabled);
|
||||
Deserializer::readOptional(result.function, node, keys::function);
|
||||
return result;
|
||||
}
|
||||
|
||||
static State::StencilTest readStencilTest(const json& node) {
|
||||
State::StencilTest result;
|
||||
State::ComparisonFunction compareOp;
|
||||
State::StencilOp stencilOp;
|
||||
if (Deserializer::readOptional(compareOp, node, keys::function)) {
|
||||
result.function = compareOp;
|
||||
}
|
||||
if (Deserializer::readOptional(stencilOp, node, keys::failOp)) {
|
||||
result.failOp = stencilOp;
|
||||
}
|
||||
if (Deserializer::readOptional(stencilOp, node, keys::depthFailOp)) {
|
||||
result.depthFailOp = stencilOp;
|
||||
}
|
||||
if (Deserializer::readOptional(stencilOp, node, keys::passOp)) {
|
||||
result.passOp = stencilOp;
|
||||
}
|
||||
if (Deserializer::readOptional(compareOp, node, keys::function)) {
|
||||
result.function = compareOp;
|
||||
}
|
||||
Deserializer::readOptional(result.reference, node, keys::reference);
|
||||
Deserializer::readOptional(result.readMask, node, keys::readMask);
|
||||
return result;
|
||||
}
|
||||
|
||||
static State::StencilActivation readStencilActivation(const json& node) {
|
||||
auto jsonString = node.dump(2);
|
||||
State::StencilActivation result;
|
||||
bool enabled;
|
||||
if (Deserializer::readOptional(enabled, node, keys::enabled)) {
|
||||
result.enabled = enabled;
|
||||
}
|
||||
Deserializer::readOptional(result.frontWriteMask, node, keys::frontWriteMask);
|
||||
Deserializer::readOptional(result.backWriteMask, node, keys::backWriteMask);
|
||||
return result;
|
||||
}
|
||||
|
||||
StatePointer readState(const json& node) {
|
||||
if (node.is_null()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
State::Data data;
|
||||
Deserializer::readOptionalTransformed<State::Flags>(data.flags, node, keys::flags, &readStateFlags);
|
||||
Deserializer::readOptionalTransformed<State::BlendFunction>(data.blendFunction, node, keys::blendFunction, &readBlendFunction);
|
||||
Deserializer::readOptionalTransformed<State::DepthTest>(data.depthTest, node, keys::depthTest, &readDepthTest);
|
||||
Deserializer::readOptionalTransformed<State::StencilActivation>(data.stencilActivation, node, keys::stencilActivation, &readStencilActivation);
|
||||
Deserializer::readOptionalTransformed<State::StencilTest>(data.stencilTestFront, node, keys::stencilTestFront, &readStencilTest);
|
||||
Deserializer::readOptionalTransformed<State::StencilTest>(data.stencilTestBack, node, keys::stencilTestBack, &readStencilTest);
|
||||
Deserializer::readOptional(data.colorWriteMask, node, keys::colorWriteMask);
|
||||
Deserializer::readOptional(data.cullMode, node, keys::cullMode);
|
||||
Deserializer::readOptional(data.depthBias, node, keys::depthBias);
|
||||
Deserializer::readOptional(data.depthBiasSlopeScale, node, keys::depthBiasSlopeScale);
|
||||
Deserializer::readOptional(data.fillMode, node, keys::fillMode);
|
||||
Deserializer::readOptional(data.sampleMask, node, keys::sampleMask);
|
||||
return std::make_shared<State>(data);
|
||||
}
|
||||
|
||||
PipelinePointer Deserializer::readPipeline(const json& node) {
|
||||
if (node.is_null()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto state = readState(node[keys::state]);
|
||||
uint32_t programIndex = node[keys::program];
|
||||
auto program = programs[programIndex];
|
||||
return Pipeline::create(program, state);
|
||||
}
|
||||
|
||||
Stream::FormatPointer Deserializer::readFormat(const json& node) {
|
||||
if (node.is_null()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto result = std::make_shared<Stream::Format>();
|
||||
auto& format = *result;
|
||||
const auto& attributesNode = node[keys::attributes];
|
||||
for (const auto& attributeNode : attributesNode) {
|
||||
uint8_t slot = attributeNode[keys::slot];
|
||||
auto& attribute = format._attributes[slot];
|
||||
attribute._slot = slot;
|
||||
attribute._channel = attributeNode[keys::channel];
|
||||
readOptionalTransformed<Element>(attribute._element, attributeNode, keys::element,
|
||||
[](const json& node) { return readElement(node); });
|
||||
readOptional(attribute._frequency, attributeNode, keys::frequency);
|
||||
readOptional(attribute._offset, attributeNode, keys::offset);
|
||||
}
|
||||
format.evaluateCache();
|
||||
return result;
|
||||
}
|
||||
|
||||
TextureTablePointer Deserializer::readTextureTable(const json& node) {
|
||||
if (node.is_null()) {
|
||||
return nullptr;
|
||||
}
|
||||
TextureTablePointer result = std::make_shared<TextureTable>();
|
||||
auto& table = *result;
|
||||
auto count = node.size();
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
uint32_t index = node[i];
|
||||
table.setTexture(i, textures[index]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
TextureView Deserializer::readTextureView(const json& node) {
|
||||
TextureView result;
|
||||
auto texturePointerReader = [this](const json& node) {
|
||||
uint32_t textureIndex = node;
|
||||
return textures[textureIndex];
|
||||
};
|
||||
readOptionalTransformed<TexturePointer>(result._texture, node, keys::texture, texturePointerReader);
|
||||
readOptionalTransformed<Element>(result._element, node, keys::element, &readElement);
|
||||
readOptional(result._subresource, node, keys::subresource);
|
||||
return result;
|
||||
}
|
||||
|
||||
FramebufferPointer Deserializer::readFramebuffer(const json& node) {
|
||||
if (node.is_null()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FramebufferPointer result;
|
||||
{
|
||||
std::string name;
|
||||
readOptional(name, node, keys::name);
|
||||
result.reset(Framebuffer::create(name));
|
||||
}
|
||||
auto& framebuffer = *result;
|
||||
readOptional(framebuffer._bufferMask, node, keys::bufferMask);
|
||||
readOptional(framebuffer._height, node, keys::height);
|
||||
readOptional(framebuffer._width, node, keys::width);
|
||||
readOptional(framebuffer._numSamples, node, keys::sampleCount);
|
||||
auto textureViewReader = [this](const json& node) -> TextureView { return readTextureView(node); };
|
||||
readOptionalTransformed<TextureView>(framebuffer._depthStencilBuffer, node, keys::depthStencilAttachment,
|
||||
textureViewReader);
|
||||
if (framebuffer._depthStencilBuffer) {
|
||||
framebuffer._depthStamp++;
|
||||
}
|
||||
if (node.count(keys::colorAttachments)) {
|
||||
const auto& colorAttachmentsNode = node[keys::colorAttachments];
|
||||
size_t count = colorAttachmentsNode.size();
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
const auto& colorAttachmentNode = colorAttachmentsNode[i];
|
||||
if (colorAttachmentNode.is_null()) {
|
||||
continue;
|
||||
}
|
||||
framebuffer._renderBuffers[i] = readTextureView(colorAttachmentNode);
|
||||
framebuffer._colorStamps[i]++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QueryPointer Deserializer::readQuery(const json& node) {
|
||||
if (node.is_null()) {
|
||||
return nullptr;
|
||||
}
|
||||
std::string name = node[keys::name];
|
||||
return std::make_shared<Query>([](const Query&) {}, name);
|
||||
}
|
||||
|
||||
std::vector<uint8_t> Deserializer::fromBase64(const json& node) {
|
||||
std::vector<uint8_t> result;
|
||||
auto decoded = QByteArray::fromBase64(QByteArray{ node.get<std::string>().c_str() });
|
||||
result.resize(decoded.size());
|
||||
memcpy(result.data(), decoded.data(), decoded.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::unordered_map<std::string, Batch::Command> getCommandNameMap() {
|
||||
static std::unordered_map<std::string, Batch::Command> result;
|
||||
if (result.empty()) {
|
||||
for (Batch::Command i = Batch::COMMAND_draw; i < Batch::NUM_COMMANDS; i = (Batch::Command)(i + 1)) {
|
||||
result[keys::COMMAND_NAMES[i]] = i;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Deserializer::readCommand(const json& commandNode, Batch& batch) {
|
||||
size_t count = commandNode.size();
|
||||
std::string commandName = commandNode[0];
|
||||
Batch::Command command = getCommandNameMap()[commandName];
|
||||
batch._commands.push_back(command);
|
||||
batch._commandOffsets.push_back(batch._params.size());
|
||||
for (size_t i = 1; i < count; ++i) {
|
||||
batch._params.emplace_back(commandNode[i].get<size_t>());
|
||||
}
|
||||
}
|
||||
|
||||
Batch::NamedBatchData Deserializer::readNamedBatchData(const json& node) {
|
||||
Batch::NamedBatchData result;
|
||||
readOptionalVectorTransformed<BufferPointer>(result.buffers, node, keys::buffers, [this](const json& node) {
|
||||
uint32_t index = node;
|
||||
return buffers[index];
|
||||
});
|
||||
readOptionalVectorTransformed<Batch::DrawCallInfo>(result.drawCallInfos, node, keys::drawCallInfos,
|
||||
[](const json& node) -> Batch::DrawCallInfo {
|
||||
Batch::DrawCallInfo result{ 0 };
|
||||
*((uint32_t*)&result) = node;
|
||||
return result;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
BatchPointer Deserializer::readBatch(const json& node) {
|
||||
if (node.is_null()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string batchName;
|
||||
if (node.count(keys::name)) {
|
||||
batchName = node[keys::name];
|
||||
}
|
||||
BatchPointer result = std::make_shared<Batch>(batchName);
|
||||
auto& batch = *result;
|
||||
readOptional(batch._enableStereo, node, keys::stereo);
|
||||
readOptional(batch._enableSkybox, node, keys::skybox);
|
||||
readOptionalTransformed<glm::vec2>(batch._projectionJitter, node, keys::projectionJitter, &readVec2);
|
||||
readOptional(batch._drawcallUniform, node, keys::drawcallUniform);
|
||||
readOptional(batch._drawcallUniformReset, node, keys::drawcallUniformReset);
|
||||
readPointerCache(batch._textures, node, keys::textures, textures);
|
||||
readPointerCache(batch._textureTables, node, keys::textureTables, textureTables);
|
||||
readPointerCache(batch._buffers, node, keys::buffers, buffers);
|
||||
readPointerCache(batch._pipelines, node, keys::pipelines, pipelines);
|
||||
readPointerCache(batch._streamFormats, node, keys::formats, formats);
|
||||
readPointerCache(batch._framebuffers, node, keys::framebuffers, framebuffers);
|
||||
readPointerCache(batch._swapChains, node, keys::swapchains, swapchains);
|
||||
readPointerCache(batch._queries, node, keys::queries, queries);
|
||||
|
||||
readOptionalVectorTransformed<Batch::DrawCallInfo>(batch._drawCallInfos, node, keys::drawCallInfos,
|
||||
[](const json& node) -> Batch::DrawCallInfo {
|
||||
Batch::DrawCallInfo result{ 0 };
|
||||
*((uint32_t*)&result) = node;
|
||||
return result;
|
||||
});
|
||||
|
||||
readOptionalTransformed<std::vector<uint8_t>>(batch._data, node, keys::data,
|
||||
[](const json& node) { return fromBase64(node); });
|
||||
|
||||
for (const auto& commandNode : node[keys::commands]) {
|
||||
readCommand(commandNode, batch);
|
||||
}
|
||||
readBatchCacheTransformed<Transform, Transform>(batch._transforms, node, keys::transforms, &readTransform);
|
||||
readBatchCacheTransformed<std::string>(batch._profileRanges, node, keys::profileRanges);
|
||||
readBatchCacheTransformed<std::string>(batch._names, node, keys::names);
|
||||
|
||||
auto objectTransformReader = [](const json& node) -> Batch::TransformObject {
|
||||
Batch::TransformObject result;
|
||||
result._model = readMat4(node);
|
||||
result._modelInverse = glm::inverse(result._model);
|
||||
return result;
|
||||
};
|
||||
readOptionalVectorTransformed<Batch::TransformObject>(batch._objects, node, keys::objects, objectTransformReader);
|
||||
|
||||
if (node.count(keys::namedData)) {
|
||||
const auto& namedDataNode = node[keys::namedData];
|
||||
for (auto itr = namedDataNode.begin(); itr != namedDataNode.end(); ++itr) {
|
||||
auto name = itr.key();
|
||||
batch._namedData[name] = readNamedBatchData(itr.value());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
StereoState readStereoState(const json& node) {
|
||||
StereoState result;
|
||||
Deserializer::readOptional(result._enable, node, keys::enable);
|
||||
Deserializer::readOptional(result._contextDisable, node, keys::contextDisable);
|
||||
Deserializer::readOptional(result._skybox, node, keys::skybox);
|
||||
if (node.count(keys::eyeProjections)) {
|
||||
auto projections = node[keys::eyeProjections];
|
||||
result._eyeProjections[0] = Deserializer::readMat4(projections[0]);
|
||||
result._eyeProjections[1] = Deserializer::readMat4(projections[1]);
|
||||
}
|
||||
if (node.count(keys::eyeViews)) {
|
||||
auto views = node[keys::eyeViews];
|
||||
result._eyeViews[0] = Deserializer::readMat4(views[0]);
|
||||
result._eyeViews[1] = Deserializer::readMat4(views[1]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
FramePointer Deserializer::deserializeFrame() {
|
||||
{
|
||||
std::string filename{ basename + ".json" };
|
||||
if (0 == basename.find("assets:")) {
|
||||
auto lastSlash = basename.rfind('/');
|
||||
basedir = basename.substr(0, lastSlash);
|
||||
} else {
|
||||
basedir = QFileInfo(basename.c_str()).absolutePath().toStdString();
|
||||
}
|
||||
storage::FileStorage mappedFile(filename.c_str());
|
||||
frameNode = json::parse(std::string((const char*)mappedFile.data(), mappedFile.size()));
|
||||
}
|
||||
|
||||
FramePointer result = std::make_shared<Frame>();
|
||||
auto& frame = *result;
|
||||
|
||||
if (frameNode[keys::binary].is_string()) {
|
||||
binaryFile = frameNode[keys::binary];
|
||||
if (QFileInfo(binaryFile.c_str()).isRelative()) {
|
||||
binaryFile = basedir + "/" + binaryFile;
|
||||
}
|
||||
} else {
|
||||
binaryFile = basename + ".bin";
|
||||
}
|
||||
|
||||
|
||||
if (frameNode.count(keys::buffers)) {
|
||||
readBuffers(frameNode[keys::buffers]);
|
||||
}
|
||||
|
||||
shaders = readArray<ShaderPointer>(frameNode, keys::shaders, [](const json& node) { return readShader(node); });
|
||||
// Must come after shaders
|
||||
programs = readArray<ShaderPointer>(frameNode, keys::programs, [this](const json& node) { return readProgram(node); });
|
||||
// Must come after programs
|
||||
pipelines = readArray<PipelinePointer>(frameNode, keys::pipelines, [this](const json& node) { return readPipeline(node); });
|
||||
|
||||
formats = readArray<Stream::FormatPointer>(frameNode, keys::formats, [](const json& node) { return readFormat(node); });
|
||||
|
||||
auto textureReader = [this](const json& node) { return readTexture(node, externalTexture); };
|
||||
textures = readArray<TexturePointer>(frameNode, keys::textures, textureReader);
|
||||
if (textureLoader) {
|
||||
std::vector<uint32_t> capturedTextures = readNumericVector<uint32_t>(frameNode[keys::capturedTextures]);
|
||||
for (const auto& index : capturedTextures) {
|
||||
const auto& texturePointer = textures[index];
|
||||
uint16 layers = std::max<uint16>(texturePointer->getNumSlices(), 1);
|
||||
for (uint16 layer = 0; layer < layers; ++layer) {
|
||||
std::string filename = basename + "." + std::to_string(index) + "." + std::to_string(layer) + ".png";
|
||||
textureLoader(filename, texturePointer, layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Must come after textures
|
||||
auto textureTableReader = [this](const json& node) { return readTextureTable(node); };
|
||||
textureTables = readArray<TextureTablePointer>(frameNode, keys::textureTables, textureTableReader);
|
||||
|
||||
// Must come after textures
|
||||
auto framebufferReader = [this](const json& node) { return readFramebuffer(node); };
|
||||
framebuffers = readArray<FramebufferPointer>(frameNode, keys::framebuffers, framebufferReader);
|
||||
|
||||
// Must come after textures & framebuffers
|
||||
swapchains =
|
||||
readArray<SwapChainPointer>(frameNode, keys::swapchains, [this](const json& node) { return readSwapchain(node); });
|
||||
|
||||
queries = readArray<QueryPointer>(frameNode, keys::queries, [this](const json& node) { return readQuery(node); });
|
||||
frame.framebuffer = framebuffers[frameNode[keys::framebuffer].get<uint32_t>()];
|
||||
frame.view = readMat4(frameNode[keys::view]);
|
||||
frame.pose = readMat4(frameNode[keys::pose]);
|
||||
frame.frameIndex = frameNode[keys::frameIndex];
|
||||
frame.stereoState = readStereoState(frameNode[keys::stereo]);
|
||||
if (frameNode.count(keys::batches)) {
|
||||
for (const auto& batchNode : frameNode[keys::batches]) {
|
||||
frame.batches.push_back(readBatch(batchNode));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
FramePointer Deserializer::readFrame() {
|
||||
auto result = deserializeFrame();
|
||||
result->finish();
|
||||
return result;
|
||||
}
|
||||
|
||||
void Deserializer::optimizeFrame(const IndexOptimizer& optimizer) {
|
||||
auto result = deserializeFrame();
|
||||
auto& frame = *result;
|
||||
|
||||
|
||||
// optimize the index buffers?
|
||||
struct CurrentIndexBuffer {
|
||||
Offset offset{ 0 };
|
||||
BufferPointer buffer;
|
||||
Type type{ gpu::Type::INT32 };
|
||||
Primitive primitve{ Primitive::TRIANGLES };
|
||||
uint32_t numIndices{ 0 };
|
||||
uint32_t startIndex{ 0 };
|
||||
};
|
||||
|
||||
std::vector<CurrentIndexBuffer> captured;
|
||||
for (auto& batch : frame.batches) {
|
||||
|
||||
CurrentIndexBuffer currentIndexBuffer;
|
||||
batch->forEachCommand([&](Batch::Command cmd, const Batch::Param* params){
|
||||
switch(cmd) {
|
||||
case Batch::Command::COMMAND_setIndexBuffer:
|
||||
currentIndexBuffer.offset = params[0]._size;
|
||||
currentIndexBuffer.buffer = batch->_buffers.get(params[1]._int);
|
||||
currentIndexBuffer.type = (Type)params[2]._int;
|
||||
break;
|
||||
|
||||
case Batch::Command::COMMAND_drawIndexed:
|
||||
currentIndexBuffer.startIndex = params[0]._int;
|
||||
currentIndexBuffer.numIndices = params[1]._int;
|
||||
currentIndexBuffer.primitve = (Primitive)params[2]._int;
|
||||
captured.emplace_back(currentIndexBuffer);
|
||||
break;
|
||||
|
||||
case Batch::Command::COMMAND_drawIndexedInstanced:
|
||||
currentIndexBuffer.startIndex = params[1]._int;
|
||||
currentIndexBuffer.numIndices = params[2]._int;
|
||||
currentIndexBuffer.primitve = (Primitive)params[3]._int;
|
||||
captured.emplace_back(currentIndexBuffer);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
std::string optimizedBinaryFile = basename + "_optimized.bin";
|
||||
QFile(binaryFile.c_str()).copy(optimizedBinaryFile.c_str());
|
||||
{
|
||||
storage::FileStorage mappedFile(optimizedBinaryFile.c_str());
|
||||
std::set<BufferPointer> uniqueBuffers;
|
||||
for (const auto& capturedIndexData : captured) {
|
||||
if (uniqueBuffers.count(capturedIndexData.buffer)) {
|
||||
continue;
|
||||
}
|
||||
uniqueBuffers.insert(capturedIndexData.buffer);
|
||||
auto bufferOffset = bufferOffsets[capturedIndexData.buffer];
|
||||
auto& buffer = *capturedIndexData.buffer;
|
||||
const auto& count = capturedIndexData.numIndices;
|
||||
auto indices = (uint32_t*)buffer.editData();
|
||||
optimizer(capturedIndexData.primitve, count / 3, count, indices);
|
||||
memcpy(mappedFile.mutableData() + bufferOffset, indices, sizeof(uint32_t) * count);
|
||||
}
|
||||
}
|
||||
frameNode[keys::binary] = optimizedBinaryFile;
|
||||
{
|
||||
std::string frameJson = frameNode.dump();
|
||||
std::string filename = basename + "_optimized.json";
|
||||
storage::FileStorage::create(filename.c_str(), frameJson.size(), (const uint8_t*)frameJson.data());
|
||||
}
|
||||
}
|
844
libraries/gpu/src/gpu/FrameWriter.cpp
Normal file
844
libraries/gpu/src/gpu/FrameWriter.cpp
Normal file
|
@ -0,0 +1,844 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2018/10/14
|
||||
// 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 "FrameIO.h"
|
||||
#include "Frame.h"
|
||||
#include "Batch.h"
|
||||
#include "TextureTable.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "FrameIOKeys.h"
|
||||
|
||||
namespace gpu {
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
class Serializer {
|
||||
public:
|
||||
const std::string basename;
|
||||
const TextureCapturer textureCapturer;
|
||||
std::unordered_map<ShaderPointer, uint32_t> shaderMap;
|
||||
std::unordered_map<ShaderPointer, uint32_t> programMap;
|
||||
std::unordered_map<TexturePointer, uint32_t> textureMap;
|
||||
std::unordered_map<TextureTablePointer, uint32_t> textureTableMap;
|
||||
std::unordered_map<BufferPointer, uint32_t> bufferMap;
|
||||
std::unordered_map<Stream::FormatPointer, uint32_t> formatMap;
|
||||
std::unordered_map<PipelinePointer, uint32_t> pipelineMap;
|
||||
std::unordered_map<FramebufferPointer, uint32_t> framebufferMap;
|
||||
std::unordered_map<SwapChainPointer, uint32_t> swapchainMap;
|
||||
std::unordered_map<QueryPointer, uint32_t> queryMap;
|
||||
|
||||
Serializer(const std::string& basename, const TextureCapturer& capturer) : basename(basename), textureCapturer(capturer) {}
|
||||
|
||||
template <typename T>
|
||||
static uint32_t getGlobalIndex(const T& value, std::unordered_map<T, uint32_t>& map) {
|
||||
if (map.count(value) == 0) {
|
||||
uint32_t result = (uint32_t)map.size();
|
||||
map[value] = result;
|
||||
return result;
|
||||
}
|
||||
return map[value];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static json serializePointerCache(const typename Batch::Cache<T>::Vector& cache, std::unordered_map<T, uint32_t>& map) {
|
||||
json result = json::array();
|
||||
const auto count = cache._items.size();
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
const auto& cacheEntry = cache._items[i];
|
||||
const auto& val = cacheEntry._data;
|
||||
result.push_back(getGlobalIndex(val, map));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, typename TT = const T&>
|
||||
static json serializeDataCache(const typename Batch::Cache<T>::Vector& cache,
|
||||
std::function<TT(const T&)> f = [](const T& t) -> TT { return t; }) {
|
||||
json result = json::array();
|
||||
const auto count = cache._items.size();
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
const auto& cacheEntry = cache._items[i];
|
||||
const auto& val = cacheEntry._data;
|
||||
result.push_back(f(val));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, typename TT = const T&>
|
||||
static json writeVector(const std::vector<T>& v,
|
||||
const std::function<TT(const T&)>& f = [](const T& t) -> TT { return t; }) {
|
||||
auto node = json::array();
|
||||
for (const auto& e : v) {
|
||||
node.push_back(f(e));
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static json writeNumericVector(const std::vector<T>& v) {
|
||||
return writeVector<T, const T&>(v);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static json writeUintVector(const std::vector<T>& v) {
|
||||
return writeVector<T, const uint32_t&>(v, [](const T& t) -> const uint32_t& {
|
||||
return reinterpret_cast<const uint32_t&>(t);
|
||||
});
|
||||
}
|
||||
|
||||
template <size_t N = 1>
|
||||
static json writeFloatArray(const float* f) {
|
||||
json result = json::array();
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
result.push_back(f[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::vector<T> mapToVector(const std::unordered_map<T, uint32_t>& map) {
|
||||
std::vector<T> result;
|
||||
result.resize(map.size());
|
||||
for (const auto& entry : map) {
|
||||
if (result[entry.second]) {
|
||||
throw std::runtime_error("Invalid map");
|
||||
}
|
||||
result[entry.second] = entry.first;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T, typename F>
|
||||
std::function<json(const T&)> memberWriter(F f) {
|
||||
return std::bind(f, this, std::placeholders::_1);
|
||||
}
|
||||
|
||||
void writeFrame(const Frame& frame);
|
||||
json writeBatch(const Batch& batch);
|
||||
json writeTextureTable(const TextureTablePointer& textureTable);
|
||||
json writeTextureView(const TextureView& textureView);
|
||||
json writeFramebuffer(const FramebufferPointer& texture);
|
||||
json writePipeline(const PipelinePointer& pipeline);
|
||||
json writeSwapchain(const SwapChainPointer& swapchain);
|
||||
json writeProgram(const ShaderPointer& program);
|
||||
json writeNamedBatchData(const Batch::NamedBatchData& namedData);
|
||||
|
||||
json writeCapturableTextures(const Frame& frame);
|
||||
void writeBinaryBlob();
|
||||
static std::string toBase64(const std::vector<uint8_t>& v);
|
||||
static json writeIrradiance(const SHPointer& irradiance);
|
||||
static json writeMat4(const glm::mat4& m) {
|
||||
static const glm::mat4 IDENTITY;
|
||||
if (m == IDENTITY) {
|
||||
return json();
|
||||
}
|
||||
return writeFloatArray<16>(&m[0][0]);
|
||||
}
|
||||
static json writeVec4(const glm::vec4& v) { return writeFloatArray<4>(&v[0]); }
|
||||
static json writeVec3(const glm::vec3& v) { return writeFloatArray<3>(&v[0]); }
|
||||
static json writeVec2(const glm::vec2& v) { return writeFloatArray<2>(&v[0]); }
|
||||
static json writeTransform(const Transform& t) { return writeMat4(t.getMatrix()); }
|
||||
static json writeCommand(size_t index, const Batch& batch);
|
||||
static json writeSampler(const Sampler& sampler);
|
||||
static json writeTexture(const TexturePointer& texture);
|
||||
static json writeFormat(const Stream::FormatPointer& format);
|
||||
static json writeQuery(const QueryPointer& query);
|
||||
static json writeShader(const ShaderPointer& shader);
|
||||
static json writeBuffer(const BufferPointer& bufferPointer);
|
||||
|
||||
static const TextureView DEFAULT_TEXTURE_VIEW;
|
||||
static const Sampler DEFAULT_SAMPLER;
|
||||
|
||||
template <typename T, typename F>
|
||||
void serializeMap(json& frameNode, const char* key, const std::unordered_map<T, uint32_t>& map, F f) {
|
||||
auto& node = frameNode[key] = json::array();
|
||||
for (const auto& item : mapToVector(map)) {
|
||||
node.push_back(f(item));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void writeFrame(const std::string& filename, const FramePointer& frame, const TextureCapturer& capturer) {
|
||||
Serializer(filename, capturer).writeFrame(*frame);
|
||||
}
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
using namespace gpu;
|
||||
|
||||
const TextureView Serializer::DEFAULT_TEXTURE_VIEW = TextureView();
|
||||
const Sampler Serializer::DEFAULT_SAMPLER = Sampler();
|
||||
|
||||
std::string Serializer::toBase64(const std::vector<uint8_t>& v) {
|
||||
return QByteArray((const char*)v.data(), (int)v.size()).toBase64().toStdString();
|
||||
}
|
||||
|
||||
json Serializer::writeCommand(size_t index, const Batch& batch) {
|
||||
const auto& command = batch._commands[index];
|
||||
auto offset = batch._commandOffsets[index];
|
||||
auto endOffset = batch._params.size();
|
||||
if ((index + 1) < batch._commands.size()) {
|
||||
endOffset = batch._commandOffsets[index + 1];
|
||||
}
|
||||
|
||||
json result = json::array();
|
||||
result.push_back(keys::COMMAND_NAMES[command]);
|
||||
while (offset < endOffset) {
|
||||
result.push_back(batch._params[offset]._size);
|
||||
++offset;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
json Serializer::writeNamedBatchData(const Batch::NamedBatchData& namedData) {
|
||||
json result = json::object();
|
||||
auto& buffersNode = result[keys::buffers] = json::array();
|
||||
for (const auto& buffer : namedData.buffers) {
|
||||
buffersNode.push_back(getGlobalIndex(buffer, bufferMap));
|
||||
}
|
||||
result[keys::drawCallInfos] = writeUintVector(namedData.drawCallInfos);
|
||||
return result;
|
||||
}
|
||||
|
||||
json Serializer::writeBatch(const Batch& batch) {
|
||||
json batchNode;
|
||||
static const Batch DEFAULT_BATCH;
|
||||
|
||||
batchNode[keys::name] = batch.getName();
|
||||
if (batch._enableSkybox != DEFAULT_BATCH._enableSkybox) {
|
||||
batchNode[keys::skybox] = batch._enableSkybox;
|
||||
}
|
||||
if (batch._enableStereo != DEFAULT_BATCH._enableStereo) {
|
||||
batchNode[keys::stereo] = batch._enableStereo;
|
||||
}
|
||||
if (batch._projectionJitter != DEFAULT_BATCH._projectionJitter) {
|
||||
batchNode[keys::projectionJitter] = writeVec2(batch._projectionJitter);
|
||||
}
|
||||
if (batch._drawcallUniform != DEFAULT_BATCH._drawcallUniform) {
|
||||
batchNode[keys::drawcallUniform] = batch._drawcallUniform;
|
||||
}
|
||||
if (batch._drawcallUniformReset != DEFAULT_BATCH._drawcallUniformReset) {
|
||||
batchNode[keys::drawcallUniformReset] = batch._drawcallUniformReset;
|
||||
}
|
||||
if (0 != batch._textures.size()) {
|
||||
batchNode[keys::textures] = serializePointerCache(batch._textures, textureMap);
|
||||
}
|
||||
if (0 != batch._textureTables.size()) {
|
||||
batchNode[keys::textureTables] = serializePointerCache(batch._textureTables, textureTableMap);
|
||||
}
|
||||
if (0 != batch._buffers.size()) {
|
||||
batchNode[keys::buffers] = serializePointerCache(batch._buffers, bufferMap);
|
||||
}
|
||||
if (0 != batch._pipelines.size()) {
|
||||
batchNode[keys::pipelines] = serializePointerCache(batch._pipelines, pipelineMap);
|
||||
}
|
||||
if (0 != batch._streamFormats.size()) {
|
||||
batchNode[keys::formats] = serializePointerCache(batch._streamFormats, formatMap);
|
||||
}
|
||||
if (0 != batch._framebuffers.size()) {
|
||||
batchNode[keys::framebuffers] = serializePointerCache(batch._framebuffers, framebufferMap);
|
||||
}
|
||||
if (0 != batch._swapChains.size()) {
|
||||
batchNode[keys::swapchains] = serializePointerCache(batch._swapChains, swapchainMap);
|
||||
}
|
||||
if (0 != batch._queries.size()) {
|
||||
batchNode[keys::queries] = serializePointerCache(batch._queries, queryMap);
|
||||
}
|
||||
if (!batch._drawCallInfos.empty()) {
|
||||
batchNode[keys::drawCallInfos] = writeUintVector(batch._drawCallInfos);
|
||||
}
|
||||
if (!batch._data.empty()) {
|
||||
batchNode[keys::data] = toBase64(batch._data);
|
||||
}
|
||||
|
||||
{
|
||||
auto& node = batchNode[keys::commands] = json::array();
|
||||
size_t commandCount = batch._commands.size();
|
||||
for (size_t i = 0; i < commandCount; ++i) {
|
||||
node.push_back(writeCommand(i, batch));
|
||||
}
|
||||
}
|
||||
|
||||
if (0 != batch._transforms.size()) {
|
||||
batchNode[keys::transforms] =
|
||||
serializeDataCache<Transform, json>(batch._transforms, [](const Transform& t) { return writeTransform(t); });
|
||||
}
|
||||
if (0 != batch._profileRanges.size()) {
|
||||
batchNode[keys::profileRanges] = serializeDataCache<std::string>(batch._profileRanges);
|
||||
}
|
||||
if (0 != batch._names.size()) {
|
||||
batchNode[keys::names] = serializeDataCache<std::string>(batch._names);
|
||||
}
|
||||
if (0 != batch._objects.size()) {
|
||||
auto transform = [](const Batch::TransformObject& object) -> json { return writeMat4(object._model); };
|
||||
batchNode[keys::objects] = writeVector<Batch::TransformObject, json>(batch._objects, transform);
|
||||
}
|
||||
|
||||
if (!batch._namedData.empty()) {
|
||||
auto& namedDataNode = batchNode[keys::namedData] = json::object();
|
||||
for (const auto& entry : batch._namedData) {
|
||||
namedDataNode[entry.first] = writeNamedBatchData(entry.second);
|
||||
}
|
||||
}
|
||||
|
||||
// LambdaCache _lambdas;
|
||||
|
||||
return batchNode;
|
||||
}
|
||||
|
||||
json Serializer::writeSampler(const Sampler& sampler) {
|
||||
json result = json::object();
|
||||
if (sampler.getBorderColor() != DEFAULT_SAMPLER.getBorderColor()) {
|
||||
result[keys::borderColor] = writeVec4(sampler.getBorderColor());
|
||||
}
|
||||
if (sampler.getMaxAnisotropy() != DEFAULT_SAMPLER.getMaxAnisotropy()) {
|
||||
result[keys::maxAnisotropy] = sampler.getMaxAnisotropy();
|
||||
}
|
||||
if (sampler.getWrapModeU() != DEFAULT_SAMPLER.getWrapModeU()) {
|
||||
result[keys::wrapModeU] = sampler.getWrapModeU();
|
||||
}
|
||||
if (sampler.getWrapModeV() != DEFAULT_SAMPLER.getWrapModeV()) {
|
||||
result[keys::wrapModeV] = sampler.getWrapModeV();
|
||||
}
|
||||
if (sampler.getWrapModeW() != DEFAULT_SAMPLER.getWrapModeW()) {
|
||||
result[keys::wrapModeW] = sampler.getWrapModeW();
|
||||
}
|
||||
if (sampler.getFilter() != DEFAULT_SAMPLER.getFilter()) {
|
||||
result[keys::filter] = sampler.getFilter();
|
||||
}
|
||||
if (sampler.getComparisonFunction() != DEFAULT_SAMPLER.getComparisonFunction()) {
|
||||
result[keys::comparisonFunction] = sampler.getComparisonFunction();
|
||||
}
|
||||
if (sampler.getMinMip() != DEFAULT_SAMPLER.getMinMip()) {
|
||||
result[keys::minMip] = sampler.getMinMip();
|
||||
}
|
||||
if (sampler.getMaxMip() != DEFAULT_SAMPLER.getMaxMip()) {
|
||||
result[keys::maxMip] = sampler.getMaxMip();
|
||||
}
|
||||
if (sampler.getMipOffset() != DEFAULT_SAMPLER.getMipOffset()) {
|
||||
result[keys::mipOffset] = sampler.getMipOffset();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
json Serializer::writeBuffer(const BufferPointer& bufferPointer) {
|
||||
if (!bufferPointer) {
|
||||
return json();
|
||||
}
|
||||
|
||||
return json(bufferPointer->getSize());
|
||||
}
|
||||
|
||||
json Serializer::writeIrradiance(const SHPointer& irradiancePointer) {
|
||||
if (!irradiancePointer) {
|
||||
return json();
|
||||
}
|
||||
|
||||
json result = json::object();
|
||||
const auto& irradiance = *irradiancePointer;
|
||||
result[keys::L00] = writeVec3(irradiance.L00);
|
||||
result[keys::L1m1] = writeVec3(irradiance.L1m1);
|
||||
result[keys::L10] = writeVec3(irradiance.L10);
|
||||
result[keys::L11] = writeVec3(irradiance.L11);
|
||||
result[keys::L2m2] = writeVec3(irradiance.L2m2);
|
||||
result[keys::L2m1] = writeVec3(irradiance.L2m1);
|
||||
result[keys::L20] = writeVec3(irradiance.L20);
|
||||
result[keys::L21] = writeVec3(irradiance.L21);
|
||||
result[keys::L22] = writeVec3(irradiance.L22);
|
||||
return result;
|
||||
}
|
||||
|
||||
json Serializer::writeTexture(const TexturePointer& texturePointer) {
|
||||
if (!texturePointer) {
|
||||
return json();
|
||||
}
|
||||
|
||||
const auto& texture = *texturePointer;
|
||||
json result = json::object();
|
||||
if (!texture.source().empty()) {
|
||||
result[keys::source] = texture.source();
|
||||
}
|
||||
const auto usageType = texture.getUsageType();
|
||||
result[keys::usageType] = usageType;
|
||||
result[keys::type] = texture.getType();
|
||||
result[keys::width] = texture._width;
|
||||
result[keys::height] = texture._height;
|
||||
result[keys::depth] = texture._depth;
|
||||
result[keys::mips] = texture._maxMipLevel + 1;
|
||||
result[keys::samples] = texture._numSamples;
|
||||
result[keys::layers] = texture._numSlices;
|
||||
result[keys::texelFormat] = texture.getTexelFormat().getRaw();
|
||||
if (texture.isIrradianceValid()) {
|
||||
result["irradiance"] = writeIrradiance(texture._irradiance);
|
||||
}
|
||||
if (texture._sampler != DEFAULT_SAMPLER) {
|
||||
result[keys::sampler] = writeSampler(texture._sampler);
|
||||
}
|
||||
if (usageType == TextureUsageType::RENDERBUFFER) {
|
||||
// TODO figure out if the buffer contents need to be preserved (if it's used as an input before it's ever written to)
|
||||
// This might be the case for things like the TAA output attachments from the previous frame
|
||||
} else if (usageType == TextureUsageType::EXTERNAL) {
|
||||
// TODO serialize the current GL contents (if any) to the JSON
|
||||
} else {
|
||||
const auto* storage = texture._storage.get();
|
||||
const auto* ktxStorage = dynamic_cast<const Texture::KtxStorage*>(storage);
|
||||
if (ktxStorage) {
|
||||
result[keys::ktxFile] = ktxStorage->_filename;
|
||||
} else {
|
||||
// TODO serialize the backing storage
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
json Serializer::writeTextureView(const TextureView& textureView) {
|
||||
static const auto DEFAULT_TEXTURE_VIEW = TextureView();
|
||||
json result = json::object();
|
||||
if (textureView._texture) {
|
||||
result[keys::texture] = getGlobalIndex(textureView._texture, textureMap);
|
||||
}
|
||||
if (textureView._subresource != DEFAULT_TEXTURE_VIEW._subresource) {
|
||||
result[keys::subresource] = textureView._subresource;
|
||||
}
|
||||
if (textureView._element != DEFAULT_TEXTURE_VIEW._element) {
|
||||
result[keys::element] = textureView._element.getRaw();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
json Serializer::writeFramebuffer(const FramebufferPointer& framebufferPointer) {
|
||||
if (!framebufferPointer) {
|
||||
return json();
|
||||
}
|
||||
|
||||
auto result = json::object();
|
||||
const auto& framebuffer = *framebufferPointer;
|
||||
if (!framebuffer._name.empty()) {
|
||||
result[keys::name] = framebuffer._name;
|
||||
}
|
||||
if (framebuffer._bufferMask != 0) {
|
||||
result[keys::bufferMask] = framebuffer._bufferMask;
|
||||
}
|
||||
if (framebuffer._height != 0) {
|
||||
result[keys::height] = framebuffer._height;
|
||||
}
|
||||
if (framebuffer._width != 0) {
|
||||
result[keys::width] = framebuffer._width;
|
||||
}
|
||||
if (framebuffer._numSamples != 0 && framebuffer._numSamples != 1) {
|
||||
result[keys::sampleCount] = framebuffer._numSamples;
|
||||
}
|
||||
|
||||
if (framebuffer._depthStencilBuffer.isValid()) {
|
||||
result[keys::depthStencilAttachment] = writeTextureView(framebuffer._depthStencilBuffer);
|
||||
}
|
||||
|
||||
if (!framebuffer._renderBuffers.empty()) {
|
||||
size_t rendereBufferCount = 0;
|
||||
for (size_t i = 0; i < framebuffer._renderBuffers.size(); ++i) {
|
||||
if (framebuffer._renderBuffers[i].isValid()) {
|
||||
rendereBufferCount = i + 1;
|
||||
}
|
||||
}
|
||||
if (rendereBufferCount > 0) {
|
||||
auto& node = result[keys::colorAttachments] = json::array();
|
||||
for (size_t i = 0; i < rendereBufferCount; ++i) {
|
||||
node.push_back(writeTextureView(framebuffer._renderBuffers[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//SwapchainPointer _swapchain;
|
||||
return result;
|
||||
}
|
||||
|
||||
json Serializer::writeTextureTable(const TextureTablePointer& textureTablePointer) {
|
||||
auto tableNode = json::array();
|
||||
const auto& textureTable = *textureTablePointer;
|
||||
for (const auto& texture : textureTable.getTextures()) {
|
||||
tableNode.push_back(getGlobalIndex(texture, textureMap));
|
||||
}
|
||||
return tableNode;
|
||||
}
|
||||
|
||||
json Serializer::writeFormat(const Stream::FormatPointer& formatPointer) {
|
||||
if (!formatPointer) {
|
||||
return json();
|
||||
}
|
||||
|
||||
const auto& format = *formatPointer;
|
||||
json result = json::object();
|
||||
auto& attributesNode = result[keys::attributes] = json::array();
|
||||
static const auto DEFAULT_ATTRIBUTE = Stream::Attribute();
|
||||
for (const auto& entry : format._attributes) {
|
||||
const auto& attribute = entry.second;
|
||||
auto attributeNode = json::object();
|
||||
attributeNode[keys::slot] = attribute._slot;
|
||||
attributeNode[keys::channel] = attribute._channel;
|
||||
if (DEFAULT_ATTRIBUTE._element.getRaw() != attribute._element.getRaw()) {
|
||||
attributeNode[keys::element] = attribute._element.getRaw();
|
||||
}
|
||||
if (DEFAULT_ATTRIBUTE._frequency != attribute._frequency) {
|
||||
attributeNode[keys::frequency] = attribute._frequency;
|
||||
}
|
||||
if (DEFAULT_ATTRIBUTE._offset != attribute._offset) {
|
||||
attributeNode[keys::offset] = attribute._offset;
|
||||
}
|
||||
attributesNode.push_back(attributeNode);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#define SET_IF_NOT_DEFAULT(FIELD) \
|
||||
if (value.FIELD != DEFAULT.FIELD) { \
|
||||
result[keys::FIELD] = value.FIELD; \
|
||||
}
|
||||
|
||||
#define SET_IF_NOT_DEFAULT_(FIELD) \
|
||||
if (value._##FIELD != DEFAULT._##FIELD) { \
|
||||
result[keys::FIELD] = value._##FIELD; \
|
||||
}
|
||||
|
||||
#define SET_IF_NOT_DEFAULT_TRANSFORM(FIELD, TRANSFORM) \
|
||||
if (value.FIELD != DEFAULT.FIELD) { \
|
||||
result[keys::FIELD] = TRANSFORM(value.FIELD); \
|
||||
}
|
||||
|
||||
static json writeBlendFunction(const State::BlendFunction& value) {
|
||||
static const State::BlendFunction DEFAULT;
|
||||
json result = json::object();
|
||||
SET_IF_NOT_DEFAULT(enabled);
|
||||
SET_IF_NOT_DEFAULT(sourceColor);
|
||||
SET_IF_NOT_DEFAULT(sourceAlpha);
|
||||
SET_IF_NOT_DEFAULT(destColor);
|
||||
SET_IF_NOT_DEFAULT(destAlpha);
|
||||
SET_IF_NOT_DEFAULT(destColor);
|
||||
SET_IF_NOT_DEFAULT(opAlpha);
|
||||
SET_IF_NOT_DEFAULT(opColor);
|
||||
return result;
|
||||
}
|
||||
|
||||
static json writeStateFlags(const State::Flags& value) {
|
||||
static const State::Flags DEFAULT;
|
||||
json result = json::object();
|
||||
SET_IF_NOT_DEFAULT(frontFaceClockwise);
|
||||
SET_IF_NOT_DEFAULT(depthClampEnable);
|
||||
SET_IF_NOT_DEFAULT(scissorEnable);
|
||||
SET_IF_NOT_DEFAULT(multisampleEnable);
|
||||
SET_IF_NOT_DEFAULT(antialisedLineEnable);
|
||||
SET_IF_NOT_DEFAULT(alphaToCoverageEnable);
|
||||
return result;
|
||||
}
|
||||
|
||||
static json writeDepthTest(const State::DepthTest& value) {
|
||||
static const State::DepthTest DEFAULT;
|
||||
json result = json::object();
|
||||
SET_IF_NOT_DEFAULT(writeMask);
|
||||
SET_IF_NOT_DEFAULT(enabled);
|
||||
SET_IF_NOT_DEFAULT(function);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static json writeStereoState(const StereoState& value) {
|
||||
static const StereoState DEFAULT;
|
||||
json result = json::object();
|
||||
SET_IF_NOT_DEFAULT_(enable);
|
||||
SET_IF_NOT_DEFAULT_(contextDisable);
|
||||
SET_IF_NOT_DEFAULT_(skybox);
|
||||
if ((value._eyeProjections[0] != DEFAULT._eyeProjections[0]) || (value._eyeProjections[1] != DEFAULT._eyeProjections[1])) {
|
||||
json projections = json::array();
|
||||
projections.push_back(Serializer::writeMat4(value._eyeProjections[0]));
|
||||
projections.push_back(Serializer::writeMat4(value._eyeProjections[1]));
|
||||
result[keys::eyeProjections] = projections;
|
||||
}
|
||||
if ((value._eyeViews[0] != DEFAULT._eyeViews[0]) || (value._eyeViews[1] != DEFAULT._eyeViews[1])) {
|
||||
json views = json::array();
|
||||
views.push_back(Serializer::writeMat4(value._eyeViews[0]));
|
||||
views.push_back(Serializer::writeMat4(value._eyeViews[1]));
|
||||
result[keys::eyeViews] = views;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static json writeStencilTest(const State::StencilTest& value) {
|
||||
static const State::StencilTest DEFAULT;
|
||||
json result = json::object();
|
||||
SET_IF_NOT_DEFAULT(function);
|
||||
SET_IF_NOT_DEFAULT(failOp);
|
||||
SET_IF_NOT_DEFAULT(depthFailOp);
|
||||
SET_IF_NOT_DEFAULT(passOp);
|
||||
SET_IF_NOT_DEFAULT(reference);
|
||||
SET_IF_NOT_DEFAULT(readMask);
|
||||
return result;
|
||||
}
|
||||
|
||||
static json writeStencilActivation(const State::StencilActivation& value) {
|
||||
static const State::StencilActivation DEFAULT;
|
||||
json result = json::object();
|
||||
SET_IF_NOT_DEFAULT(frontWriteMask);
|
||||
SET_IF_NOT_DEFAULT(backWriteMask);
|
||||
SET_IF_NOT_DEFAULT(enabled);
|
||||
return result;
|
||||
}
|
||||
|
||||
json writeState(const StatePointer& statePointer) {
|
||||
if (!statePointer) {
|
||||
return json();
|
||||
}
|
||||
const auto& state = *statePointer;
|
||||
const auto& value = state.getValues();
|
||||
const auto& DEFAULT = State::DEFAULT;
|
||||
auto result = json::object();
|
||||
SET_IF_NOT_DEFAULT(colorWriteMask);
|
||||
SET_IF_NOT_DEFAULT(cullMode);
|
||||
SET_IF_NOT_DEFAULT(depthBias);
|
||||
SET_IF_NOT_DEFAULT(depthBiasSlopeScale);
|
||||
SET_IF_NOT_DEFAULT(fillMode);
|
||||
SET_IF_NOT_DEFAULT(sampleMask);
|
||||
SET_IF_NOT_DEFAULT_TRANSFORM(blendFunction, writeBlendFunction);
|
||||
SET_IF_NOT_DEFAULT_TRANSFORM(flags, writeStateFlags);
|
||||
SET_IF_NOT_DEFAULT_TRANSFORM(depthTest, writeDepthTest);
|
||||
SET_IF_NOT_DEFAULT_TRANSFORM(stencilActivation, writeStencilActivation);
|
||||
SET_IF_NOT_DEFAULT_TRANSFORM(stencilTestFront, writeStencilTest);
|
||||
SET_IF_NOT_DEFAULT_TRANSFORM(stencilTestBack, writeStencilTest);
|
||||
return result;
|
||||
}
|
||||
|
||||
json Serializer::writePipeline(const PipelinePointer& pipelinePointer) {
|
||||
if (!pipelinePointer) {
|
||||
return json();
|
||||
}
|
||||
const auto& pipeline = *pipelinePointer;
|
||||
auto result = json::object();
|
||||
result[keys::state] = writeState(pipeline.getState());
|
||||
result[keys::program] = getGlobalIndex(pipeline.getProgram(), programMap);
|
||||
return result;
|
||||
}
|
||||
|
||||
json Serializer::writeProgram(const ShaderPointer& programPointer) {
|
||||
if (!programPointer) {
|
||||
return json();
|
||||
}
|
||||
const auto& program = *programPointer;
|
||||
auto result = json::array();
|
||||
for (const auto& shader : program._shaders) {
|
||||
result.push_back(getGlobalIndex(shader, shaderMap));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
json Serializer::writeShader(const ShaderPointer& shaderPointer) {
|
||||
if (!shaderPointer) {
|
||||
return json();
|
||||
}
|
||||
auto result = json::object();
|
||||
const auto& shader = *shaderPointer;
|
||||
result[keys::id] = shader._source.id;
|
||||
result[keys::name] = shader._source.name;
|
||||
result[keys::type] = shader._type;
|
||||
return result;
|
||||
}
|
||||
|
||||
json Serializer::writeSwapchain(const SwapChainPointer& swapchainPointer) {
|
||||
auto framebufferSwapchainPointer = std::static_pointer_cast<FramebufferSwapChain>(swapchainPointer);
|
||||
if (!framebufferSwapchainPointer) {
|
||||
return json();
|
||||
}
|
||||
const FramebufferSwapChain& swapchain = *framebufferSwapchainPointer;
|
||||
auto result = json::object();
|
||||
result[keys::size] = swapchain.getSize();
|
||||
auto& framebuffersNode = result[keys::framebuffers] = json::array();
|
||||
for (uint32_t i = 0; i < swapchain.getSize(); ++i) {
|
||||
uint32_t index = getGlobalIndex(swapchain.get(i), framebufferMap);
|
||||
framebuffersNode.push_back(index);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
json Serializer::writeQuery(const QueryPointer& queryPointer) {
|
||||
if (!queryPointer) {
|
||||
return json();
|
||||
}
|
||||
|
||||
const auto& query = *queryPointer;
|
||||
auto result = json::object();
|
||||
result[keys::name] = query._name;
|
||||
return result;
|
||||
}
|
||||
|
||||
json Serializer::writeCapturableTextures(const Frame& frame) {
|
||||
if (!textureCapturer) {
|
||||
return json::array();
|
||||
}
|
||||
|
||||
std::unordered_set<TexturePointer> writtenRenderbuffers;
|
||||
std::unordered_set<TexturePointer> captureTextures;
|
||||
|
||||
auto maybeCaptureTexture = [&](const TexturePointer& texture) {
|
||||
// Not a valid texture
|
||||
if (!texture) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Not a renderbuffer
|
||||
if (texture->getUsageType() != TextureUsageType::RENDERBUFFER) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Already used in a target framebuffer
|
||||
if (writtenRenderbuffers.count(texture) > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
captureTextures.insert(texture);
|
||||
};
|
||||
|
||||
for (const auto& batchPtr : frame.batches) {
|
||||
const auto& batch = *batchPtr;
|
||||
batch.forEachCommand([&](Batch::Command command, const Batch::Param* params) {
|
||||
switch (command) {
|
||||
case Batch::COMMAND_setResourceTexture: {
|
||||
const auto& texture = batch._textures.get(params[0]._uint);
|
||||
maybeCaptureTexture(texture);
|
||||
} break;
|
||||
|
||||
case Batch::COMMAND_setResourceTextureTable: {
|
||||
const auto& textureTablePointer = batch._textureTables.get(params[0]._uint);
|
||||
if (textureTablePointer) {
|
||||
for (const auto& texture : textureTablePointer->getTextures()) {
|
||||
maybeCaptureTexture(texture);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case Batch::COMMAND_setFramebuffer: {
|
||||
const auto& framebufferPointer = batch._framebuffers.get(params[0]._uint);
|
||||
if (framebufferPointer) {
|
||||
const auto& framebuffer = *framebufferPointer;
|
||||
for (const auto& colorAttachment : framebuffer._renderBuffers) {
|
||||
if (colorAttachment._texture) {
|
||||
writtenRenderbuffers.insert(colorAttachment._texture);
|
||||
}
|
||||
}
|
||||
if (framebuffer._depthStencilBuffer._texture) {
|
||||
writtenRenderbuffers.insert(framebuffer._depthStencilBuffer._texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case Batch::COMMAND_setResourceFramebufferSwapChainTexture:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}); // for each command
|
||||
} // for each batch
|
||||
|
||||
for (const auto& entry : textureMap) {
|
||||
const auto& texturePointer = entry.first;
|
||||
if (!texturePointer) {
|
||||
continue;
|
||||
}
|
||||
const auto& texture = *texturePointer;
|
||||
auto usageType = texture.getUsageType();
|
||||
if (usageType == TextureUsageType::RESOURCE || usageType == TextureUsageType::STRICT_RESOURCE) {
|
||||
const auto* storage = texture._storage.get();
|
||||
const auto* ktxStorage = dynamic_cast<const Texture::KtxStorage*>(storage);
|
||||
if (!ktxStorage) {
|
||||
captureTextures.insert(texturePointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
json result = json::array();
|
||||
for (const auto& texture : captureTextures) {
|
||||
if (textureCapturer) {
|
||||
auto index = textureMap[texture];
|
||||
auto layers = std::max<uint16>(texture->getNumSlices(), 1);
|
||||
for (uint16 layer = 0; layer < layers; ++layer) {
|
||||
std::string textureFilename = basename + "." + std::to_string(index) + "." + std::to_string(layer) + ".png";
|
||||
textureCapturer(textureFilename, texture, layer);
|
||||
}
|
||||
result.push_back(index);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Serializer::writeFrame(const Frame& frame) {
|
||||
json frameNode = json::object();
|
||||
|
||||
frameNode[keys::batches] = json::array();
|
||||
for (const auto& batchPointer : frame.batches) {
|
||||
frameNode[keys::batches].push_back(writeBatch(*batchPointer));
|
||||
}
|
||||
|
||||
frameNode[keys::stereo] = writeStereoState(frame.stereoState);
|
||||
frameNode[keys::capturedTextures] = writeCapturableTextures(frame);
|
||||
frameNode[keys::frameIndex] = frame.frameIndex;
|
||||
frameNode[keys::view] = writeMat4(frame.view);
|
||||
frameNode[keys::pose] = writeMat4(frame.pose);
|
||||
frameNode[keys::framebuffer] = getGlobalIndex(frame.framebuffer, framebufferMap);
|
||||
using namespace std::placeholders;
|
||||
serializeMap(frameNode, keys::swapchains, swapchainMap, std::bind(&Serializer::writeSwapchain, this, _1));
|
||||
serializeMap(frameNode, keys::framebuffers, framebufferMap, [this](const auto& t) { return writeFramebuffer(t); });
|
||||
serializeMap(frameNode, keys::textureTables, textureTableMap, [this](const auto& t) { return writeTextureTable(t); });
|
||||
serializeMap(frameNode, keys::pipelines, pipelineMap, [this](const auto& t) { return writePipeline(t); });
|
||||
serializeMap(frameNode, keys::programs, programMap, [this](const auto& t) { return writeProgram(t); });
|
||||
serializeMap(frameNode, keys::shaders, shaderMap, writeShader);
|
||||
serializeMap(frameNode, keys::queries, queryMap, writeQuery);
|
||||
serializeMap(frameNode, keys::formats, formatMap, writeFormat);
|
||||
|
||||
// Serialize textures and buffers last, since the maps they use can be populated by some of the above code
|
||||
// Serialize textures
|
||||
serializeMap(frameNode, keys::textures, textureMap, writeTexture);
|
||||
// Serialize buffers
|
||||
serializeMap(frameNode, keys::buffers, bufferMap, writeBuffer);
|
||||
|
||||
{
|
||||
std::string frameJson = frameNode.dump();
|
||||
std::string filename = basename + ".json";
|
||||
storage::FileStorage::create(filename.c_str(), frameJson.size(), (const uint8_t*)frameJson.data());
|
||||
}
|
||||
|
||||
writeBinaryBlob();
|
||||
frameNode[keys::binary] = basename + ".bin";
|
||||
}
|
||||
|
||||
void Serializer::writeBinaryBlob() {
|
||||
const auto buffers = mapToVector(bufferMap);
|
||||
auto accumulator = [](size_t total, const BufferPointer& buffer) { return total + (buffer ? buffer->getSize() : 0); };
|
||||
size_t totalSize = std::accumulate(buffers.begin(), buffers.end(), (size_t)0, accumulator);
|
||||
|
||||
const auto blobFilename = basename + ".bin";
|
||||
QFile file(blobFilename.c_str());
|
||||
if (!file.open(QFile::ReadWrite | QIODevice::Truncate)) {
|
||||
throw std::runtime_error("Unable to open file for writing");
|
||||
}
|
||||
if (!file.resize(totalSize)) {
|
||||
throw std::runtime_error("Unable to resize file");
|
||||
}
|
||||
|
||||
auto mapped = file.map(0, totalSize);
|
||||
size_t offset = 0;
|
||||
|
||||
for (const auto& bufferPointer : buffers) {
|
||||
if (!bufferPointer) {
|
||||
continue;
|
||||
}
|
||||
const auto& buffer = *bufferPointer;
|
||||
const auto bufferSize = buffer.getSize();
|
||||
const auto& bufferData = buffer._renderSysmem.readData();
|
||||
memcpy(mapped + offset, bufferData, bufferSize);
|
||||
offset += bufferSize;
|
||||
}
|
||||
if (!file.unmap(mapped)) {
|
||||
throw std::runtime_error("Unable to unmap file");
|
||||
}
|
||||
}
|
|
@ -173,6 +173,8 @@ protected:
|
|||
void updateSize(const TexturePointer& texture);
|
||||
bool assignDepthStencilBuffer(const TexturePointer& texture, const Format& format, uint32 subresource);
|
||||
|
||||
friend class Serializer;
|
||||
friend class Deserializer;
|
||||
// Non exposed
|
||||
Framebuffer(const Framebuffer& framebuffer) = delete;
|
||||
Framebuffer() {}
|
||||
|
|
159
libraries/gpu/src/gpu/PointerStorage.h
Normal file
159
libraries/gpu/src/gpu/PointerStorage.h
Normal file
|
@ -0,0 +1,159 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2019/01/25
|
||||
// Copyright 2013-2019 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#pragma once
|
||||
#ifndef hifi_gpu_PointerStorage_h
|
||||
#define hifi_gpu_PointerStorage_h
|
||||
|
||||
namespace gpu {
|
||||
|
||||
//
|
||||
// GL Backend pointer storage mechanism
|
||||
// One of the following three defines must be defined.
|
||||
// GPU_POINTER_STORAGE_SHARED
|
||||
|
||||
// The platonic ideal, use references to smart pointers.
|
||||
// However, this produces artifacts because there are too many places in the code right now that
|
||||
// create temporary values (undesirable smart pointer duplications) and then those temp variables
|
||||
// get passed on and have their reference taken, and then invalidated
|
||||
// GPU_POINTER_STORAGE_REF
|
||||
|
||||
// Raw pointer manipulation. Seems more dangerous than the reference wrappers,
|
||||
// but in practice, the danger of grabbing a reference to a temporary variable
|
||||
// is causing issues
|
||||
// GPU_POINTER_STORAGE_RAW
|
||||
|
||||
#if defined(GPU_POINTER_STORAGE_SHARED)
|
||||
template <typename T>
|
||||
static inline bool compare(const std::shared_ptr<T>& a, const std::shared_ptr<T>& b) {
|
||||
return a == b;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T* acquire(const std::shared_ptr<T>& pointer) {
|
||||
return pointer.get();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void reset(std::shared_ptr<T>& pointer) {
|
||||
return pointer.reset();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline bool valid(const std::shared_ptr<T>& pointer) {
|
||||
return pointer.operator bool();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void assign(std::shared_ptr<T>& pointer, const std::shared_ptr<T>& source) {
|
||||
pointer = source;
|
||||
}
|
||||
|
||||
using BufferReference = BufferPointer;
|
||||
using TextureReference = TexturePointer;
|
||||
using FramebufferReference = FramebufferPointer;
|
||||
using FormatReference = Stream::FormatPointer;
|
||||
using PipelineReference = PipelinePointer;
|
||||
|
||||
#define GPU_REFERENCE_INIT_VALUE nullptr
|
||||
|
||||
#elif defined(GPU_POINTER_STORAGE_REF)
|
||||
|
||||
template <typename T>
|
||||
class PointerReferenceWrapper : public std::reference_wrapper<const std::shared_ptr<T>> {
|
||||
using Parent = std::reference_wrapper<const std::shared_ptr<T>>;
|
||||
|
||||
public:
|
||||
using Pointer = std::shared_ptr<T>;
|
||||
PointerReferenceWrapper() : Parent(EMPTY()) {}
|
||||
PointerReferenceWrapper(const Pointer& pointer) : Parent(pointer) {}
|
||||
void clear() { *this = EMPTY(); }
|
||||
|
||||
private:
|
||||
static const Pointer& EMPTY() {
|
||||
static const Pointer EMPTY_VALUE;
|
||||
return EMPTY_VALUE;
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
static bool compare(const PointerReferenceWrapper<T>& reference, const std::shared_ptr<T>& pointer) {
|
||||
return reference.get() == pointer;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T* acquire(const PointerReferenceWrapper<T>& reference) {
|
||||
return reference.get().get();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void assign(PointerReferenceWrapper<T>& reference, const std::shared_ptr<T>& pointer) {
|
||||
reference = pointer;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool valid(const PointerReferenceWrapper<T>& reference) {
|
||||
return reference.get().operator bool();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void reset(PointerReferenceWrapper<T>& reference) {
|
||||
return reference.clear();
|
||||
}
|
||||
|
||||
using BufferReference = PointerReferenceWrapper<Buffer>;
|
||||
using TextureReference = PointerReferenceWrapper<Texture>;
|
||||
using FramebufferReference = PointerReferenceWrapper<Framebuffer>;
|
||||
using FormatReference = PointerReferenceWrapper<Stream::Format>;
|
||||
using PipelineReference = PointerReferenceWrapper<Pipeline>;
|
||||
|
||||
#define GPU_REFERENCE_INIT_VALUE
|
||||
|
||||
#elif defined(GPU_POINTER_STORAGE_RAW)
|
||||
|
||||
template <typename T>
|
||||
static bool compare(const T* const& rawPointer, const std::shared_ptr<T>& pointer) {
|
||||
return rawPointer == pointer.get();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T* acquire(T* const& rawPointer) {
|
||||
return rawPointer;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline bool valid(const T* const& rawPointer) {
|
||||
return rawPointer;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void reset(T*& rawPointer) {
|
||||
rawPointer = nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void assign(T*& rawPointer, const std::shared_ptr<T>& pointer) {
|
||||
rawPointer = pointer.get();
|
||||
}
|
||||
|
||||
using BufferReference = Buffer*;
|
||||
using TextureReference = Texture*;
|
||||
using FramebufferReference = Framebuffer*;
|
||||
using FormatReference = Stream::Format*;
|
||||
using PipelineReference = Pipeline*;
|
||||
|
||||
#define GPU_REFERENCE_INIT_VALUE nullptr
|
||||
|
||||
#else
|
||||
|
||||
#error "No GPU pointer storage scheme defined"
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif // hifi_gpu_PointerStorage_h
|
|
@ -45,6 +45,9 @@ namespace gpu {
|
|||
const std::string _name;
|
||||
uint64_t _queryResult { 0 };
|
||||
uint64_t _usecBatchElapsedTime { 0 };
|
||||
|
||||
friend class Serializer;
|
||||
friend class Deserializer;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<Query> QueryPointer;
|
||||
|
|
|
@ -167,6 +167,8 @@ protected:
|
|||
const Pointer& vertexShader,
|
||||
const Pointer& geometryShader,
|
||||
const Pointer& pixelShader);
|
||||
friend class Serializer;
|
||||
friend class Deserializer;
|
||||
};
|
||||
|
||||
typedef Shader::Pointer ShaderPointer;
|
||||
|
|
|
@ -33,19 +33,19 @@ State::Signature State::evalSignature(const Data& state) {
|
|||
if (state.cullMode != State::DEFAULT.cullMode) {
|
||||
signature.set(State::CULL_MODE);
|
||||
}
|
||||
if (state.frontFaceClockwise != State::DEFAULT.frontFaceClockwise) {
|
||||
if (state.flags.frontFaceClockwise != State::DEFAULT.flags.frontFaceClockwise) {
|
||||
signature.set(State::FRONT_FACE_CLOCKWISE);
|
||||
}
|
||||
if (state.depthClampEnable != State::DEFAULT.depthClampEnable) {
|
||||
if (state.flags.depthClampEnable != State::DEFAULT.flags.depthClampEnable) {
|
||||
signature.set(State::DEPTH_CLAMP_ENABLE);
|
||||
}
|
||||
if (state.scissorEnable != State::DEFAULT.scissorEnable) {
|
||||
if (state.flags.scissorEnable != State::DEFAULT.flags.scissorEnable) {
|
||||
signature.set(State::SCISSOR_ENABLE);
|
||||
}
|
||||
if (state.multisampleEnable != State::DEFAULT.multisampleEnable) {
|
||||
if (state.flags.multisampleEnable != State::DEFAULT.flags.multisampleEnable) {
|
||||
signature.set(State::MULTISAMPLE_ENABLE);
|
||||
}
|
||||
if (state.antialisedLineEnable != State::DEFAULT.antialisedLineEnable) {
|
||||
if (state.flags.antialisedLineEnable != State::DEFAULT.flags.antialisedLineEnable) {
|
||||
signature.set(State::ANTIALISED_LINE_ENABLE);
|
||||
}
|
||||
if (state.depthBias != State::DEFAULT.depthBias) {
|
||||
|
@ -69,7 +69,7 @@ State::Signature State::evalSignature(const Data& state) {
|
|||
if (state.sampleMask != State::DEFAULT.sampleMask) {
|
||||
signature.set(State::SAMPLE_MASK);
|
||||
}
|
||||
if (state.alphaToCoverageEnable != State::DEFAULT.alphaToCoverageEnable) {
|
||||
if (state.flags.alphaToCoverageEnable != State::DEFAULT.flags.alphaToCoverageEnable) {
|
||||
signature.set(State::ALPHA_TO_COVERAGE_ENABLE);
|
||||
}
|
||||
if (state.blendFunction != State::DEFAULT.blendFunction) {
|
||||
|
@ -86,3 +86,28 @@ State::State(const Data& values) :
|
|||
_values(values) {
|
||||
_signature = evalSignature(_values);
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
static std::string hex(T t) {
|
||||
std::stringstream stream;
|
||||
stream << std::hex << t;
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::string State::getKey() const {
|
||||
std::string key;
|
||||
key = hex(*(int*)&_values.depthBias);
|
||||
key += ":" + hex(*(int*)&_values.depthBiasSlopeScale);
|
||||
key += ":" + hex(_values.depthTest.getRaw());
|
||||
key += ":" + hex(_values.stencilActivation.getRaw());
|
||||
key += ":" + hex(_values.stencilTestFront.getRaw());
|
||||
key += ":" + hex(_values.stencilTestBack.getRaw());
|
||||
key += ":" + hex(_values.blendFunction.getRaw());
|
||||
key += ":" + hex(_values.sampleMask);
|
||||
// fillMode, cullMode, colorMaskWrite and the flags consume 32 bits alltogether
|
||||
static_assert(0 == offsetof(State::Data, fillMode) % 4, "Validate fillMode offset");
|
||||
key += ":" + hex(*(int*)&_values.fillMode);
|
||||
return key;
|
||||
}
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue