mirror of
https://github.com/overte-org/overte.git
synced 2025-04-08 14:32:38 +02:00
Merge branch 'master' of github.com:highfidelity/hifi into 20816-installOnAndroid
This commit is contained in:
commit
cc3a74b7a0
237 changed files with 11063 additions and 1477 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>
|
44
android/apps/interface/src/main/assets/avatars.json
Normal file
44
android/apps/interface/src/main/assets/avatars.json
Normal file
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"avatars": [
|
||||
{
|
||||
"name": "Wooden Mannequin",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/7fe80a1e-f445-4800-9e89-40e677b03bee/large/hifi-mp-7fe80a1e-f445-4800-9e89-40e677b03bee.jpg",
|
||||
"url": "qrc:////meshes/defaultAvatar_full.fst"
|
||||
},
|
||||
{
|
||||
"name": "Anime-Styled Boy",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/46e0fd52-3cff-462f-ba97-927338d88295/thumbnail/hifi-mp-46e0fd52-3cff-462f-ba97-927338d88295.jpg",
|
||||
"url": "http://mpassets.highfidelity.com/46e0fd52-3cff-462f-ba97-927338d88295-v1/AnimeBoy2.fst"
|
||||
},
|
||||
{
|
||||
"name": "Anime-Styled Girl",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/1e7e43f6-1757-44d3-baa4-756827d96311/large/hifi-mp-1e7e43f6-1757-44d3-baa4-756827d96311.jpg",
|
||||
"url": "http://mpassets.highfidelity.com/0dce3426-55c8-4641-8dd5-d76eb575b64a-v1/Anime_F_Outfit.fst"
|
||||
},
|
||||
{
|
||||
"name": "Last Legends: Male Avatar",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/28569047-6f1a-4100-af67-8054ec397cc3/thumbnail/hifi-mp-28569047-6f1a-4100-af67-8054ec397cc3.jpg",
|
||||
"url": "http://mpassets.highfidelity.com/28569047-6f1a-4100-af67-8054ec397cc3-v1/LLMale2.fst"
|
||||
},
|
||||
{
|
||||
"name": "Last Legends: Female Avatar",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/8d823be5-6197-4418-b984-eb94160ed956/thumbnail/hifi-mp-8d823be5-6197-4418-b984-eb94160ed956.jpg",
|
||||
"url": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/46e0fd52-3cff-462f-ba97-927338d88295/thumbnail/hifi-mp-46e0fd52-3cff-462f-ba97-927338d88295.jpg"
|
||||
},
|
||||
{
|
||||
"name": "Matthew: Photo-real avatar",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/b652081b-a199-425e-ae5c-7815721bdc09/thumbnail/hifi-mp-b652081b-a199-425e-ae5c-7815721bdc09.jpg",
|
||||
"url": "http://mpassets.highfidelity.com/b652081b-a199-425e-ae5c-7815721bdc09-v1/matthew.fst"
|
||||
},
|
||||
{
|
||||
"name": "Priscilla: Photo real avatar",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/e7565f93-8bc5-47c2-b6eb-b3b31d4a1339/thumbnail/hifi-mp-e7565f93-8bc5-47c2-b6eb-b3b31d4a1339.jpg",
|
||||
"url": "http://mpassets.highfidelity.com/e7565f93-8bc5-47c2-b6eb-b3b31d4a1339-v1/priscilla.fst"
|
||||
},
|
||||
{
|
||||
"name": "H1-F1 Optical Interpreter bot",
|
||||
"preview_image": "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/469c8b66-e3c2-47fb-9820-e306b1dd15c4/large/hifi-mp-469c8b66-e3c2-47fb-9820-e306b1dd15c4.jpg",
|
||||
"url": "http://mpassets.highfidelity.com/469c8b66-e3c2-47fb-9820-e306b1dd15c4-v1/optical_interpreter[1].fst"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -493,6 +493,34 @@ Java_io_highfidelity_hifiinterface_SplashActivity_registerLoadCompleteListener(J
|
|||
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_io_highfidelity_hifiinterface_fragment_ProfileFragment_getDisplayName(JNIEnv *env,
|
||||
jobject instance) {
|
||||
|
||||
QString displayName = AndroidHelper::instance().getDisplayName();
|
||||
return env->NewStringUTF(displayName.toLatin1().data());
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_io_highfidelity_hifiinterface_fragment_ProfileFragment_setDisplayName(JNIEnv *env,
|
||||
jobject instance,
|
||||
jstring name_) {
|
||||
const char *c_name = env->GetStringUTFChars(name_, 0);
|
||||
const QString name = QString::fromUtf8(c_name);
|
||||
env->ReleaseStringUTFChars(name_, c_name);
|
||||
AndroidHelper::instance().setDisplayName(name);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_io_highfidelity_hifiinterface_fragment_ProfileFragment_setAvatarUrl(JNIEnv *env,
|
||||
jobject instance,
|
||||
jstring url_) {
|
||||
const char *url = env->GetStringUTFChars(url_, 0);
|
||||
QString avatarUrl = QString::fromUtf8(url);
|
||||
AndroidHelper::instance().setMyAvatarUrl(avatarUrl);
|
||||
env->ReleaseStringUTFChars(url_, url);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_io_highfidelity_hifiinterface_MainActivity_logout(JNIEnv *env, jobject instance) {
|
||||
DependencyManager::get<AccountManager>()->logout();
|
||||
|
|
|
@ -33,13 +33,15 @@ import com.squareup.picasso.Picasso;
|
|||
import io.highfidelity.hifiinterface.fragment.FriendsFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.HomeFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.PolicyFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.ProfileFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.SettingsFragment;
|
||||
import io.highfidelity.hifiinterface.fragment.SignupFragment;
|
||||
import io.highfidelity.hifiinterface.task.DownloadProfileImageTask;
|
||||
|
||||
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener,
|
||||
HomeFragment.OnHomeInteractionListener,
|
||||
FriendsFragment.OnHomeInteractionListener {
|
||||
FriendsFragment.OnHomeInteractionListener,
|
||||
ProfileFragment.OnProfileInteractionListener {
|
||||
|
||||
private static final int PROFILE_PICTURE_PLACEHOLDER = R.drawable.default_profile_avatar;
|
||||
public static final String DEFAULT_FRAGMENT = "Home";
|
||||
|
@ -61,6 +63,7 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
private View mProfilePanel;
|
||||
private TextView mLogoutOption;
|
||||
private MenuItem mPeopleMenuItem;
|
||||
private MenuItem mProfileMenuItem;
|
||||
|
||||
private boolean backToScene;
|
||||
private String backToUrl;
|
||||
|
@ -83,6 +86,8 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
|
||||
mPeopleMenuItem = mNavigationView.getMenu().findItem(R.id.action_people);
|
||||
|
||||
mProfileMenuItem = mNavigationView.getMenu().findItem(R.id.action_profile);
|
||||
|
||||
updateDebugMenu(mNavigationView.getMenu());
|
||||
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
|
@ -162,6 +167,12 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
loadFragment(fragment, getString(R.string.people), getString(R.string.tagFragmentPeople), true, true);
|
||||
}
|
||||
|
||||
private void loadProfileFragment() {
|
||||
Fragment fragment = ProfileFragment.newInstance();
|
||||
|
||||
loadFragment(fragment, getString(R.string.profile), getString(R.string.tagFragmentProfile), true, true);
|
||||
}
|
||||
|
||||
private void loadSettingsFragment() {
|
||||
SettingsFragment fragment = SettingsFragment.newInstance();
|
||||
|
||||
|
@ -261,6 +272,9 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
case R.id.action_people:
|
||||
loadPeopleFragment();
|
||||
return true;
|
||||
case R.id.action_profile:
|
||||
loadProfileFragment();
|
||||
break;
|
||||
case R.id.action_debug_settings:
|
||||
loadSettingsFragment();
|
||||
return true;
|
||||
|
@ -351,6 +365,21 @@ public class MainActivity extends AppCompatActivity implements NavigationView.On
|
|||
goToUser(username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelProfileEdit() {
|
||||
loadHomeFragment(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompleteProfileEdit() {
|
||||
loadHomeFragment(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAvatarChosen() {
|
||||
loadHomeFragment(false);
|
||||
}
|
||||
|
||||
private class RoundProfilePictureCallback implements Callback {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
package io.highfidelity.hifiinterface.fragment;
|
||||
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.GridLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import io.highfidelity.hifiinterface.R;
|
||||
import io.highfidelity.hifiinterface.provider.AvatarProvider;
|
||||
import io.highfidelity.hifiinterface.view.AvatarAdapter;
|
||||
|
||||
public class ProfileFragment extends Fragment {
|
||||
|
||||
private TextView mDisplayName;
|
||||
|
||||
private Button mOkButton;
|
||||
private OnProfileInteractionListener mListener;
|
||||
private AvatarProvider mAvatarsProvider;
|
||||
|
||||
private native String getDisplayName();
|
||||
private native void setDisplayName(String name);
|
||||
private native void setAvatarUrl(String url);
|
||||
|
||||
public ProfileFragment() {
|
||||
// Required empty public constructor
|
||||
}
|
||||
|
||||
public static ProfileFragment newInstance() {
|
||||
ProfileFragment fragment = new ProfileFragment();
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
View rootView = inflater.inflate(R.layout.fragment_profile, container, false);
|
||||
|
||||
mDisplayName = rootView.findViewById(R.id.displayName);
|
||||
mDisplayName.setText(getDisplayName());
|
||||
mDisplayName.setOnEditorActionListener((textView, actionId, keyEvent) -> onDisplayNameEditorAction(textView, actionId, keyEvent));
|
||||
|
||||
mOkButton = rootView.findViewById(R.id.okButton);
|
||||
mOkButton.setOnClickListener(view -> onOkButtonClicked());
|
||||
|
||||
rootView.findViewById(R.id.cancel).setOnClickListener(view -> onCancelProfileEdit());
|
||||
|
||||
RecyclerView avatarsView = rootView.findViewById(R.id.gridview);
|
||||
int numberOfColumns = 1;
|
||||
mAvatarsProvider = new AvatarProvider(getContext());
|
||||
GridLayoutManager gridLayoutMgr = new GridLayoutManager(getContext(), numberOfColumns);
|
||||
avatarsView.setLayoutManager(gridLayoutMgr);
|
||||
AvatarAdapter avatarAdapter = new AvatarAdapter(getContext(), mAvatarsProvider);
|
||||
avatarsView.setAdapter(avatarAdapter);
|
||||
avatarAdapter.loadAvatars();
|
||||
|
||||
avatarAdapter.setClickListener((view, position, avatar) -> {
|
||||
setAvatarUrl(avatar.avatarUrl);
|
||||
if (mListener != null) {
|
||||
mListener.onAvatarChosen();
|
||||
}
|
||||
});
|
||||
return rootView;
|
||||
}
|
||||
|
||||
private void onOkButtonClicked() {
|
||||
setDisplayName(mDisplayName.getText().toString());
|
||||
View view = getActivity().getCurrentFocus();
|
||||
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
if (mListener != null) {
|
||||
mListener.onCompleteProfileEdit();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean onDisplayNameEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
mOkButton.performClick();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void onCancelProfileEdit() {
|
||||
View view = getActivity().getCurrentFocus();
|
||||
InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
|
||||
if (mListener != null) {
|
||||
mListener.onCancelProfileEdit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the back pressed event and returns true if it was managed by this Fragment
|
||||
* @return
|
||||
*/
|
||||
public boolean onBackPressed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof OnProfileInteractionListener) {
|
||||
mListener = (OnProfileInteractionListener) context;
|
||||
} else {
|
||||
throw new RuntimeException(context.toString()
|
||||
+ " must implement OnProfileInteractionListener");
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnProfileInteractionListener {
|
||||
void onCancelProfileEdit();
|
||||
void onCompleteProfileEdit();
|
||||
void onAvatarChosen();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package io.highfidelity.hifiinterface.provider;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.highfidelity.hifiinterface.view.AvatarAdapter;
|
||||
|
||||
/**
|
||||
* Created by gcalero on 1/21/19
|
||||
*/
|
||||
public class AvatarProvider {
|
||||
|
||||
private static final String AVATARS_JSON = "avatars.json";
|
||||
private static final String JSON_FIELD_NAME = "name";
|
||||
private static final String JSON_FIELD_URL = "url";
|
||||
private static final String JSON_FIELD_IMAGE = "preview_image";
|
||||
private static final String JSON_FIELD_AVATARS_ARRAY = "avatars";
|
||||
private final Context mContext;
|
||||
|
||||
public interface AvatarsCallback {
|
||||
void retrieveOk(List<AvatarAdapter.Avatar> avatars);
|
||||
void retrieveError(Exception e, String message);
|
||||
}
|
||||
|
||||
public AvatarProvider(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public void retrieve(AvatarsCallback avatarsCallback)
|
||||
{
|
||||
try {
|
||||
JSONObject obj = new JSONObject(loadJSONFromAssets());
|
||||
JSONArray m_jArry = obj.getJSONArray(JSON_FIELD_AVATARS_ARRAY);
|
||||
ArrayList<AvatarAdapter.Avatar> avatars = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < m_jArry.length(); i++) {
|
||||
JSONObject jo_inside = m_jArry.getJSONObject(i);
|
||||
AvatarAdapter.Avatar anAvatar = new AvatarAdapter.Avatar();
|
||||
anAvatar.avatarName = jo_inside.getString(JSON_FIELD_NAME);
|
||||
anAvatar.avatarPreviewUrl = jo_inside.getString(JSON_FIELD_IMAGE);
|
||||
anAvatar.avatarUrl = jo_inside.getString(JSON_FIELD_URL);
|
||||
avatars.add(anAvatar);
|
||||
}
|
||||
avatarsCallback.retrieveOk(avatars);
|
||||
} catch (IOException e) {
|
||||
avatarsCallback.retrieveError(e, "Failed retrieving avatar JSON");
|
||||
} catch (JSONException e) {
|
||||
avatarsCallback.retrieveError(e, "Failed parsing avatar JSON");
|
||||
}
|
||||
}
|
||||
|
||||
private String loadJSONFromAssets() throws IOException {
|
||||
String json = null;
|
||||
InputStream is = mContext.getAssets().open(AVATARS_JSON);
|
||||
int size = is.available();
|
||||
byte[] buffer = new byte[size];
|
||||
is.read(buffer);
|
||||
is.close();
|
||||
json = new String(buffer, "UTF-8");
|
||||
return json;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
package io.highfidelity.hifiinterface.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.squareup.picasso.Picasso;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.highfidelity.hifiinterface.R;
|
||||
import io.highfidelity.hifiinterface.provider.AvatarProvider;
|
||||
|
||||
/**
|
||||
* Created by gcalero on 1/21/19
|
||||
*/
|
||||
public class AvatarAdapter extends RecyclerView.Adapter<AvatarAdapter.ViewHolder> {
|
||||
|
||||
private static final String TAG = "Interface";
|
||||
private final Context mContext;
|
||||
private final LayoutInflater mInflater;
|
||||
private final AvatarProvider mProvider;
|
||||
private List<Avatar> mAvatars = new ArrayList<>();
|
||||
private ItemClickListener mClickListener;
|
||||
|
||||
public AvatarAdapter(Context context, AvatarProvider provider) {
|
||||
mContext = context;
|
||||
mInflater = LayoutInflater.from(mContext);
|
||||
mProvider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View view = mInflater.inflate(R.layout.avatar_item, parent, false);
|
||||
return new AvatarAdapter.ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
AvatarAdapter.Avatar anAvatar = mAvatars.get(position);
|
||||
assert(holder.mName != null);
|
||||
holder.mName.setText(anAvatar.avatarName);
|
||||
Uri uri = Uri.parse(anAvatar.avatarPreviewUrl);
|
||||
Picasso.get().load(uri).into(holder.mPreviewImage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mAvatars.size();
|
||||
}
|
||||
|
||||
public void loadAvatars() {
|
||||
mProvider.retrieve(new AvatarProvider.AvatarsCallback() {
|
||||
@Override
|
||||
public void retrieveOk(List<AvatarAdapter.Avatar> avatars) {
|
||||
mAvatars = new ArrayList<>(avatars);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retrieveError(Exception e, String message) {
|
||||
Log.e(TAG, message, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setClickListener(ItemClickListener clickListener) {
|
||||
mClickListener = clickListener;
|
||||
}
|
||||
|
||||
public interface ItemClickListener {
|
||||
void onItemClick(View view, int position, Avatar avatar);
|
||||
}
|
||||
|
||||
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
|
||||
|
||||
TextView mName;
|
||||
ImageView mPreviewImage;
|
||||
|
||||
public ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
mName = itemView.findViewById(R.id.avatarName);
|
||||
assert (mName != null);
|
||||
mPreviewImage = itemView.findViewById(R.id.avatarPreview);
|
||||
itemView.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
int position= getAdapterPosition();
|
||||
if (mClickListener != null) {
|
||||
mClickListener.onItemClick(view, position, mAvatars.get(position));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Avatar {
|
||||
public String avatarName;
|
||||
public String avatarUrl;
|
||||
public String avatarPreviewUrl;
|
||||
|
||||
public Avatar() { }
|
||||
}
|
||||
}
|
23
android/apps/interface/src/main/res/layout/avatar_item.xml
Normal file
23
android/apps/interface/src/main/res/layout/avatar_item.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="5dp">
|
||||
<TextView
|
||||
android:id="@+id/avatarName"
|
||||
android:fontFamily="@font/raleway_bold"
|
||||
android:textSize="20sp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/colorButton1" />
|
||||
<ImageView
|
||||
android:id="@+id/avatarPreview"
|
||||
android:background="@color/backgroundDark"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="190dp"
|
||||
android:scaleType="fitCenter"
|
||||
app:layout_constraintTop_toBottomOf="@id/avatarName"/>
|
||||
</android.support.constraint.ConstraintLayout>
|
107
android/apps/interface/src/main/res/layout/fragment_profile.xml
Normal file
107
android/apps/interface/src/main/res/layout/fragment_profile.xml
Normal file
|
@ -0,0 +1,107 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/backgroundLight">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:src="@drawable/encourage_login_background"
|
||||
android:scaleType="fitXY" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#B2000000" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/header"
|
||||
android:layout_width="@dimen/header_hifi_width"
|
||||
android:layout_height="@dimen/header_hifi_height"
|
||||
android:layout_marginTop="@dimen/header_hifi_margin_top"
|
||||
android:contentDescription="HighFidelity"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/hifi_header" />
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
android:id="@+id/profileForm"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="@dimen/profile_margin_top"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/header"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:visibility="visible">
|
||||
<EditText
|
||||
android:id="@+id/displayName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="27dp"
|
||||
android:layout_marginLeft="@dimen/profile_margin"
|
||||
android:layout_marginRight="@dimen/profile_margin"
|
||||
android:layout_marginBottom="@dimen/profile_margin"
|
||||
android:background="@color/white_opaque"
|
||||
android:paddingLeft="@dimen/edit_text_padding"
|
||||
android:ems="10"
|
||||
android:fontFamily="sans-serif"
|
||||
android:textSize="@dimen/login_edit_text_size"
|
||||
android:inputType="textCapWords"
|
||||
android:textColor="@color/editTextColor"
|
||||
android:textColorHint="@color/editTextColor"
|
||||
android:gravity="left|center_vertical"
|
||||
android:hint="@string/displayName"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/displayName"
|
||||
app:layout_constraintBottom_toTopOf="@id/okButton"
|
||||
android:layout_marginTop="@dimen/profile_margin_avatars_top"
|
||||
android:layout_marginBottom="@dimen/profile_margin_avatars_bottom"
|
||||
android:layout_marginLeft="@dimen/profile_margin"
|
||||
android:layout_marginRight="@dimen/profile_margin">
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/gridview"
|
||||
android:paddingTop="@dimen/list_vertical_padding"
|
||||
android:paddingBottom="@dimen/list_vertical_padding"
|
||||
android:clipToPadding="false"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
/>
|
||||
</FrameLayout>
|
||||
<TextView
|
||||
android:id="@+id/cancel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginRight="8dp"
|
||||
app:layout_constraintLeft_toLeftOf="@id/displayName"
|
||||
app:layout_constraintTop_toTopOf="@id/okButton"
|
||||
app:layout_constraintBottom_toBottomOf="@id/okButton"
|
||||
app:layout_constraintRight_toLeftOf="@id/okButton"
|
||||
android:textColor="@color/white_opaque"
|
||||
android:fontFamily="@font/raleway_bold"
|
||||
android:textSize="@dimen/button_medium_text_size"
|
||||
android:text="@string/cancel_uppercase" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/okButton"
|
||||
android:layout_width="@dimen/button_medium_width"
|
||||
android:layout_height="@dimen/button_medium_height"
|
||||
android:background="@drawable/rounded_button_color3"
|
||||
android:fontFamily="@font/raleway_bold"
|
||||
android:layout_marginTop="@dimen/button_medium_margin"
|
||||
android:layout_marginBottom="@dimen/button_medium_margin"
|
||||
android:text="@string/ok"
|
||||
android:textColor="@color/white_opaque"
|
||||
android:textAllCaps="false"
|
||||
android:textSize="@dimen/button_medium_text_size"
|
||||
app:layout_constraintRight_toRightOf="@id/displayName"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
|
@ -9,6 +9,10 @@
|
|||
android:id="@+id/action_people"
|
||||
android:title="@string/people"
|
||||
/>
|
||||
<item
|
||||
android:id="@+id/action_profile"
|
||||
android:title="@string/profile"
|
||||
/>
|
||||
<item
|
||||
android:id="@+id/action_debug_settings"
|
||||
android:title="@string/settings"
|
||||
|
|
|
@ -54,5 +54,8 @@
|
|||
<dimen name="login_menu_button_margin_top">77dp</dimen>
|
||||
<dimen name="login_menu_text_size">20sp</dimen>
|
||||
<dimen name="login_edit_text_padding">14dp</dimen>
|
||||
|
||||
<dimen name="profile_margin_top">20dp</dimen>
|
||||
<dimen name="profile_margin">35dp</dimen>
|
||||
<dimen name="profile_margin_avatars_top">40dp</dimen>
|
||||
<dimen name="profile_margin_avatars_bottom">70dp</dimen>
|
||||
</resources>
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
<string name="tagFragmentSignup">tagFragmentSignup</string>
|
||||
<string name="tagFragmentPolicy">tagFragmentPolicy</string>
|
||||
<string name="tagFragmentPeople">tagFragmentPeople</string>
|
||||
<string name="tagFragmentProfile">tagFragmentProfile</string>
|
||||
<string name="tagSettings">tagSettings</string>
|
||||
<string name="tagFragmentSignedIn">tagFragmentSignedIn</string>
|
||||
<string name="settings">Settings</string>
|
||||
|
@ -54,4 +55,7 @@
|
|||
<string name="take_me_in_world">No thanks, take me in-world!</string>
|
||||
<string name="be_anywere">BE ANYWHERE, WITH ANYONE \nRIGHT NOW</string>
|
||||
<string name="steam_log_in">STEAM LOG IN</string>
|
||||
<string name="displayName">Name to display</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="profile">Avatar</string>
|
||||
</resources>
|
||||
|
|
|
@ -7,15 +7,12 @@ docker build --build-arg BUILD_UID=`id -u` -t "${DOCKER_IMAGE_NAME}" -f docker/D
|
|||
|
||||
# The Jenkins PR builds use VERSION_CODE, but the release builds use VERSION
|
||||
# So make sure we use VERSION_CODE consistently
|
||||
if [-z "$VERSION_CODE"]; then
|
||||
export VERSION_CODE=$VERSION
|
||||
fi
|
||||
test -z "$VERSION_CODE" && export VERSION_CODE=$VERSION
|
||||
|
||||
docker run \
|
||||
--rm \
|
||||
--security-opt seccomp:unconfined \
|
||||
-v "${WORKSPACE}":/home/jenkins/hifi \
|
||||
-v /home/jenkins/.gradle:/home/jenkins/.gradle \
|
||||
-e RELEASE_NUMBER \
|
||||
-e RELEASE_TYPE \
|
||||
-e ANDROID_APP \
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -743,7 +743,7 @@ void AudioMixer::parseSettingsObject(const QJsonObject& settingsObject) {
|
|||
float coefficient = coefficientObject.value(COEFFICIENT).toString().toFloat(&ok);
|
||||
|
||||
|
||||
if (ok && coefficient >= 0.0f && coefficient <= 1.0f &&
|
||||
if (ok && coefficient <= 1.0f &&
|
||||
itSource != end(_audioZones) &&
|
||||
itListener != end(_audioZones)) {
|
||||
|
||||
|
|
|
@ -770,13 +770,29 @@ float computeGain(float masterListenerGain, const AvatarAudioStream& listeningNo
|
|||
break;
|
||||
}
|
||||
}
|
||||
// translate the zone setting to gain per log2(distance)
|
||||
float g = glm::clamp(1.0f - attenuationPerDoublingInDistance, EPSILON, 1.0f);
|
||||
|
||||
// calculate the attenuation using the distance to this node
|
||||
// reference attenuation of 0dB at distance = 1.0m
|
||||
gain *= fastExp2f(fastLog2f(g) * fastLog2f(std::max(distance, HRTF_NEARFIELD_MIN)));
|
||||
gain = std::min(gain, 1.0f / HRTF_NEARFIELD_MIN);
|
||||
if (attenuationPerDoublingInDistance < 0.0f) {
|
||||
// translate a negative zone setting to distance limit
|
||||
const float MIN_DISTANCE_LIMIT = ATTN_DISTANCE_REF + 1.0f; // silent after 1m
|
||||
float distanceLimit = std::max(-attenuationPerDoublingInDistance, MIN_DISTANCE_LIMIT);
|
||||
|
||||
// calculate the LINEAR attenuation using the distance to this node
|
||||
// reference attenuation of 0dB at distance = ATTN_DISTANCE_REF
|
||||
float d = distance - ATTN_DISTANCE_REF;
|
||||
gain *= std::max(1.0f - d / (distanceLimit - ATTN_DISTANCE_REF), 0.0f);
|
||||
gain = std::min(gain, ATTN_GAIN_MAX);
|
||||
|
||||
} else {
|
||||
// translate a positive zone setting to gain per log2(distance)
|
||||
const float MIN_ATTENUATION_COEFFICIENT = 0.001f; // -60dB per log2(distance)
|
||||
float g = glm::clamp(1.0f - attenuationPerDoublingInDistance, MIN_ATTENUATION_COEFFICIENT, 1.0f);
|
||||
|
||||
// calculate the LOGARITHMIC attenuation using the distance to this node
|
||||
// reference attenuation of 0dB at distance = ATTN_DISTANCE_REF
|
||||
float d = (1.0f / ATTN_DISTANCE_REF) * std::max(distance, HRTF_NEARFIELD_MIN);
|
||||
gain *= fastExp2f(fastLog2f(g) * fastLog2f(d));
|
||||
gain = std::min(gain, ATTN_GAIN_MAX);
|
||||
}
|
||||
|
||||
return gain;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -15,8 +15,8 @@ print = functools.partial(print, flush=True)
|
|||
# Encapsulates the vcpkg system
|
||||
class VcpkgRepo:
|
||||
CMAKE_TEMPLATE = """
|
||||
set(CMAKE_TOOLCHAIN_FILE "{}" CACHE FILEPATH "Toolchain file")
|
||||
set(CMAKE_TOOLCHAIN_FILE_UNCACHED "{}")
|
||||
get_filename_component(CMAKE_TOOLCHAIN_FILE "{}" ABSOLUTE CACHE)
|
||||
get_filename_component(CMAKE_TOOLCHAIN_FILE_UNCACHED "{}" ABSOLUTE)
|
||||
set(VCPKG_INSTALL_ROOT "{}")
|
||||
set(VCPKG_TOOLS_DIR "{}")
|
||||
"""
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -1215,7 +1215,7 @@ Rectangle {
|
|||
if (userIndex !== -1) {
|
||||
['userName', 'admin', 'connection', 'profileUrl', 'placeName'].forEach(function (name) {
|
||||
var value = message.params[name];
|
||||
if (value === undefined) {
|
||||
if (value === undefined || value == "") {
|
||||
return;
|
||||
}
|
||||
nearbyUserModel.setProperty(userIndex, name, value);
|
||||
|
|
|
@ -25,7 +25,6 @@ Item {
|
|||
HifiConstants { id: hifi; }
|
||||
|
||||
id: root;
|
||||
|
||||
property int parentAppTitleBarHeight;
|
||||
property int parentAppNavBarHeight;
|
||||
property string currentActiveView: "sendAssetHome";
|
||||
|
|
|
@ -3,10 +3,13 @@ import Hifi 1.0
|
|||
|
||||
import "../../dialogs"
|
||||
import "../../controls"
|
||||
import stylesUit 1.0
|
||||
|
||||
Item {
|
||||
Rectangle {
|
||||
HifiConstants { id: hifi; }
|
||||
id: tabletRoot
|
||||
objectName: "tabletRoot"
|
||||
color: hifi.colors.baseGray
|
||||
property string username: "Unknown user"
|
||||
property string usernameShort: "Unknown user"
|
||||
property var rootMenu;
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <src/ui/LoginDialog.h>
|
||||
#include "Application.h"
|
||||
#include "Constants.h"
|
||||
#include "avatar/AvatarManager.h"
|
||||
|
||||
#if defined(qApp)
|
||||
#undef qApp
|
||||
|
@ -152,3 +153,16 @@ void AndroidHelper::signupFailed(QNetworkReply* reply) {
|
|||
emit handleSignupFailed(DEFAULT_SIGN_UP_FAILURE_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
QString AndroidHelper::getDisplayName() {
|
||||
return DependencyManager::get<AvatarManager>()->getMyAvatar()->getDisplayName();
|
||||
}
|
||||
|
||||
void AndroidHelper::setDisplayName(const QString &displayName) {
|
||||
DependencyManager::get<AvatarManager>()->getMyAvatar()->setDisplayName(displayName);
|
||||
}
|
||||
|
||||
void AndroidHelper::setMyAvatarUrl(const QString &avatarUrl) {
|
||||
QUrl url = QUrl(avatarUrl);
|
||||
DependencyManager::get<AvatarManager>()->getMyAvatar()->useFullAvatarURL(url);
|
||||
}
|
||||
|
|
|
@ -41,6 +41,9 @@ public:
|
|||
void operator=(AndroidHelper const&) = delete;
|
||||
|
||||
void signup(QString email, QString username, QString password);
|
||||
QString getDisplayName();
|
||||
void setDisplayName(const QString &displayName);
|
||||
void setMyAvatarUrl(const QString &avatarUrl);
|
||||
|
||||
public slots:
|
||||
void showLoginDialog(QUrl url);
|
||||
|
|
|
@ -161,6 +161,7 @@
|
|||
#include "avatar/AvatarManager.h"
|
||||
#include "avatar/MyHead.h"
|
||||
#include "avatar/AvatarPackager.h"
|
||||
#include "avatar/MyCharacterController.h"
|
||||
#include "CrashRecoveryHandler.h"
|
||||
#include "CrashHandler.h"
|
||||
#include "devices/DdeFaceTracker.h"
|
||||
|
@ -760,6 +761,11 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
static const auto SUPPRESS_SETTINGS_RESET = "--suppress-settings-reset";
|
||||
bool suppressPrompt = cmdOptionExists(argc, const_cast<const char**>(argv), SUPPRESS_SETTINGS_RESET);
|
||||
|
||||
// set the OCULUS_STORE property so the oculus plugin can know if we ran from the Oculus Store
|
||||
static const auto OCULUS_STORE_ARG = "--oculus-store";
|
||||
bool isStore = cmdOptionExists(argc, const_cast<const char**>(argv), OCULUS_STORE_ARG);
|
||||
qApp->setProperty(hifi::properties::OCULUS_STORE, isStore);
|
||||
|
||||
// Ignore any previous crashes if running from command line with a test script.
|
||||
bool inTestMode { false };
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
|
@ -801,6 +807,10 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) {
|
|||
if (auto steamClient = pluginManager->getSteamClientPlugin()) {
|
||||
steamClient->init();
|
||||
}
|
||||
if (auto oculusPlatform = pluginManager->getOculusPlatformPlugin()) {
|
||||
oculusPlatform->init();
|
||||
}
|
||||
|
||||
PROFILE_SET_THREAD_NAME("Main Thread");
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
|
@ -1133,10 +1143,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
|||
qCDebug(interfaceapp) << "[VERSION] We will use DEVELOPMENT global services.";
|
||||
#endif
|
||||
|
||||
// set the OCULUS_STORE property so the oculus plugin can know if we ran from the Oculus Store
|
||||
static const QString OCULUS_STORE_ARG = "--oculus-store";
|
||||
bool isStore = arguments().indexOf(OCULUS_STORE_ARG) != -1;
|
||||
setProperty(hifi::properties::OCULUS_STORE, isStore);
|
||||
bool isStore = property(hifi::properties::OCULUS_STORE).toBool();
|
||||
|
||||
DependencyManager::get<WalletScriptingInterface>()->setLimitedCommerce(isStore); // Or we could make it a separate arg, or if either arg is set, etc. And should this instead by a hifi::properties?
|
||||
|
||||
updateHeartbeat();
|
||||
|
@ -2417,7 +2425,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";
|
||||
|
@ -2714,7 +2721,14 @@ Application::~Application() {
|
|||
avatarManager->handleProcessedPhysicsTransaction(transaction);
|
||||
|
||||
avatarManager->deleteAllAvatars();
|
||||
|
||||
|
||||
auto myCharacterController = getMyAvatar()->getCharacterController();
|
||||
myCharacterController->clearDetailedMotionStates();
|
||||
|
||||
myCharacterController->buildPhysicsTransaction(transaction);
|
||||
_physicsEngine->processTransaction(transaction);
|
||||
myCharacterController->handleProcessedPhysicsTransaction(transaction);
|
||||
|
||||
_physicsEngine->setCharacterController(nullptr);
|
||||
|
||||
// the _shapeManager should have zero references
|
||||
|
@ -2738,6 +2752,10 @@ Application::~Application() {
|
|||
steamClient->shutdown();
|
||||
}
|
||||
|
||||
if (auto oculusPlatform = PluginManager::getInstance()->getOculusPlatformPlugin()) {
|
||||
oculusPlatform->shutdown();
|
||||
}
|
||||
|
||||
DependencyManager::destroy<PluginManager>();
|
||||
|
||||
DependencyManager::destroy<CompositorHelper>(); // must be destroyed before the FramebufferCache
|
||||
|
@ -3544,8 +3562,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 +4113,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();
|
||||
|
@ -4164,7 +4197,9 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
if (!isShifted && !isMeta && !isOption && !event->isAutoRepeat()) {
|
||||
AudioInjectorOptions options;
|
||||
options.localOnly = true;
|
||||
options.positionSet = false; // system sound
|
||||
options.stereo = true;
|
||||
|
||||
Setting::Handle<bool> notificationSounds{ MenuOption::NotificationSounds, true };
|
||||
Setting::Handle<bool> notificationSoundSnapshot{ MenuOption::NotificationSoundsSnapshot, true };
|
||||
if (notificationSounds.get() && notificationSoundSnapshot.get()) {
|
||||
|
@ -6273,7 +6308,9 @@ void Application::update(float deltaTime) {
|
|||
avatarManager->buildPhysicsTransaction(transaction);
|
||||
_physicsEngine->processTransaction(transaction);
|
||||
avatarManager->handleProcessedPhysicsTransaction(transaction);
|
||||
|
||||
myAvatar->getCharacterController()->buildPhysicsTransaction(transaction);
|
||||
_physicsEngine->processTransaction(transaction);
|
||||
myAvatar->getCharacterController()->handleProcessedPhysicsTransaction(transaction);
|
||||
myAvatar->prepareForPhysicsSimulation();
|
||||
_physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) {
|
||||
dynamic->prepareForPhysicsSimulation();
|
||||
|
@ -8246,7 +8283,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 +8304,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));
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "InterfaceLogging.h"
|
||||
#include "Menu.h"
|
||||
#include "MyAvatar.h"
|
||||
#include "DebugDraw.h"
|
||||
#include "SceneScriptingInterface.h"
|
||||
|
||||
// 50 times per second - target is 45hz, but this helps account for any small deviations
|
||||
|
@ -396,21 +397,45 @@ void AvatarManager::buildPhysicsTransaction(PhysicsEngine::Transaction& transact
|
|||
if (isInPhysics) {
|
||||
transaction.objectsToRemove.push_back(avatar->_motionState);
|
||||
avatar->_motionState = nullptr;
|
||||
auto& detailedMotionStates = avatar->getDetailedMotionStates();
|
||||
for (auto& mState : detailedMotionStates) {
|
||||
transaction.objectsToRemove.push_back(mState);
|
||||
}
|
||||
avatar->resetDetailedMotionStates();
|
||||
|
||||
} else {
|
||||
ShapeInfo shapeInfo;
|
||||
avatar->computeShapeInfo(shapeInfo);
|
||||
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
||||
if (shape) {
|
||||
AvatarMotionState* motionState = new AvatarMotionState(avatar, shape);
|
||||
motionState->setMass(avatar->computeMass());
|
||||
avatar->_motionState = motionState;
|
||||
transaction.objectsToAdd.push_back(motionState);
|
||||
if (avatar->getDetailedMotionStates().size() == 0) {
|
||||
avatar->createDetailedMotionStates(avatar);
|
||||
for (auto dMotionState : avatar->getDetailedMotionStates()) {
|
||||
transaction.objectsToAdd.push_back(dMotionState);
|
||||
}
|
||||
}
|
||||
if (avatar->getDetailedMotionStates().size() > 0) {
|
||||
ShapeInfo shapeInfo;
|
||||
avatar->computeShapeInfo(shapeInfo);
|
||||
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
||||
if (shape) {
|
||||
AvatarMotionState* motionState = new AvatarMotionState(avatar, shape);
|
||||
motionState->setMass(avatar->computeMass());
|
||||
avatar->_motionState = motionState;
|
||||
transaction.objectsToAdd.push_back(motionState);
|
||||
} else {
|
||||
failedShapeBuilds.insert(avatar);
|
||||
}
|
||||
} else {
|
||||
failedShapeBuilds.insert(avatar);
|
||||
}
|
||||
}
|
||||
} else if (isInPhysics) {
|
||||
transaction.objectsToChange.push_back(avatar->_motionState);
|
||||
|
||||
auto& detailedMotionStates = avatar->getDetailedMotionStates();
|
||||
for (auto& mState : detailedMotionStates) {
|
||||
if (mState) {
|
||||
transaction.objectsToChange.push_back(mState);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
_avatarsToChangeInPhysics.swap(failedShapeBuilds);
|
||||
|
@ -450,6 +475,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar
|
|||
_spaceProxiesToDelete.push_back(avatar->getSpaceIndex());
|
||||
}
|
||||
AvatarHashMap::handleRemovedAvatar(avatar, removalReason);
|
||||
avatar->tearDownGrabs();
|
||||
|
||||
avatar->die();
|
||||
queuePhysicsChange(avatar);
|
||||
|
@ -509,6 +535,7 @@ void AvatarManager::deleteAllAvatars() {
|
|||
if (avatar != _myAvatar) {
|
||||
auto otherAvatar = std::static_pointer_cast<OtherAvatar>(avatar);
|
||||
assert(!otherAvatar->_motionState);
|
||||
assert(otherAvatar->getDetailedMotionStates().size() == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -601,103 +628,135 @@ AvatarSharedPointer AvatarManager::getAvatarBySessionID(const QUuid& sessionID)
|
|||
|
||||
RayToAvatarIntersectionResult AvatarManager::findRayIntersection(const PickRay& ray,
|
||||
const QScriptValue& avatarIdsToInclude,
|
||||
const QScriptValue& avatarIdsToDiscard) {
|
||||
const QScriptValue& avatarIdsToDiscard,
|
||||
bool pickAgainstMesh) {
|
||||
QVector<EntityItemID> avatarsToInclude = qVectorEntityItemIDFromScriptValue(avatarIdsToInclude);
|
||||
QVector<EntityItemID> avatarsToDiscard = qVectorEntityItemIDFromScriptValue(avatarIdsToDiscard);
|
||||
|
||||
return findRayIntersectionVector(ray, avatarsToInclude, avatarsToDiscard);
|
||||
return findRayIntersectionVector(ray, avatarsToInclude, avatarsToDiscard, pickAgainstMesh);
|
||||
}
|
||||
|
||||
RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const PickRay& ray,
|
||||
const QVector<EntityItemID>& avatarsToInclude,
|
||||
const QVector<EntityItemID>& avatarsToDiscard) {
|
||||
const QVector<EntityItemID>& avatarsToDiscard,
|
||||
bool pickAgainstMesh) {
|
||||
RayToAvatarIntersectionResult result;
|
||||
if (QThread::currentThread() != thread()) {
|
||||
BLOCKING_INVOKE_METHOD(const_cast<AvatarManager*>(this), "findRayIntersectionVector",
|
||||
Q_RETURN_ARG(RayToAvatarIntersectionResult, result),
|
||||
Q_ARG(const PickRay&, ray),
|
||||
Q_ARG(const QVector<EntityItemID>&, avatarsToInclude),
|
||||
Q_ARG(const QVector<EntityItemID>&, avatarsToDiscard));
|
||||
Q_ARG(const QVector<EntityItemID>&, avatarsToDiscard),
|
||||
Q_ARG(bool, pickAgainstMesh));
|
||||
return result;
|
||||
}
|
||||
|
||||
// It's better to intersect the ray against the avatar's actual mesh, but this is currently difficult to
|
||||
// do, because the transformed mesh data only exists over in GPU-land. As a compromise, this code
|
||||
// intersects against the avatars capsule and then against the (T-pose) mesh. The end effect is that picking
|
||||
// against the avatar is sort-of right, but you likely wont be able to pick against the arms.
|
||||
PROFILE_RANGE(simulation_physics, __FUNCTION__);
|
||||
|
||||
// TODO -- find a way to extract transformed avatar mesh data from the rendering engine.
|
||||
float distance = (float)INT_MAX; // with FLT_MAX bullet rayTest does not return results
|
||||
glm::vec3 rayDirection = glm::normalize(ray.direction);
|
||||
std::vector<MyCharacterController::RayAvatarResult> physicsResults = _myAvatar->getCharacterController()->rayTest(glmToBullet(ray.origin), glmToBullet(rayDirection), distance, QVector<uint>());
|
||||
if (physicsResults.size() > 0) {
|
||||
glm::vec3 rayDirectionInv = { rayDirection.x != 0.0f ? 1.0f / rayDirection.x : INFINITY,
|
||||
rayDirection.y != 0.0f ? 1.0f / rayDirection.y : INFINITY,
|
||||
rayDirection.z != 0.0f ? 1.0f / rayDirection.z : INFINITY };
|
||||
|
||||
std::vector<SortedAvatar> sortedAvatars;
|
||||
auto avatarHashCopy = getHashCopy();
|
||||
for (auto avatarData : avatarHashCopy) {
|
||||
auto avatar = std::static_pointer_cast<Avatar>(avatarData);
|
||||
if ((avatarsToInclude.size() > 0 && !avatarsToInclude.contains(avatar->getID())) ||
|
||||
(avatarsToDiscard.size() > 0 && avatarsToDiscard.contains(avatar->getID()))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float distance = FLT_MAX;
|
||||
#if 0
|
||||
// if we weren't picking against the capsule, we would want to pick against the avatarBounds...
|
||||
SkeletonModelPointer avatarModel = avatar->getSkeletonModel();
|
||||
AABox avatarBounds = avatarModel->getRenderableMeshBound();
|
||||
if (avatarBounds.contains(ray.origin)) {
|
||||
distance = 0.0f;
|
||||
} else {
|
||||
float boundDistance = FLT_MAX;
|
||||
BoxFace face;
|
||||
glm::vec3 surfaceNormal;
|
||||
if (avatarBounds.findRayIntersection(ray.origin, ray.direction, boundDistance, face, surfaceNormal)) {
|
||||
distance = boundDistance;
|
||||
}
|
||||
}
|
||||
#else
|
||||
glm::vec3 start;
|
||||
glm::vec3 end;
|
||||
float radius;
|
||||
avatar->getCapsule(start, end, radius);
|
||||
findRayCapsuleIntersection(ray.origin, ray.direction, start, end, radius, distance);
|
||||
#endif
|
||||
|
||||
if (distance < FLT_MAX) {
|
||||
sortedAvatars.emplace_back(distance, avatar);
|
||||
}
|
||||
}
|
||||
|
||||
if (sortedAvatars.size() > 1) {
|
||||
static auto comparator = [](const SortedAvatar& left, const SortedAvatar& right) { return left.first < right.first; };
|
||||
std::sort(sortedAvatars.begin(), sortedAvatars.end(), comparator);
|
||||
}
|
||||
|
||||
for (auto it = sortedAvatars.begin(); it != sortedAvatars.end(); ++it) {
|
||||
const SortedAvatar& sortedAvatar = *it;
|
||||
// We can exit once avatarCapsuleDistance > bestDistance
|
||||
if (sortedAvatar.first > result.distance) {
|
||||
break;
|
||||
}
|
||||
|
||||
float distance = FLT_MAX;
|
||||
BoxFace face;
|
||||
MyCharacterController::RayAvatarResult rayAvatarResult;
|
||||
AvatarPointer avatar = nullptr;
|
||||
|
||||
BoxFace face = BoxFace::UNKNOWN_FACE;
|
||||
glm::vec3 surfaceNormal;
|
||||
QVariantMap extraInfo;
|
||||
SkeletonModelPointer avatarModel = sortedAvatar.second->getSkeletonModel();
|
||||
if (avatarModel->findRayIntersectionAgainstSubMeshes(ray.origin, ray.direction, distance, face, surfaceNormal, extraInfo, true)) {
|
||||
if (distance < result.distance) {
|
||||
result.intersects = true;
|
||||
result.avatarID = sortedAvatar.second->getID();
|
||||
result.distance = distance;
|
||||
result.face = face;
|
||||
result.surfaceNormal = surfaceNormal;
|
||||
result.extraInfo = extraInfo;
|
||||
|
||||
for (auto &hit : physicsResults) {
|
||||
auto avatarID = hit._intersectWithAvatar;
|
||||
if ((avatarsToInclude.size() > 0 && !avatarsToInclude.contains(avatarID)) ||
|
||||
(avatarsToDiscard.size() > 0 && avatarsToDiscard.contains(avatarID))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_myAvatar->getSessionUUID() != avatarID) {
|
||||
auto avatarMap = getHashCopy();
|
||||
AvatarHash::iterator itr = avatarMap.find(avatarID);
|
||||
if (itr != avatarMap.end()) {
|
||||
avatar = std::static_pointer_cast<Avatar>(*itr);
|
||||
}
|
||||
} else {
|
||||
avatar = _myAvatar;
|
||||
}
|
||||
if (!hit._isBound) {
|
||||
rayAvatarResult = hit;
|
||||
} else if (avatar) {
|
||||
auto &multiSpheres = avatar->getMultiSphereShapes();
|
||||
if (multiSpheres.size() > 0) {
|
||||
std::vector<MyCharacterController::RayAvatarResult> boxHits;
|
||||
for (size_t i = 0; i < hit._boundJoints.size(); i++) {
|
||||
assert(hit._boundJoints[i] < multiSpheres.size());
|
||||
auto &mSphere = multiSpheres[hit._boundJoints[i]];
|
||||
if (mSphere.isValid()) {
|
||||
float boundDistance = FLT_MAX;
|
||||
BoxFace face;
|
||||
glm::vec3 surfaceNormal;
|
||||
auto &bbox = mSphere.getBoundingBox();
|
||||
if (bbox.findRayIntersection(ray.origin, rayDirection, rayDirectionInv, boundDistance, face, surfaceNormal)) {
|
||||
MyCharacterController::RayAvatarResult boxHit;
|
||||
boxHit._distance = boundDistance;
|
||||
boxHit._intersect = true;
|
||||
boxHit._intersectionNormal = surfaceNormal;
|
||||
boxHit._intersectionPoint = ray.origin + boundDistance * rayDirection;
|
||||
boxHit._intersectWithAvatar = avatarID;
|
||||
boxHit._intersectWithJoint = mSphere.getJointIndex();
|
||||
boxHits.push_back(boxHit);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (boxHits.size() > 0) {
|
||||
if (boxHits.size() > 1) {
|
||||
std::sort(boxHits.begin(), boxHits.end(), [](const MyCharacterController::RayAvatarResult& hitA,
|
||||
const MyCharacterController::RayAvatarResult& hitB) {
|
||||
return hitA._distance < hitB._distance;
|
||||
});
|
||||
}
|
||||
rayAvatarResult = boxHits[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pickAgainstMesh) {
|
||||
glm::vec3 localRayOrigin = avatar->worldToJointPoint(ray.origin, rayAvatarResult._intersectWithJoint);
|
||||
glm::vec3 localRayPoint = avatar->worldToJointPoint(ray.origin + rayDirection, rayAvatarResult._intersectWithJoint);
|
||||
|
||||
auto avatarOrientation = avatar->getWorldOrientation();
|
||||
auto avatarPosition = avatar->getWorldPosition();
|
||||
|
||||
auto jointOrientation = avatarOrientation * avatar->getAbsoluteDefaultJointRotationInObjectFrame(rayAvatarResult._intersectWithJoint);
|
||||
auto jointPosition = avatarPosition + (avatarOrientation * avatar->getAbsoluteDefaultJointTranslationInObjectFrame(rayAvatarResult._intersectWithJoint));
|
||||
|
||||
auto defaultFrameRayOrigin = jointPosition + jointOrientation * localRayOrigin;
|
||||
auto defaultFrameRayPoint = jointPosition + jointOrientation * localRayPoint;
|
||||
auto defaultFrameRayDirection = defaultFrameRayPoint - defaultFrameRayOrigin;
|
||||
|
||||
if (avatar->getSkeletonModel()->findRayIntersectionAgainstSubMeshes(defaultFrameRayOrigin, defaultFrameRayDirection, distance, face, surfaceNormal, extraInfo, true, false)) {
|
||||
auto newDistance = glm::length(vec3FromVariant(extraInfo["worldIntersectionPoint"]) - defaultFrameRayOrigin);
|
||||
rayAvatarResult._distance = newDistance;
|
||||
rayAvatarResult._intersectionPoint = ray.origin + newDistance * rayDirection;
|
||||
rayAvatarResult._intersectionNormal = surfaceNormal;
|
||||
extraInfo["worldIntersectionPoint"] = vec3toVariant(rayAvatarResult._intersectionPoint);
|
||||
break;
|
||||
}
|
||||
} else if (rayAvatarResult._intersect){
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (rayAvatarResult._intersect) {
|
||||
result.intersects = true;
|
||||
result.avatarID = rayAvatarResult._intersectWithAvatar;
|
||||
result.distance = rayAvatarResult._distance;
|
||||
result.surfaceNormal = rayAvatarResult._intersectionNormal;
|
||||
result.jointIndex = rayAvatarResult._intersectWithJoint;
|
||||
result.intersection = ray.origin + rayAvatarResult._distance * rayDirection;
|
||||
result.extraInfo = extraInfo;
|
||||
result.face = face;
|
||||
}
|
||||
}
|
||||
|
||||
if (result.intersects) {
|
||||
result.intersection = ray.origin + ray.direction * result.distance;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include <EntitySimulation.h> // for SetOfEntities
|
||||
|
||||
#include "AvatarMotionState.h"
|
||||
#include "DetailedMotionState.h"
|
||||
#include "MyAvatar.h"
|
||||
#include "OtherAvatar.h"
|
||||
|
||||
|
@ -136,21 +137,25 @@ public:
|
|||
* @param {PickRay} ray
|
||||
* @param {Uuid[]} [avatarsToInclude=[]]
|
||||
* @param {Uuid[]} [avatarsToDiscard=[]]
|
||||
* @param {boolean} pickAgainstMesh
|
||||
* @returns {RayToAvatarIntersectionResult}
|
||||
*/
|
||||
Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersection(const PickRay& ray,
|
||||
const QScriptValue& avatarIdsToInclude = QScriptValue(),
|
||||
const QScriptValue& avatarIdsToDiscard = QScriptValue());
|
||||
const QScriptValue& avatarIdsToDiscard = QScriptValue(),
|
||||
bool pickAgainstMesh = true);
|
||||
/**jsdoc
|
||||
* @function AvatarManager.findRayIntersectionVector
|
||||
* @param {PickRay} ray
|
||||
* @param {Uuid[]} avatarsToInclude
|
||||
* @param {Uuid[]} avatarsToDiscard
|
||||
* @param {boolean} pickAgainstMesh
|
||||
* @returns {RayToAvatarIntersectionResult}
|
||||
*/
|
||||
Q_INVOKABLE RayToAvatarIntersectionResult findRayIntersectionVector(const PickRay& ray,
|
||||
const QVector<EntityItemID>& avatarsToInclude,
|
||||
const QVector<EntityItemID>& avatarsToDiscard);
|
||||
const QVector<EntityItemID>& avatarsToDiscard,
|
||||
bool pickAgainstMesh);
|
||||
|
||||
/**jsdoc
|
||||
* @function AvatarManager.findParabolaIntersectionVector
|
||||
|
|
188
interface/src/avatar/DetailedMotionState.cpp
Normal file
188
interface/src/avatar/DetailedMotionState.cpp
Normal file
|
@ -0,0 +1,188 @@
|
|||
//
|
||||
// DetailedMotionState.cpp
|
||||
// interface/src/avatar/
|
||||
//
|
||||
// Created by Luis Cuenca 1/11/2019
|
||||
// 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 "DetailedMotionState.h"
|
||||
|
||||
#include <PhysicsCollisionGroups.h>
|
||||
#include <PhysicsEngine.h>
|
||||
#include <PhysicsHelpers.h>
|
||||
#include "MyAvatar.h"
|
||||
|
||||
|
||||
DetailedMotionState::DetailedMotionState(AvatarPointer avatar, const btCollisionShape* shape, int jointIndex) :
|
||||
ObjectMotionState(shape), _avatar(avatar), _jointIndex(jointIndex) {
|
||||
assert(_avatar);
|
||||
if (!_avatar->isMyAvatar()) {
|
||||
_otherAvatar = std::static_pointer_cast<OtherAvatar>(_avatar);
|
||||
}
|
||||
_type = MOTIONSTATE_TYPE_DETAILED;
|
||||
}
|
||||
|
||||
void DetailedMotionState::handleEasyChanges(uint32_t& flags) {
|
||||
ObjectMotionState::handleEasyChanges(flags);
|
||||
if (flags & Simulation::DIRTY_PHYSICS_ACTIVATION && !_body->isActive()) {
|
||||
_body->activate();
|
||||
}
|
||||
}
|
||||
|
||||
bool DetailedMotionState::handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) {
|
||||
return ObjectMotionState::handleHardAndEasyChanges(flags, engine);
|
||||
}
|
||||
|
||||
DetailedMotionState::~DetailedMotionState() {
|
||||
assert(_avatar);
|
||||
_avatar = nullptr;
|
||||
}
|
||||
|
||||
// virtual
|
||||
uint32_t DetailedMotionState::getIncomingDirtyFlags() {
|
||||
return _body ? _dirtyFlags : 0;
|
||||
}
|
||||
|
||||
void DetailedMotionState::clearIncomingDirtyFlags() {
|
||||
if (_body) {
|
||||
_dirtyFlags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
PhysicsMotionType DetailedMotionState::computePhysicsMotionType() const {
|
||||
// TODO?: support non-DYNAMIC motion for avatars? (e.g. when sitting)
|
||||
return MOTION_TYPE_KINEMATIC;
|
||||
}
|
||||
|
||||
// virtual and protected
|
||||
const btCollisionShape* DetailedMotionState::computeNewShape() {
|
||||
btCollisionShape* shape = nullptr;
|
||||
if (!_avatar->isMyAvatar()) {
|
||||
if (_otherAvatar != nullptr) {
|
||||
shape = _otherAvatar->createCollisionShape(_jointIndex, _isBound, _boundJoints);
|
||||
}
|
||||
} else {
|
||||
std::shared_ptr<MyAvatar> myAvatar = std::static_pointer_cast<MyAvatar>(_avatar);
|
||||
if (myAvatar) {
|
||||
shape = myAvatar->getCharacterController()->createDetailedCollisionShapeForJoint(_jointIndex);
|
||||
}
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
|
||||
// virtual
|
||||
bool DetailedMotionState::isMoving() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
// virtual
|
||||
void DetailedMotionState::getWorldTransform(btTransform& worldTrans) const {
|
||||
worldTrans.setOrigin(glmToBullet(getObjectPosition()));
|
||||
worldTrans.setRotation(glmToBullet(getObjectRotation()));
|
||||
}
|
||||
|
||||
// virtual
|
||||
void DetailedMotionState::setWorldTransform(const btTransform& worldTrans) {
|
||||
_body->setWorldTransform(worldTrans);
|
||||
}
|
||||
|
||||
// These pure virtual methods must be implemented for each MotionState type
|
||||
// and make it possible to implement more complicated methods in this base class.
|
||||
|
||||
// virtual
|
||||
float DetailedMotionState::getObjectRestitution() const {
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
// virtual
|
||||
float DetailedMotionState::getObjectFriction() const {
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
// virtual
|
||||
float DetailedMotionState::getObjectLinearDamping() const {
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
// virtual
|
||||
float DetailedMotionState::getObjectAngularDamping() const {
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
// virtual
|
||||
glm::vec3 DetailedMotionState::getObjectPosition() const {
|
||||
if (_otherAvatar != nullptr) {
|
||||
auto bodyLOD = _otherAvatar->getBodyLOD();
|
||||
if (bodyLOD == OtherAvatar::BodyLOD::Sphere) {
|
||||
return _avatar->getFitBounds().calcCenter();
|
||||
}
|
||||
}
|
||||
return _avatar->getJointPosition(_jointIndex);
|
||||
}
|
||||
|
||||
// virtual
|
||||
glm::quat DetailedMotionState::getObjectRotation() const {
|
||||
return _avatar->getWorldOrientation() * _avatar->getAbsoluteJointRotationInObjectFrame(_jointIndex);
|
||||
}
|
||||
|
||||
// virtual
|
||||
glm::vec3 DetailedMotionState::getObjectLinearVelocity() const {
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
// virtual
|
||||
glm::vec3 DetailedMotionState::getObjectAngularVelocity() const {
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
// virtual
|
||||
glm::vec3 DetailedMotionState::getObjectGravity() const {
|
||||
return glm::vec3(0.0f);
|
||||
}
|
||||
|
||||
// virtual
|
||||
const QUuid DetailedMotionState::getObjectID() const {
|
||||
return _avatar->getSessionUUID();
|
||||
}
|
||||
|
||||
QString DetailedMotionState::getName() const {
|
||||
return _avatar->getName() + "_" + _jointIndex;
|
||||
}
|
||||
|
||||
// virtual
|
||||
QUuid DetailedMotionState::getSimulatorID() const {
|
||||
return _avatar->getSessionUUID();
|
||||
}
|
||||
|
||||
// virtual
|
||||
void DetailedMotionState::computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const {
|
||||
group = BULLET_COLLISION_GROUP_DETAILED_AVATAR;
|
||||
mask = Physics::getDefaultCollisionMask(group);
|
||||
}
|
||||
|
||||
// virtual
|
||||
float DetailedMotionState::getMass() const {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void DetailedMotionState::setRigidBody(btRigidBody* body) {
|
||||
ObjectMotionState::setRigidBody(body);
|
||||
if (_body) {
|
||||
// remove angular dynamics from this body
|
||||
_body->setAngularFactor(0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
void DetailedMotionState::setShape(const btCollisionShape* shape) {
|
||||
ObjectMotionState::setShape(shape);
|
||||
}
|
||||
|
||||
void DetailedMotionState::forceActive() {
|
||||
if (_body && !_body->isActive()) {
|
||||
_body->setActivationState(ACTIVE_TAG);
|
||||
}
|
||||
}
|
100
interface/src/avatar/DetailedMotionState.h
Normal file
100
interface/src/avatar/DetailedMotionState.h
Normal file
|
@ -0,0 +1,100 @@
|
|||
//
|
||||
// DetailedMotionState.h
|
||||
// interface/src/avatar/
|
||||
//
|
||||
// Created by Luis Cuenca 1/11/2019
|
||||
// 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_DetailedMotionState_h
|
||||
#define hifi_DetailedMotionState_h
|
||||
|
||||
#include <QSet>
|
||||
|
||||
#include <ObjectMotionState.h>
|
||||
#include <BulletUtil.h>
|
||||
|
||||
#include "OtherAvatar.h"
|
||||
|
||||
class DetailedMotionState : public ObjectMotionState {
|
||||
public:
|
||||
DetailedMotionState(AvatarPointer avatar, const btCollisionShape* shape, int jointIndex);
|
||||
|
||||
virtual void handleEasyChanges(uint32_t& flags) override;
|
||||
virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override;
|
||||
|
||||
virtual PhysicsMotionType getMotionType() const override { return _motionType; }
|
||||
|
||||
virtual uint32_t getIncomingDirtyFlags() override;
|
||||
virtual void clearIncomingDirtyFlags() override;
|
||||
|
||||
virtual PhysicsMotionType computePhysicsMotionType() const override;
|
||||
|
||||
virtual bool isMoving() const override;
|
||||
|
||||
// this relays incoming position/rotation to the RigidBody
|
||||
virtual void getWorldTransform(btTransform& worldTrans) const override;
|
||||
|
||||
// this relays outgoing position/rotation to the EntityItem
|
||||
virtual void setWorldTransform(const btTransform& worldTrans) override;
|
||||
|
||||
|
||||
// These pure virtual methods must be implemented for each MotionState type
|
||||
// and make it possible to implement more complicated methods in this base class.
|
||||
|
||||
// pure virtual overrides from ObjectMotionState
|
||||
virtual float getObjectRestitution() const override;
|
||||
virtual float getObjectFriction() const override;
|
||||
virtual float getObjectLinearDamping() const override;
|
||||
virtual float getObjectAngularDamping() const override;
|
||||
|
||||
virtual glm::vec3 getObjectPosition() const override;
|
||||
virtual glm::quat getObjectRotation() const override;
|
||||
virtual glm::vec3 getObjectLinearVelocity() const override;
|
||||
virtual glm::vec3 getObjectAngularVelocity() const override;
|
||||
virtual glm::vec3 getObjectGravity() const override;
|
||||
|
||||
virtual const QUuid getObjectID() const override;
|
||||
|
||||
virtual QString getName() const override;
|
||||
virtual QUuid getSimulatorID() const override;
|
||||
|
||||
void addDirtyFlags(uint32_t flags) { _dirtyFlags |= flags; }
|
||||
|
||||
virtual void computeCollisionGroupAndMask(int32_t& group, int32_t& mask) const override;
|
||||
|
||||
virtual float getMass() const override;
|
||||
void forceActive();
|
||||
QUuid getAvatarID() const { return _avatar->getID(); }
|
||||
int getJointIndex() const { return _jointIndex; }
|
||||
void setIsBound(bool isBound, std::vector<int> boundJoints) { _isBound = isBound; _boundJoints = boundJoints; }
|
||||
bool getIsBound(std::vector<int>& boundJoints) const { boundJoints = _boundJoints; return _isBound; }
|
||||
|
||||
friend class AvatarManager;
|
||||
friend class Avatar;
|
||||
|
||||
protected:
|
||||
void setRigidBody(btRigidBody* body) override;
|
||||
void setShape(const btCollisionShape* shape) override;
|
||||
|
||||
// the dtor had been made protected to force the compiler to verify that it is only
|
||||
// ever called by the Avatar class dtor.
|
||||
~DetailedMotionState();
|
||||
|
||||
virtual bool isReadyToComputeShape() const override { return true; }
|
||||
virtual const btCollisionShape* computeNewShape() override;
|
||||
|
||||
AvatarPointer _avatar;
|
||||
float _diameter { 0.0f };
|
||||
|
||||
uint32_t _dirtyFlags;
|
||||
int _jointIndex { -1 };
|
||||
OtherAvatarPointer _otherAvatar { nullptr };
|
||||
bool _isBound { false };
|
||||
std::vector<int> _boundJoints;
|
||||
};
|
||||
|
||||
#endif // hifi_DetailedMotionState_h
|
|
@ -134,7 +134,7 @@ MyAvatar::MyAvatar(QThread* thread) :
|
|||
_scriptedMotorFrame(SCRIPTED_MOTOR_CAMERA_FRAME),
|
||||
_scriptedMotorMode(SCRIPTED_MOTOR_SIMPLE_MODE),
|
||||
_motionBehaviors(AVATAR_MOTION_DEFAULTS),
|
||||
_characterController(this),
|
||||
_characterController(std::shared_ptr<MyAvatar>(this)),
|
||||
_eyeContactTarget(LEFT_EYE),
|
||||
_realWorldFieldOfView("realWorldFieldOfView",
|
||||
DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES),
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -1171,77 +1171,6 @@ controller::Pose MyAvatar::getRightHandTipPose() const {
|
|||
return pose;
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::worldToJointPoint(const glm::vec3& position, const int jointIndex) const {
|
||||
glm::vec3 jointPos = getWorldPosition();//default value if no or invalid joint specified
|
||||
glm::quat jointRot = getWorldOrientation();//default value if no or invalid joint specified
|
||||
if (jointIndex != -1) {
|
||||
if (_skeletonModel->getJointPositionInWorldFrame(jointIndex, jointPos)) {
|
||||
_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot);
|
||||
} else {
|
||||
qWarning() << "Invalid joint index specified: " << jointIndex;
|
||||
}
|
||||
}
|
||||
glm::vec3 modelOffset = position - jointPos;
|
||||
glm::vec3 jointSpacePosition = glm::inverse(jointRot) * modelOffset;
|
||||
|
||||
return jointSpacePosition;
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::worldToJointDirection(const glm::vec3& worldDir, const int jointIndex) const {
|
||||
glm::quat jointRot = getWorldOrientation();//default value if no or invalid joint specified
|
||||
if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) {
|
||||
qWarning() << "Invalid joint index specified: " << jointIndex;
|
||||
}
|
||||
|
||||
glm::vec3 jointSpaceDir = glm::inverse(jointRot) * worldDir;
|
||||
return jointSpaceDir;
|
||||
}
|
||||
|
||||
glm::quat MyAvatar::worldToJointRotation(const glm::quat& worldRot, const int jointIndex) const {
|
||||
glm::quat jointRot = getWorldOrientation();//default value if no or invalid joint specified
|
||||
if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) {
|
||||
qWarning() << "Invalid joint index specified: " << jointIndex;
|
||||
}
|
||||
glm::quat jointSpaceRot = glm::inverse(jointRot) * worldRot;
|
||||
return jointSpaceRot;
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::jointToWorldPoint(const glm::vec3& jointSpacePos, const int jointIndex) const {
|
||||
glm::vec3 jointPos = getWorldPosition();//default value if no or invalid joint specified
|
||||
glm::quat jointRot = getWorldOrientation();//default value if no or invalid joint specified
|
||||
|
||||
if (jointIndex != -1) {
|
||||
if (_skeletonModel->getJointPositionInWorldFrame(jointIndex, jointPos)) {
|
||||
_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot);
|
||||
} else {
|
||||
qWarning() << "Invalid joint index specified: " << jointIndex;
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 worldOffset = jointRot * jointSpacePos;
|
||||
glm::vec3 worldPos = jointPos + worldOffset;
|
||||
|
||||
return worldPos;
|
||||
}
|
||||
|
||||
glm::vec3 MyAvatar::jointToWorldDirection(const glm::vec3& jointSpaceDir, const int jointIndex) const {
|
||||
glm::quat jointRot = getWorldOrientation();//default value if no or invalid joint specified
|
||||
if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) {
|
||||
qWarning() << "Invalid joint index specified: " << jointIndex;
|
||||
}
|
||||
glm::vec3 worldDir = jointRot * jointSpaceDir;
|
||||
return worldDir;
|
||||
}
|
||||
|
||||
glm::quat MyAvatar::jointToWorldRotation(const glm::quat& jointSpaceRot, const int jointIndex) const {
|
||||
glm::quat jointRot = getWorldOrientation();//default value if no or invalid joint specified
|
||||
if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) {
|
||||
qWarning() << "Invalid joint index specified: " << jointIndex;
|
||||
}
|
||||
glm::quat worldRot = jointRot * jointSpaceRot;
|
||||
return worldRot;
|
||||
}
|
||||
|
||||
// virtual
|
||||
void MyAvatar::render(RenderArgs* renderArgs) {
|
||||
// don't render if we've been asked to disable local rendering
|
||||
|
@ -3114,16 +3043,15 @@ void MyAvatar::postUpdate(float deltaTime, const render::ScenePointer& scene) {
|
|||
|
||||
if (_skeletonModel && _skeletonModel->isLoaded()) {
|
||||
const Rig& rig = _skeletonModel->getRig();
|
||||
const HFMModel& hfmModel = _skeletonModel->getHFMModel();
|
||||
for (int i = 0; i < rig.getJointStateCount(); i++) {
|
||||
AnimPose jointPose;
|
||||
rig.getAbsoluteJointPoseInRigFrame(i, jointPose);
|
||||
const HFMJointShapeInfo& shapeInfo = hfmModel.joints[i].shapeInfo;
|
||||
const AnimPose pose = rigToWorldPose * jointPose;
|
||||
for (size_t j = 0; j < shapeInfo.debugLines.size() / 2; j++) {
|
||||
glm::vec3 pointA = pose.xformPoint(shapeInfo.debugLines[2 * j]);
|
||||
glm::vec3 pointB = pose.xformPoint(shapeInfo.debugLines[2 * j + 1]);
|
||||
DebugDraw::getInstance().drawRay(pointA, pointB, DEBUG_COLORS[i % NUM_DEBUG_COLORS]);
|
||||
int jointCount = rig.getJointStateCount();
|
||||
if (jointCount == (int)_multiSphereShapes.size()) {
|
||||
for (int i = 0; i < jointCount; i++) {
|
||||
AnimPose jointPose;
|
||||
rig.getAbsoluteJointPoseInRigFrame(i, jointPose);
|
||||
const AnimPose pose = rigToWorldPose * jointPose;
|
||||
auto &multiSphere = _multiSphereShapes[i];
|
||||
auto debugLines = multiSphere.getDebugLines();
|
||||
DebugDraw::getInstance().drawRays(debugLines, DEBUG_COLORS[i % NUM_DEBUG_COLORS], pose.trans(), pose.rot());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4815,7 +4743,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 +5301,7 @@ void MyAvatar::releaseGrab(const QUuid& grabID) {
|
|||
|
||||
_avatarGrabsLock.withWriteLock([&] {
|
||||
if (_avatarGrabData.remove(grabID)) {
|
||||
_deletedAvatarGrabs.insert(grabID);
|
||||
_grabsToDelete.push_back(grabID);
|
||||
tellHandler = true;
|
||||
}
|
||||
});
|
||||
|
@ -5383,3 +5311,4 @@ void MyAvatar::releaseGrab(const QUuid& grabID) {
|
|||
_clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::Grab, grabID);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
class AvatarActionHold;
|
||||
class ModelItemID;
|
||||
class MyHead;
|
||||
class DetailedMotionState;
|
||||
|
||||
enum eyeContactTarget {
|
||||
LEFT_EYE,
|
||||
|
@ -802,56 +803,6 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE controller::Pose getRightHandTipPose() const;
|
||||
|
||||
// world-space to avatar-space rigconversion functions
|
||||
/**jsdoc
|
||||
* @function MyAvatar.worldToJointPoint
|
||||
* @param {Vec3} position
|
||||
* @param {number} [jointIndex=-1]
|
||||
* @returns {Vec3}
|
||||
*/
|
||||
Q_INVOKABLE glm::vec3 worldToJointPoint(const glm::vec3& position, const int jointIndex = -1) const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.worldToJointDirection
|
||||
* @param {Vec3} direction
|
||||
* @param {number} [jointIndex=-1]
|
||||
* @returns {Vec3}
|
||||
*/
|
||||
Q_INVOKABLE glm::vec3 worldToJointDirection(const glm::vec3& direction, const int jointIndex = -1) const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.worldToJointRotation
|
||||
* @param {Quat} rotation
|
||||
* @param {number} [jointIndex=-1]
|
||||
* @returns {Quat}
|
||||
*/
|
||||
Q_INVOKABLE glm::quat worldToJointRotation(const glm::quat& rotation, const int jointIndex = -1) const;
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.jointToWorldPoint
|
||||
* @param {vec3} position
|
||||
* @param {number} [jointIndex=-1]
|
||||
* @returns {Vec3}
|
||||
*/
|
||||
Q_INVOKABLE glm::vec3 jointToWorldPoint(const glm::vec3& position, const int jointIndex = -1) const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.jointToWorldDirection
|
||||
* @param {Vec3} direction
|
||||
* @param {number} [jointIndex=-1]
|
||||
* @returns {Vec3}
|
||||
*/
|
||||
Q_INVOKABLE glm::vec3 jointToWorldDirection(const glm::vec3& direction, const int jointIndex = -1) const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.jointToWorldRotation
|
||||
* @param {Quat} rotation
|
||||
* @param {number} [jointIndex=-1]
|
||||
* @returns {Quat}
|
||||
*/
|
||||
Q_INVOKABLE glm::quat jointToWorldRotation(const glm::quat& rotation, const int jointIndex = -1) const;
|
||||
|
||||
AvatarWeakPointer getLookAtTargetAvatar() const { return _lookAtTargetAvatar; }
|
||||
void updateLookAtTargetAvatar();
|
||||
void computeMyLookAtTarget(const AvatarHash& hash);
|
||||
|
|
|
@ -12,8 +12,10 @@
|
|||
#include "MyCharacterController.h"
|
||||
|
||||
#include <BulletUtil.h>
|
||||
#include "BulletCollision/NarrowPhaseCollision/btRaycastCallback.h"
|
||||
|
||||
#include "MyAvatar.h"
|
||||
#include "DetailedMotionState.h"
|
||||
|
||||
// TODO: make avatars stand on steep slope
|
||||
// TODO: make avatars not snag on low ceilings
|
||||
|
@ -24,7 +26,7 @@ void MyCharacterController::RayShotgunResult::reset() {
|
|||
walkable = true;
|
||||
}
|
||||
|
||||
MyCharacterController::MyCharacterController(MyAvatar* avatar) {
|
||||
MyCharacterController::MyCharacterController(std::shared_ptr<MyAvatar> avatar) {
|
||||
|
||||
assert(avatar);
|
||||
_avatar = avatar;
|
||||
|
@ -44,7 +46,6 @@ void MyCharacterController::setDynamicsWorld(btDynamicsWorld* world) {
|
|||
void MyCharacterController::updateShapeIfNecessary() {
|
||||
if (_pendingFlags & PENDING_FLAG_UPDATE_SHAPE) {
|
||||
_pendingFlags &= ~PENDING_FLAG_UPDATE_SHAPE;
|
||||
|
||||
if (_radius > 0.0f) {
|
||||
// create RigidBody if it doesn't exist
|
||||
if (!_rigidBody) {
|
||||
|
@ -375,3 +376,121 @@ void MyCharacterController::updateMassProperties() {
|
|||
|
||||
_rigidBody->setMassProps(mass, inertia);
|
||||
}
|
||||
|
||||
btCollisionShape* MyCharacterController::createDetailedCollisionShapeForJoint(int jointIndex) {
|
||||
ShapeInfo shapeInfo;
|
||||
_avatar->computeDetailedShapeInfo(shapeInfo, jointIndex);
|
||||
if (shapeInfo.getType() != SHAPE_TYPE_NONE) {
|
||||
btCollisionShape* shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
||||
if (shape) {
|
||||
shape->setMargin(0.001f);
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DetailedMotionState* MyCharacterController::createDetailedMotionStateForJoint(int jointIndex) {
|
||||
auto shape = createDetailedCollisionShapeForJoint(jointIndex);
|
||||
if (shape) {
|
||||
DetailedMotionState* motionState = new DetailedMotionState(_avatar, shape, jointIndex);
|
||||
motionState->setMass(_avatar->computeMass());
|
||||
return motionState;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void MyCharacterController::clearDetailedMotionStates() {
|
||||
_pendingFlags |= PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION;
|
||||
// We make sure we don't add them again
|
||||
_pendingFlags &= ~PENDING_FLAG_ADD_DETAILED_TO_SIMULATION;
|
||||
}
|
||||
|
||||
void MyCharacterController::resetDetailedMotionStates() {
|
||||
_detailedMotionStates.clear();
|
||||
}
|
||||
|
||||
void MyCharacterController::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) {
|
||||
for (size_t i = 0; i < _detailedMotionStates.size(); i++) {
|
||||
_detailedMotionStates[i]->forceActive();
|
||||
}
|
||||
if (_pendingFlags & PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION) {
|
||||
_pendingFlags &= ~PENDING_FLAG_REMOVE_DETAILED_FROM_SIMULATION;
|
||||
for (size_t i = 0; i < _detailedMotionStates.size(); i++) {
|
||||
transaction.objectsToRemove.push_back(_detailedMotionStates[i]);
|
||||
}
|
||||
_detailedMotionStates.clear();
|
||||
}
|
||||
if (_pendingFlags & PENDING_FLAG_ADD_DETAILED_TO_SIMULATION) {
|
||||
_pendingFlags &= ~PENDING_FLAG_ADD_DETAILED_TO_SIMULATION;
|
||||
for (int i = 0; i < _avatar->getJointCount(); i++) {
|
||||
auto dMotionState = createDetailedMotionStateForJoint(i);
|
||||
if (dMotionState) {
|
||||
_detailedMotionStates.push_back(dMotionState);
|
||||
transaction.objectsToAdd.push_back(dMotionState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MyCharacterController::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction) {
|
||||
// things on objectsToRemove are ready for delete
|
||||
for (auto object : transaction.objectsToRemove) {
|
||||
delete object;
|
||||
}
|
||||
transaction.clear();
|
||||
}
|
||||
|
||||
|
||||
class DetailedRayResultCallback : public btCollisionWorld::AllHitsRayResultCallback {
|
||||
public:
|
||||
DetailedRayResultCallback()
|
||||
: btCollisionWorld::AllHitsRayResultCallback(btVector3(0.0f, 0.0f, 0.0f), btVector3(0.0f, 0.0f, 0.0f)) {
|
||||
// the RayResultCallback's group and mask must match MY_AVATAR
|
||||
m_collisionFilterGroup = BULLET_COLLISION_GROUP_DETAILED_RAY;
|
||||
m_collisionFilterMask = BULLET_COLLISION_MASK_DETAILED_RAY;
|
||||
}
|
||||
|
||||
virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) override {
|
||||
return AllHitsRayResultCallback::addSingleResult(rayResult, normalInWorldSpace);
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<MyCharacterController::RayAvatarResult> MyCharacterController::rayTest(const btVector3& origin, const btVector3& direction,
|
||||
const btScalar& length, const QVector<uint>& jointsToExclude) const {
|
||||
std::vector<RayAvatarResult> foundAvatars;
|
||||
if (_dynamicsWorld) {
|
||||
btVector3 end = origin + length * direction;
|
||||
DetailedRayResultCallback rayCallback = DetailedRayResultCallback();
|
||||
rayCallback.m_flags |= btTriangleRaycastCallback::kF_KeepUnflippedNormal;
|
||||
rayCallback.m_flags |= btTriangleRaycastCallback::kF_UseSubSimplexConvexCastRaytest;
|
||||
_dynamicsWorld->rayTest(origin, end, rayCallback);
|
||||
if (rayCallback.m_hitFractions.size() > 0) {
|
||||
foundAvatars.reserve(rayCallback.m_hitFractions.size());
|
||||
for (int i = 0; i < rayCallback.m_hitFractions.size(); i++) {
|
||||
auto object = rayCallback.m_collisionObjects[i];
|
||||
ObjectMotionState* motionState = static_cast<ObjectMotionState*>(object->getUserPointer());
|
||||
if (motionState && motionState->getType() == MOTIONSTATE_TYPE_DETAILED) {
|
||||
DetailedMotionState* detailedMotionState = dynamic_cast<DetailedMotionState*>(motionState);
|
||||
MyCharacterController::RayAvatarResult result;
|
||||
result._intersect = true;
|
||||
result._intersectWithAvatar = detailedMotionState->getAvatarID();
|
||||
result._intersectionPoint = bulletToGLM(rayCallback.m_hitPointWorld[i]);
|
||||
result._intersectionNormal = bulletToGLM(rayCallback.m_hitNormalWorld[i]);
|
||||
result._distance = length * rayCallback.m_hitFractions[i];
|
||||
result._intersectWithJoint = detailedMotionState->getJointIndex();
|
||||
result._isBound = detailedMotionState->getIsBound(result._boundJoints);
|
||||
btVector3 center;
|
||||
btScalar radius;
|
||||
detailedMotionState->getShape()->getBoundingSphere(center, radius);
|
||||
result._maxDistance = (float)radius;
|
||||
foundAvatars.push_back(result);
|
||||
}
|
||||
}
|
||||
std::sort(foundAvatars.begin(), foundAvatars.end(), [](const RayAvatarResult& resultA, const RayAvatarResult& resultB) {
|
||||
return resultA._distance < resultB._distance;
|
||||
});
|
||||
}
|
||||
}
|
||||
return foundAvatars;
|
||||
}
|
33
interface/src/avatar/MyCharacterController.h
Executable file → Normal file
33
interface/src/avatar/MyCharacterController.h
Executable file → Normal file
|
@ -15,13 +15,15 @@
|
|||
|
||||
#include <CharacterController.h>
|
||||
//#include <SharedUtil.h>
|
||||
#include <PhysicsEngine.h>
|
||||
|
||||
class btCollisionShape;
|
||||
class MyAvatar;
|
||||
class DetailedMotionState;
|
||||
|
||||
class MyCharacterController : public CharacterController {
|
||||
public:
|
||||
explicit MyCharacterController(MyAvatar* avatar);
|
||||
explicit MyCharacterController(std::shared_ptr<MyAvatar> avatar);
|
||||
~MyCharacterController ();
|
||||
|
||||
void setDynamicsWorld(btDynamicsWorld* world) override;
|
||||
|
@ -42,6 +44,31 @@ public:
|
|||
|
||||
void setDensity(btScalar density) { _density = density; }
|
||||
|
||||
btCollisionShape* createDetailedCollisionShapeForJoint(int jointIndex);
|
||||
DetailedMotionState* createDetailedMotionStateForJoint(int jointIndex);
|
||||
std::vector<DetailedMotionState*>& getDetailedMotionStates() { return _detailedMotionStates; }
|
||||
void clearDetailedMotionStates();
|
||||
void resetDetailedMotionStates();
|
||||
|
||||
void buildPhysicsTransaction(PhysicsEngine::Transaction& transaction);
|
||||
void handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction);
|
||||
|
||||
|
||||
struct RayAvatarResult {
|
||||
bool _intersect { false };
|
||||
bool _isBound { false };
|
||||
QUuid _intersectWithAvatar;
|
||||
int _intersectWithJoint { -1 };
|
||||
float _distance { 0.0f };
|
||||
float _maxDistance { 0.0f };
|
||||
QVariantMap _extraInfo;
|
||||
glm::vec3 _intersectionPoint;
|
||||
glm::vec3 _intersectionNormal;
|
||||
std::vector<int> _boundJoints;
|
||||
};
|
||||
std::vector<RayAvatarResult> rayTest(const btVector3& origin, const btVector3& direction, const btScalar& length,
|
||||
const QVector<uint>& jointsToExclude) const;
|
||||
|
||||
int32_t computeCollisionMask() const override;
|
||||
void handleChangedCollisionMask() override;
|
||||
|
||||
|
@ -56,12 +83,14 @@ private:
|
|||
btConvexHullShape* computeShape() const;
|
||||
|
||||
protected:
|
||||
MyAvatar* _avatar { nullptr };
|
||||
std::shared_ptr<MyAvatar> _avatar { nullptr };
|
||||
|
||||
// shotgun scan data
|
||||
btAlignedObjectArray<btVector3> _topPoints;
|
||||
btAlignedObjectArray<btVector3> _bottomPoints;
|
||||
btScalar _density { 1.0f };
|
||||
|
||||
std::vector<DetailedMotionState*> _detailedMotionStates;
|
||||
};
|
||||
|
||||
#endif // hifi_MyCharacterController_h
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "Application.h"
|
||||
#include "AvatarMotionState.h"
|
||||
#include "DetailedMotionState.h"
|
||||
|
||||
const float DISPLAYNAME_FADE_TIME = 0.5f;
|
||||
const float DISPLAYNAME_FADE_FACTOR = pow(0.01f, 1.0f / DISPLAYNAME_FADE_TIME);
|
||||
|
@ -110,29 +111,155 @@ void OtherAvatar::updateSpaceProxy(workload::Transaction& transaction) const {
|
|||
|
||||
int OtherAvatar::parseDataFromBuffer(const QByteArray& buffer) {
|
||||
int32_t bytesRead = Avatar::parseDataFromBuffer(buffer);
|
||||
for (size_t i = 0; i < _detailedMotionStates.size(); i++) {
|
||||
_detailedMotionStates[i]->forceActive();
|
||||
}
|
||||
if (_moving && _motionState) {
|
||||
_motionState->addDirtyFlags(Simulation::DIRTY_POSITION);
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
btCollisionShape* OtherAvatar::createCollisionShape(int jointIndex, bool& isBound, std::vector<int>& boundJoints) {
|
||||
ShapeInfo shapeInfo;
|
||||
isBound = false;
|
||||
QString jointName = "";
|
||||
if (jointIndex > -1 && jointIndex < (int)_multiSphereShapes.size()) {
|
||||
jointName = _multiSphereShapes[jointIndex].getJointName();
|
||||
}
|
||||
switch (_bodyLOD) {
|
||||
case BodyLOD::Sphere:
|
||||
shapeInfo.setSphere(0.5f * getFitBounds().getDimensions().y);
|
||||
boundJoints.clear();
|
||||
for (auto &spheres : _multiSphereShapes) {
|
||||
if (spheres.isValid()) {
|
||||
boundJoints.push_back(spheres.getJointIndex());
|
||||
}
|
||||
}
|
||||
isBound = true;
|
||||
break;
|
||||
case BodyLOD::MultiSphereLow:
|
||||
if (jointName.contains("RightHand", Qt::CaseInsensitive) || jointName.contains("LeftHand", Qt::CaseInsensitive)) {
|
||||
if (jointName.size() <= QString("RightHand").size()) {
|
||||
AABox handBound;
|
||||
for (auto &spheres : _multiSphereShapes) {
|
||||
if (spheres.isValid() && spheres.getJointName().contains(jointName, Qt::CaseInsensitive)) {
|
||||
boundJoints.push_back(spheres.getJointIndex());
|
||||
handBound += spheres.getBoundingBox();
|
||||
}
|
||||
}
|
||||
shapeInfo.setSphere(0.5f * handBound.getLargestDimension());
|
||||
glm::vec3 jointPosition;
|
||||
glm::quat jointRotation;
|
||||
_skeletonModel->getJointPositionInWorldFrame(jointIndex, jointPosition);
|
||||
_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRotation);
|
||||
glm::vec3 positionOffset = glm::inverse(jointRotation) * (handBound.calcCenter() - jointPosition);
|
||||
shapeInfo.setOffset(positionOffset);
|
||||
isBound = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BodyLOD::MultiSphereHigh:
|
||||
computeDetailedShapeInfo(shapeInfo, jointIndex);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (shapeInfo.getType() != SHAPE_TYPE_NONE) {
|
||||
auto shape = const_cast<btCollisionShape*>(ObjectMotionState::getShapeManager()->getShape(shapeInfo));
|
||||
if (shape) {
|
||||
shape->setMargin(0.001f);
|
||||
}
|
||||
return shape;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DetailedMotionState* OtherAvatar::createMotionState(std::shared_ptr<OtherAvatar> avatar, int jointIndex) {
|
||||
bool isBound = false;
|
||||
std::vector<int> boundJoints;
|
||||
btCollisionShape* shape = createCollisionShape(jointIndex, isBound, boundJoints);
|
||||
if (shape) {
|
||||
DetailedMotionState* motionState = new DetailedMotionState(avatar, shape, jointIndex);
|
||||
motionState->setMass(computeMass());
|
||||
motionState->setIsBound(isBound, boundJoints);
|
||||
return motionState;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void OtherAvatar::resetDetailedMotionStates() {
|
||||
for (size_t i = 0; i < _detailedMotionStates.size(); i++) {
|
||||
_detailedMotionStates[i] = nullptr;
|
||||
}
|
||||
_detailedMotionStates.clear();
|
||||
}
|
||||
|
||||
void OtherAvatar::setWorkloadRegion(uint8_t region) {
|
||||
_workloadRegion = region;
|
||||
QString printRegion = "";
|
||||
if (region == workload::Region::R1) {
|
||||
printRegion = "R1";
|
||||
} else if (region == workload::Region::R2) {
|
||||
printRegion = "R2";
|
||||
} else if (region == workload::Region::R3) {
|
||||
printRegion = "R3";
|
||||
} else {
|
||||
printRegion = "invalid";
|
||||
}
|
||||
qCDebug(avatars) << "Setting workload region to " << printRegion;
|
||||
computeShapeLOD();
|
||||
}
|
||||
|
||||
void OtherAvatar::computeShapeLOD() {
|
||||
// auto newBodyLOD = _workloadRegion < workload::Region::R3 ? BodyLOD::MultiSphereShapes : BodyLOD::CapsuleShape;
|
||||
// auto newBodyLOD = BodyLOD::CapsuleShape;
|
||||
BodyLOD newLOD;
|
||||
switch (_workloadRegion) {
|
||||
case workload::Region::R1:
|
||||
newLOD = BodyLOD::MultiSphereHigh;
|
||||
break;
|
||||
case workload::Region::R2:
|
||||
newLOD = BodyLOD::MultiSphereLow;
|
||||
break;
|
||||
case workload::Region::UNKNOWN:
|
||||
case workload::Region::INVALID:
|
||||
case workload::Region::R3:
|
||||
default:
|
||||
newLOD = BodyLOD::Sphere;
|
||||
break;
|
||||
}
|
||||
if (newLOD != _bodyLOD) {
|
||||
_bodyLOD = newLOD;
|
||||
if (isInPhysicsSimulation()) {
|
||||
qCDebug(avatars) << "Changing to body LOD " << newLOD;
|
||||
_needsReinsertion = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool OtherAvatar::isInPhysicsSimulation() const {
|
||||
return _motionState != nullptr && _detailedMotionStates.size() > 0;
|
||||
}
|
||||
|
||||
bool OtherAvatar::shouldBeInPhysicsSimulation() const {
|
||||
return (_workloadRegion < workload::Region::R3 && !isDead());
|
||||
return !isDead() && !(isInPhysicsSimulation() && _needsReinsertion);
|
||||
}
|
||||
|
||||
bool OtherAvatar::needsPhysicsUpdate() const {
|
||||
constexpr uint32_t FLAGS_OF_INTEREST = Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS | Simulation::DIRTY_POSITION | Simulation::DIRTY_COLLISION_GROUP;
|
||||
return (_motionState && (bool)(_motionState->getIncomingDirtyFlags() & FLAGS_OF_INTEREST));
|
||||
return (_needsReinsertion || (_motionState && (bool)(_motionState->getIncomingDirtyFlags() & FLAGS_OF_INTEREST)));
|
||||
}
|
||||
|
||||
void OtherAvatar::rebuildCollisionShape() {
|
||||
if (_motionState) {
|
||||
_motionState->addDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
|
||||
}
|
||||
for (size_t i = 0; i < _detailedMotionStates.size(); i++) {
|
||||
if (_detailedMotionStates[i]) {
|
||||
_detailedMotionStates[i]->addDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OtherAvatar::setCollisionWithOtherAvatarsFlags() {
|
||||
|
@ -141,6 +268,25 @@ void OtherAvatar::setCollisionWithOtherAvatarsFlags() {
|
|||
}
|
||||
}
|
||||
|
||||
void OtherAvatar::createDetailedMotionStates(const std::shared_ptr<OtherAvatar>& avatar) {
|
||||
auto& detailedMotionStates = getDetailedMotionStates();
|
||||
assert(detailedMotionStates.empty());
|
||||
if (_bodyLOD == BodyLOD::Sphere) {
|
||||
auto dMotionState = createMotionState(avatar, -1);
|
||||
if (dMotionState) {
|
||||
detailedMotionStates.push_back(dMotionState);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < getJointCount(); i++) {
|
||||
auto dMotionState = createMotionState(avatar, i);
|
||||
if (dMotionState) {
|
||||
detailedMotionStates.push_back(dMotionState);
|
||||
}
|
||||
}
|
||||
}
|
||||
_needsReinsertion = false;
|
||||
}
|
||||
|
||||
void OtherAvatar::simulate(float deltaTime, bool inView) {
|
||||
PROFILE_RANGE(simulation, "simulate");
|
||||
|
||||
|
@ -217,7 +363,7 @@ void OtherAvatar::simulate(float deltaTime, bool inView) {
|
|||
|
||||
{
|
||||
PROFILE_RANGE(simulation, "grabs");
|
||||
updateGrabs();
|
||||
applyGrabChanges();
|
||||
}
|
||||
|
||||
updateFadingStatus();
|
||||
|
|
22
interface/src/avatar/OtherAvatar.h
Executable file → Normal file
22
interface/src/avatar/OtherAvatar.h
Executable file → Normal file
|
@ -21,12 +21,19 @@
|
|||
|
||||
class AvatarManager;
|
||||
class AvatarMotionState;
|
||||
class DetailedMotionState;
|
||||
|
||||
class OtherAvatar : public Avatar {
|
||||
public:
|
||||
explicit OtherAvatar(QThread* thread);
|
||||
virtual ~OtherAvatar();
|
||||
|
||||
enum BodyLOD {
|
||||
Sphere = 0,
|
||||
MultiSphereLow, // No finger joints
|
||||
MultiSphereHigh // All joints
|
||||
};
|
||||
|
||||
virtual void instantiableAvatar() override { };
|
||||
virtual void createOrb() override;
|
||||
virtual void indicateLoadingStatus(LoadingStatus loadingStatus) override;
|
||||
|
@ -39,13 +46,22 @@ public:
|
|||
|
||||
int parseDataFromBuffer(const QByteArray& buffer) override;
|
||||
|
||||
bool isInPhysicsSimulation() const { return _motionState != nullptr; }
|
||||
bool isInPhysicsSimulation() const;
|
||||
void rebuildCollisionShape() override;
|
||||
|
||||
void setWorkloadRegion(uint8_t region);
|
||||
bool shouldBeInPhysicsSimulation() const;
|
||||
bool needsPhysicsUpdate() const;
|
||||
|
||||
btCollisionShape* createCollisionShape(int jointIndex, bool& isBound, std::vector<int>& boundJoints);
|
||||
DetailedMotionState* createMotionState(std::shared_ptr<OtherAvatar> avatar, int jointIndex);
|
||||
void createDetailedMotionStates(const std::shared_ptr<OtherAvatar>& avatar);
|
||||
std::vector<DetailedMotionState*>& getDetailedMotionStates() { return _detailedMotionStates; }
|
||||
void resetDetailedMotionStates();
|
||||
BodyLOD getBodyLOD() { return _bodyLOD; }
|
||||
void computeShapeLOD();
|
||||
|
||||
void updateCollisionGroup(bool myAvatarCollide);
|
||||
bool getCollideWithOtherAvatars() const { return _collideWithOtherAvatars; }
|
||||
|
||||
void setCollisionWithOtherAvatarsFlags() override;
|
||||
|
@ -64,10 +80,14 @@ protected:
|
|||
std::shared_ptr<Sphere3DOverlay> _otherAvatarOrbMeshPlaceholder { nullptr };
|
||||
OverlayID _otherAvatarOrbMeshPlaceholderID { UNKNOWN_OVERLAY_ID };
|
||||
AvatarMotionState* _motionState { nullptr };
|
||||
std::vector<DetailedMotionState*> _detailedMotionStates;
|
||||
int32_t _spaceIndex { -1 };
|
||||
uint8_t _workloadRegion { workload::Region::INVALID };
|
||||
BodyLOD _bodyLOD { BodyLOD::Sphere };
|
||||
bool _needsReinsertion { false };
|
||||
};
|
||||
|
||||
using OtherAvatarPointer = std::shared_ptr<OtherAvatar>;
|
||||
using AvatarPointer = std::shared_ptr<Avatar>;
|
||||
|
||||
#endif // hifi_OtherAvatar_h
|
||||
|
|
|
@ -56,7 +56,7 @@ PickResultPointer RayPick::getOverlayIntersection(const PickRay& pick) {
|
|||
}
|
||||
|
||||
PickResultPointer RayPick::getAvatarIntersection(const PickRay& pick) {
|
||||
RayToAvatarIntersectionResult avatarRes = DependencyManager::get<AvatarManager>()->findRayIntersectionVector(pick, getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>());
|
||||
RayToAvatarIntersectionResult avatarRes = DependencyManager::get<AvatarManager>()->findRayIntersectionVector(pick, getIncludeItemsAs<EntityItemID>(), getIgnoreItemsAs<EntityItemID>(), true);
|
||||
if (avatarRes.intersects) {
|
||||
return std::make_shared<RayPickResult>(IntersectionType::AVATAR, avatarRes.avatarID, avatarRes.distance, avatarRes.intersection, pick, avatarRes.surfaceNormal, avatarRes.extraInfo);
|
||||
} else {
|
||||
|
|
|
@ -59,7 +59,7 @@ void AddressBarDialog::loadHome() {
|
|||
auto locationBookmarks = DependencyManager::get<LocationBookmarks>();
|
||||
QString homeLocation = locationBookmarks->addressForBookmark(LocationBookmarks::HOME_BOOKMARK);
|
||||
if (homeLocation == "") {
|
||||
homeLocation = DEFAULT_HIFI_ADDRESS;
|
||||
homeLocation = DEFAULT_HOME_ADDRESS;
|
||||
}
|
||||
DependencyManager::get<AddressManager>()->handleLookupString(homeLocation);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -151,10 +151,10 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn
|
|||
if (_phase < 0.0f) {
|
||||
_phase = 0.0f;
|
||||
}
|
||||
|
||||
|
||||
// detect loop trigger events
|
||||
if (_phase >= 1.0f) {
|
||||
triggersOut.setTrigger(_id + "Loop");
|
||||
triggersOut.setTrigger(_id + "OnLoop");
|
||||
_phase = glm::fract(_phase);
|
||||
}
|
||||
|
||||
|
|
|
@ -209,15 +209,14 @@ public:
|
|||
void set(const QString& key, const QString& value) { _map[key] = AnimVariant(value); }
|
||||
void unset(const QString& key) { _map.erase(key); }
|
||||
|
||||
void setTrigger(const QString& key) { _triggers.insert(key); }
|
||||
void clearTriggers() { _triggers.clear(); }
|
||||
void setTrigger(const QString& key) { _map[key] = AnimVariant(true); }
|
||||
|
||||
void setRigToGeometryTransform(const glm::mat4& rigToGeometry) {
|
||||
_rigToGeometryMat = rigToGeometry;
|
||||
_rigToGeometryRot = glmExtractRotation(rigToGeometry);
|
||||
}
|
||||
|
||||
void clearMap() { _map.clear(); }
|
||||
void clearMap() { _map.clear(); _triggers.clear(); }
|
||||
bool hasKey(const QString& key) const { return _map.find(key) != _map.end(); }
|
||||
|
||||
const AnimVariant& get(const QString& key) const {
|
||||
|
@ -238,7 +237,7 @@ public:
|
|||
// For stat debugging.
|
||||
std::map<QString, QString> toDebugMap() const;
|
||||
|
||||
#ifdef NDEBUG
|
||||
#ifndef NDEBUG
|
||||
void dump() const {
|
||||
qCDebug(animation) << "AnimVariantMap =";
|
||||
for (auto& pair : _map) {
|
||||
|
|
|
@ -1207,9 +1207,7 @@ void Rig::updateAnimations(float deltaTime, const glm::mat4& rootTransform, cons
|
|||
_networkPoseSet._relativePoses = _animSkeleton->getRelativeDefaultPoses();
|
||||
}
|
||||
_lastAnimVars = _animVars;
|
||||
_animVars.clearTriggers();
|
||||
_animVars = triggersOut;
|
||||
_networkVars.clearTriggers();
|
||||
_networkVars = networkTriggersOut;
|
||||
_lastContext = context;
|
||||
}
|
||||
|
|
|
@ -1310,10 +1310,17 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) {
|
|||
memset(_localScratchBuffer, 0, bytesToRead);
|
||||
if (0 < injectorBuffer->readData((char*)_localScratchBuffer, bytesToRead)) {
|
||||
|
||||
float gain = injector->getVolume();
|
||||
|
||||
if (injector->isAmbisonic()) {
|
||||
|
||||
// no distance attenuation
|
||||
float gain = injector->getVolume();
|
||||
if (injector->isPositionSet()) {
|
||||
|
||||
// distance attenuation
|
||||
glm::vec3 relativePosition = injector->getPosition() - _positionGetter();
|
||||
float distance = glm::max(glm::length(relativePosition), EPSILON);
|
||||
gain = gainForSource(distance, gain);
|
||||
}
|
||||
|
||||
//
|
||||
// Calculate the soundfield orientation relative to the listener.
|
||||
|
@ -1327,33 +1334,49 @@ bool AudioClient::mixLocalAudioInjectors(float* mixBuffer) {
|
|||
float qy = -relativeOrientation.x;
|
||||
float qz = relativeOrientation.y;
|
||||
|
||||
// Ambisonic gets spatialized into mixBuffer
|
||||
// spatialize into mixBuffer
|
||||
injector->getLocalFOA().render(_localScratchBuffer, mixBuffer, HRTF_DATASET_INDEX,
|
||||
qw, qx, qy, qz, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
|
||||
|
||||
} else if (injector->isStereo()) {
|
||||
|
||||
// calculate distance, gain
|
||||
glm::vec3 relativePosition = injector->getPosition() - _positionGetter();
|
||||
float distance = glm::max(glm::length(relativePosition), EPSILON);
|
||||
float gain = gainForSource(distance, injector->getVolume());
|
||||
if (injector->isPositionSet()) {
|
||||
|
||||
// stereo gets directly mixed into mixBuffer
|
||||
for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; i++) {
|
||||
mixBuffer[i] += convertToFloat(_localScratchBuffer[i]) * gain;
|
||||
// distance attenuation
|
||||
glm::vec3 relativePosition = injector->getPosition() - _positionGetter();
|
||||
float distance = glm::max(glm::length(relativePosition), EPSILON);
|
||||
gain = gainForSource(distance, gain);
|
||||
}
|
||||
|
||||
} else {
|
||||
// direct mix into mixBuffer
|
||||
for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; i++) {
|
||||
mixBuffer[2*i+0] += convertToFloat(_localScratchBuffer[2*i+0]) * gain;
|
||||
mixBuffer[2*i+1] += convertToFloat(_localScratchBuffer[2*i+1]) * gain;
|
||||
}
|
||||
|
||||
// calculate distance, gain and azimuth for hrtf
|
||||
glm::vec3 relativePosition = injector->getPosition() - _positionGetter();
|
||||
float distance = glm::max(glm::length(relativePosition), EPSILON);
|
||||
float gain = gainForSource(distance, injector->getVolume());
|
||||
float azimuth = azimuthForSource(relativePosition);
|
||||
} else { // injector is mono
|
||||
|
||||
// mono gets spatialized into mixBuffer
|
||||
injector->getLocalHRTF().render(_localScratchBuffer, mixBuffer, HRTF_DATASET_INDEX,
|
||||
azimuth, distance, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
|
||||
if (injector->isPositionSet()) {
|
||||
|
||||
// distance attenuation
|
||||
glm::vec3 relativePosition = injector->getPosition() - _positionGetter();
|
||||
float distance = glm::max(glm::length(relativePosition), EPSILON);
|
||||
gain = gainForSource(distance, gain);
|
||||
|
||||
float azimuth = azimuthForSource(relativePosition);
|
||||
|
||||
// spatialize into mixBuffer
|
||||
injector->getLocalHRTF().render(_localScratchBuffer, mixBuffer, HRTF_DATASET_INDEX,
|
||||
azimuth, distance, gain, AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
|
||||
} else {
|
||||
|
||||
// direct mix into mixBuffer
|
||||
for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; i++) {
|
||||
float sample = convertToFloat(_localScratchBuffer[i]) * gain;
|
||||
mixBuffer[2*i+0] += sample;
|
||||
mixBuffer[2*i+1] += sample;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -1956,8 +1979,10 @@ float AudioClient::azimuthForSource(const glm::vec3& relativePosition) {
|
|||
float AudioClient::gainForSource(float distance, float volume) {
|
||||
|
||||
// attenuation = -6dB * log2(distance)
|
||||
// reference attenuation of 0dB at distance = 1.0m
|
||||
float gain = volume / std::max(distance, HRTF_NEARFIELD_MIN);
|
||||
// reference attenuation of 0dB at distance = ATTN_DISTANCE_REF
|
||||
float d = (1.0f / ATTN_DISTANCE_REF) * std::max(distance, HRTF_NEARFIELD_MIN);
|
||||
float gain = volume / d;
|
||||
gain = std::min(gain, ATTN_GAIN_MAX);
|
||||
|
||||
return gain;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,10 @@ static const float HRTF_NEARFIELD_MAX = 1.0f; // distance in meters
|
|||
static const float HRTF_NEARFIELD_MIN = 0.125f; // distance in meters
|
||||
static const float HRTF_HEAD_RADIUS = 0.0875f; // average human head in meters
|
||||
|
||||
// Distance attenuation
|
||||
static const float ATTN_DISTANCE_REF = 2.0f; // distance where attn is 0dB
|
||||
static const float ATTN_GAIN_MAX = 16.0f; // max gain allowed by distance attn (+24dB)
|
||||
|
||||
class AudioHRTF {
|
||||
|
||||
public:
|
||||
|
|
|
@ -67,6 +67,7 @@ public:
|
|||
|
||||
bool isLocalOnly() const { return _options.localOnly; }
|
||||
float getVolume() const { return _options.volume; }
|
||||
bool isPositionSet() const { return _options.positionSet; }
|
||||
glm::vec3 getPosition() const { return _options.position; }
|
||||
glm::quat getOrientation() const { return _options.orientation; }
|
||||
bool isStereo() const { return _options.stereo; }
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
AudioInjectorOptions::AudioInjectorOptions() :
|
||||
position(0.0f, 0.0f, 0.0f),
|
||||
positionSet(true), // default to legacy behavior
|
||||
volume(1.0f),
|
||||
loop(false),
|
||||
orientation(glm::vec3(0.0f, 0.0f, 0.0f)),
|
||||
|
@ -29,12 +30,13 @@ AudioInjectorOptions::AudioInjectorOptions() :
|
|||
secondOffset(0.0f),
|
||||
pitch(1.0f)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QScriptValue injectorOptionsToScriptValue(QScriptEngine* engine, const AudioInjectorOptions& injectorOptions) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("position", vec3ToScriptValue(engine, injectorOptions.position));
|
||||
if (injectorOptions.positionSet) {
|
||||
obj.setProperty("position", vec3ToScriptValue(engine, injectorOptions.position));
|
||||
}
|
||||
obj.setProperty("volume", injectorOptions.volume);
|
||||
obj.setProperty("loop", injectorOptions.loop);
|
||||
obj.setProperty("orientation", quatToScriptValue(engine, injectorOptions.orientation));
|
||||
|
@ -68,12 +70,18 @@ void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOpt
|
|||
return;
|
||||
}
|
||||
|
||||
if (injectorOptions.positionSet == false) {
|
||||
qWarning() << "Audio injector options: injectorOptionsFromScriptValue() called more than once?";
|
||||
}
|
||||
injectorOptions.positionSet = false;
|
||||
|
||||
QScriptValueIterator it(object);
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
|
||||
if (it.name() == "position") {
|
||||
vec3FromScriptValue(object.property("position"), injectorOptions.position);
|
||||
injectorOptions.positionSet = true;
|
||||
} else if (it.name() == "orientation") {
|
||||
quatFromScriptValue(object.property("orientation"), injectorOptions.orientation);
|
||||
} else if (it.name() == "volume") {
|
||||
|
|
|
@ -21,6 +21,7 @@ class AudioInjectorOptions {
|
|||
public:
|
||||
AudioInjectorOptions();
|
||||
glm::vec3 position;
|
||||
bool positionSet;
|
||||
float volume;
|
||||
bool loop;
|
||||
glm::quat orientation;
|
||||
|
|
|
@ -298,7 +298,9 @@ void Avatar::setTargetScale(float targetScale) {
|
|||
_scaleChanged = usecTimestampNow();
|
||||
_avatarScaleChanged = _scaleChanged;
|
||||
_isAnimatingScale = true;
|
||||
|
||||
for (auto& sphere : _multiSphereShapes) {
|
||||
sphere.setScale(_targetScale);
|
||||
}
|
||||
emit targetScaleChanged(targetScale);
|
||||
}
|
||||
}
|
||||
|
@ -324,88 +326,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 +406,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 +425,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) {
|
||||
|
@ -717,6 +724,7 @@ void Avatar::postUpdate(float deltaTime, const render::ScenePointer& scene) {
|
|||
}
|
||||
|
||||
fixupModelsInScene(scene);
|
||||
updateFitBoundingBox();
|
||||
}
|
||||
|
||||
void Avatar::render(RenderArgs* renderArgs) {
|
||||
|
@ -1283,6 +1291,79 @@ glm::vec3 Avatar::getAbsoluteJointScaleInObjectFrame(int index) const {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
glm::vec3 Avatar::worldToJointPoint(const glm::vec3& position, const int jointIndex) const {
|
||||
glm::vec3 jointPos = getWorldPosition();//default value if no or invalid joint specified
|
||||
glm::quat jointRot = getWorldOrientation();//default value if no or invalid joint specified
|
||||
if (jointIndex != -1) {
|
||||
if (_skeletonModel->getJointPositionInWorldFrame(jointIndex, jointPos)) {
|
||||
_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot);
|
||||
} else {
|
||||
qWarning() << "Invalid joint index specified: " << jointIndex;
|
||||
}
|
||||
}
|
||||
glm::vec3 modelOffset = position - jointPos;
|
||||
glm::vec3 jointSpacePosition = glm::inverse(jointRot) * modelOffset;
|
||||
|
||||
return jointSpacePosition;
|
||||
}
|
||||
|
||||
glm::vec3 Avatar::worldToJointDirection(const glm::vec3& worldDir, const int jointIndex) const {
|
||||
glm::quat jointRot = getWorldOrientation();//default value if no or invalid joint specified
|
||||
if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) {
|
||||
qWarning() << "Invalid joint index specified: " << jointIndex;
|
||||
}
|
||||
|
||||
glm::vec3 jointSpaceDir = glm::inverse(jointRot) * worldDir;
|
||||
return jointSpaceDir;
|
||||
}
|
||||
|
||||
glm::quat Avatar::worldToJointRotation(const glm::quat& worldRot, const int jointIndex) const {
|
||||
glm::quat jointRot = getWorldOrientation();//default value if no or invalid joint specified
|
||||
if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) {
|
||||
qWarning() << "Invalid joint index specified: " << jointIndex;
|
||||
}
|
||||
glm::quat jointSpaceRot = glm::inverse(jointRot) * worldRot;
|
||||
return jointSpaceRot;
|
||||
}
|
||||
|
||||
glm::vec3 Avatar::jointToWorldPoint(const glm::vec3& jointSpacePos, const int jointIndex) const {
|
||||
glm::vec3 jointPos = getWorldPosition();//default value if no or invalid joint specified
|
||||
glm::quat jointRot = getWorldOrientation();//default value if no or invalid joint specified
|
||||
|
||||
if (jointIndex != -1) {
|
||||
if (_skeletonModel->getJointPositionInWorldFrame(jointIndex, jointPos)) {
|
||||
_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot);
|
||||
} else {
|
||||
qWarning() << "Invalid joint index specified: " << jointIndex;
|
||||
}
|
||||
}
|
||||
|
||||
glm::vec3 worldOffset = jointRot * jointSpacePos;
|
||||
glm::vec3 worldPos = jointPos + worldOffset;
|
||||
|
||||
return worldPos;
|
||||
}
|
||||
|
||||
glm::vec3 Avatar::jointToWorldDirection(const glm::vec3& jointSpaceDir, const int jointIndex) const {
|
||||
glm::quat jointRot = getWorldOrientation();//default value if no or invalid joint specified
|
||||
if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) {
|
||||
qWarning() << "Invalid joint index specified: " << jointIndex;
|
||||
}
|
||||
glm::vec3 worldDir = jointRot * jointSpaceDir;
|
||||
return worldDir;
|
||||
}
|
||||
|
||||
glm::quat Avatar::jointToWorldRotation(const glm::quat& jointSpaceRot, const int jointIndex) const {
|
||||
glm::quat jointRot = getWorldOrientation();//default value if no or invalid joint specified
|
||||
if ((jointIndex != -1) && (!_skeletonModel->getJointRotationInWorldFrame(jointIndex, jointRot))) {
|
||||
qWarning() << "Invalid joint index specified: " << jointIndex;
|
||||
}
|
||||
glm::quat worldRot = jointRot * jointSpaceRot;
|
||||
return worldRot;
|
||||
}
|
||||
|
||||
|
||||
void Avatar::invalidateJointIndicesCache() const {
|
||||
QWriteLocker writeLock(&_modelJointIndicesCacheLock);
|
||||
_modelJointsCached = false;
|
||||
|
@ -1420,6 +1501,7 @@ void Avatar::setModelURLFinished(bool success) {
|
|||
// rig is ready
|
||||
void Avatar::rigReady() {
|
||||
buildUnscaledEyeHeightCache();
|
||||
computeMultiSphereShapes();
|
||||
}
|
||||
|
||||
// rig has been reset.
|
||||
|
@ -1427,6 +1509,48 @@ void Avatar::rigReset() {
|
|||
clearUnscaledEyeHeightCache();
|
||||
}
|
||||
|
||||
void Avatar::computeMultiSphereShapes() {
|
||||
const Rig& rig = getSkeletonModel()->getRig();
|
||||
glm::vec3 scale = extractScale(rig.getGeometryOffsetPose());
|
||||
const HFMModel& geometry = getSkeletonModel()->getHFMModel();
|
||||
int jointCount = rig.getJointStateCount();
|
||||
_multiSphereShapes.clear();
|
||||
_multiSphereShapes.reserve(jointCount);
|
||||
for (int i = 0; i < jointCount; i++) {
|
||||
const HFMJointShapeInfo& shapeInfo = geometry.joints[i].shapeInfo;
|
||||
std::vector<btVector3> btPoints;
|
||||
int lineCount = (int)shapeInfo.debugLines.size();
|
||||
btPoints.reserve(lineCount);
|
||||
for (int j = 0; j < lineCount; j++) {
|
||||
const glm::vec3 &point = shapeInfo.debugLines[j];
|
||||
auto rigPoint = scale * point;
|
||||
btVector3 btPoint = glmToBullet(rigPoint);
|
||||
btPoints.push_back(btPoint);
|
||||
}
|
||||
auto jointName = rig.nameOfJoint(i).toUpper();
|
||||
MultiSphereShape multiSphereShape;
|
||||
if (multiSphereShape.computeMultiSphereShape(i, jointName, btPoints)) {
|
||||
multiSphereShape.calculateDebugLines();
|
||||
multiSphereShape.setScale(_targetScale);
|
||||
}
|
||||
_multiSphereShapes.push_back(multiSphereShape);
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::updateFitBoundingBox() {
|
||||
_fitBoundingBox = AABox();
|
||||
if (getJointCount() == (int)_multiSphereShapes.size()) {
|
||||
for (int i = 0; i < getJointCount(); i++) {
|
||||
auto &shape = _multiSphereShapes[i];
|
||||
glm::vec3 jointPosition;
|
||||
glm::quat jointRotation;
|
||||
_skeletonModel->getJointPositionInWorldFrame(i, jointPosition);
|
||||
_skeletonModel->getJointRotationInWorldFrame(i, jointRotation);
|
||||
_fitBoundingBox += shape.updateBoundingBox(jointPosition, jointRotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create new model, can return an instance of a SoftAttachmentModel rather then Model
|
||||
static std::shared_ptr<Model> allocateAttachmentModel(bool isSoft, const Rig& rigOverride, bool isCauterized) {
|
||||
if (isSoft) {
|
||||
|
@ -1606,6 +1730,21 @@ void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) {
|
|||
shapeInfo.setOffset(offset);
|
||||
}
|
||||
|
||||
void Avatar::computeDetailedShapeInfo(ShapeInfo& shapeInfo, int jointIndex) {
|
||||
if (jointIndex > -1 && jointIndex < (int)_multiSphereShapes.size()) {
|
||||
auto& data = _multiSphereShapes[jointIndex].getSpheresData();
|
||||
std::vector<glm::vec3> positions;
|
||||
std::vector<btScalar> radiuses;
|
||||
positions.reserve(data.size());
|
||||
radiuses.reserve(data.size());
|
||||
for (auto& sphere : data) {
|
||||
positions.push_back(sphere._position);
|
||||
radiuses.push_back(sphere._radius);
|
||||
}
|
||||
shapeInfo.setMultiSphere(positions, radiuses);
|
||||
}
|
||||
}
|
||||
|
||||
void Avatar::getCapsule(glm::vec3& start, glm::vec3& end, float& radius) {
|
||||
ShapeInfo shapeInfo;
|
||||
computeShapeInfo(shapeInfo);
|
||||
|
@ -1929,3 +2068,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,12 +26,15 @@
|
|||
#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"
|
||||
#include "MultiSphereShape.h"
|
||||
|
||||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar);
|
||||
|
@ -222,12 +228,63 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE virtual glm::vec3 getAbsoluteDefaultJointTranslationInObjectFrame(int index) const;
|
||||
|
||||
|
||||
virtual glm::vec3 getAbsoluteJointScaleInObjectFrame(int index) const override;
|
||||
virtual glm::quat getAbsoluteJointRotationInObjectFrame(int index) const override;
|
||||
virtual glm::vec3 getAbsoluteJointTranslationInObjectFrame(int index) const override;
|
||||
virtual bool setAbsoluteJointRotationInObjectFrame(int index, const glm::quat& rotation) override { return false; }
|
||||
virtual bool setAbsoluteJointTranslationInObjectFrame(int index, const glm::vec3& translation) override { return false; }
|
||||
|
||||
// world-space to avatar-space rigconversion functions
|
||||
/**jsdoc
|
||||
* @function MyAvatar.worldToJointPoint
|
||||
* @param {Vec3} position
|
||||
* @param {number} [jointIndex=-1]
|
||||
* @returns {Vec3}
|
||||
*/
|
||||
Q_INVOKABLE glm::vec3 worldToJointPoint(const glm::vec3& position, const int jointIndex = -1) const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.worldToJointDirection
|
||||
* @param {Vec3} direction
|
||||
* @param {number} [jointIndex=-1]
|
||||
* @returns {Vec3}
|
||||
*/
|
||||
Q_INVOKABLE glm::vec3 worldToJointDirection(const glm::vec3& direction, const int jointIndex = -1) const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.worldToJointRotation
|
||||
* @param {Quat} rotation
|
||||
* @param {number} [jointIndex=-1]
|
||||
* @returns {Quat}
|
||||
*/
|
||||
Q_INVOKABLE glm::quat worldToJointRotation(const glm::quat& rotation, const int jointIndex = -1) const;
|
||||
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.jointToWorldPoint
|
||||
* @param {vec3} position
|
||||
* @param {number} [jointIndex=-1]
|
||||
* @returns {Vec3}
|
||||
*/
|
||||
Q_INVOKABLE glm::vec3 jointToWorldPoint(const glm::vec3& position, const int jointIndex = -1) const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.jointToWorldDirection
|
||||
* @param {Vec3} direction
|
||||
* @param {number} [jointIndex=-1]
|
||||
* @returns {Vec3}
|
||||
*/
|
||||
Q_INVOKABLE glm::vec3 jointToWorldDirection(const glm::vec3& direction, const int jointIndex = -1) const;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.jointToWorldRotation
|
||||
* @param {Quat} rotation
|
||||
* @param {number} [jointIndex=-1]
|
||||
* @returns {Quat}
|
||||
*/
|
||||
Q_INVOKABLE glm::quat jointToWorldRotation(const glm::quat& rotation, const int jointIndex = -1) const;
|
||||
|
||||
virtual void setSkeletonModelURL(const QUrl& skeletonModelURL) override;
|
||||
virtual void setAttachmentData(const QVector<AttachmentData>& attachmentData) override;
|
||||
|
||||
|
@ -315,6 +372,8 @@ public:
|
|||
virtual void rebuildCollisionShape() = 0;
|
||||
|
||||
virtual void computeShapeInfo(ShapeInfo& shapeInfo);
|
||||
virtual void computeDetailedShapeInfo(ShapeInfo& shapeInfo, int jointIndex);
|
||||
|
||||
void getCapsule(glm::vec3& start, glm::vec3& end, float& radius);
|
||||
float computeMass();
|
||||
/**jsdoc
|
||||
|
@ -393,6 +452,7 @@ public:
|
|||
|
||||
float getBoundingRadius() const;
|
||||
AABox getRenderBounds() const; // THis call is accessible from rendering thread only to report the bounding box of the avatar during the frame.
|
||||
AABox getFitBounds() const { return _fitBoundingBox; }
|
||||
|
||||
void addToScene(AvatarSharedPointer self, const render::ScenePointer& scene);
|
||||
void ensureInScene(AvatarSharedPointer self, const render::ScenePointer& scene);
|
||||
|
@ -436,6 +496,9 @@ public:
|
|||
|
||||
void accumulateGrabPositions(std::map<QUuid, GrabLocationAccumulator>& grabAccumulators);
|
||||
|
||||
const std::vector<MultiSphereShape>& getMultiSphereShapes() const { return _multiSphereShapes; }
|
||||
void tearDownGrabs();
|
||||
|
||||
signals:
|
||||
void targetScaleChanged(float targetScale);
|
||||
|
||||
|
@ -503,6 +566,8 @@ protected:
|
|||
virtual const QString& getSessionDisplayNameForTransport() const override { return _empty; } // Save a tiny bit of bandwidth. Mixer won't look at what we send.
|
||||
QString _empty{};
|
||||
virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) override { _sessionDisplayName = sessionDisplayName; } // don't use no-op setter!
|
||||
void computeMultiSphereShapes();
|
||||
void updateFitBoundingBox();
|
||||
|
||||
SkeletonModelPointer _skeletonModel;
|
||||
|
||||
|
@ -538,7 +603,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 +690,18 @@ protected:
|
|||
|
||||
static void metaBlendshapeOperator(render::ItemID renderItemID, int blendshapeNumber, const QVector<BlendshapeOffset>& blendshapeOffsets,
|
||||
const QVector<int>& blendedMeshSizes, const render::ItemIDs& subItemIDs);
|
||||
|
||||
std::vector<MultiSphereShape> _multiSphereShapes;
|
||||
AABox _fitBoundingBox;
|
||||
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
|
||||
|
|
|
@ -632,9 +632,11 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
|
||||
// include jointData if there is room for the most minimal section. i.e. no translations or rotations.
|
||||
IF_AVATAR_SPACE(PACKET_HAS_JOINT_DATA, AvatarDataPacket::minJointDataSize(numJoints)) {
|
||||
// Allow for faux joints + translation bit-vector:
|
||||
const ptrdiff_t minSizeForJoint = sizeof(AvatarDataPacket::SixByteQuat)
|
||||
+ jointBitVectorSize + AvatarDataPacket::FAUX_JOINTS_SIZE;
|
||||
// Minimum space required for another rotation joint -
|
||||
// size of joint + following translation bit-vector + translation scale + faux joints:
|
||||
const ptrdiff_t minSizeForJoint = sizeof(AvatarDataPacket::SixByteQuat) + jointBitVectorSize +
|
||||
sizeof(float) + AvatarDataPacket::FAUX_JOINTS_SIZE;
|
||||
|
||||
auto startSection = destinationBuffer;
|
||||
|
||||
// compute maxTranslationDimension before we send any joint data.
|
||||
|
@ -724,6 +726,7 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent
|
|||
const JointData& data = joints[i];
|
||||
const JointData& last = lastSentJointData[i];
|
||||
|
||||
// Note minSizeForJoint is conservative since there isn't a following bit-vector + scale.
|
||||
if (packetEnd - destinationBuffer >= minSizeForJoint) {
|
||||
if (!data.translationIsDefaultPose) {
|
||||
if (sendAll || last.translationIsDefaultPose || (!cullSmallChanges && last.translation != data.translation)
|
||||
|
@ -2900,6 +2903,7 @@ QScriptValue RayToAvatarIntersectionResultToScriptValue(QScriptEngine* engine, c
|
|||
obj.setProperty("intersection", intersection);
|
||||
QScriptValue surfaceNormal = vec3ToScriptValue(engine, value.surfaceNormal);
|
||||
obj.setProperty("surfaceNormal", surfaceNormal);
|
||||
obj.setProperty("jointIndex", value.jointIndex);
|
||||
obj.setProperty("extraInfo", engine->toScriptValue(value.extraInfo));
|
||||
return obj;
|
||||
}
|
||||
|
@ -2919,6 +2923,7 @@ void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, Ra
|
|||
if (surfaceNormal.isValid()) {
|
||||
vec3FromScriptValue(surfaceNormal, value.surfaceNormal);
|
||||
}
|
||||
value.jointIndex = object.property("jointIndex").toInt32();
|
||||
value.extraInfo = object.property("extraInfo").toVariant().toMap();
|
||||
}
|
||||
|
||||
|
@ -3010,7 +3015,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);
|
||||
|
@ -1620,6 +1615,7 @@ public:
|
|||
BoxFace face;
|
||||
glm::vec3 intersection;
|
||||
glm::vec3 surfaceNormal;
|
||||
int jointIndex { -1 };
|
||||
QVariantMap extraInfo;
|
||||
};
|
||||
Q_DECLARE_METATYPE(RayToAvatarIntersectionResult)
|
||||
|
|
|
@ -15,8 +15,10 @@
|
|||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QFileInfo>
|
||||
|
||||
#include <QtGui/QImage>
|
||||
#include <QtGui/QImageWriter>
|
||||
#include <QtGui/QOpenGLFramebufferObject>
|
||||
|
||||
#include <NumericalConstants.h>
|
||||
|
@ -30,6 +32,7 @@
|
|||
#include <gl/OffscreenGLCanvas.h>
|
||||
|
||||
#include <gpu/Texture.h>
|
||||
#include <gpu/FrameIO.h>
|
||||
#include <shaders/Shaders.h>
|
||||
#include <gpu/gl/GLShared.h>
|
||||
#include <gpu/gl/GLBackend.h>
|
||||
|
@ -465,11 +468,43 @@ void OpenGLDisplayPlugin::submitFrame(const gpu::FramePointer& newFrame) {
|
|||
});
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor) {
|
||||
renderFromTexture(batch, texture, viewport, scissor, gpu::FramebufferPointer());
|
||||
void OpenGLDisplayPlugin::captureFrame(const std::string& filename) const {
|
||||
withOtherThreadContext([&] {
|
||||
using namespace gpu;
|
||||
auto glBackend = const_cast<OpenGLDisplayPlugin&>(*this).getGLBackend();
|
||||
FramebufferPointer framebuffer{ Framebuffer::create("captureFramebuffer") };
|
||||
TextureCapturer captureLambda = [&](const std::string& filename, const gpu::TexturePointer& texture, uint16 layer) {
|
||||
QImage image;
|
||||
if (texture->getUsageType() == TextureUsageType::STRICT_RESOURCE) {
|
||||
image = QImage{ 1, 1, QImage::Format_ARGB32 };
|
||||
auto storedImage = texture->accessStoredMipFace(0, 0);
|
||||
memcpy(image.bits(), storedImage->data(), image.sizeInBytes());
|
||||
//if (texture == textureCache->getWhiteTexture()) {
|
||||
//} else if (texture == textureCache->getBlackTexture()) {
|
||||
//} else if (texture == textureCache->getBlueTexture()) {
|
||||
//} else if (texture == textureCache->getGrayTexture()) {
|
||||
} else {
|
||||
ivec4 rect = { 0, 0, texture->getWidth(), texture->getHeight() };
|
||||
framebuffer->setRenderBuffer(0, texture, layer);
|
||||
glBackend->syncGPUObject(*framebuffer);
|
||||
|
||||
image = QImage{ rect.z, rect.w, QImage::Format_ARGB32 };
|
||||
glBackend->downloadFramebuffer(framebuffer, rect, image);
|
||||
}
|
||||
QImageWriter(filename.c_str()).write(image);
|
||||
};
|
||||
|
||||
if (_currentFrame) {
|
||||
gpu::writeFrame(filename, _currentFrame, captureLambda);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor, gpu::FramebufferPointer copyFbo /*=gpu::FramebufferPointer()*/) {
|
||||
void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor) {
|
||||
renderFromTexture(batch, texture, viewport, scissor, nullptr);
|
||||
}
|
||||
|
||||
void OpenGLDisplayPlugin::renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer& texture, const glm::ivec4& viewport, const glm::ivec4& scissor, const gpu::FramebufferPointer& copyFbo /*=gpu::FramebufferPointer()*/) {
|
||||
auto fbo = gpu::FramebufferPointer();
|
||||
batch.enableStereo(false);
|
||||
batch.resetViewTransform();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -14,20 +14,20 @@
|
|||
|
||||
#include <ObjectMotionState.h>
|
||||
|
||||
#include "RenderableLightEntityItem.h"
|
||||
#include "RenderableLineEntityItem.h"
|
||||
#include "RenderableModelEntityItem.h"
|
||||
#include "RenderableParticleEffectEntityItem.h"
|
||||
#include "RenderablePolyVoxEntityItem.h"
|
||||
#include "RenderablePolyLineEntityItem.h"
|
||||
#include "RenderableShapeEntityItem.h"
|
||||
#include "RenderableModelEntityItem.h"
|
||||
#include "RenderableTextEntityItem.h"
|
||||
#include "RenderableImageEntityItem.h"
|
||||
#include "RenderableWebEntityItem.h"
|
||||
#include "RenderableParticleEffectEntityItem.h"
|
||||
#include "RenderableLineEntityItem.h"
|
||||
#include "RenderablePolyLineEntityItem.h"
|
||||
#include "RenderablePolyVoxEntityItem.h"
|
||||
#include "RenderableGridEntityItem.h"
|
||||
#include "RenderableGizmoEntityItem.h"
|
||||
#include "RenderableLightEntityItem.h"
|
||||
#include "RenderableZoneEntityItem.h"
|
||||
#include "RenderableMaterialEntityItem.h"
|
||||
#include "RenderableImageEntityItem.h"
|
||||
#include "RenderableGridEntityItem.h"
|
||||
|
||||
|
||||
using namespace render;
|
||||
using namespace render::entities;
|
||||
|
@ -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();
|
||||
|
@ -284,6 +284,10 @@ EntityRenderer::Pointer EntityRenderer::addToScene(EntityTreeRenderer& renderer,
|
|||
result = make_renderer<GridEntityRenderer>(entity);
|
||||
break;
|
||||
|
||||
case Type::Gizmo:
|
||||
result = make_renderer<GizmoEntityRenderer>(entity);
|
||||
break;
|
||||
|
||||
case Type::Light:
|
||||
result = make_renderer<LightEntityRenderer>(entity);
|
||||
break;
|
||||
|
@ -468,3 +472,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
|
||||
|
|
289
libraries/entities-renderer/src/RenderableGizmoEntityItem.cpp
Normal file
289
libraries/entities-renderer/src/RenderableGizmoEntityItem.cpp
Normal file
|
@ -0,0 +1,289 @@
|
|||
//
|
||||
// Created by Sam Gondelman on 1/22/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 "RenderableGizmoEntityItem.h"
|
||||
|
||||
#include <DependencyManager.h>
|
||||
#include <GeometryCache.h>
|
||||
|
||||
using namespace render;
|
||||
using namespace render::entities;
|
||||
|
||||
GizmoEntityRenderer::GizmoEntityRenderer(const EntityItemPointer& entity) : Parent(entity)
|
||||
{
|
||||
}
|
||||
|
||||
GizmoEntityRenderer::~GizmoEntityRenderer() {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
if (geometryCache) {
|
||||
if (_ringGeometryID) {
|
||||
geometryCache->releaseID(_ringGeometryID);
|
||||
}
|
||||
if (_majorTicksGeometryID) {
|
||||
geometryCache->releaseID(_majorTicksGeometryID);
|
||||
}
|
||||
if (_minorTicksGeometryID) {
|
||||
geometryCache->releaseID(_minorTicksGeometryID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GizmoEntityRenderer::isTransparent() const {
|
||||
bool ringTransparent = _gizmoType == GizmoType::RING && (_ringProperties.getInnerStartAlpha() < 1.0f ||
|
||||
_ringProperties.getInnerEndAlpha() < 1.0f || _ringProperties.getOuterStartAlpha() < 1.0f ||
|
||||
_ringProperties.getOuterEndAlpha() < 1.0f);
|
||||
|
||||
return Parent::isTransparent() || ringTransparent;
|
||||
}
|
||||
|
||||
bool GizmoEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
|
||||
bool needsUpdate = resultWithReadLock<bool>([&] {
|
||||
if (_gizmoType != entity->getGizmoType()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_ringProperties != entity->getRingProperties()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
return needsUpdate;
|
||||
}
|
||||
|
||||
void GizmoEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) {
|
||||
bool dirty = false;
|
||||
RingGizmoPropertyGroup ringProperties = entity->getRingProperties();
|
||||
withWriteLock([&] {
|
||||
_gizmoType = entity->getGizmoType();
|
||||
if (_ringProperties != ringProperties) {
|
||||
_ringProperties = ringProperties;
|
||||
dirty = true;
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
if (dirty || _prevPrimitiveMode != _primitiveMode || !_ringGeometryID || !_majorTicksGeometryID || !_minorTicksGeometryID) {
|
||||
_prevPrimitiveMode = _primitiveMode;
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
if (!_ringGeometryID) {
|
||||
_ringGeometryID = geometryCache->allocateID();
|
||||
}
|
||||
|
||||
const float FULL_CIRCLE = 360.0f;
|
||||
const float SLICES = 180.0f;
|
||||
const float SLICE_ANGLE_RADIANS = glm::radians(FULL_CIRCLE / SLICES);
|
||||
if (_primitiveMode == PrimitiveMode::SOLID) {
|
||||
|
||||
QVector<glm::vec3> points;
|
||||
QVector<glm::vec4> colors;
|
||||
|
||||
vec4 innerStartColor = vec4(toGlm(ringProperties.getInnerStartColor()), ringProperties.getInnerStartAlpha());
|
||||
vec4 outerStartColor = vec4(toGlm(ringProperties.getOuterStartColor()), ringProperties.getOuterStartAlpha());
|
||||
vec4 innerEndColor = vec4(toGlm(ringProperties.getInnerEndColor()), ringProperties.getInnerEndAlpha());
|
||||
vec4 outerEndColor = vec4(toGlm(ringProperties.getOuterEndColor()), ringProperties.getOuterEndAlpha());
|
||||
float startAtRadians = glm::radians(ringProperties.getStartAngle());
|
||||
float endAtRadians = glm::radians(ringProperties.getEndAngle());
|
||||
|
||||
const auto totalRange = endAtRadians - startAtRadians;
|
||||
if (ringProperties.getInnerRadius() <= 0) {
|
||||
_solidPrimitive = gpu::TRIANGLE_FAN;
|
||||
points << vec3();
|
||||
colors << innerStartColor;
|
||||
for (float angleRadians = startAtRadians; angleRadians < endAtRadians; angleRadians += SLICE_ANGLE_RADIANS) {
|
||||
float range = (angleRadians - startAtRadians) / totalRange;
|
||||
points << 0.5f * glm::vec3(cosf(angleRadians), 0.0f, sinf(angleRadians));
|
||||
colors << glm::mix(outerStartColor, outerEndColor, range);
|
||||
}
|
||||
points << 0.5f * glm::vec3(cosf(endAtRadians), 0.0f, sinf(endAtRadians));
|
||||
colors << outerEndColor;
|
||||
} else {
|
||||
_solidPrimitive = gpu::TRIANGLE_STRIP;
|
||||
for (float angleRadians = startAtRadians; angleRadians < endAtRadians; angleRadians += SLICE_ANGLE_RADIANS) {
|
||||
float range = (angleRadians - startAtRadians) / totalRange;
|
||||
|
||||
points << 0.5f * glm::vec3(cosf(angleRadians) * ringProperties.getInnerRadius(), 0.0f, sinf(angleRadians) * ringProperties.getInnerRadius());
|
||||
colors << glm::mix(innerStartColor, innerEndColor, range);
|
||||
|
||||
points << 0.5f * glm::vec3(cosf(angleRadians), 0.0f, sinf(angleRadians));
|
||||
colors << glm::mix(outerStartColor, outerEndColor, range);
|
||||
}
|
||||
points << 0.5f * glm::vec3(cosf(endAtRadians) * ringProperties.getInnerRadius(), 0.0f, sinf(endAtRadians) * ringProperties.getInnerRadius());
|
||||
colors << innerEndColor;
|
||||
|
||||
points << 0.5f * glm::vec3(cosf(endAtRadians), 0.0f, sinf(endAtRadians));
|
||||
colors << outerEndColor;
|
||||
}
|
||||
geometryCache->updateVertices(_ringGeometryID, points, colors);
|
||||
} else {
|
||||
_solidPrimitive = gpu::LINE_STRIP;
|
||||
QVector<glm::vec3> points;
|
||||
|
||||
float startAtRadians = glm::radians(ringProperties.getStartAngle());
|
||||
float endAtRadians = glm::radians(ringProperties.getEndAngle());
|
||||
|
||||
float angleRadians = startAtRadians;
|
||||
glm::vec3 firstPoint = 0.5f * glm::vec3(cosf(angleRadians), 0.0f, sinf(angleRadians));
|
||||
points << firstPoint;
|
||||
|
||||
while (angleRadians < endAtRadians) {
|
||||
angleRadians += SLICE_ANGLE_RADIANS;
|
||||
glm::vec3 thisPoint = 0.5f * glm::vec3(cosf(angleRadians), 0.0f, sinf(angleRadians));
|
||||
points << thisPoint;
|
||||
}
|
||||
|
||||
// get the last slice portion....
|
||||
angleRadians = endAtRadians;
|
||||
glm::vec3 lastPoint = 0.5f * glm::vec3(cosf(angleRadians), 0.0f, sinf(angleRadians));
|
||||
points << lastPoint;
|
||||
geometryCache->updateVertices(_ringGeometryID, points, glm::vec4(toGlm(ringProperties.getOuterStartColor()), ringProperties.getOuterStartAlpha()));
|
||||
}
|
||||
|
||||
if (ringProperties.getHasTickMarks()) {
|
||||
if (ringProperties.getMajorTickMarksAngle() > 0.0f && ringProperties.getMajorTickMarksLength() != 0.0f) {
|
||||
QVector<glm::vec3> points;
|
||||
if (!_majorTicksGeometryID) {
|
||||
_majorTicksGeometryID = geometryCache->allocateID();
|
||||
}
|
||||
|
||||
float startAngle = ringProperties.getStartAngle();
|
||||
float tickMarkAngle = ringProperties.getMajorTickMarksAngle();
|
||||
float angle = startAngle - fmodf(startAngle, tickMarkAngle) + tickMarkAngle;
|
||||
|
||||
float tickMarkLength = 0.5f * ringProperties.getMajorTickMarksLength();
|
||||
float startRadius = (tickMarkLength > 0.0f) ? 0.5f * ringProperties.getInnerRadius() : 0.5f;
|
||||
float endRadius = startRadius + tickMarkLength;
|
||||
|
||||
while (angle <= ringProperties.getEndAngle()) {
|
||||
float angleInRadians = glm::radians(angle);
|
||||
|
||||
glm::vec3 thisPointA = startRadius * glm::vec3(cosf(angleInRadians), 0.0f, sinf(angleInRadians));
|
||||
glm::vec3 thisPointB = endRadius * glm::vec3(cosf(angleInRadians), 0.0f, sinf(angleInRadians));
|
||||
|
||||
points << thisPointA << thisPointB;
|
||||
|
||||
angle += tickMarkAngle;
|
||||
}
|
||||
|
||||
glm::vec4 color(toGlm(ringProperties.getMajorTickMarksColor()), 1.0f); // TODO: alpha
|
||||
geometryCache->updateVertices(_majorTicksGeometryID, points, color);
|
||||
}
|
||||
if (ringProperties.getMinorTickMarksAngle() > 0.0f && ringProperties.getMinorTickMarksLength() != 0.0f) {
|
||||
QVector<glm::vec3> points;
|
||||
if (!_minorTicksGeometryID) {
|
||||
_minorTicksGeometryID = geometryCache->allocateID();
|
||||
}
|
||||
|
||||
float startAngle = ringProperties.getStartAngle();
|
||||
float tickMarkAngle = ringProperties.getMinorTickMarksAngle();
|
||||
float angle = startAngle - fmodf(startAngle, tickMarkAngle) + tickMarkAngle;
|
||||
|
||||
float tickMarkLength = 0.5f * ringProperties.getMinorTickMarksLength();
|
||||
float startRadius = (tickMarkLength > 0.0f) ? 0.5f * ringProperties.getInnerRadius() : 0.5f;
|
||||
float endRadius = startRadius + tickMarkLength;
|
||||
|
||||
while (angle <= ringProperties.getEndAngle()) {
|
||||
float angleInRadians = glm::radians(angle);
|
||||
|
||||
glm::vec3 thisPointA = startRadius * glm::vec3(cosf(angleInRadians), 0.0f, sinf(angleInRadians));
|
||||
glm::vec3 thisPointB = endRadius * glm::vec3(cosf(angleInRadians), 0.0f, sinf(angleInRadians));
|
||||
|
||||
points << thisPointA << thisPointB;
|
||||
|
||||
angle += tickMarkAngle;
|
||||
}
|
||||
|
||||
glm::vec4 color(toGlm(ringProperties.getMinorTickMarksColor()), 1.0f); // TODO: alpha
|
||||
geometryCache->updateVertices(_minorTicksGeometryID, points, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void* key = (void*)this;
|
||||
AbstractViewStateInterface::instance()->pushPostUpdateLambda(key, [this, entity]() {
|
||||
withWriteLock([&] {
|
||||
updateModelTransformAndBound();
|
||||
_renderTransform = getModelTransform();
|
||||
_renderTransform.postScale(entity->getScaledDimensions());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Item::Bound GizmoEntityRenderer::getBound() {
|
||||
auto bound = Parent::getBound();
|
||||
if (_ringProperties.getHasTickMarks()) {
|
||||
glm::vec3 scale = bound.getScale();
|
||||
for (int i = 0; i < 3; i += 2) {
|
||||
if (_ringProperties.getMajorTickMarksLength() + _ringProperties.getInnerRadius() > 1.0f) {
|
||||
scale[i] *= _ringProperties.getMajorTickMarksLength() + _ringProperties.getInnerRadius();
|
||||
} else if (_ringProperties.getMajorTickMarksLength() < -2.0f) {
|
||||
scale[i] *= -_ringProperties.getMajorTickMarksLength() - 1.0f;
|
||||
}
|
||||
|
||||
if (_ringProperties.getMinorTickMarksLength() + _ringProperties.getInnerRadius() > 1.0f) {
|
||||
scale[i] *= _ringProperties.getMinorTickMarksLength() + _ringProperties.getInnerRadius();
|
||||
} else if (_ringProperties.getMinorTickMarksLength() < -2.0f) {
|
||||
scale[i] *= -_ringProperties.getMinorTickMarksLength() - 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
bound.setScaleStayCentered(scale);
|
||||
return bound;
|
||||
}
|
||||
return bound;
|
||||
}
|
||||
|
||||
ShapeKey GizmoEntityRenderer::getShapeKey() {
|
||||
auto builder = render::ShapeKey::Builder().withoutCullFace();
|
||||
if (isTransparent()) {
|
||||
builder.withTranslucent();
|
||||
}
|
||||
if (_primitiveMode == PrimitiveMode::LINES) {
|
||||
builder.withUnlit().withDepthBias();
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
void GizmoEntityRenderer::doRender(RenderArgs* args) {
|
||||
PerformanceTimer perfTimer("RenderableGizmoEntityItem::render");
|
||||
Q_ASSERT(args->_batch);
|
||||
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
|
||||
if (_gizmoType == GizmoType::RING) {
|
||||
Transform transform;
|
||||
bool hasTickMarks;
|
||||
glm::vec4 tickProperties;
|
||||
withReadLock([&] {
|
||||
transform = _renderTransform;
|
||||
hasTickMarks = _ringProperties.getHasTickMarks();
|
||||
tickProperties = glm::vec4(_ringProperties.getMajorTickMarksAngle(), _ringProperties.getMajorTickMarksLength(),
|
||||
_ringProperties.getMinorTickMarksAngle(), _ringProperties.getMinorTickMarksLength());
|
||||
});
|
||||
|
||||
bool wireframe = render::ShapeKey(args->_globalShapeKey).isWireframe() || _primitiveMode == PrimitiveMode::LINES;
|
||||
geometryCache->bindSimpleProgram(batch, false, isTransparent(), false, wireframe, true, true, _renderLayer != RenderLayer::WORLD);
|
||||
|
||||
batch.setModelTransform(transform);
|
||||
|
||||
// Background circle
|
||||
geometryCache->renderVertices(batch, wireframe ? gpu::LINE_STRIP : _solidPrimitive, _ringGeometryID);
|
||||
|
||||
// Ticks
|
||||
if (hasTickMarks) {
|
||||
if (tickProperties.x > 0.0f && tickProperties.y != 0.0f) {
|
||||
geometryCache->renderVertices(batch, gpu::LINES, _majorTicksGeometryID);
|
||||
}
|
||||
if (tickProperties.z > 0.0f && tickProperties.w != 0.0f) {
|
||||
geometryCache->renderVertices(batch, gpu::LINES, _minorTicksGeometryID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
48
libraries/entities-renderer/src/RenderableGizmoEntityItem.h
Normal file
48
libraries/entities-renderer/src/RenderableGizmoEntityItem.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
//
|
||||
// Created by Sam Gondelman on 1/22/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_RenderableGizmoEntityItem_h
|
||||
#define hifi_RenderableGizmoEntityItem_h
|
||||
|
||||
#include "RenderableEntityItem.h"
|
||||
|
||||
#include <GizmoEntityItem.h>
|
||||
|
||||
namespace render { namespace entities {
|
||||
|
||||
class GizmoEntityRenderer : public TypedEntityRenderer<GizmoEntityItem> {
|
||||
using Parent = TypedEntityRenderer<GizmoEntityItem>;
|
||||
using Pointer = std::shared_ptr<GizmoEntityRenderer>;
|
||||
public:
|
||||
GizmoEntityRenderer(const EntityItemPointer& entity);
|
||||
~GizmoEntityRenderer();
|
||||
|
||||
protected:
|
||||
Item::Bound getBound() override;
|
||||
ShapeKey getShapeKey() override;
|
||||
|
||||
bool isTransparent() const override;
|
||||
|
||||
private:
|
||||
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
|
||||
virtual void doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) override;
|
||||
virtual void doRender(RenderArgs* args) override;
|
||||
|
||||
GizmoType _gizmoType;
|
||||
RingGizmoPropertyGroup _ringProperties;
|
||||
PrimitiveMode _prevPrimitiveMode;
|
||||
|
||||
int _ringGeometryID { 0 };
|
||||
int _majorTicksGeometryID { 0 };
|
||||
int _minorTicksGeometryID { 0 };
|
||||
gpu::Primitive _solidPrimitive { gpu::TRIANGLE_FAN };
|
||||
|
||||
};
|
||||
|
||||
} }
|
||||
#endif // hifi_RenderableGizmoEntityItem_h
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -53,7 +53,7 @@ bool ShapeEntityRenderer::needsRenderUpdate() const {
|
|||
}
|
||||
|
||||
auto mat = _materials.find("0");
|
||||
if (mat != _materials.end() && mat->second.needsUpdate()) {
|
||||
if (mat != _materials.end() && (mat->second.needsUpdate() || mat->second.areTexturesLoading())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -179,7 +188,7 @@ bool ShapeEntityRenderer::useMaterialPipeline(const graphics::MultiMaterial& mat
|
|||
|
||||
ShapeKey ShapeEntityRenderer::getShapeKey() {
|
||||
auto mat = _materials.find("0");
|
||||
if (mat != _materials.end() && mat->second.needsUpdate()) {
|
||||
if (mat != _materials.end() && (mat->second.needsUpdate() || mat->second.areTexturesLoading())) {
|
||||
RenderPipelines::updateMultiMaterial(mat->second);
|
||||
}
|
||||
|
||||
|
@ -247,7 +256,8 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
|
|||
batch.setModelTransform(_renderTransform); // use a transform with scale, rotation, registration point and translation
|
||||
materials = _materials["0"];
|
||||
auto& schema = materials.getSchemaBuffer().get<graphics::MultiMaterial::Schema>();
|
||||
outColor = glm::vec4(schema._albedo, schema._opacity);
|
||||
outColor = glm::vec4(ColorUtils::tosRGBVec3(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;
|
||||
|
@ -299,7 +309,7 @@ scriptable::ScriptableModelBase ShapeEntityRenderer::getScriptableModel() {
|
|||
result.appendMaterials(_materials);
|
||||
auto materials = _materials.find("0");
|
||||
if (materials != _materials.end()) {
|
||||
vertexColor = materials->second.getSchemaBuffer().get<graphics::MultiMaterial::Schema>()._albedo;
|
||||
vertexColor = ColorUtils::tosRGBVec3(materials->second.getSchemaBuffer().get<graphics::MultiMaterial::Schema>()._albedo);
|
||||
}
|
||||
}
|
||||
if (auto mesh = geometryCache->meshFromShape(geometryShape, vertexColor)) {
|
||||
|
|
|
@ -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,8 @@ BloomPropertyGroup EntityItemProperties::_staticBloom;
|
|||
KeyLightPropertyGroup EntityItemProperties::_staticKeyLight;
|
||||
AmbientLightPropertyGroup EntityItemProperties::_staticAmbientLight;
|
||||
GrabPropertyGroup EntityItemProperties::_staticGrab;
|
||||
PulsePropertyGroup EntityItemProperties::_staticPulse;
|
||||
RingGizmoPropertyGroup EntityItemProperties::_staticRing;
|
||||
|
||||
EntityPropertyList PROP_LAST_ITEM = (EntityPropertyList)(PROP_AFTER_LAST_ITEM - 1);
|
||||
|
||||
|
@ -430,6 +432,31 @@ void EntityItemProperties::setInputModeFromString(const QString& webInputMode) {
|
|||
}
|
||||
}
|
||||
|
||||
QHash<QString, GizmoType> stringToGizmoTypeLookup;
|
||||
|
||||
void addGizmoType(GizmoType mode) {
|
||||
stringToGizmoTypeLookup[GizmoTypeHelpers::getNameForGizmoType(mode)] = mode;
|
||||
}
|
||||
|
||||
void buildStringToGizmoTypeLookup() {
|
||||
addGizmoType(GizmoType::RING);
|
||||
}
|
||||
|
||||
QString EntityItemProperties::getGizmoTypeAsString() const {
|
||||
return GizmoTypeHelpers::getNameForGizmoType(_gizmoType);
|
||||
}
|
||||
|
||||
void EntityItemProperties::setGizmoTypeFromString(const QString& gizmoType) {
|
||||
if (stringToGizmoTypeLookup.empty()) {
|
||||
buildStringToGizmoTypeLookup();
|
||||
}
|
||||
auto gizmoTypeItr = stringToGizmoTypeLookup.find(gizmoType.toLower());
|
||||
if (gizmoTypeItr != stringToGizmoTypeLookup.end()) {
|
||||
_gizmoType = gizmoTypeItr.value();
|
||||
_gizmoTypeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
||||
EntityPropertyFlags changedProperties;
|
||||
|
||||
|
@ -514,6 +541,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
|
||||
|
@ -649,6 +677,10 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_MAJOR_GRID_EVERY, majorGridEvery);
|
||||
CHECK_PROPERTY_CHANGE(PROP_MINOR_GRID_EVERY, minorGridEvery);
|
||||
|
||||
// Gizmo
|
||||
CHECK_PROPERTY_CHANGE(PROP_GIZMO_TYPE, gizmoType);
|
||||
changedProperties += _ring.getChangedProperties();
|
||||
|
||||
return changedProperties;
|
||||
}
|
||||
|
||||
|
@ -842,6 +874,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* @see {@link Entities.EntityProperties-PolyLine|EntityProperties-PolyLine}
|
||||
* @see {@link Entities.EntityProperties-PolyVox|EntityProperties-PolyVox}
|
||||
* @see {@link Entities.EntityProperties-Grid|EntityProperties-Grid}
|
||||
* @see {@link Entities.EntityProperties-Gizmo|EntityProperties-Gizmo}
|
||||
* @see {@link Entities.EntityProperties-Light|EntityProperties-Light}
|
||||
* @see {@link Entities.EntityProperties-Zone|EntityProperties-Zone}
|
||||
* @see {@link Entities.EntityProperties-Material|EntityProperties-Material}
|
||||
|
@ -1115,6 +1148,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 +1276,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 +1317,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 +1347,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 +1457,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 +1481,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",
|
||||
|
@ -1454,6 +1494,14 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* });
|
||||
*/
|
||||
|
||||
/**jsdoc
|
||||
* The <code>"Gizmo"</code> {@link Entities.EntityType|EntityType} displays an entity that could be used as UI.
|
||||
* It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}.
|
||||
* @typedef {object} Entities.EntityProperties-Gizmo
|
||||
* @property {GizmoType} gizmoType="ring" - The gizmo type of the entity.
|
||||
* @property {Entities.RingGizmo} ring - The ring gizmo properties.
|
||||
*/
|
||||
|
||||
QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool skipDefaults, bool allowUnknownCreateTime,
|
||||
bool strictSemantics, EntityPsuedoPropertyFlags psueudoPropertyFlags) const {
|
||||
|
||||
|
@ -1579,6 +1627,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 +1701,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,11 +1717,13 @@ 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);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_TEXT_COLOR, textColor, u8vec3Color);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXT_ALPHA, textAlpha);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_TYPED(PROP_BACKGROUND_COLOR, backgroundColor, getBackgroundColor(), u8vec3Color);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_TYPED(PROP_BACKGROUND_COLOR, backgroundColor, u8vec3Color);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_BACKGROUND_ALPHA, backgroundAlpha);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BILLBOARD_MODE, billboardMode, getBillboardModeAsString());
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LEFT_MARGIN, leftMargin);
|
||||
|
@ -1708,6 +1760,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 +1825,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,12 +1846,19 @@ 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);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MINOR_GRID_EVERY, minorGridEvery);
|
||||
}
|
||||
|
||||
// Gizmo only
|
||||
if (_type == EntityTypes::Gizmo) {
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_GIZMO_TYPE, gizmoType, getGizmoTypeAsString());
|
||||
_ring.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties);
|
||||
}
|
||||
|
||||
/**jsdoc
|
||||
* The axis-aligned bounding box of an entity.
|
||||
* @typedef {object} Entities.BoundingBox
|
||||
|
@ -1972,6 +2033,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
|
||||
|
@ -2107,6 +2169,10 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
|||
COPY_PROPERTY_FROM_QSCRIPTVALUE(majorGridEvery, uint32_t, setMajorGridEvery);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(minorGridEvery, float, setMinorGridEvery);
|
||||
|
||||
// Gizmo
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(gizmoType, GizmoType);
|
||||
_ring.copyFromScriptValue(object, _defaultSettings);
|
||||
|
||||
// Handle conversions from old 'textures' property to "imageURL"
|
||||
{
|
||||
QScriptValue V = object.property("textures");
|
||||
|
@ -2245,6 +2311,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
|
||||
|
@ -2380,6 +2447,10 @@ void EntityItemProperties::merge(const EntityItemProperties& other) {
|
|||
COPY_PROPERTY_IF_CHANGED(majorGridEvery);
|
||||
COPY_PROPERTY_IF_CHANGED(minorGridEvery);
|
||||
|
||||
// Gizmo
|
||||
COPY_PROPERTY_IF_CHANGED(gizmoType);
|
||||
_ring.merge(other._ring);
|
||||
|
||||
_lastEdited = usecTimestampNow();
|
||||
}
|
||||
|
||||
|
@ -2552,6 +2623,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
|
||||
|
@ -2752,6 +2830,32 @@ bool EntityItemProperties::getPropertyInfo(const QString& propertyName, EntityPr
|
|||
ADD_PROPERTY_TO_MAP(PROP_GRID_FOLLOW_CAMERA, FollowCamera, followCamera, bool);
|
||||
ADD_PROPERTY_TO_MAP(PROP_MAJOR_GRID_EVERY, MajorGridEvery, majorGridEvery, uint32_t);
|
||||
ADD_PROPERTY_TO_MAP(PROP_MINOR_GRID_EVERY, MinorGridEvery, minorGridEvery, float);
|
||||
|
||||
// Gizmo
|
||||
ADD_PROPERTY_TO_MAP(PROP_GIZMO_TYPE, GizmoType, gizmoType, GizmoType);
|
||||
{ // RingGizmo
|
||||
ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_START_ANGLE, Ring, ring, StartAngle, startAngle, RingGizmoPropertyGroup::MIN_ANGLE, RingGizmoPropertyGroup::MAX_ANGLE);
|
||||
ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_END_ANGLE, Ring, ring, EndAngle, endAngle, RingGizmoPropertyGroup::MIN_ANGLE, RingGizmoPropertyGroup::MAX_ANGLE);
|
||||
ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_INNER_RADIUS, Ring, ring, InnerRadius, innerRadius, RingGizmoPropertyGroup::MIN_RADIUS, RingGizmoPropertyGroup::MAX_RADIUS);
|
||||
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_INNER_START_COLOR, Ring, ring, InnerStartColor, innerStartColor);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_INNER_END_COLOR, Ring, ring, InnerEndColor, innerEndColor);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_OUTER_START_COLOR, Ring, ring, OuterStartColor, outerStartColor);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_OUTER_END_COLOR, Ring, ring, OuterEndColor, outerEndColor);
|
||||
|
||||
ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_INNER_START_ALPHA, Ring, ring, InnerStartAlpha, innerStartAlpha, RingGizmoPropertyGroup::MIN_ALPHA, RingGizmoPropertyGroup::MAX_ALPHA);
|
||||
ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_INNER_END_ALPHA, Ring, ring, InnerEndAlpha, innerEndAlpha, RingGizmoPropertyGroup::MIN_ALPHA, RingGizmoPropertyGroup::MAX_ALPHA);
|
||||
ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_OUTER_START_ALPHA, Ring, ring, OuterStartAlpha, outerStartAlpha, RingGizmoPropertyGroup::MIN_ALPHA, RingGizmoPropertyGroup::MAX_ALPHA);
|
||||
ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_OUTER_END_ALPHA, Ring, ring, OuterEndAlpha, outerEndAlpha, RingGizmoPropertyGroup::MIN_ALPHA, RingGizmoPropertyGroup::MAX_ALPHA);
|
||||
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_HAS_TICK_MARKS, Ring, ring, HasTickMarks, hasTickMarks);
|
||||
ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_MAJOR_TICK_MARKS_ANGLE, Ring, ring, MajorTickMarksAngle, majorTickMarksAngle, RingGizmoPropertyGroup::MIN_ANGLE, RingGizmoPropertyGroup::MAX_ANGLE);
|
||||
ADD_GROUP_PROPERTY_TO_MAP_WITH_RANGE(PROP_MINOR_TICK_MARKS_ANGLE, Ring, ring, MinorTickMarksAngle, minorTickMarksAngle, RingGizmoPropertyGroup::MIN_ANGLE, RingGizmoPropertyGroup::MAX_ANGLE);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_MAJOR_TICK_MARKS_LENGTH, Ring, ring, MajorTickMarksLength, majorTickMarksLength);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_MINOR_TICK_MARKS_LENGTH, Ring, ring, MinorTickMarksLength, minorTickMarksLength);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_MAJOR_TICK_MARKS_COLOR, Ring, ring, MajorTickMarksColor, majorTickMarksColor);
|
||||
ADD_GROUP_PROPERTY_TO_MAP(PROP_MINOR_TICK_MARKS_COLOR, Ring, ring, MinorTickMarksColor, minorTickMarksColor);
|
||||
}
|
||||
});
|
||||
|
||||
auto iter = _propertyInfos.find(propertyName);
|
||||
|
@ -2952,6 +3056,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 +3130,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 +3195,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 +3232,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 +3255,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,11 +3270,21 @@ 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());
|
||||
APPEND_ENTITY_PROPERTY(PROP_MINOR_GRID_EVERY, properties.getMinorGridEvery());
|
||||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::Gizmo) {
|
||||
APPEND_ENTITY_PROPERTY(PROP_GIZMO_TYPE, (uint32_t)properties.getGizmoType());
|
||||
_staticRing.setProperties(properties);
|
||||
_staticRing.appendToEditPacket(packetData, requestedProperties, propertyFlags,
|
||||
propertiesDidntFit, propertyCount, appendState);
|
||||
}
|
||||
}
|
||||
|
||||
if (propertyCount > 0) {
|
||||
|
@ -3401,6 +3531,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 +3603,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 +3657,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 +3692,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 +3714,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,12 +3727,18 @@ 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);
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_MINOR_GRID_EVERY, float, setMinorGridEvery);
|
||||
}
|
||||
|
||||
if (properties.getType() == EntityTypes::Gizmo) {
|
||||
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_GIZMO_TYPE, GizmoType, setGizmoType);
|
||||
properties.getRing().decodeFromEditPacket(propertyFlags, dataAt, processedBytes);
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
|
@ -3793,6 +3936,7 @@ void EntityItemProperties::markAllChanged() {
|
|||
_shapeTypeChanged = true;
|
||||
_colorChanged = true;
|
||||
_alphaChanged = true;
|
||||
_pulse.markAllChanged();
|
||||
_texturesChanged = true;
|
||||
_compoundShapeURLChanged = true;
|
||||
|
||||
|
@ -3928,6 +4072,10 @@ void EntityItemProperties::markAllChanged() {
|
|||
_followCameraChanged = true;
|
||||
_majorGridEveryChanged = true;
|
||||
_minorGridEveryChanged = true;
|
||||
|
||||
// Gizmo
|
||||
_gizmoTypeChanged = true;
|
||||
_ring.markAllChanged();
|
||||
}
|
||||
|
||||
// The minimum bounding box for the entity.
|
||||
|
@ -4257,6 +4405,7 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
|||
if (alphaChanged()) {
|
||||
out += "alpha";
|
||||
}
|
||||
getPulse().listChangedProperties(out);
|
||||
if (texturesChanged()) {
|
||||
out += "textures";
|
||||
}
|
||||
|
@ -4600,6 +4749,12 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
|||
out += "minorGridEvery";
|
||||
}
|
||||
|
||||
// Gizmo
|
||||
if (gizmoTypeChanged()) {
|
||||
out += "gizmoType";
|
||||
}
|
||||
getRing().listChangedProperties(out);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include "LineEntityItem.h"
|
||||
#include "PolyVoxEntityItem.h"
|
||||
#include "GridEntityItem.h"
|
||||
#include "GizmoEntityItem.h"
|
||||
#include "LightEntityItem.h"
|
||||
#include "ZoneEntityItem.h"
|
||||
|
||||
|
@ -51,12 +52,15 @@
|
|||
#include "SkyboxPropertyGroup.h"
|
||||
#include "HazePropertyGroup.h"
|
||||
#include "BloomPropertyGroup.h"
|
||||
#include "PulsePropertyGroup.h"
|
||||
#include "RingGizmoPropertyGroup.h"
|
||||
|
||||
#include "MaterialMappingMode.h"
|
||||
#include "BillboardMode.h"
|
||||
#include "RenderLayer.h"
|
||||
#include "PrimitiveMode.h"
|
||||
#include "WebInputMode.h"
|
||||
#include "GizmoType.h"
|
||||
|
||||
const quint64 UNKNOWN_CREATED_TIME = 0;
|
||||
|
||||
|
@ -100,6 +104,7 @@ class EntityItemProperties {
|
|||
friend class PolyLineEntityItem;
|
||||
friend class PolyVoxEntityItem;
|
||||
friend class GridEntityItem;
|
||||
friend class GizmoEntityItem;
|
||||
friend class LightEntityItem;
|
||||
friend class ZoneEntityItem;
|
||||
friend class MaterialEntityItem;
|
||||
|
@ -233,8 +238,9 @@ public:
|
|||
// Common
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_SHAPE_TYPE, ShapeType, shapeType, ShapeType, SHAPE_TYPE_NONE);
|
||||
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_REF(PROP_COLOR, Color, color, u8vec3Color, ENTITY_ITEM_DEFAULT_COLOR);
|
||||
DEFINE_PROPERTY(PROP_ALPHA, Alpha, alpha, float, ENTITY_ITEM_DEFAULT_ALPHA);
|
||||
DEFINE_PROPERTY_GROUP(Pulse, pulse, PulsePropertyGroup);
|
||||
DEFINE_PROPERTY_REF(PROP_TEXTURES, Textures, textures, QString, "");
|
||||
|
||||
// Particles
|
||||
|
@ -370,6 +376,10 @@ public:
|
|||
DEFINE_PROPERTY(PROP_MAJOR_GRID_EVERY, MajorGridEvery, majorGridEvery, uint32_t, GridEntityItem::DEFAULT_MAJOR_GRID_EVERY);
|
||||
DEFINE_PROPERTY(PROP_MINOR_GRID_EVERY, MinorGridEvery, minorGridEvery, float, GridEntityItem::DEFAULT_MINOR_GRID_EVERY);
|
||||
|
||||
// Gizmo
|
||||
DEFINE_PROPERTY_REF_ENUM(PROP_GIZMO_TYPE, GizmoType, gizmoType, GizmoType, GizmoType::RING);
|
||||
DEFINE_PROPERTY_GROUP(Ring, ring, RingGizmoPropertyGroup);
|
||||
|
||||
static QString getComponentModeAsString(uint32_t mode);
|
||||
|
||||
std::array<ComponentPair, COMPONENT_MODE_ITEM_COUNT>::const_iterator findComponent(const QString& mode);
|
||||
|
|
|
@ -43,6 +43,7 @@ const quint32 ENTITY_ITEM_DEFAULT_ENTITY_INSTANCE_NUMBER = 0;
|
|||
const QString ENTITY_ITEM_DEFAULT_CERTIFICATE_ID = QString("");
|
||||
const quint32 ENTITY_ITEM_DEFAULT_STATIC_CERTIFICATE_VERSION = 0;
|
||||
|
||||
const glm::u8vec3 ENTITY_ITEM_DEFAULT_COLOR = { 255, 255, 255 };
|
||||
const float ENTITY_ITEM_DEFAULT_ALPHA = 1.0f;
|
||||
const bool ENTITY_ITEM_DEFAULT_VISIBLE = true;
|
||||
const bool ENTITY_ITEM_DEFAULT_VISIBLE_IN_SECONDARY_CAMERA = true;
|
||||
|
|
|
@ -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,
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -326,6 +331,28 @@ enum EntityPropertyList {
|
|||
PROP_MAJOR_GRID_EVERY = PROP_DERIVED_1,
|
||||
PROP_MINOR_GRID_EVERY = PROP_DERIVED_2,
|
||||
|
||||
// Gizmo
|
||||
PROP_GIZMO_TYPE = PROP_DERIVED_0,
|
||||
// Ring
|
||||
PROP_START_ANGLE = PROP_DERIVED_1,
|
||||
PROP_END_ANGLE = PROP_DERIVED_2,
|
||||
PROP_INNER_RADIUS = PROP_DERIVED_3,
|
||||
PROP_INNER_START_COLOR = PROP_DERIVED_4,
|
||||
PROP_INNER_END_COLOR = PROP_DERIVED_5,
|
||||
PROP_OUTER_START_COLOR = PROP_DERIVED_6,
|
||||
PROP_OUTER_END_COLOR = PROP_DERIVED_7,
|
||||
PROP_INNER_START_ALPHA = PROP_DERIVED_8,
|
||||
PROP_INNER_END_ALPHA = PROP_DERIVED_9,
|
||||
PROP_OUTER_START_ALPHA = PROP_DERIVED_10,
|
||||
PROP_OUTER_END_ALPHA = PROP_DERIVED_11,
|
||||
PROP_HAS_TICK_MARKS = PROP_DERIVED_12,
|
||||
PROP_MAJOR_TICK_MARKS_ANGLE = PROP_DERIVED_13,
|
||||
PROP_MINOR_TICK_MARKS_ANGLE = PROP_DERIVED_14,
|
||||
PROP_MAJOR_TICK_MARKS_LENGTH = PROP_DERIVED_15,
|
||||
PROP_MINOR_TICK_MARKS_LENGTH = PROP_DERIVED_16,
|
||||
PROP_MAJOR_TICK_MARKS_COLOR = PROP_DERIVED_17,
|
||||
PROP_MINOR_TICK_MARKS_COLOR = PROP_DERIVED_18,
|
||||
|
||||
// WARNING!!! DO NOT ADD PROPS_xxx here unless you really really meant to.... Add them UP above
|
||||
};
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "PolyLineEntityItem.h"
|
||||
#include "PolyVoxEntityItem.h"
|
||||
#include "GridEntityItem.h"
|
||||
#include "GizmoEntityItem.h"
|
||||
#include "LightEntityItem.h"
|
||||
#include "ZoneEntityItem.h"
|
||||
#include "MaterialEntityItem.h"
|
||||
|
@ -54,6 +55,7 @@ REGISTER_ENTITY_TYPE(Line)
|
|||
REGISTER_ENTITY_TYPE(PolyLine)
|
||||
REGISTER_ENTITY_TYPE(PolyVox)
|
||||
REGISTER_ENTITY_TYPE(Grid)
|
||||
REGISTER_ENTITY_TYPE(Gizmo)
|
||||
REGISTER_ENTITY_TYPE(Light)
|
||||
REGISTER_ENTITY_TYPE(Zone)
|
||||
REGISTER_ENTITY_TYPE(Material)
|
||||
|
|
|
@ -78,6 +78,8 @@ public:
|
|||
* <td>{@link Entities.EntityProperties-PolyVox|EntityProperties-PolyVox}</td></tr>
|
||||
* <tr><td><code>"Grid"</code></td><td>A grid of lines in a plane.</td>
|
||||
* <td>{@link Entities.EntityProperties-Grid|EntityProperties-Grid}</td></tr>
|
||||
* <tr><td><code>"Gizmo"</code></td><td>An entity with various UI-related properties.</td>
|
||||
* <td>{@link Entities.EntityProperties-Gizmo|EntityProperties-Gizmo}</td></tr>
|
||||
* <tr><td><code>"Light"</code></td><td>A local lighting effect.</td>
|
||||
* <td>{@link Entities.EntityProperties-Light|EntityProperties-Light}</td></tr>
|
||||
* <tr><td><code>"Zone"</code></td><td>A volume of lighting effects and avatar permissions.</td>
|
||||
|
@ -103,6 +105,7 @@ public:
|
|||
PolyLine,
|
||||
PolyVox,
|
||||
Grid,
|
||||
Gizmo,
|
||||
Light,
|
||||
Zone,
|
||||
Material,
|
||||
|
|
200
libraries/entities/src/GizmoEntityItem.cpp
Normal file
200
libraries/entities/src/GizmoEntityItem.cpp
Normal file
|
@ -0,0 +1,200 @@
|
|||
//
|
||||
// Created by Sam Gondelman on 1/22/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 "GizmoEntityItem.h"
|
||||
|
||||
#include "EntityItemProperties.h"
|
||||
|
||||
EntityItemPointer GizmoEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
Pointer entity(new GizmoEntityItem(entityID), [](EntityItem* ptr) { ptr->deleteLater(); });
|
||||
entity->setProperties(properties);
|
||||
return entity;
|
||||
}
|
||||
|
||||
// our non-pure virtual subclass for now...
|
||||
GizmoEntityItem::GizmoEntityItem(const EntityItemID& entityItemID) : EntityItem(entityItemID) {
|
||||
_type = EntityTypes::Gizmo;
|
||||
}
|
||||
|
||||
void GizmoEntityItem::setUnscaledDimensions(const glm::vec3& value) {
|
||||
// NOTE: Gizmo Entities always have a "height" of 1mm.
|
||||
EntityItem::setUnscaledDimensions(glm::vec3(value.x, ENTITY_ITEM_MIN_DIMENSION, value.z));
|
||||
}
|
||||
|
||||
EntityItemProperties GizmoEntityItem::getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const {
|
||||
EntityItemProperties properties = EntityItem::getProperties(desiredProperties, allowEmptyDesiredProperties); // get the properties from our base class
|
||||
|
||||
COPY_ENTITY_PROPERTY_TO_PROPERTIES(gizmoType, getGizmoType);
|
||||
withReadLock([&] {
|
||||
_ringProperties.getProperties(properties);
|
||||
});
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
bool GizmoEntityItem::setProperties(const EntityItemProperties& properties) {
|
||||
bool somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
|
||||
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(gizmoType, setGizmoType);
|
||||
withWriteLock([&] {
|
||||
bool ringPropertiesChanged = _ringProperties.setProperties(properties);
|
||||
somethingChanged |= ringPropertiesChanged;
|
||||
});
|
||||
|
||||
if (somethingChanged) {
|
||||
bool wantDebug = false;
|
||||
if (wantDebug) {
|
||||
uint64_t now = usecTimestampNow();
|
||||
int elapsed = now - getLastEdited();
|
||||
qCDebug(entities) << "GizmoEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
|
||||
"now=" << now << " getLastEdited()=" << getLastEdited();
|
||||
}
|
||||
setLastEdited(properties.getLastEdited());
|
||||
}
|
||||
return somethingChanged;
|
||||
}
|
||||
|
||||
int GizmoEntityItem::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_GIZMO_TYPE, GizmoType, setGizmoType);
|
||||
withWriteLock([&] {
|
||||
int bytesFromRing = _ringProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args,
|
||||
propertyFlags, overwriteLocalData,
|
||||
somethingChanged);
|
||||
bytesRead += bytesFromRing;
|
||||
dataAt += bytesFromRing;
|
||||
});
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
EntityPropertyFlags GizmoEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
|
||||
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
|
||||
|
||||
requestedProperties += PROP_GIZMO_TYPE;
|
||||
requestedProperties += _ringProperties.getEntityProperties(params);
|
||||
|
||||
return requestedProperties;
|
||||
}
|
||||
|
||||
void GizmoEntityItem::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_GIZMO_TYPE, (uint32_t)getGizmoType());
|
||||
withReadLock([&] {
|
||||
_ringProperties.appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties,
|
||||
propertyFlags, propertiesDidntFit, propertyCount, appendState);
|
||||
});
|
||||
}
|
||||
|
||||
bool GizmoEntityItem::supportsDetailedIntersection() const {
|
||||
return _gizmoType == GizmoType::RING;
|
||||
}
|
||||
|
||||
#include <qmath.h>
|
||||
|
||||
bool GizmoEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
OctreeElementPointer& element,
|
||||
float& distance, BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const {
|
||||
glm::vec3 dimensions = getScaledDimensions();
|
||||
glm::vec2 xyDimensions(dimensions.x, dimensions.z);
|
||||
glm::quat rotation = glm::angleAxis((float)M_PI_2, Vectors::RIGHT) * getWorldOrientation();
|
||||
glm::vec3 position = getWorldPosition() + rotation * (dimensions * (ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint()));
|
||||
|
||||
if (findRayRectangleIntersection(origin, direction, rotation, position, xyDimensions, distance)) {
|
||||
glm::vec3 hitPosition = origin + (distance * direction);
|
||||
glm::vec3 localHitPosition = glm::inverse(rotation) * (hitPosition - getWorldPosition());
|
||||
localHitPosition.x /= xyDimensions.x;
|
||||
localHitPosition.y /= xyDimensions.y;
|
||||
float distanceToHit = glm::length(localHitPosition);
|
||||
|
||||
if (0.5f * _ringProperties.getInnerRadius() <= distanceToHit && distanceToHit <= 0.5f) {
|
||||
glm::vec3 forward = rotation * Vectors::FRONT;
|
||||
if (glm::dot(forward, direction) > 0.0f) {
|
||||
face = MAX_Z_FACE;
|
||||
surfaceNormal = -forward;
|
||||
} else {
|
||||
face = MIN_Z_FACE;
|
||||
surfaceNormal = forward;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GizmoEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration,
|
||||
OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const {
|
||||
//// Scale the dimensions by the diameter
|
||||
glm::vec3 dimensions = getScaledDimensions();
|
||||
glm::vec2 xyDimensions(dimensions.x, dimensions.z);
|
||||
glm::quat rotation = glm::angleAxis((float)M_PI_2, Vectors::RIGHT) * getWorldOrientation();
|
||||
glm::vec3 position = getWorldPosition();
|
||||
|
||||
glm::quat inverseRot = glm::inverse(rotation);
|
||||
glm::vec3 localOrigin = inverseRot * (origin - position);
|
||||
glm::vec3 localVelocity = inverseRot * velocity;
|
||||
glm::vec3 localAcceleration = inverseRot * acceleration;
|
||||
|
||||
if (findParabolaRectangleIntersection(localOrigin, localVelocity, localAcceleration, xyDimensions, parabolicDistance)) {
|
||||
glm::vec3 localHitPosition = localOrigin + localVelocity * parabolicDistance + 0.5f * localAcceleration * parabolicDistance * parabolicDistance;
|
||||
localHitPosition.x /= xyDimensions.x;
|
||||
localHitPosition.y /= xyDimensions.y;
|
||||
float distanceToHit = glm::length(localHitPosition);
|
||||
|
||||
if (0.5f * _ringProperties.getInnerRadius() <= distanceToHit && distanceToHit <= 0.5f) {
|
||||
float localIntersectionVelocityZ = localVelocity.z + localAcceleration.z * parabolicDistance;
|
||||
glm::vec3 forward = rotation * Vectors::FRONT;
|
||||
if (localIntersectionVelocityZ > 0.0f) {
|
||||
face = MIN_Z_FACE;
|
||||
surfaceNormal = forward;
|
||||
} else {
|
||||
face = MAX_Z_FACE;
|
||||
surfaceNormal = -forward;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void GizmoEntityItem::setGizmoType(GizmoType value) {
|
||||
withWriteLock([&] {
|
||||
_gizmoType = value;
|
||||
});
|
||||
}
|
||||
|
||||
GizmoType GizmoEntityItem::getGizmoType() const {
|
||||
return resultWithReadLock<GizmoType>([&] {
|
||||
return _gizmoType;
|
||||
});
|
||||
}
|
||||
|
||||
RingGizmoPropertyGroup GizmoEntityItem::getRingProperties() const {
|
||||
return resultWithReadLock<RingGizmoPropertyGroup>([&] {
|
||||
return _ringProperties;
|
||||
});
|
||||
}
|
67
libraries/entities/src/GizmoEntityItem.h
Normal file
67
libraries/entities/src/GizmoEntityItem.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
//
|
||||
// Created by Sam Gondelman on 1/22/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_GizmoEntityItem_h
|
||||
#define hifi_GizmoEntityItem_h
|
||||
|
||||
#include "EntityItem.h"
|
||||
|
||||
#include "RingGizmoPropertyGroup.h"
|
||||
|
||||
class GizmoEntityItem : public EntityItem {
|
||||
using Pointer = std::shared_ptr<GizmoEntityItem>;
|
||||
public:
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
|
||||
GizmoEntityItem(const EntityItemID& entityItemID);
|
||||
|
||||
ALLOW_INSTANTIATION // This class can be instantiated
|
||||
|
||||
virtual void setUnscaledDimensions(const glm::vec3& value) override;
|
||||
|
||||
// methods for getting/setting all properties of an entity
|
||||
EntityItemProperties getProperties(const EntityPropertyFlags& desiredProperties, bool allowEmptyDesiredProperties) const override;
|
||||
bool setProperties(const EntityItemProperties& properties) override;
|
||||
|
||||
EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override;
|
||||
|
||||
void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
|
||||
EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData,
|
||||
EntityPropertyFlags& requestedProperties,
|
||||
EntityPropertyFlags& propertyFlags,
|
||||
EntityPropertyFlags& propertiesDidntFit,
|
||||
int& propertyCount,
|
||||
OctreeElement::AppendState& appendState) const override;
|
||||
|
||||
int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
|
||||
ReadBitstreamToTreeParams& args,
|
||||
EntityPropertyFlags& propertyFlags, bool overwriteLocalData,
|
||||
bool& somethingChanged) override;
|
||||
|
||||
bool supportsDetailedIntersection() const override;
|
||||
bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
|
||||
OctreeElementPointer& element, float& distance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override;
|
||||
bool findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity,
|
||||
const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance,
|
||||
BoxFace& face, glm::vec3& surfaceNormal,
|
||||
QVariantMap& extraInfo, bool precisionPicking) const override;
|
||||
|
||||
GizmoType getGizmoType() const;
|
||||
void setGizmoType(GizmoType value);
|
||||
|
||||
RingGizmoPropertyGroup getRingProperties() const;
|
||||
|
||||
protected:
|
||||
GizmoType _gizmoType;
|
||||
RingGizmoPropertyGroup _ringProperties;
|
||||
|
||||
};
|
||||
|
||||
#endif // hifi_GizmoEntityItem_h
|
|
@ -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;
|
||||
});
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue