mirror of
https://github.com/lubosz/overte.git
synced 2025-04-26 11:15:23 +02:00
Merge branch 'master' into 21880
# Conflicts: # interface./src/scripting/WindowScriptingInterface.h
This commit is contained in:
commit
b5028a9626
132 changed files with 2805 additions and 1411 deletions
android/app
cmake/macros
interface
resources/qml
dialogs
hifi/tablet/tabletWindows
src
libraries
avatars-renderer/src/avatars-renderer
display-plugins/src/display-plugins
entities-renderer/src
RenderableEntityItem.cppRenderableEntityItem.hRenderableMaterialEntityItem.cppRenderableModelEntityItem.cppRenderableModelEntityItem.hRenderableParticleEffectEntityItem.cppRenderablePolyLineEntityItem.cppRenderablePolyVoxEntityItem.hRenderableShapeEntityItem.cppRenderableZoneEntityItem.cpp
entities/src
EntityItem.cppEntityItem.hEntityItemProperties.cppEntityItemProperties.hEntityItemPropertiesDefaults.hEntityPropertyFlags.hEntityScriptingInterface.cppEntityScriptingInterface.h
gl/src/gl
gpu-gl-common/src/gpu/gl
gpu-gl/src/gpu
gpu-gles/src/gpu/gles
gpu/src/gpu
networking/src
physics/src
render-utils/src
CauterizedModel.cppMeshPartPayload.cppMeshPartPayload.hModel.cppModel.hRenderDeferredTask.cppRenderForwardTask.cppRenderHifi.h
render/src/render
script-engine/src
shared/src
test-utils
plugins/openvr/src
scripts
tests-manual/shaders/src
tests
animation
gpu/src
|
@ -27,6 +27,14 @@ android {
|
|||
'-DDISABLE_KTX_CACHE=OFF'
|
||||
}
|
||||
}
|
||||
signingConfigs {
|
||||
release {
|
||||
storeFile project.hasProperty("HIFI_ANDROID_KEYSTORE") ? file(HIFI_ANDROID_KEYSTORE) : null
|
||||
storePassword project.hasProperty("HIFI_ANDROID_KEYSTORE_PASSWORD") ? HIFI_ANDROID_KEYSTORE_PASSWORD : ''
|
||||
keyAlias project.hasProperty("HIFI_ANDROID_KEY_ALIAS") ? HIFI_ANDROID_KEY_ALIAS : ''
|
||||
keyPassword project.hasProperty("HIFI_ANDROID_KEY_PASSWORD") ? HIFI_ANDROID_KEY_PASSWORD : ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
|
@ -38,6 +46,10 @@ android {
|
|||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
signingConfig project.hasProperty("HIFI_ANDROID_KEYSTORE") &&
|
||||
project.hasProperty("HIFI_ANDROID_KEYSTORE_PASSWORD") &&
|
||||
project.hasProperty("HIFI_ANDROID_KEY_ALIAS") &&
|
||||
project.hasProperty("HIFI_ANDROID_KEY_PASSWORD")? signingConfigs.release : null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,12 +49,6 @@
|
|||
android:label="@string/app_name"
|
||||
android:launchMode="singleTop"
|
||||
>
|
||||
|
||||
<intent-filter>
|
||||
<category android:name="com.google.intent.category.DAYDREAM"/>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data android:name="android.app.lib_name" android:value="native-lib"/>
|
||||
<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"/>
|
||||
|
|
|
@ -14,12 +14,16 @@ package io.highfidelity.hifiinterface;
|
|||
import android.content.Intent;
|
||||
import android.content.res.AssetManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Vibrator;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.WindowManager;
|
||||
import android.util.Log;
|
||||
|
||||
import org.qtproject.qt5.android.QtLayout;
|
||||
import org.qtproject.qt5.android.QtSurface;
|
||||
import org.qtproject.qt5.android.bindings.QtActivity;
|
||||
|
||||
/*import com.google.vr.cardboard.DisplaySynchronizer;
|
||||
|
@ -31,6 +35,9 @@ import android.content.pm.ActivityInfo;
|
|||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class InterfaceActivity extends QtActivity {
|
||||
|
||||
|
@ -134,6 +141,7 @@ public class InterfaceActivity extends QtActivity {
|
|||
protected void onResume() {
|
||||
super.onResume();
|
||||
nativeEnterForeground();
|
||||
surfacesWorkaround();
|
||||
//gvrApi.resumeTracking();
|
||||
}
|
||||
|
||||
|
@ -158,6 +166,41 @@ public class InterfaceActivity extends QtActivity {
|
|||
Log.w("[VR]", "Portrait detected but not in VR mode. Should not happen");
|
||||
}
|
||||
}
|
||||
surfacesWorkaround();
|
||||
}
|
||||
|
||||
private void surfacesWorkaround() {
|
||||
FrameLayout fl = findViewById(android.R.id.content);
|
||||
if (fl.getChildCount() > 0) {
|
||||
QtLayout qtLayout = (QtLayout) fl.getChildAt(0);
|
||||
if (qtLayout.getChildCount() > 1) {
|
||||
QtSurface s1 = (QtSurface) qtLayout.getChildAt(0);
|
||||
QtSurface s2 = (QtSurface) qtLayout.getChildAt(1);
|
||||
Integer subLayer1 = 0;
|
||||
Integer subLayer2 = 0;
|
||||
try {
|
||||
String field;
|
||||
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
field = "mSubLayer";
|
||||
} else {
|
||||
field = "mWindowType";
|
||||
}
|
||||
Field f = s1.getClass().getSuperclass().getDeclaredField(field);
|
||||
f.setAccessible(true);
|
||||
subLayer1 = (Integer) f.get(s1);
|
||||
subLayer2 = (Integer) f.get(s2);
|
||||
if (subLayer1 < subLayer2) {
|
||||
s1.setVisibility(View.VISIBLE);
|
||||
s2.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
s1.setVisibility(View.INVISIBLE);
|
||||
s2.setVisibility(View.VISIBLE);
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
Log.e(TAG, "Workaround failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void openUrlInAndroidWebView(String urlString) {
|
||||
|
|
|
@ -61,16 +61,21 @@ macro(SETUP_HIFI_TESTCASE)
|
|||
endif()
|
||||
endforeach()
|
||||
|
||||
|
||||
# Find test classes to build into test executables.
|
||||
# Warn about any .cpp files that are *not* test classes (*Test[s].cpp), since those files will not be used.
|
||||
foreach (SRC_FILE ${TEST_PROJ_SRC_FILES})
|
||||
string(REGEX MATCH ".+Tests?\\.cpp$" TEST_CPP_FILE ${SRC_FILE})
|
||||
string(REGEX MATCH ".+\\.cpp$" NON_TEST_CPP_FILE ${SRC_FILE})
|
||||
string(REGEX MATCH ".+\\.qrc$" QRC_FILE ${SRC_FILE})
|
||||
if (TEST_CPP_FILE)
|
||||
list(APPEND TEST_CASE_FILES ${TEST_CPP_FILE})
|
||||
elseif (NON_TEST_CPP_FILE)
|
||||
message(WARNING "ignoring .cpp file (not a test class -- this will not be linked or compiled!): " ${NON_TEST_CPP_FILE})
|
||||
endif ()
|
||||
if (QRC_FILE)
|
||||
list(APPEND EXTRA_FILES ${QRC_FILE})
|
||||
endif()
|
||||
endforeach ()
|
||||
|
||||
if (TEST_CASE_FILES)
|
||||
|
@ -88,7 +93,7 @@ macro(SETUP_HIFI_TESTCASE)
|
|||
# grab the implemenation and header files
|
||||
set(TARGET_SRCS ${TEST_FILE}) # only one source / .cpp file (the test class)
|
||||
|
||||
add_executable(${TARGET_NAME} ${TEST_FILE})
|
||||
add_executable(${TARGET_NAME} ${TEST_FILE} ${EXTRA_FILES})
|
||||
add_test(${TARGET_NAME}-test ${TARGET_NAME})
|
||||
set_target_properties(${TARGET_NAME} PROPERTIES
|
||||
EXCLUDE_FROM_DEFAULT_BUILD TRUE
|
||||
|
|
|
@ -57,7 +57,7 @@ ModalWindow {
|
|||
property int iconSize: 40
|
||||
|
||||
property bool selectDirectory: false;
|
||||
property bool showHidden: false;
|
||||
property bool showHidden: true;
|
||||
// FIXME implement
|
||||
property bool multiSelect: false;
|
||||
property bool saveDialog: false;
|
||||
|
@ -324,8 +324,10 @@ ModalWindow {
|
|||
showDirsFirst: true
|
||||
showDotAndDotDot: false
|
||||
showFiles: !root.selectDirectory
|
||||
showHidden: root.showHidden
|
||||
Component.onCompleted: {
|
||||
showFiles = !root.selectDirectory
|
||||
showHidden = root.showHidden
|
||||
}
|
||||
|
||||
onFolderChanged: {
|
||||
|
|
|
@ -58,7 +58,7 @@ ModalWindow {
|
|||
property int iconSize: 40
|
||||
|
||||
property bool selectDirectory: false;
|
||||
property bool showHidden: false;
|
||||
property bool showHidden: true;
|
||||
// FIXME implement
|
||||
property bool multiSelect: false;
|
||||
property bool saveDialog: false;
|
||||
|
@ -325,8 +325,10 @@ ModalWindow {
|
|||
showDirsFirst: true
|
||||
showDotAndDotDot: false
|
||||
showFiles: !root.selectDirectory
|
||||
showHidden: root.showHidden
|
||||
Component.onCompleted: {
|
||||
showFiles = !root.selectDirectory
|
||||
showHidden = root.showHidden
|
||||
}
|
||||
|
||||
onFolderChanged: {
|
||||
|
|
|
@ -55,7 +55,7 @@ TabletModalWindow {
|
|||
property int iconSize: 40
|
||||
|
||||
property bool selectDirectory: false;
|
||||
property bool showHidden: false;
|
||||
property bool showHidden: true;
|
||||
// FIXME implement
|
||||
property bool multiSelect: false;
|
||||
property bool saveDialog: false;
|
||||
|
@ -288,8 +288,10 @@ TabletModalWindow {
|
|||
showDirsFirst: true
|
||||
showDotAndDotDot: false
|
||||
showFiles: !root.selectDirectory
|
||||
showHidden: root.showHidden
|
||||
Component.onCompleted: {
|
||||
showFiles = !root.selectDirectory
|
||||
showHidden = root.showHidden
|
||||
}
|
||||
|
||||
onFolderChanged: {
|
||||
|
|
|
@ -52,7 +52,7 @@ Rectangle {
|
|||
property int iconSize: 40
|
||||
|
||||
property bool selectDirectory: false;
|
||||
property bool showHidden: false;
|
||||
property bool showHidden: true;
|
||||
// FIXME implement
|
||||
property bool multiSelect: false;
|
||||
property bool saveDialog: false;
|
||||
|
@ -280,8 +280,10 @@ Rectangle {
|
|||
showDirsFirst: true
|
||||
showDotAndDotDot: false
|
||||
showFiles: !root.selectDirectory
|
||||
showHidden: root.showHidden
|
||||
Component.onCompleted: {
|
||||
showFiles = !root.selectDirectory
|
||||
showHidden = root.showHidden
|
||||
}
|
||||
|
||||
onFolderChanged: {
|
||||
|
|
|
@ -8,48 +8,46 @@
|
|||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include "AboutUtil.h"
|
||||
#include <QDate>
|
||||
#include <QLocale>
|
||||
|
||||
#include "AboutUtil.h"
|
||||
#include "BuildInfo.h"
|
||||
#include <ui/TabletScriptingInterface.h>
|
||||
#include <OffscreenQmlDialog.h>
|
||||
|
||||
#include "BuildInfo.h"
|
||||
#include "DependencyManager.h"
|
||||
#include "scripting/HMDScriptingInterface.h"
|
||||
#include "Application.h"
|
||||
#include <OffscreenQmlDialog.h>
|
||||
|
||||
AboutUtil::AboutUtil(QObject *parent) : QObject(parent) {
|
||||
QLocale locale_;
|
||||
m_DateConverted = QDate::fromString(BuildInfo::BUILD_TIME, "dd/MM/yyyy").
|
||||
toString(locale_.dateFormat(QLocale::ShortFormat));
|
||||
QLocale locale;
|
||||
_dateConverted = QDate::fromString(BuildInfo::BUILD_TIME, "dd/MM/yyyy").
|
||||
toString(locale.dateFormat(QLocale::ShortFormat));
|
||||
}
|
||||
|
||||
AboutUtil *AboutUtil::getInstance()
|
||||
{
|
||||
AboutUtil *AboutUtil::getInstance() {
|
||||
static AboutUtil instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
QString AboutUtil::buildDate() const
|
||||
{
|
||||
return m_DateConverted;
|
||||
QString AboutUtil::getBuildDate() const {
|
||||
return _dateConverted;
|
||||
}
|
||||
|
||||
QString AboutUtil::buildVersion() const
|
||||
{
|
||||
QString AboutUtil::getBuildVersion() const {
|
||||
return BuildInfo::VERSION;
|
||||
}
|
||||
|
||||
QString AboutUtil::qtVersion() const
|
||||
{
|
||||
QString AboutUtil::getQtVersion() const {
|
||||
return qVersion();
|
||||
}
|
||||
|
||||
void AboutUtil::openUrl(const QString& url) const {
|
||||
|
||||
auto tabletScriptingInterface = DependencyManager::get<TabletScriptingInterface>();
|
||||
auto tablet = dynamic_cast<TabletProxy*>(tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
|
||||
auto tablet = tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system");
|
||||
auto hmd = DependencyManager::get<HMDScriptingInterface>();
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
|
||||
|
|
|
@ -27,21 +27,18 @@
|
|||
*/
|
||||
|
||||
class AboutUtil : public QObject {
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QString buildDate READ buildDate CONSTANT)
|
||||
Q_PROPERTY(QString buildVersion READ buildVersion CONSTANT)
|
||||
Q_PROPERTY(QString qtVersion READ qtVersion CONSTANT)
|
||||
|
||||
AboutUtil(QObject* parent = nullptr);
|
||||
Q_PROPERTY(QString buildDate READ getBuildDate CONSTANT)
|
||||
Q_PROPERTY(QString buildVersion READ getBuildVersion CONSTANT)
|
||||
Q_PROPERTY(QString qtVersion READ getQtVersion CONSTANT)
|
||||
public:
|
||||
static AboutUtil* getInstance();
|
||||
~AboutUtil() {}
|
||||
|
||||
QString buildDate() const;
|
||||
QString buildVersion() const;
|
||||
QString qtVersion() const;
|
||||
QString getBuildDate() const;
|
||||
QString getBuildVersion() const;
|
||||
QString getQtVersion() const;
|
||||
|
||||
public slots:
|
||||
|
||||
|
@ -51,8 +48,8 @@ public slots:
|
|||
*/
|
||||
void openUrl(const QString &url) const;
|
||||
private:
|
||||
|
||||
QString m_DateConverted;
|
||||
AboutUtil(QObject* parent = nullptr);
|
||||
QString _dateConverted;
|
||||
};
|
||||
|
||||
#endif // hifi_AboutUtil_h
|
||||
|
|
|
@ -2492,6 +2492,7 @@ void Application::cleanupBeforeQuit() {
|
|||
}
|
||||
|
||||
_window->saveGeometry();
|
||||
_gpuContext->shutdown();
|
||||
|
||||
// Destroy third party processes after scripts have finished using them.
|
||||
#ifdef HAVE_DDE
|
||||
|
@ -3011,9 +3012,11 @@ void Application::onDesktopRootItemCreated(QQuickItem* rootItem) {
|
|||
auto surfaceContext = DependencyManager::get<OffscreenUi>()->getSurfaceContext();
|
||||
surfaceContext->setContextProperty("Stats", Stats::getInstance());
|
||||
|
||||
#if !defined(Q_OS_ANDROID)
|
||||
auto offscreenUi = DependencyManager::get<OffscreenUi>();
|
||||
auto qml = PathUtils::qmlUrl("AvatarInputsBar.qml");
|
||||
offscreenUi->show(qml, "AvatarInputsBar");
|
||||
#endif
|
||||
}
|
||||
|
||||
void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) {
|
||||
|
@ -3642,7 +3645,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
_keysPressed.insert(event->key());
|
||||
|
||||
_controllerScriptingInterface->emitKeyPressEvent(event); // send events to any registered scripts
|
||||
|
||||
// if one of our scripts have asked to capture this event, then stop processing it
|
||||
if (_controllerScriptingInterface->isKeyCaptured(event)) {
|
||||
return;
|
||||
|
@ -3727,6 +3729,13 @@ void Application::keyPressEvent(QKeyEvent* event) {
|
|||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_R:
|
||||
if (isMeta && !event->isAutoRepeat()) {
|
||||
DependencyManager::get<ScriptEngines>()->reloadAllScripts();
|
||||
DependencyManager::get<OffscreenUi>()->clearCache();
|
||||
}
|
||||
break;
|
||||
|
||||
case Qt::Key_Asterisk:
|
||||
Menu::getInstance()->triggerOption(MenuOption::DefaultSkybox);
|
||||
break;
|
||||
|
@ -7643,18 +7652,18 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa
|
|||
});
|
||||
}
|
||||
|
||||
void Application::takeSecondaryCameraSnapshot(const QString& filename) {
|
||||
postLambdaEvent([filename, this] {
|
||||
void Application::takeSecondaryCameraSnapshot(const bool& notify, const QString& filename) {
|
||||
postLambdaEvent([notify, filename, this] {
|
||||
QString snapshotPath = DependencyManager::get<Snapshot>()->saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename,
|
||||
TestScriptingInterface::getInstance()->getTestResultsLocation());
|
||||
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(snapshotPath, true);
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(snapshotPath, notify);
|
||||
});
|
||||
}
|
||||
|
||||
void Application::takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat, const QString& filename) {
|
||||
postLambdaEvent([filename, cubemapOutputFormat, cameraPosition] {
|
||||
DependencyManager::get<Snapshot>()->save360Snapshot(cameraPosition, cubemapOutputFormat, filename);
|
||||
void Application::takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat, const bool& notify, const QString& filename) {
|
||||
postLambdaEvent([notify, filename, cubemapOutputFormat, cameraPosition] {
|
||||
DependencyManager::get<Snapshot>()->save360Snapshot(cameraPosition, cubemapOutputFormat, notify, filename);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -281,8 +281,11 @@ public:
|
|||
float getGameLoopRate() const { return _gameLoopCounter.rate(); }
|
||||
|
||||
void takeSnapshot(bool notify, bool includeAnimated = false, float aspectRatio = 0.0f, const QString& filename = QString());
|
||||
void takeSecondaryCameraSnapshot(const QString& filename = QString());
|
||||
void takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat, const QString& filename = QString());
|
||||
void takeSecondaryCameraSnapshot(const bool& notify, const QString& filename = QString());
|
||||
void takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition,
|
||||
const bool& cubemapOutputFormat,
|
||||
const bool& notify,
|
||||
const QString& filename = QString());
|
||||
|
||||
void shareSnapshot(const QString& filename, const QUrl& href = QUrl(""));
|
||||
|
||||
|
|
|
@ -104,6 +104,9 @@ void AvatarManager::updateMyAvatar(float deltaTime) {
|
|||
PerformanceWarning warn(showWarnings, "AvatarManager::updateMyAvatar()");
|
||||
|
||||
_myAvatar->update(deltaTime);
|
||||
render::Transaction transaction;
|
||||
_myAvatar->updateRenderItem(transaction);
|
||||
qApp->getMain3DScene()->enqueueTransaction(transaction);
|
||||
|
||||
quint64 now = usecTimestampNow();
|
||||
quint64 dt = now - _lastSendAvatarDataTime;
|
||||
|
|
|
@ -1127,7 +1127,11 @@ void MyAvatar::setEnableDebugDrawIKChains(bool isEnabled) {
|
|||
}
|
||||
|
||||
void MyAvatar::setEnableMeshVisible(bool isEnabled) {
|
||||
_skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true);
|
||||
return Avatar::setEnableMeshVisible(isEnabled);
|
||||
}
|
||||
|
||||
bool MyAvatar::getEnableMeshVisible() const {
|
||||
return Avatar::getEnableMeshVisible();
|
||||
}
|
||||
|
||||
void MyAvatar::setEnableInverseKinematics(bool isEnabled) {
|
||||
|
@ -1479,7 +1483,10 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) {
|
|||
_skeletonModelChangeCount++;
|
||||
int skeletonModelChangeCount = _skeletonModelChangeCount;
|
||||
Avatar::setSkeletonModelURL(skeletonModelURL);
|
||||
_skeletonModel->setVisibleInScene(true, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true);
|
||||
_skeletonModel->setTagMask(render::hifi::TAG_NONE);
|
||||
_skeletonModel->setGroupCulled(true);
|
||||
_skeletonModel->setVisibleInScene(true, qApp->getMain3DScene());
|
||||
|
||||
_headBoneSet.clear();
|
||||
_cauterizationNeedsUpdate = true;
|
||||
|
||||
|
@ -2054,14 +2061,12 @@ void MyAvatar::preDisplaySide(const RenderArgs* renderArgs) {
|
|||
_attachmentData[i].jointName.compare("RightEye", Qt::CaseInsensitive) == 0 ||
|
||||
_attachmentData[i].jointName.compare("HeadTop_End", Qt::CaseInsensitive) == 0 ||
|
||||
_attachmentData[i].jointName.compare("Face", Qt::CaseInsensitive) == 0) {
|
||||
uint8_t modelRenderTagBits = shouldDrawHead ? render::ItemKey::TAG_BITS_0 : render::ItemKey::TAG_BITS_NONE;
|
||||
modelRenderTagBits |= render::ItemKey::TAG_BITS_1;
|
||||
_attachmentModels[i]->setVisibleInScene(true, qApp->getMain3DScene(),
|
||||
modelRenderTagBits, false);
|
||||
uint8_t modelRenderTagBits = shouldDrawHead ? render::hifi::TAG_ALL_VIEWS : render::hifi::TAG_SECONDARY_VIEW;
|
||||
|
||||
uint8_t castShadowRenderTagBits = render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1;
|
||||
_attachmentModels[i]->setCanCastShadow(true, qApp->getMain3DScene(),
|
||||
castShadowRenderTagBits, false);
|
||||
_attachmentModels[i]->setTagMask(modelRenderTagBits);
|
||||
_attachmentModels[i]->setGroupCulled(false);
|
||||
_attachmentModels[i]->setCanCastShadow(true);
|
||||
_attachmentModels[i]->setVisibleInScene(true, qApp->getMain3DScene());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1159,7 +1159,7 @@ public slots:
|
|||
* @function MyAvatar.getEnableMeshVisible
|
||||
* @returns {boolean} <code>true</code> if your avatar's mesh is visible, otherwise <code>false</code>.
|
||||
*/
|
||||
bool getEnableMeshVisible() const { return _skeletonModel->isVisible(); }
|
||||
bool getEnableMeshVisible() const override;
|
||||
|
||||
/**jsdoc
|
||||
* Set whether or not your avatar mesh is visible.
|
||||
|
@ -1171,7 +1171,7 @@ public slots:
|
|||
* MyAvatar.setEnableMeshVisible(true);
|
||||
* }, 10000);
|
||||
*/
|
||||
void setEnableMeshVisible(bool isEnabled);
|
||||
virtual void setEnableMeshVisible(bool isEnabled) override;
|
||||
|
||||
/**jsdoc
|
||||
* @function MyAvatar.setEnableInverseKinematics
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <shared/FileUtils.h>
|
||||
#include <shared/QtHelpers.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <MainWindow.h>
|
||||
#include <OffscreenUi.h>
|
||||
#include <StatTracker.h>
|
||||
#include <Trace.h>
|
||||
|
@ -186,3 +187,7 @@ void TestScriptingInterface::saveObject(QVariant variant, const QString& filenam
|
|||
file.write(jsonData);
|
||||
file.close();
|
||||
}
|
||||
|
||||
void TestScriptingInterface::showMaximized() {
|
||||
qApp->getWindow()->showMaximized();
|
||||
}
|
|
@ -27,70 +27,128 @@ public slots:
|
|||
|
||||
/**jsdoc
|
||||
* Exits the application
|
||||
* @function Test.quit
|
||||
*/
|
||||
void quit();
|
||||
|
||||
/**jsdoc
|
||||
* Waits for all texture transfers to be complete
|
||||
* @function Test.waitForTextureIdle
|
||||
*/
|
||||
void waitForTextureIdle();
|
||||
|
||||
/**jsdoc
|
||||
* Waits for all pending downloads to be complete
|
||||
* @function Test.waitForDownloadIdle
|
||||
*/
|
||||
void waitForDownloadIdle();
|
||||
|
||||
/**jsdoc
|
||||
* Waits for all file parsing operations to be complete
|
||||
* @function Test.waitForProcessingIdle
|
||||
*/
|
||||
void waitForProcessingIdle();
|
||||
|
||||
/**jsdoc
|
||||
* Waits for all pending downloads, parsing and texture transfers to be complete
|
||||
* @function Test.waitIdle
|
||||
*/
|
||||
void waitIdle();
|
||||
|
||||
/**jsdoc
|
||||
* Waits for establishment of connection to server
|
||||
* @function Test.waitForConnection
|
||||
* @param {int} maxWaitMs [default=10000] - Number of milliseconds to wait
|
||||
*/
|
||||
bool waitForConnection(qint64 maxWaitMs = 10000);
|
||||
|
||||
/**jsdoc
|
||||
* Waits a specific number of milliseconds
|
||||
* @function Test.wait
|
||||
* @param {int} milliseconds - Number of milliseconds to wait
|
||||
*/
|
||||
void wait(int milliseconds);
|
||||
|
||||
/**jsdoc
|
||||
* Waits for all pending downloads, parsing and texture transfers to be complete
|
||||
* @function Test.loadTestScene
|
||||
* @param {string} sceneFile - URL of scene to load
|
||||
*/
|
||||
bool loadTestScene(QString sceneFile);
|
||||
|
||||
/**jsdoc
|
||||
* Clears all caches
|
||||
* @function Test.clear
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**jsdoc
|
||||
* Start recording Chrome compatible tracing events
|
||||
* logRules can be used to specify a set of logging category rules to limit what gets captured
|
||||
* @function Test.startTracing
|
||||
* @param {string} logrules [defaultValue=""] - See implementation for explanation
|
||||
*/
|
||||
bool startTracing(QString logrules = "");
|
||||
|
||||
/**jsdoc
|
||||
* Stop recording Chrome compatible tracing events and serialize recorded events to a file
|
||||
* Using a filename with a .gz extension will automatically compress the output file
|
||||
* @function Test.stopTracing
|
||||
* @param {string} filename - Name of file to save to
|
||||
* @returns {bool} True if successful.
|
||||
*/
|
||||
bool stopTracing(QString filename);
|
||||
|
||||
/**jsdoc
|
||||
* Starts a specific trace event
|
||||
* @function Test.startTraceEvent
|
||||
* @param {string} name - Name of event
|
||||
*/
|
||||
void startTraceEvent(QString name);
|
||||
|
||||
/**jsdoc
|
||||
* Stop a specific name event
|
||||
* Using a filename with a .gz extension will automatically compress the output file
|
||||
* @function Test.endTraceEvent
|
||||
* @param {string} filename - Name of event
|
||||
*/
|
||||
void endTraceEvent(QString name);
|
||||
|
||||
/**jsdoc
|
||||
* Write detailed timing stats of next physics stepSimulation() to filename
|
||||
* @function Test.savePhysicsSimulationStats
|
||||
* @param {string} filename - Name of file to save to
|
||||
*/
|
||||
void savePhysicsSimulationStats(QString filename);
|
||||
|
||||
/**jsdoc
|
||||
* Profiles a specific function
|
||||
* @function Test.savePhysicsSimulationStats
|
||||
* @param {string} name - Name used to reference the function
|
||||
* @param {function} function - Function to profile
|
||||
*/
|
||||
Q_INVOKABLE void profileRange(const QString& name, QScriptValue function);
|
||||
|
||||
/**jsdoc
|
||||
* Clear all caches (menu command Reload Content)
|
||||
* @function Test.clearCaches
|
||||
*/
|
||||
void clearCaches();
|
||||
|
||||
/**jsdoc
|
||||
* Save a JSON object to a file in the test results location
|
||||
* @function Test.saveObject
|
||||
* @param {string} name - Name of the object
|
||||
* @param {string} filename - Name of file to save to
|
||||
*/
|
||||
void saveObject(QVariant v, const QString& filename);
|
||||
|
||||
/**jsdoc
|
||||
* Maximizes the window
|
||||
* @function Test.showMaximized
|
||||
*/
|
||||
void showMaximized();
|
||||
|
||||
private:
|
||||
bool waitForCondition(qint64 maxWaitMs, std::function<bool()> condition);
|
||||
QString _testResultsLocation;
|
||||
|
|
|
@ -446,12 +446,12 @@ void WindowScriptingInterface::takeSnapshot(bool notify, bool includeAnimated, f
|
|||
qApp->takeSnapshot(notify, includeAnimated, aspectRatio, filename);
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::takeSecondaryCameraSnapshot(const QString& filename) {
|
||||
qApp->takeSecondaryCameraSnapshot(filename);
|
||||
void WindowScriptingInterface::takeSecondaryCameraSnapshot(const bool& notify, const QString& filename) {
|
||||
qApp->takeSecondaryCameraSnapshot(notify, filename);
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat, const QString& filename) {
|
||||
qApp->takeSecondaryCamera360Snapshot(cameraPosition, cubemapOutputFormat, filename);
|
||||
void WindowScriptingInterface::takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat, const bool& notify, const QString& filename) {
|
||||
qApp->takeSecondaryCamera360Snapshot(cameraPosition, cubemapOutputFormat, notify, filename);
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::shareSnapshot(const QString& path, const QUrl& href) {
|
||||
|
|
|
@ -360,24 +360,28 @@ public slots:
|
|||
/**jsdoc
|
||||
* Takes a still snapshot of the current view from the secondary camera that can be set up through the {@link Render} API.
|
||||
* @function Window.takeSecondaryCameraSnapshot
|
||||
* @param {string} [filename=""] - If this parameter is not given, the image will be saved as "hifi-snap-by-<user name>-YYYY-MM-DD_HH-MM-SS".
|
||||
* @param {boolean} [notify=true] - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken}
|
||||
* signal.
|
||||
* @param {string} [filename=""] - If this parameter is not given, the image will be saved as "hifi-snap-by-<user name>-YYYY-MM-DD_HH-MM-SS".
|
||||
* If this parameter is <code>""</code> then the image will be saved as ".jpg".
|
||||
* Otherwise, the image will be saved to this filename, with an appended ".jpg".
|
||||
*/
|
||||
void takeSecondaryCameraSnapshot(const QString& filename = QString());
|
||||
void takeSecondaryCameraSnapshot(const bool& notify = true, const QString& filename = QString());
|
||||
|
||||
/**jsdoc
|
||||
* Takes a 360° snapshot at a given position for the secondary camera. The secondary camera does not need to have been
|
||||
* set up.
|
||||
* @function Window.takeSecondaryCamera360Snapshot
|
||||
* @param {Vec3} cameraPosition - The position of the camera for the snapshot.
|
||||
* @param {boolean} [cubemapOutputFormat=false] - If <code>true</code> then the snapshot is saved as a cube map image,
|
||||
* otherwise is saved as an equirectangular image.
|
||||
* @param {string} [filename=""] - If this parameter is not supplied, the image will be saved as "hifi-snap-by-<user name>-YYYY-MM-DD_HH-MM-SS".
|
||||
* If this parameter is <code>""</code> then the image will be saved as ".jpg".
|
||||
* Otherwise, the image will be saved to this filename, with an appended ".jpg".
|
||||
*/
|
||||
void takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat = false, const QString& filename = QString());
|
||||
* Takes a 360° snapshot at a given position for the secondary camera. The secondary camera does not need to have been
|
||||
* set up.
|
||||
* @function Window.takeSecondaryCamera360Snapshot
|
||||
* @param {Vec3} cameraPosition - The position of the camera for the snapshot.
|
||||
* @param {boolean} [cubemapOutputFormat=false] - If <code>true</code> then the snapshot is saved as a cube map image,
|
||||
* otherwise is saved as an equirectangular image.
|
||||
* @param {boolean} [notify=true] - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken}
|
||||
* signal.
|
||||
* @param {string} [filename=""] - If this parameter is not supplied, the image will be saved as "hifi-snap-by-<user name>-YYYY-MM-DD_HH-MM-SS".
|
||||
* If this parameter is <code>""</code> then the image will be saved as ".jpg".
|
||||
* Otherwise, the image will be saved to this filename, with an appended ".jpg".
|
||||
*/
|
||||
void takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat = false, const bool& notify = true, const QString& filename = QString());
|
||||
|
||||
/**jsdoc
|
||||
* Emit a {@link Window.connectionAdded|connectionAdded} or a {@link Window.connectionError|connectionError} signal that
|
||||
|
|
|
@ -122,8 +122,9 @@ static const glm::quat CAMERA_ORIENTATION_LEFT(glm::quat(glm::radians(glm::vec3(
|
|||
static const glm::quat CAMERA_ORIENTATION_BACK(glm::quat(glm::radians(glm::vec3(0.0f, 180.0f, 0.0f))));
|
||||
static const glm::quat CAMERA_ORIENTATION_RIGHT(glm::quat(glm::radians(glm::vec3(0.0f, 270.0f, 0.0f))));
|
||||
static const glm::quat CAMERA_ORIENTATION_UP(glm::quat(glm::radians(glm::vec3(90.0f, 0.0f, 0.0f))));
|
||||
void Snapshot::save360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat, const QString& filename) {
|
||||
void Snapshot::save360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat, const bool& notify, const QString& filename) {
|
||||
_snapshotFilename = filename;
|
||||
_notify360 = notify;
|
||||
_cubemapOutputFormat = cubemapOutputFormat;
|
||||
SecondaryCameraJobConfig* secondaryCameraRenderConfig = static_cast<SecondaryCameraJobConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera"));
|
||||
|
||||
|
@ -247,7 +248,8 @@ void Snapshot::convertToCubemap() {
|
|||
|
||||
painter.end();
|
||||
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->snapshot360Taken(saveSnapshot(outputImage, _snapshotFilename), true);
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->snapshot360Taken(saveSnapshot(outputImage, _snapshotFilename),
|
||||
_notify360);
|
||||
}
|
||||
|
||||
void Snapshot::convertToEquirectangular() {
|
||||
|
@ -327,7 +329,8 @@ void Snapshot::convertToEquirectangular() {
|
|||
}
|
||||
}
|
||||
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->snapshot360Taken(saveSnapshot(outputImage, _snapshotFilename), true);
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->snapshot360Taken(saveSnapshot(outputImage, _snapshotFilename),
|
||||
_notify360);
|
||||
}
|
||||
|
||||
QTemporaryFile* Snapshot::saveTempSnapshot(QImage image) {
|
||||
|
|
|
@ -50,7 +50,10 @@ class Snapshot : public QObject, public Dependency {
|
|||
public:
|
||||
Snapshot();
|
||||
QString saveSnapshot(QImage image, const QString& filename, const QString& pathname = QString());
|
||||
void save360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat, const QString& filename);
|
||||
void save360Snapshot(const glm::vec3& cameraPosition,
|
||||
const bool& cubemapOutputFormat,
|
||||
const bool& notify,
|
||||
const QString& filename);
|
||||
QTemporaryFile* saveTempSnapshot(QImage image);
|
||||
SnapshotMetaData* parseSnapshotData(QString snapshotPath);
|
||||
|
||||
|
@ -89,6 +92,7 @@ private:
|
|||
const QString& userSelectedFilename = QString(),
|
||||
const QString& userSelectedPathname = QString());
|
||||
QString _snapshotFilename;
|
||||
bool _notify360;
|
||||
bool _cubemapOutputFormat;
|
||||
QTimer _snapshotTimer;
|
||||
qint16 _snapshotIndex;
|
||||
|
|
|
@ -103,10 +103,9 @@ void ModelOverlay::update(float deltatime) {
|
|||
if (_visibleDirty) {
|
||||
_visibleDirty = false;
|
||||
// don't show overlays in mirrors or spectator-cam unless _isVisibleInSecondaryCamera is true
|
||||
_model->setVisibleInScene(getVisible(), scene,
|
||||
render::ItemKey::TAG_BITS_0 |
|
||||
(_isVisibleInSecondaryCamera ? render::ItemKey::TAG_BITS_1 : render::ItemKey::TAG_BITS_NONE),
|
||||
false);
|
||||
uint8_t modelRenderTagMask = (_isVisibleInSecondaryCamera ? render::hifi::TAG_ALL_VIEWS : render::hifi::TAG_MAIN_VIEW);
|
||||
_model->setTagMask(modelRenderTagMask, scene);
|
||||
_model->setVisibleInScene(getVisible(), scene);
|
||||
}
|
||||
if (_drawInFrontDirty) {
|
||||
_drawInFrontDirty = false;
|
||||
|
|
|
@ -132,7 +132,6 @@ private:
|
|||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const Overlay::Pointer& overlay);
|
||||
template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay);
|
||||
template <> int payloadGetLayer(const Overlay::Pointer& overlay);
|
||||
template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args);
|
||||
template <> const ShapeKey shapeGetShapeKey(const Overlay::Pointer& overlay);
|
||||
template <> uint32_t metaFetchMetaSubItems(const Overlay::Pointer& overlay, ItemIDs& subItems);
|
||||
|
|
|
@ -35,14 +35,18 @@ namespace render {
|
|||
auto builder = ItemKey::Builder().withTypeShape();
|
||||
if (overlay->is3D()) {
|
||||
auto overlay3D = std::static_pointer_cast<Base3DOverlay>(overlay);
|
||||
if (overlay3D->getDrawInFront() || overlay3D->getDrawHUDLayer()) {
|
||||
builder.withLayered();
|
||||
if (overlay3D->getDrawInFront()) {
|
||||
builder.withLayer(render::hifi::LAYER_3D_FRONT);
|
||||
} else if (overlay3D->getDrawHUDLayer()) {
|
||||
builder.withLayer(render::hifi::LAYER_3D_HUD);
|
||||
}
|
||||
|
||||
if (overlay->isTransparent()) {
|
||||
builder.withTransparent();
|
||||
}
|
||||
} else {
|
||||
builder.withViewSpace();
|
||||
builder.withLayer(render::hifi::LAYER_2D);
|
||||
}
|
||||
|
||||
if (!overlay->getVisible()) {
|
||||
|
@ -50,30 +54,14 @@ namespace render {
|
|||
}
|
||||
|
||||
// always visible in primary view. if isVisibleInSecondaryCamera, also draw in secondary view
|
||||
uint32_t viewTaskBits = render::ItemKey::TAG_BITS_0 |
|
||||
(overlay->getIsVisibleInSecondaryCamera() ? render::ItemKey::TAG_BITS_1 : render::ItemKey::TAG_BITS_NONE);
|
||||
|
||||
builder.withTagBits(viewTaskBits);
|
||||
render::hifi::Tag viewTagBits = overlay->getIsVisibleInSecondaryCamera() ? render::hifi::TAG_ALL_VIEWS : render::hifi::TAG_MAIN_VIEW;
|
||||
builder.withTagBits(viewTagBits);
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
template <> const Item::Bound payloadGetBound(const Overlay::Pointer& overlay) {
|
||||
return overlay->getBounds();
|
||||
}
|
||||
template <> int payloadGetLayer(const Overlay::Pointer& overlay) {
|
||||
if (overlay->is3D()) {
|
||||
auto overlay3D = std::dynamic_pointer_cast<Base3DOverlay>(overlay);
|
||||
if (overlay3D->getDrawInFront()) {
|
||||
return Item::LAYER_3D_FRONT;
|
||||
} else if (overlay3D->getDrawHUDLayer()) {
|
||||
return Item::LAYER_3D_HUD;
|
||||
} else {
|
||||
return Item::LAYER_3D;
|
||||
}
|
||||
} else {
|
||||
return Item::LAYER_2D;
|
||||
}
|
||||
}
|
||||
template <> void payloadRender(const Overlay::Pointer& overlay, RenderArgs* args) {
|
||||
if (args) {
|
||||
overlay->render(args);
|
||||
|
@ -83,7 +71,6 @@ namespace render {
|
|||
return overlay->getShapeKey();
|
||||
}
|
||||
|
||||
|
||||
template <> uint32_t metaFetchMetaSubItems(const Overlay::Pointer& overlay, ItemIDs& subItems) {
|
||||
return overlay->fetchMetaSubItems(subItems);
|
||||
}
|
||||
|
|
|
@ -52,10 +52,15 @@ const glm::vec3 HAND_TO_PALM_OFFSET(0.0f, 0.12f, 0.08f);
|
|||
|
||||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const AvatarSharedPointer& avatar) {
|
||||
return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(ItemKey::TAG_BITS_0 | ItemKey::TAG_BITS_1).withMetaCullGroup();
|
||||
ItemKey::Builder keyBuilder = ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(render::hifi::TAG_ALL_VIEWS).withMetaCullGroup();
|
||||
auto avatarPtr = static_pointer_cast<Avatar>(avatar);
|
||||
if (!avatarPtr->getEnableMeshVisible()) {
|
||||
keyBuilder.withInvisible();
|
||||
}
|
||||
return keyBuilder.build();
|
||||
}
|
||||
template <> const Item::Bound payloadGetBound(const AvatarSharedPointer& avatar) {
|
||||
return static_pointer_cast<Avatar>(avatar)->getBounds();
|
||||
return static_pointer_cast<Avatar>(avatar)->getRenderBounds();
|
||||
}
|
||||
template <> void payloadRender(const AvatarSharedPointer& avatar, RenderArgs* args) {
|
||||
auto avatarPtr = static_pointer_cast<Avatar>(avatar);
|
||||
|
@ -164,6 +169,11 @@ AABox Avatar::getBounds() const {
|
|||
return _skeletonModel->getRenderableMeshBound();
|
||||
}
|
||||
|
||||
|
||||
AABox Avatar::getRenderBounds() const {
|
||||
return _renderBound;
|
||||
}
|
||||
|
||||
void Avatar::animateScaleChanges(float deltaTime) {
|
||||
|
||||
if (_isAnimatingScale) {
|
||||
|
@ -569,16 +579,26 @@ static TextRenderer3D* textRenderer(TextRendererType type) {
|
|||
|
||||
void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||
auto avatarPayload = new render::Payload<AvatarData>(self);
|
||||
auto avatarPayloadPointer = Avatar::PayloadPointer(avatarPayload);
|
||||
|
||||
auto avatarPayloadPointer = std::shared_ptr<render::Payload<AvatarData>>(avatarPayload);
|
||||
if (_renderItemID == render::Item::INVALID_ITEM_ID) {
|
||||
_renderItemID = scene->allocateID();
|
||||
}
|
||||
// INitialize the _render bound as we are creating the avatar render item
|
||||
_renderBound = getBounds();
|
||||
transaction.resetItem(_renderItemID, avatarPayloadPointer);
|
||||
_skeletonModel->addToScene(scene, transaction);
|
||||
_skeletonModel->setTagMask(render::hifi::TAG_ALL_VIEWS);
|
||||
_skeletonModel->setGroupCulled(true);
|
||||
_skeletonModel->setCanCastShadow(true);
|
||||
_skeletonModel->setVisibleInScene(_isMeshVisible, scene);
|
||||
|
||||
processMaterials();
|
||||
for (auto& attachmentModel : _attachmentModels) {
|
||||
attachmentModel->addToScene(scene, transaction);
|
||||
attachmentModel->setTagMask(render::hifi::TAG_ALL_VIEWS);
|
||||
attachmentModel->setGroupCulled(false);
|
||||
attachmentModel->setCanCastShadow(true);
|
||||
attachmentModel->setVisibleInScene(_isMeshVisible, scene);
|
||||
}
|
||||
|
||||
_mustFadeIn = true;
|
||||
|
@ -637,7 +657,15 @@ void Avatar::removeFromScene(AvatarSharedPointer self, const render::ScenePointe
|
|||
|
||||
void Avatar::updateRenderItem(render::Transaction& transaction) {
|
||||
if (render::Item::isValidID(_renderItemID)) {
|
||||
transaction.updateItem<render::Payload<AvatarData>>(_renderItemID, [](render::Payload<AvatarData>& p) {});
|
||||
auto renderBound = getBounds();
|
||||
transaction.updateItem<AvatarData>(_renderItemID,
|
||||
[renderBound](AvatarData& avatar) {
|
||||
auto avatarPtr = dynamic_cast<Avatar*>(&avatar);
|
||||
if (avatarPtr) {
|
||||
avatarPtr->_renderBound = renderBound;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -759,6 +787,18 @@ void Avatar::render(RenderArgs* renderArgs) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void Avatar::setEnableMeshVisible(bool isEnabled) {
|
||||
if (_isMeshVisible != isEnabled) {
|
||||
_isMeshVisible = isEnabled;
|
||||
_needMeshVisibleSwitch = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Avatar::getEnableMeshVisible() const {
|
||||
return _isMeshVisible;
|
||||
}
|
||||
|
||||
void Avatar::fixupModelsInScene(const render::ScenePointer& scene) {
|
||||
bool canTryFade{ false };
|
||||
|
||||
|
@ -770,6 +810,12 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) {
|
|||
if (_skeletonModel->isRenderable() && _skeletonModel->needsFixupInScene()) {
|
||||
_skeletonModel->removeFromScene(scene, transaction);
|
||||
_skeletonModel->addToScene(scene, transaction);
|
||||
|
||||
_skeletonModel->setTagMask(render::hifi::TAG_ALL_VIEWS);
|
||||
_skeletonModel->setGroupCulled(true);
|
||||
_skeletonModel->setCanCastShadow(true);
|
||||
_skeletonModel->setVisibleInScene(_isMeshVisible, scene);
|
||||
|
||||
processMaterials();
|
||||
canTryFade = true;
|
||||
_isAnimatingScale = true;
|
||||
|
@ -778,9 +824,25 @@ void Avatar::fixupModelsInScene(const render::ScenePointer& scene) {
|
|||
if (attachmentModel->isRenderable() && attachmentModel->needsFixupInScene()) {
|
||||
attachmentModel->removeFromScene(scene, transaction);
|
||||
attachmentModel->addToScene(scene, transaction);
|
||||
|
||||
attachmentModel->setTagMask(render::hifi::TAG_ALL_VIEWS);
|
||||
attachmentModel->setGroupCulled(false);
|
||||
attachmentModel->setCanCastShadow(true);
|
||||
attachmentModel->setVisibleInScene(_isMeshVisible, scene);
|
||||
}
|
||||
}
|
||||
|
||||
if (_needMeshVisibleSwitch) {
|
||||
_skeletonModel->setVisibleInScene(_isMeshVisible, scene);
|
||||
for (auto attachmentModel : _attachmentModels) {
|
||||
if (attachmentModel->isRenderable()) {
|
||||
attachmentModel->setVisibleInScene(_isMeshVisible, scene);
|
||||
}
|
||||
}
|
||||
updateRenderItem(transaction);
|
||||
_needMeshVisibleSwitch = false;
|
||||
}
|
||||
|
||||
if (_mustFadeIn && canTryFade) {
|
||||
// Do it now to be sure all the sub items are ready and the fade is sent to them too
|
||||
fade(transaction, render::Transition::USER_ENTER_DOMAIN);
|
||||
|
|
|
@ -74,7 +74,6 @@ public:
|
|||
virtual void instantiableAvatar() = 0;
|
||||
|
||||
typedef render::Payload<AvatarData> Payload;
|
||||
typedef std::shared_ptr<render::Item::PayloadInterface> PayloadPointer;
|
||||
|
||||
void init();
|
||||
void updateAvatarEntities();
|
||||
|
@ -322,6 +321,7 @@ public:
|
|||
bool hasNewJointData() const { return _hasNewJointData; }
|
||||
|
||||
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.
|
||||
|
||||
void addToScene(AvatarSharedPointer self, const render::ScenePointer& scene);
|
||||
void ensureInScene(AvatarSharedPointer self, const render::ScenePointer& scene);
|
||||
|
@ -356,6 +356,10 @@ public:
|
|||
|
||||
virtual void setAvatarEntityDataChanged(bool value) override;
|
||||
|
||||
// Show hide the model representation of the avatar
|
||||
virtual void setEnableMeshVisible(bool isEnabled);
|
||||
virtual bool getEnableMeshVisible() const;
|
||||
|
||||
void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) override;
|
||||
void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) override;
|
||||
|
||||
|
@ -532,6 +536,10 @@ protected:
|
|||
std::mutex _materialsLock;
|
||||
|
||||
void processMaterials();
|
||||
|
||||
AABox _renderBound;
|
||||
bool _isMeshVisible{ true };
|
||||
bool _needMeshVisibleSwitch{ true };
|
||||
};
|
||||
|
||||
#endif // hifi_Avatar_h
|
||||
|
|
|
@ -35,7 +35,7 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
|
|||
_useDualQuaternionSkinning = true;
|
||||
|
||||
// Avatars all cast shadow
|
||||
_canCastShadow = true;
|
||||
setCanCastShadow(true);
|
||||
|
||||
assert(_owningAvatar);
|
||||
}
|
||||
|
|
|
@ -46,6 +46,8 @@
|
|||
|
||||
const char* SRGB_TO_LINEAR_FRAG = R"SCRIBE(
|
||||
|
||||
// OpenGLDisplayPlugin_present.frag
|
||||
|
||||
uniform sampler2D colorMap;
|
||||
|
||||
in vec2 varTexCoord0;
|
||||
|
|
|
@ -157,16 +157,20 @@ Item::Bound EntityRenderer::getBound() {
|
|||
return _bound;
|
||||
}
|
||||
|
||||
render::hifi::Tag EntityRenderer::getTagMask() const {
|
||||
return _isVisibleInSecondaryCamera ? render::hifi::TAG_ALL_VIEWS : render::hifi::TAG_MAIN_VIEW;
|
||||
}
|
||||
|
||||
ItemKey EntityRenderer::getKey() {
|
||||
if (isTransparent()) {
|
||||
return ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1);
|
||||
return ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(getTagMask());
|
||||
}
|
||||
|
||||
// This allows shapes to cast shadows
|
||||
if (_canCastShadow) {
|
||||
return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1).withShadowCaster();
|
||||
return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(getTagMask()).withShadowCaster();
|
||||
} else {
|
||||
return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1);
|
||||
return ItemKey::Builder::opaqueShape().withTypeMeta().withTagBits(getTagMask());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -380,6 +384,7 @@ void EntityRenderer::doRenderUpdateSynchronous(const ScenePointer& scene, Transa
|
|||
|
||||
_moving = entity->isMovingRelativeToParent();
|
||||
_visible = entity->getVisible();
|
||||
setIsVisibleInSecondaryCamera(entity->isVisibleInSecondaryCamera());
|
||||
_canCastShadow = entity->getCanCastShadow();
|
||||
_cauterized = entity->getCauterized();
|
||||
_needsRenderUpdate = false;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "AbstractViewStateInterface.h"
|
||||
#include "EntitiesRendererLogging.h"
|
||||
#include <graphics-scripting/Forward.h>
|
||||
#include <RenderHifi.h>
|
||||
|
||||
class EntityTreeRenderer;
|
||||
|
||||
|
@ -74,6 +75,7 @@ protected:
|
|||
virtual Item::Bound getBound() override;
|
||||
virtual void render(RenderArgs* args) override final;
|
||||
virtual uint32_t metaFetchMetaSubItems(ItemIDs& subItems) override;
|
||||
virtual render::hifi::Tag getTagMask() const;
|
||||
|
||||
// Returns true if the item in question needs to have updateInScene called because of internal rendering state changes
|
||||
virtual bool needsRenderUpdate() const;
|
||||
|
@ -97,6 +99,8 @@ protected:
|
|||
bool isFading() const { return _isFading; }
|
||||
virtual bool isTransparent() const { return _isFading ? Interpolate::calculateFadeRatio(_fadeStartTime) < 1.0f : false; }
|
||||
inline bool isValidRenderItem() const { return _renderItemID != Item::INVALID_ITEM_ID; }
|
||||
|
||||
virtual void setIsVisibleInSecondaryCamera(bool value) { _isVisibleInSecondaryCamera = value; }
|
||||
|
||||
template <typename F, typename T>
|
||||
T withReadLockResult(const std::function<T()>& f) {
|
||||
|
@ -129,6 +133,7 @@ protected:
|
|||
bool _isFading{ _entitiesShouldFadeFunction() };
|
||||
bool _prevIsTransparent { false };
|
||||
bool _visible { false };
|
||||
bool _isVisibleInSecondaryCamera { false };
|
||||
bool _canCastShadow { false };
|
||||
bool _cauterized { false };
|
||||
bool _moving { false };
|
||||
|
|
|
@ -43,7 +43,7 @@ void MaterialEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer&
|
|||
|
||||
ItemKey MaterialEntityRenderer::getKey() {
|
||||
ItemKey::Builder builder;
|
||||
builder.withTypeShape().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1);
|
||||
builder.withTypeShape().withTagBits(getTagMask());
|
||||
|
||||
if (!_visible) {
|
||||
builder.withInvisible();
|
||||
|
|
|
@ -1060,9 +1060,9 @@ ModelEntityRenderer::ModelEntityRenderer(const EntityItemPointer& entity) : Pare
|
|||
|
||||
void ModelEntityRenderer::setKey(bool didVisualGeometryRequestSucceed) {
|
||||
if (didVisualGeometryRequestSucceed) {
|
||||
_itemKey = ItemKey::Builder().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1);
|
||||
_itemKey = ItemKey::Builder().withTypeMeta().withTagBits(getTagMask());
|
||||
} else {
|
||||
_itemKey = ItemKey::Builder().withTypeMeta().withTypeShape().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1);
|
||||
_itemKey = ItemKey::Builder().withTypeMeta().withTypeShape().withTagBits(getTagMask());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1070,6 +1070,13 @@ ItemKey ModelEntityRenderer::getKey() {
|
|||
return _itemKey;
|
||||
}
|
||||
|
||||
render::hifi::Tag ModelEntityRenderer::getTagMask() const {
|
||||
// Default behavior for model is to not be visible in main view if cauterized (aka parented to the avatar's neck joint)
|
||||
return _cauterized ?
|
||||
(_isVisibleInSecondaryCamera ? render::hifi::TAG_SECONDARY_VIEW : render::hifi::TAG_NONE) :
|
||||
Parent::getTagMask(); // calculate which views to be shown in
|
||||
}
|
||||
|
||||
uint32_t ModelEntityRenderer::metaFetchMetaSubItems(ItemIDs& subItems) {
|
||||
if (_model) {
|
||||
auto metaSubItems = _subRenderItemIDs;
|
||||
|
@ -1329,6 +1336,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
emit DependencyManager::get<scriptable::ModelProviderFactory>()->
|
||||
modelAddedToScene(entity->getEntityItemID(), NestableType::Entity, _model);
|
||||
}
|
||||
_didLastVisualGeometryRequestSucceed = didVisualGeometryRequestSucceed;
|
||||
});
|
||||
connect(model.get(), &Model::requestRenderUpdate, this, &ModelEntityRenderer::requestRenderUpdate);
|
||||
connect(entity.get(), &RenderableModelEntityItem::requestCollisionGeometryUpdate, this, &ModelEntityRenderer::flagForCollisionGeometryUpdate);
|
||||
|
@ -1386,21 +1394,22 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
entity->updateModelBounds();
|
||||
entity->stopModelOverrideIfNoParent();
|
||||
|
||||
// Default behavior for model is to not be visible in main view if cauterized (aka parented to the avatar's neck joint)
|
||||
uint32_t viewTaskBits = _cauterized ?
|
||||
render::ItemKey::TAG_BITS_1 : // draw in every view except the main one (view zero)
|
||||
render::ItemKey::TAG_BITS_ALL; // draw in all views
|
||||
|
||||
if (model->isVisible() != _visible || model->getViewTagBits() != viewTaskBits) {
|
||||
render::hifi::Tag tagMask = getTagMask();
|
||||
if (model->isVisible() != _visible) {
|
||||
// FIXME: this seems like it could be optimized if we tracked our last known visible state in
|
||||
// the renderable item. As it stands now the model checks it's visible/invisible state
|
||||
// so most of the time we don't do anything in this function.
|
||||
model->setVisibleInScene(_visible, scene, viewTaskBits, false);
|
||||
model->setVisibleInScene(_visible, scene);
|
||||
}
|
||||
|
||||
if (model->getTagMask() != tagMask) {
|
||||
model->setTagMask(tagMask, scene);
|
||||
}
|
||||
|
||||
// TODO? early exit here when not visible?
|
||||
|
||||
if (model->canCastShadow() != _canCastShadow) {
|
||||
model->setCanCastShadow(_canCastShadow, scene, viewTaskBits, false);
|
||||
model->setCanCastShadow(_canCastShadow, scene);
|
||||
}
|
||||
|
||||
if (_needsCollisionGeometryUpdate) {
|
||||
|
@ -1473,6 +1482,11 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce
|
|||
}
|
||||
}
|
||||
|
||||
void ModelEntityRenderer::setIsVisibleInSecondaryCamera(bool value) {
|
||||
Parent::setIsVisibleInSecondaryCamera(value);
|
||||
setKey(_didLastVisualGeometryRequestSucceed);
|
||||
}
|
||||
|
||||
void ModelEntityRenderer::flagForCollisionGeometryUpdate() {
|
||||
_needsCollisionGeometryUpdate = true;
|
||||
emit requestRenderUpdate();
|
||||
|
|
|
@ -164,6 +164,10 @@ protected:
|
|||
void flagForCollisionGeometryUpdate();
|
||||
void setCollisionMeshKey(const void* key);
|
||||
|
||||
render::hifi::Tag getTagMask() const override;
|
||||
|
||||
void setIsVisibleInSecondaryCamera(bool value) override;
|
||||
|
||||
private:
|
||||
void animate(const TypedEntityPointer& entity);
|
||||
void mapJoints(const TypedEntityPointer& entity, const QStringList& modelJointNames);
|
||||
|
@ -202,6 +206,8 @@ private:
|
|||
|
||||
render::ItemKey _itemKey { render::ItemKey::Builder().withTypeMeta() };
|
||||
|
||||
bool _didLastVisualGeometryRequestSucceed { false };
|
||||
|
||||
void processMaterials();
|
||||
};
|
||||
|
||||
|
|
|
@ -147,9 +147,9 @@ void ParticleEffectEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEn
|
|||
|
||||
ItemKey ParticleEffectEntityRenderer::getKey() {
|
||||
if (_visible) {
|
||||
return ItemKey::Builder::transparentShape().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1);
|
||||
return ItemKey::Builder::transparentShape().withTagBits(getTagMask());
|
||||
} else {
|
||||
return ItemKey::Builder().withInvisible().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1).build();
|
||||
return ItemKey::Builder().withInvisible().withTagBits(getTagMask()).build();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -112,7 +112,7 @@ PolyLineEntityRenderer::PolyLineEntityRenderer(const EntityItemPointer& entity)
|
|||
}
|
||||
|
||||
ItemKey PolyLineEntityRenderer::getKey() {
|
||||
return ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1);
|
||||
return ItemKey::Builder::transparentShape().withTypeMeta().withTagBits(getTagMask());
|
||||
}
|
||||
|
||||
ShapeKey PolyLineEntityRenderer::getShapeKey() {
|
||||
|
|
|
@ -169,7 +169,7 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
virtual ItemKey getKey() override { return ItemKey::Builder::opaqueShape().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1); }
|
||||
virtual ItemKey getKey() override { return ItemKey::Builder::opaqueShape().withTagBits(getTagMask()); }
|
||||
virtual ShapeKey getShapeKey() override;
|
||||
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
|
||||
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, Transaction& transaction, const TypedEntityPointer& entity) override;
|
||||
|
|
|
@ -139,7 +139,7 @@ bool ShapeEntityRenderer::isTransparent() const {
|
|||
|
||||
ItemKey ShapeEntityRenderer::getKey() {
|
||||
ItemKey::Builder builder;
|
||||
builder.withTypeShape().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1);
|
||||
builder.withTypeShape().withTypeMeta().withTagBits(getTagMask());
|
||||
|
||||
withReadLock([&] {
|
||||
if (isTransparent()) {
|
||||
|
|
|
@ -269,7 +269,7 @@ void ZoneEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointe
|
|||
|
||||
|
||||
ItemKey ZoneEntityRenderer::getKey() {
|
||||
return ItemKey::Builder().withTypeMeta().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1).build();
|
||||
return ItemKey::Builder().withTypeMeta().withTagBits(getTagMask()).build();
|
||||
}
|
||||
|
||||
bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const {
|
||||
|
|
|
@ -1383,6 +1383,7 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) {
|
|||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(visible, setVisible);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(canCastShadow, setCanCastShadow);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(userData, setUserData);
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(isVisibleInSecondaryCamera, setIsVisibleInSecondaryCamera);
|
||||
|
||||
// Certifiable Properties
|
||||
SET_ENTITY_PROPERTY_FROM_PROPERTIES(itemName, setItemName);
|
||||
|
@ -2760,6 +2761,28 @@ void EntityItem::setVisible(bool value) {
|
|||
}
|
||||
}
|
||||
|
||||
bool EntityItem::isVisibleInSecondaryCamera() const {
|
||||
bool result;
|
||||
withReadLock([&] {
|
||||
result = _isVisibleInSecondaryCamera;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
void EntityItem::setIsVisibleInSecondaryCamera(bool value) {
|
||||
bool changed = false;
|
||||
withWriteLock([&] {
|
||||
if (_isVisibleInSecondaryCamera != value) {
|
||||
changed = true;
|
||||
_isVisibleInSecondaryCamera = value;
|
||||
}
|
||||
});
|
||||
|
||||
if (changed) {
|
||||
emit requestRenderUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
bool EntityItem::getCanCastShadow() const {
|
||||
bool result;
|
||||
withReadLock([&] {
|
||||
|
|
|
@ -277,6 +277,9 @@ public:
|
|||
bool getVisible() const;
|
||||
void setVisible(bool value);
|
||||
|
||||
bool isVisibleInSecondaryCamera() const;
|
||||
void setIsVisibleInSecondaryCamera(bool value);
|
||||
|
||||
bool getCanCastShadow() const;
|
||||
void setCanCastShadow(bool value);
|
||||
|
||||
|
@ -578,6 +581,7 @@ protected:
|
|||
glm::vec3 _registrationPoint { ENTITY_ITEM_DEFAULT_REGISTRATION_POINT };
|
||||
float _angularDamping { ENTITY_ITEM_DEFAULT_ANGULAR_DAMPING };
|
||||
bool _visible { ENTITY_ITEM_DEFAULT_VISIBLE };
|
||||
bool _isVisibleInSecondaryCamera { ENTITY_ITEM_DEFAULT_VISIBLE_IN_SECONDARY_CAMERA };
|
||||
bool _canCastShadow{ ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW };
|
||||
bool _collisionless { ENTITY_ITEM_DEFAULT_COLLISIONLESS };
|
||||
uint16_t _collisionMask { ENTITY_COLLISION_MASK_DEFAULT };
|
||||
|
|
|
@ -368,6 +368,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
CHECK_PROPERTY_CHANGE(PROP_MATERIAL_MAPPING_SCALE, materialMappingScale);
|
||||
CHECK_PROPERTY_CHANGE(PROP_MATERIAL_MAPPING_ROT, materialMappingRot);
|
||||
CHECK_PROPERTY_CHANGE(PROP_MATERIAL_DATA, materialData);
|
||||
CHECK_PROPERTY_CHANGE(PROP_VISIBLE_IN_SECONDARY_CAMERA, isVisibleInSecondaryCamera);
|
||||
|
||||
// Certifiable Properties
|
||||
CHECK_PROPERTY_CHANGE(PROP_ITEM_NAME, itemName);
|
||||
|
@ -490,6 +491,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
|
|||
* {@link Entities.EntityType|Model} and {@link Entities.EntityType|Shape} entities. Shadows are cast if inside a
|
||||
* {@link Entities.EntityType|Zone} entity with <code>castShadows</code> enabled in its
|
||||
* {@link Entities.EntityProperties-Zone|keyLight} property.
|
||||
* @property {boolean} isVisibleInSecondaryCamera=true - Whether or not the entity is rendered in the secondary camera. If <code>true</code> then the entity is rendered.
|
||||
*
|
||||
* @property {Vec3} position=0,0,0 - The position of the entity.
|
||||
* @property {Quat} rotation=0,0,0,1 - The orientation of the entity with respect to world coordinates.
|
||||
|
@ -1235,6 +1237,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool
|
|||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCKED, locked);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_USER_DATA, userData);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ALPHA, alpha);
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VISIBLE_IN_SECONDARY_CAMERA, isVisibleInSecondaryCamera);
|
||||
|
||||
// Certifiable Properties
|
||||
COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ITEM_NAME, itemName);
|
||||
|
@ -1574,6 +1577,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool
|
|||
COPY_PROPERTY_FROM_QSCRIPTVALUE(materialMappingScale, glmVec2, setMaterialMappingScale);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(materialMappingRot, float, setMaterialMappingRot);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(materialData, QString, setMaterialData);
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(isVisibleInSecondaryCamera, bool, setIsVisibleInSecondaryCamera);
|
||||
|
||||
// Certifiable Properties
|
||||
COPY_PROPERTY_FROM_QSCRIPTVALUE(itemName, QString, setItemName);
|
||||
|
@ -1905,7 +1909,7 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
|
|||
ADD_PROPERTY_TO_MAP(PROP_ANGULAR_VELOCITY, AngularVelocity, angularVelocity, glm::vec3);
|
||||
ADD_PROPERTY_TO_MAP(PROP_ANGULAR_DAMPING, AngularDamping, angularDamping, float);
|
||||
ADD_PROPERTY_TO_MAP(PROP_COLLISIONLESS, Collisionless, collisionless, bool);
|
||||
ADD_PROPERTY_TO_MAP(PROP_DYNAMIC, unused, ignoreForCollisions, unused); // legacy support
|
||||
ADD_PROPERTY_TO_MAP(PROP_COLLISIONLESS, unused, ignoreForCollisions, unused); // legacy support
|
||||
ADD_PROPERTY_TO_MAP(PROP_COLLISION_MASK, unused, collisionMask, unused);
|
||||
ADD_PROPERTY_TO_MAP(PROP_COLLISION_MASK, unused, collidesWith, unused);
|
||||
ADD_PROPERTY_TO_MAP(PROP_DYNAMIC, unused, collisionsWillMove, unused); // legacy support
|
||||
|
@ -1953,6 +1957,8 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue
|
|||
ADD_PROPERTY_TO_MAP(PROP_MATERIAL_MAPPING_ROT, MaterialMappingRot, materialMappingRot, float);
|
||||
ADD_PROPERTY_TO_MAP(PROP_MATERIAL_DATA, MaterialData, materialData, QString);
|
||||
|
||||
ADD_PROPERTY_TO_MAP(PROP_VISIBLE_IN_SECONDARY_CAMERA, IsVisibleInSecondaryCamera, isVisibleInSecondaryCamera, bool);
|
||||
|
||||
// Certifiable Properties
|
||||
ADD_PROPERTY_TO_MAP(PROP_ITEM_NAME, ItemName, itemName, QString);
|
||||
ADD_PROPERTY_TO_MAP(PROP_ITEM_DESCRIPTION, ItemDescription, itemDescription, QString);
|
||||
|
@ -3050,6 +3056,8 @@ void EntityItemProperties::markAllChanged() {
|
|||
_cloneDynamicChanged = true;
|
||||
_cloneAvatarEntityChanged = true;
|
||||
_cloneOriginIDChanged = true;
|
||||
|
||||
_isVisibleInSecondaryCameraChanged = true;
|
||||
}
|
||||
|
||||
// The minimum bounding box for the entity.
|
||||
|
@ -3328,6 +3336,9 @@ QList<QString> EntityItemProperties::listChangedProperties() {
|
|||
if (materialDataChanged()) {
|
||||
out += "materialData";
|
||||
}
|
||||
if (isVisibleInSecondaryCameraChanged()) {
|
||||
out += "isVisibleInSecondaryCamera";
|
||||
}
|
||||
|
||||
// Certifiable Properties
|
||||
if (itemNameChanged()) {
|
||||
|
|
|
@ -233,6 +233,8 @@ public:
|
|||
DEFINE_PROPERTY_REF(PROP_MATERIAL_MAPPING_ROT, MaterialMappingRot, materialMappingRot, float, 0);
|
||||
DEFINE_PROPERTY_REF(PROP_MATERIAL_DATA, MaterialData, materialData, QString, "");
|
||||
|
||||
DEFINE_PROPERTY(PROP_VISIBLE_IN_SECONDARY_CAMERA, IsVisibleInSecondaryCamera, isVisibleInSecondaryCamera, bool, ENTITY_ITEM_DEFAULT_VISIBLE_IN_SECONDARY_CAMERA);
|
||||
|
||||
// Certifiable Properties - related to Proof of Purchase certificates
|
||||
DEFINE_PROPERTY_REF(PROP_ITEM_NAME, ItemName, itemName, QString, ENTITY_ITEM_DEFAULT_ITEM_NAME);
|
||||
DEFINE_PROPERTY_REF(PROP_ITEM_DESCRIPTION, ItemDescription, itemDescription, QString, ENTITY_ITEM_DEFAULT_ITEM_DESCRIPTION);
|
||||
|
|
|
@ -46,6 +46,7 @@ const quint32 ENTITY_ITEM_DEFAULT_STATIC_CERTIFICATE_VERSION = 0;
|
|||
const float ENTITY_ITEM_DEFAULT_ALPHA = 1.0f;
|
||||
const float ENTITY_ITEM_DEFAULT_LOCAL_RENDER_ALPHA = 1.0f;
|
||||
const bool ENTITY_ITEM_DEFAULT_VISIBLE = true;
|
||||
const bool ENTITY_ITEM_DEFAULT_VISIBLE_IN_SECONDARY_CAMERA = true;
|
||||
const bool ENTITY_ITEM_DEFAULT_CAN_CAST_SHADOW { true };
|
||||
|
||||
const QString ENTITY_ITEM_DEFAULT_SCRIPT = QString("");
|
||||
|
|
|
@ -249,6 +249,8 @@ enum EntityPropertyList {
|
|||
PROP_MATERIAL_MAPPING_ROT,
|
||||
PROP_MATERIAL_DATA,
|
||||
|
||||
PROP_VISIBLE_IN_SECONDARY_CAMERA, // not sent over the wire, only used locally
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// ATTENTION: add new properties to end of list just ABOVE this line
|
||||
PROP_AFTER_LAST_ITEM,
|
||||
|
|
|
@ -1688,15 +1688,21 @@ QVector<QUuid> EntityScriptingInterface::getChildrenIDs(const QUuid& parentID) {
|
|||
if (!_entityTree) {
|
||||
return result;
|
||||
}
|
||||
|
||||
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(parentID);
|
||||
if (!entity) {
|
||||
qCDebug(entities) << "EntityScriptingInterface::getChildrenIDs - no entity with ID" << parentID;
|
||||
return result;
|
||||
}
|
||||
|
||||
_entityTree->withReadLock([&] {
|
||||
entity->forEachChild([&](SpatiallyNestablePointer child) {
|
||||
QSharedPointer<SpatialParentFinder> parentFinder = DependencyManager::get<SpatialParentFinder>();
|
||||
if (!parentFinder) {
|
||||
return;
|
||||
}
|
||||
bool success;
|
||||
SpatiallyNestableWeakPointer parentWP = parentFinder->find(parentID, success);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
SpatiallyNestablePointer parent = parentWP.lock();
|
||||
if (!parent) {
|
||||
return;
|
||||
}
|
||||
parent->forEachChild([&](SpatiallyNestablePointer child) {
|
||||
result.push_back(child->getID());
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1226,12 +1226,11 @@ public slots:
|
|||
|
||||
|
||||
/**jsdoc
|
||||
* Get the IDs of entities, overlays, and avatars that are directly parented to an entity. To get all descendants of an
|
||||
* entity, recurse on the IDs returned by the function.
|
||||
* Get the IDs of entities, overlays, and avatars that are directly parented to an entity, overlay, or avatar model. Recurse on the IDs returned by the function to get all descendants of an entity, overlay, or avatar.
|
||||
* @function Entities.getChildrenIDs
|
||||
* @param {Uuid} parentID - The ID of the entity to get the children IDs of.
|
||||
* @param {Uuid} parentID - The ID of the entity, overlay, or avatar to get the children IDs of.
|
||||
* @returns {Uuid[]} An array of entity, overlay, and avatar IDs that are parented directly to the <code>parentID</code>
|
||||
* entity. Does not include children's children, etc. The array is empty if no children can be found or
|
||||
* entity, overlay, or avatar. Does not include children's children, etc. The array is empty if no children can be found or
|
||||
* <code>parentID</code> cannot be found.
|
||||
* @example <caption>Report the children of an entity.</caption>
|
||||
* function createEntity(description, position, parent) {
|
||||
|
|
|
@ -2,15 +2,64 @@
|
|||
|
||||
#include "GLLogging.h"
|
||||
|
||||
namespace gl {
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QJsonValue>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QFileInfo>
|
||||
#include <QtCore/QCryptographicHash>
|
||||
|
||||
#include <shared/FileUtils.h>
|
||||
|
||||
using namespace gl;
|
||||
|
||||
void Uniform::load(GLuint glprogram, int index) {
|
||||
const GLint NAME_LENGTH = 256;
|
||||
GLchar glname[NAME_LENGTH];
|
||||
GLint length = 0;
|
||||
glGetActiveUniform(glprogram, index, NAME_LENGTH, &length, &size, &type, glname);
|
||||
name = std::string(glname, length);
|
||||
location = glGetUniformLocation(glprogram, glname);
|
||||
}
|
||||
|
||||
Uniforms gl::loadUniforms(GLuint glprogram) {
|
||||
GLint uniformsCount = 0;
|
||||
glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount);
|
||||
|
||||
Uniforms result;
|
||||
result.resize(uniformsCount);
|
||||
for (int i = 0; i < uniformsCount; i++) {
|
||||
result[i].load(glprogram, i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef SEPARATE_PROGRAM
|
||||
bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, GLuint &programObject, std::string& message) {
|
||||
bool gl::compileShader(GLenum shaderDomain,
|
||||
const std::string& shaderSource,
|
||||
GLuint& shaderObject,
|
||||
GLuint& programObject,
|
||||
std::string& message) {
|
||||
return compileShader(shaderDomain, std::vector<std::string>{ shaderSource }, shaderObject, programObject, message);
|
||||
}
|
||||
#else
|
||||
bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, std::string& message) {
|
||||
bool gl::compileShader(GLenum shaderDomain, const std::string& shaderSource, GLuint& shaderObject, std::string& message) {
|
||||
return compileShader(shaderDomain, std::vector<std::string>{ shaderSource }, shaderObject, message);
|
||||
}
|
||||
#endif
|
||||
if (shaderSource.empty()) {
|
||||
|
||||
#ifdef SEPARATE_PROGRAM
|
||||
bool gl::compileShader(GLenum shaderDomain,
|
||||
const std::string& shaderSource,
|
||||
GLuint& shaderObject,
|
||||
GLuint& programObject,
|
||||
std::string& message) {
|
||||
#else
|
||||
bool gl::compileShader(GLenum shaderDomain,
|
||||
const std::vector<std::string>& shaderSources,
|
||||
GLuint& shaderObject,
|
||||
std::string& message) {
|
||||
#endif
|
||||
if (shaderSources.empty()) {
|
||||
qCDebug(glLogging) << "GLShader::compileShader - no GLSL shader source code ? so failed to create";
|
||||
return false;
|
||||
}
|
||||
|
@ -23,9 +72,11 @@ namespace gl {
|
|||
}
|
||||
|
||||
// Assign the source
|
||||
const int NUM_SOURCE_STRINGS = 2;
|
||||
const GLchar* srcstr[] = { defines.c_str(), shaderSource.c_str() };
|
||||
glShaderSource(glshader, NUM_SOURCE_STRINGS, srcstr, NULL);
|
||||
std::vector<const GLchar*> cstrs;
|
||||
for (const auto& str : shaderSources) {
|
||||
cstrs.push_back(str.c_str());
|
||||
}
|
||||
glShaderSource(glshader, static_cast<GLint>(cstrs.size()), cstrs.data(), NULL);
|
||||
|
||||
// Compile !
|
||||
glCompileShader(glshader);
|
||||
|
@ -66,7 +117,7 @@ namespace gl {
|
|||
|
||||
qCCritical(glLogging) << "GLShader::compileShader - failed to compile the gl shader object:";
|
||||
int lineNumber = 0;
|
||||
for (auto s : srcstr) {
|
||||
for (const auto& s : cstrs) {
|
||||
QString str(s);
|
||||
QStringList lines = str.split("\n");
|
||||
for (auto& line : lines) {
|
||||
|
@ -142,7 +193,7 @@ namespace gl {
|
|||
return true;
|
||||
}
|
||||
|
||||
GLuint compileProgram(const std::vector<GLuint>& glshaders, std::string& message, std::vector<GLchar>& binary) {
|
||||
GLuint gl::compileProgram(const std::vector<GLuint>& glshaders, std::string& message, CachedShader& cachedShader) {
|
||||
// A brand new program:
|
||||
GLuint glprogram = glCreateProgram();
|
||||
if (!glprogram) {
|
||||
|
@ -150,14 +201,21 @@ GLuint compileProgram(const std::vector<GLuint>& glshaders, std::string& message
|
|||
return 0;
|
||||
}
|
||||
|
||||
// glProgramParameteri(glprogram, GL_PROGRAM_, GL_TRUE);
|
||||
// Create the program from the sub shaders
|
||||
for (auto so : glshaders) {
|
||||
glAttachShader(glprogram, so);
|
||||
}
|
||||
bool binaryLoaded = false;
|
||||
|
||||
// Link!
|
||||
glLinkProgram(glprogram);
|
||||
if (glshaders.empty() && cachedShader) {
|
||||
glProgramBinary(glprogram, cachedShader.format, cachedShader.binary.data(), (GLsizei)cachedShader.binary.size());
|
||||
binaryLoaded = true;
|
||||
} else {
|
||||
// glProgramParameteri(glprogram, GL_PROGRAM_, GL_TRUE);
|
||||
// Create the program from the sub shaders
|
||||
for (auto so : glshaders) {
|
||||
glAttachShader(glprogram, so);
|
||||
}
|
||||
|
||||
// Link!
|
||||
glLinkProgram(glprogram);
|
||||
}
|
||||
|
||||
GLint linked = 0;
|
||||
glGetProgramiv(glprogram, GL_LINK_STATUS, &linked);
|
||||
|
@ -205,25 +263,73 @@ GLuint compileProgram(const std::vector<GLuint>& glshaders, std::string& message
|
|||
}
|
||||
|
||||
// If linked get the binaries
|
||||
if (linked) {
|
||||
if (linked && !binaryLoaded) {
|
||||
GLint binaryLength = 0;
|
||||
glGetProgramiv(glprogram, GL_PROGRAM_BINARY_LENGTH, &binaryLength);
|
||||
|
||||
if (binaryLength > 0) {
|
||||
GLint numBinFormats = 0;
|
||||
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &numBinFormats);
|
||||
if (numBinFormats > 0) {
|
||||
binary.resize(binaryLength);
|
||||
std::vector<GLint> binFormats(numBinFormats);
|
||||
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, binFormats.data());
|
||||
|
||||
GLenum programBinFormat;
|
||||
glGetProgramBinary(glprogram, binaryLength, NULL, &programBinFormat, binary.data());
|
||||
}
|
||||
cachedShader.binary.resize(binaryLength);
|
||||
glGetProgramBinary(glprogram, binaryLength, NULL, &cachedShader.format, cachedShader.binary.data());
|
||||
}
|
||||
}
|
||||
|
||||
return glprogram;
|
||||
}
|
||||
|
||||
const QString& getShaderCacheFile() {
|
||||
static const QString SHADER_CACHE_FOLDER{ "shaders" };
|
||||
static const QString SHADER_CACHE_FILE_NAME{ "cache.json" };
|
||||
static const QString SHADER_CACHE_FILE = FileUtils::standardPath(SHADER_CACHE_FOLDER) + SHADER_CACHE_FILE_NAME;
|
||||
return SHADER_CACHE_FILE;
|
||||
}
|
||||
|
||||
static const char* SHADER_JSON_TYPE_KEY = "type";
|
||||
static const char* SHADER_JSON_SOURCE_KEY = "source";
|
||||
static const char* SHADER_JSON_DATA_KEY = "data";
|
||||
|
||||
void gl::loadShaderCache(ShaderCache& cache) {
|
||||
QString shaderCacheFile = getShaderCacheFile();
|
||||
if (QFileInfo(shaderCacheFile).exists()) {
|
||||
QString json = FileUtils::readFile(shaderCacheFile);
|
||||
auto root = QJsonDocument::fromJson(json.toUtf8()).object();
|
||||
for (const auto& qhash : root.keys()) {
|
||||
auto programObject = root[qhash].toObject();
|
||||
QByteArray qbinary = QByteArray::fromBase64(programObject[SHADER_JSON_DATA_KEY].toString().toUtf8());
|
||||
std::string hash = qhash.toStdString();
|
||||
auto& cachedShader = cache[hash];
|
||||
cachedShader.binary.resize(qbinary.size());
|
||||
memcpy(cachedShader.binary.data(), qbinary.data(), qbinary.size());
|
||||
cachedShader.format = (GLenum)programObject[SHADER_JSON_TYPE_KEY].toInt();
|
||||
cachedShader.source = programObject[SHADER_JSON_SOURCE_KEY].toString().toStdString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gl::saveShaderCache(const ShaderCache& cache) {
|
||||
QByteArray json;
|
||||
{
|
||||
QVariantMap variantMap;
|
||||
for (const auto& entry : cache) {
|
||||
const auto& key = entry.first;
|
||||
const auto& type = entry.second.format;
|
||||
const auto& binary = entry.second.binary;
|
||||
QVariantMap qentry;
|
||||
qentry[SHADER_JSON_TYPE_KEY] = QVariant(type);
|
||||
qentry[SHADER_JSON_SOURCE_KEY] = QString(entry.second.source.c_str());
|
||||
qentry[SHADER_JSON_DATA_KEY] = QByteArray{ binary.data(), (int)binary.size() }.toBase64();
|
||||
variantMap[key.c_str()] = qentry;
|
||||
}
|
||||
json = QJsonDocument::fromVariant(variantMap).toJson(QJsonDocument::Indented);
|
||||
}
|
||||
|
||||
if (!json.isEmpty()) {
|
||||
QString shaderCacheFile = getShaderCacheFile();
|
||||
QFile saveFile(shaderCacheFile);
|
||||
saveFile.open(QFile::WriteOnly | QFile::Text | QFile::Truncate);
|
||||
saveFile.write(json);
|
||||
saveFile.close();
|
||||
}
|
||||
}
|
||||
|
||||
std::string gl::getShaderHash(const std::string& shaderSource) {
|
||||
return QCryptographicHash::hash(QByteArray(shaderSource.c_str()), QCryptographicHash::Md5).toBase64().toStdString();
|
||||
}
|
||||
|
|
|
@ -14,15 +14,47 @@
|
|||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace gl {
|
||||
|
||||
struct Uniform {
|
||||
std::string name;
|
||||
GLint size{ -1 };
|
||||
GLenum type{ GL_FLOAT };
|
||||
GLint location{ -1 };
|
||||
void load(GLuint glprogram, int index);
|
||||
};
|
||||
|
||||
using Uniforms = std::vector<Uniform>;
|
||||
|
||||
Uniforms loadUniforms(GLuint glprogram);
|
||||
|
||||
struct CachedShader {
|
||||
GLenum format{ 0 };
|
||||
std::string source;
|
||||
std::vector<char> binary;
|
||||
inline operator bool() const {
|
||||
return format != 0 && !binary.empty();
|
||||
}
|
||||
};
|
||||
|
||||
using ShaderCache = std::unordered_map<std::string, CachedShader>;
|
||||
|
||||
std::string getShaderHash(const std::string& shaderSource);
|
||||
void loadShaderCache(ShaderCache& cache);
|
||||
void saveShaderCache(const ShaderCache& cache);
|
||||
|
||||
|
||||
#ifdef SEPARATE_PROGRAM
|
||||
bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, GLuint &programObject, std::string& message);
|
||||
bool compileShader(GLenum shaderDomain, const std::string& shaderSource, GLuint &shaderObject, GLuint &programObject, std::string& message);
|
||||
bool compileShader(GLenum shaderDomain, const std::vector<std::string>& shaderSources, GLuint &shaderObject, GLuint &programObject, std::string& message);
|
||||
#else
|
||||
bool compileShader(GLenum shaderDomain, const std::string& shaderSource, const std::string& defines, GLuint &shaderObject, std::string& message);
|
||||
bool compileShader(GLenum shaderDomain, const std::string& shaderSource, GLuint &shaderObject, std::string& message);
|
||||
bool compileShader(GLenum shaderDomain, const std::vector<std::string>& shaderSources, GLuint &shaderObject, std::string& message);
|
||||
#endif
|
||||
|
||||
GLuint compileProgram(const std::vector<GLuint>& glshaders, std::string& message, std::vector<GLchar>& binary);
|
||||
GLuint compileProgram(const std::vector<GLuint>& glshaders, std::string& message, CachedShader& binary);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -124,13 +124,16 @@ void GLBackend::init() {
|
|||
GLBackend::GLBackend() {
|
||||
_pipeline._cameraCorrectionBuffer._buffer->flush();
|
||||
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &_uboAlignment);
|
||||
initShaderBinaryCache();
|
||||
}
|
||||
|
||||
GLBackend::~GLBackend() {}
|
||||
|
||||
GLBackend::~GLBackend() {
|
||||
void GLBackend::shutdown() {
|
||||
killInput();
|
||||
killTransform();
|
||||
killTextureManagementStage();
|
||||
killShaderBinaryCache();
|
||||
}
|
||||
|
||||
void GLBackend::renderPassTransfer(const Batch& batch) {
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <QtCore/QLoggingCategory>
|
||||
|
||||
#include <gl/Config.h>
|
||||
#include <gl/GLShaders.h>
|
||||
|
||||
#include <gpu/Forward.h>
|
||||
#include <gpu/Context.h>
|
||||
|
@ -71,6 +72,9 @@ public:
|
|||
|
||||
virtual ~GLBackend();
|
||||
|
||||
// Shutdown rendering and persist any required resources
|
||||
void shutdown() override;
|
||||
|
||||
void setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool reset = false);
|
||||
void render(const Batch& batch) final override;
|
||||
|
||||
|
@ -455,6 +459,13 @@ protected:
|
|||
virtual GLShader* compileBackendProgram(const Shader& program, const Shader::CompilationHandler& handler);
|
||||
virtual GLShader* compileBackendShader(const Shader& shader, const Shader::CompilationHandler& handler);
|
||||
virtual std::string getBackendShaderHeader() const = 0;
|
||||
// For a program, this will return a string containing all the source files (without any
|
||||
// backend headers or defines). For a vertex, fragment or geometry shader, this will
|
||||
// return the fully customized shader with all the version and backend specific
|
||||
// preprocessor directives
|
||||
// The program string returned can be used as a key for a cache of shader binaries
|
||||
// The shader strings can be reliably sent to the low level `compileShader` functions
|
||||
virtual std::string getShaderSource(const Shader& shader, int version) final;
|
||||
virtual void makeProgramBindings(ShaderObject& shaderObject);
|
||||
class ElementResource {
|
||||
public:
|
||||
|
@ -465,12 +476,12 @@ protected:
|
|||
ElementResource getFormatFromGLUniform(GLenum gltype);
|
||||
static const GLint UNUSED_SLOT {-1};
|
||||
static bool isUnusedSlot(GLint binding) { return (binding == UNUSED_SLOT); }
|
||||
virtual int makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,
|
||||
virtual int makeUniformSlots(const ShaderObject& program, const Shader::BindingSet& slotBindings,
|
||||
Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers);
|
||||
virtual int makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers);
|
||||
virtual int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& resourceBuffers) = 0;
|
||||
virtual int makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs);
|
||||
virtual int makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs);
|
||||
virtual int makeUniformBlockSlots(const ShaderObject& program, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers);
|
||||
virtual int makeResourceBufferSlots(const ShaderObject& program, const Shader::BindingSet& slotBindings, Shader::SlotSet& resourceBuffers) = 0;
|
||||
virtual int makeInputSlots(const ShaderObject& program, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs);
|
||||
virtual int makeOutputSlots(const ShaderObject& program, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs);
|
||||
|
||||
|
||||
// Synchronize the state cache of this Backend with the actual real state of the GL Context
|
||||
|
@ -489,6 +500,19 @@ protected:
|
|||
|
||||
void resetStages();
|
||||
|
||||
// Stores cached binary versions of the shaders for quicker startup on subsequent runs
|
||||
// Note that shaders in the cache can still fail to load due to hardware or driver
|
||||
// changes that invalidate the cached binary, in which case we fall back on compiling
|
||||
// the source again
|
||||
struct ShaderBinaryCache {
|
||||
std::mutex _mutex;
|
||||
std::vector<GLint> _formats;
|
||||
std::unordered_map<std::string, ::gl::CachedShader> _binaries;
|
||||
} _shaderBinaryCache;
|
||||
|
||||
virtual void initShaderBinaryCache();
|
||||
virtual void killShaderBinaryCache();
|
||||
|
||||
struct TextureManagementStageState {
|
||||
bool _sparseCapable { false };
|
||||
GLTextureTransferEnginePointer _transferEngine;
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
using namespace gpu;
|
||||
using namespace gpu::gl;
|
||||
using CachedShader = ::gl::CachedShader;
|
||||
|
||||
|
||||
// Shader domain
|
||||
static const size_t NUM_SHADER_DOMAINS = 3;
|
||||
|
@ -68,9 +70,45 @@ static const std::array<std::string, GLShader::NumVersions> VERSION_DEFINES { {
|
|||
stereoVersion
|
||||
} };
|
||||
|
||||
static std::string getShaderTypeString(Shader::Type type) {
|
||||
switch (type) {
|
||||
case Shader::Type::VERTEX:
|
||||
return "vertex";
|
||||
case Shader::Type::PIXEL:
|
||||
return "pixel";
|
||||
case Shader::Type::GEOMETRY:
|
||||
return "geometry";
|
||||
case Shader::Type::PROGRAM:
|
||||
return "program";
|
||||
default:
|
||||
qFatal("Unexpected shader type %d", type);
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
std::string GLBackend::getShaderSource(const Shader& shader, int version) {
|
||||
if (shader.isProgram()) {
|
||||
std::string result;
|
||||
result.append("// VERSION " + std::to_string(version));
|
||||
for (const auto& subShader : shader.getShaders()) {
|
||||
result.append("//-------- ");
|
||||
result.append(getShaderTypeString(subShader->getType()));
|
||||
result.append("\n");
|
||||
result.append(subShader->getSource().getCode());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string shaderDefines = getBackendShaderHeader() + "\n"
|
||||
+ (supportsBindless() ? textureTableVersion : "\n")
|
||||
+ DOMAIN_DEFINES[shader.getType()] + "\n"
|
||||
+ VERSION_DEFINES[version];
|
||||
|
||||
return shaderDefines + "\n" + shader.getSource().getCode();
|
||||
}
|
||||
|
||||
GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::CompilationHandler& handler) {
|
||||
// Any GLSLprogram ? normally yes...
|
||||
const std::string& shaderSource = shader.getSource().getCode();
|
||||
GLenum shaderDomain = SHADER_DOMAINS[shader.getType()];
|
||||
GLShader::ShaderObjects shaderObjects;
|
||||
Shader::CompilationLogs compilationLogs(GLShader::NumVersions);
|
||||
|
@ -78,11 +116,7 @@ GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::Co
|
|||
|
||||
for (int version = 0; version < GLShader::NumVersions; version++) {
|
||||
auto& shaderObject = shaderObjects[version];
|
||||
|
||||
std::string shaderDefines = getBackendShaderHeader() + "\n"
|
||||
+ (supportsBindless() ? textureTableVersion : "\n")
|
||||
+ DOMAIN_DEFINES[shader.getType()] + "\n"
|
||||
+ VERSION_DEFINES[version];
|
||||
auto shaderSource = getShaderSource(shader, version);
|
||||
if (handler) {
|
||||
bool retest = true;
|
||||
std::string currentSrc = shaderSource;
|
||||
|
@ -90,7 +124,7 @@ GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::Co
|
|||
// The retest bool is set to false as soon as the compilation succeed to wexit the while loop.
|
||||
// The handler tells us if we should retry or not while returning a modified version of the source.
|
||||
while (retest) {
|
||||
bool result = ::gl::compileShader(shaderDomain, currentSrc, shaderDefines, shaderObject.glshader, compilationLogs[version].message);
|
||||
bool result = ::gl::compileShader(shaderDomain, currentSrc, shaderObject.glshader, compilationLogs[version].message);
|
||||
compilationLogs[version].compiled = result;
|
||||
if (!result) {
|
||||
std::string newSrc;
|
||||
|
@ -101,7 +135,7 @@ GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::Co
|
|||
}
|
||||
}
|
||||
} else {
|
||||
compilationLogs[version].compiled = ::gl::compileShader(shaderDomain, shaderSource, shaderDefines, shaderObject.glshader, compilationLogs[version].message);
|
||||
compilationLogs[version].compiled = ::gl::compileShader(shaderDomain, shaderSource, shaderObject.glshader, compilationLogs[version].message);
|
||||
}
|
||||
|
||||
if (!compilationLogs[version].compiled) {
|
||||
|
@ -120,43 +154,80 @@ GLShader* GLBackend::compileBackendShader(const Shader& shader, const Shader::Co
|
|||
return object;
|
||||
}
|
||||
|
||||
std::atomic<size_t> gpuBinaryShadersLoaded;
|
||||
|
||||
GLShader* GLBackend::compileBackendProgram(const Shader& program, const Shader::CompilationHandler& handler) {
|
||||
if (!program.isProgram()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GLShader::ShaderObjects programObjects;
|
||||
|
||||
program.incrementCompilationAttempt();
|
||||
Shader::CompilationLogs compilationLogs(GLShader::NumVersions);
|
||||
|
||||
for (int version = 0; version < GLShader::NumVersions; version++) {
|
||||
auto& programObject = programObjects[version];
|
||||
auto programSource = getShaderSource(program, version);
|
||||
auto hash = ::gl::getShaderHash(programSource);
|
||||
|
||||
// Let's go through every shaders and make sure they are ready to go
|
||||
std::vector< GLuint > shaderGLObjects;
|
||||
for (auto subShader : program.getShaders()) {
|
||||
auto object = GLShader::sync((*this), *subShader, handler);
|
||||
if (object) {
|
||||
shaderGLObjects.push_back(object->_shaderObjects[version].glshader);
|
||||
} else {
|
||||
qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - One of the shaders of the program is not compiled?";
|
||||
compilationLogs[version].compiled = false;
|
||||
compilationLogs[version].message = std::string("Failed to compile, one of the shaders of the program is not compiled ?");
|
||||
program.setCompilationLogs(compilationLogs);
|
||||
return nullptr;
|
||||
CachedShader cachedBinary;
|
||||
{
|
||||
Lock shaderCacheLock{ _shaderBinaryCache._mutex };
|
||||
if (_shaderBinaryCache._binaries.count(hash) != 0) {
|
||||
cachedBinary = _shaderBinaryCache._binaries[hash];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GLuint glprogram = 0;
|
||||
|
||||
// If we have a cached binary program, try to load it instead of compiling the individual shaders
|
||||
if (cachedBinary) {
|
||||
glprogram = ::gl::compileProgram({}, compilationLogs[version].message, cachedBinary);
|
||||
if (0 != glprogram) {
|
||||
++gpuBinaryShadersLoaded;
|
||||
}
|
||||
}
|
||||
|
||||
// If we have no program, then either no cached binary, or the binary failed to load (perhaps a GPU driver update invalidated the cache)
|
||||
if (0 == glprogram) {
|
||||
cachedBinary = CachedShader();
|
||||
{
|
||||
std::unique_lock<std::mutex> shaderCacheLock{ _shaderBinaryCache._mutex };
|
||||
_shaderBinaryCache._binaries.erase(hash);
|
||||
}
|
||||
// Let's go through every shaders and make sure they are ready to go
|
||||
std::vector<GLuint> shaderGLObjects;
|
||||
shaderGLObjects.reserve(program.getShaders().size());
|
||||
for (auto subShader : program.getShaders()) {
|
||||
auto object = GLShader::sync((*this), *subShader, handler);
|
||||
if (object) {
|
||||
shaderGLObjects.push_back(object->_shaderObjects[version].glshader);
|
||||
} else {
|
||||
qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - One of the shaders of the program is not compiled?";
|
||||
compilationLogs[version].compiled = false;
|
||||
compilationLogs[version].message = std::string("Failed to compile, one of the shaders of the program is not compiled ?");
|
||||
program.setCompilationLogs(compilationLogs);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
glprogram = ::gl::compileProgram(shaderGLObjects, compilationLogs[version].message, cachedBinary);
|
||||
if (cachedBinary) {
|
||||
cachedBinary.source = programSource;
|
||||
std::unique_lock<std::mutex> shaderCacheLock{ _shaderBinaryCache._mutex };
|
||||
_shaderBinaryCache._binaries[hash] = cachedBinary;
|
||||
}
|
||||
}
|
||||
|
||||
GLuint glprogram = ::gl::compileProgram(shaderGLObjects, compilationLogs[version].message, compilationLogs[version].binary);
|
||||
if (glprogram == 0) {
|
||||
qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << compilationLogs[version].message.c_str();
|
||||
program.setCompilationLogs(compilationLogs);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
compilationLogs[version].compiled = true;
|
||||
programObject.glprogram = glprogram;
|
||||
|
||||
makeProgramBindings(programObject);
|
||||
}
|
||||
// Compilation feedback
|
||||
|
@ -338,20 +409,15 @@ GLBackend::ElementResource GLBackend::getFormatFromGLUniform(GLenum gltype) {
|
|||
|
||||
};
|
||||
|
||||
int GLBackend::makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,
|
||||
int GLBackend::makeUniformSlots(const ShaderObject& shaderProgram, const Shader::BindingSet& slotBindings,
|
||||
Shader::SlotSet& uniforms, Shader::SlotSet& textures, Shader::SlotSet& samplers) {
|
||||
GLint uniformsCount = 0;
|
||||
auto& glprogram = shaderProgram.glprogram;
|
||||
|
||||
glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount);
|
||||
|
||||
for (int i = 0; i < uniformsCount; i++) {
|
||||
const GLint NAME_LENGTH = 256;
|
||||
GLchar name[NAME_LENGTH];
|
||||
GLint length = 0;
|
||||
GLint size = 0;
|
||||
GLenum type = 0;
|
||||
glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name);
|
||||
GLint location = glGetUniformLocation(glprogram, name);
|
||||
for (const auto& uniform : shaderProgram.uniforms) {
|
||||
const auto& type = uniform.type;
|
||||
const auto& location = uniform.location;
|
||||
const auto& size = uniform.size;
|
||||
const auto& name = uniform.name;
|
||||
const GLint INVALID_UNIFORM_LOCATION = -1;
|
||||
|
||||
// Try to make sense of the gltype
|
||||
|
@ -359,8 +425,8 @@ int GLBackend::makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slot
|
|||
|
||||
// The uniform as a standard var type
|
||||
if (location != INVALID_UNIFORM_LOCATION) {
|
||||
auto sname = uniform.name;
|
||||
// Let's make sure the name doesn't contains an array element
|
||||
std::string sname(name);
|
||||
auto foundBracket = sname.find_first_of('[');
|
||||
if (foundBracket != std::string::npos) {
|
||||
// std::string arrayname = sname.substr(0, foundBracket);
|
||||
|
@ -397,10 +463,11 @@ int GLBackend::makeUniformSlots(GLuint glprogram, const Shader::BindingSet& slot
|
|||
}
|
||||
}
|
||||
|
||||
return uniformsCount;
|
||||
return static_cast<uint32_t>(shaderProgram.uniforms.size());
|
||||
}
|
||||
|
||||
int GLBackend::makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) {
|
||||
int GLBackend::makeUniformBlockSlots(const ShaderObject& shaderProgram, const Shader::BindingSet& slotBindings, Shader::SlotSet& buffers) {
|
||||
const auto& glprogram = shaderProgram.glprogram;
|
||||
GLint buffersCount = 0;
|
||||
|
||||
glGetProgramiv(glprogram, GL_ACTIVE_UNIFORM_BLOCKS, &buffersCount);
|
||||
|
@ -479,7 +546,8 @@ int GLBackend::makeUniformBlockSlots(GLuint glprogram, const Shader::BindingSet&
|
|||
return buffersCount;
|
||||
}
|
||||
|
||||
int GLBackend::makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs) {
|
||||
int GLBackend::makeInputSlots(const ShaderObject& shaderProgram, const Shader::BindingSet& slotBindings, Shader::SlotSet& inputs) {
|
||||
const auto& glprogram = shaderProgram.glprogram;
|
||||
GLint inputsCount = 0;
|
||||
|
||||
glGetProgramiv(glprogram, GL_ACTIVE_ATTRIBUTES, &inputsCount);
|
||||
|
@ -501,7 +569,7 @@ int GLBackend::makeInputSlots(GLuint glprogram, const Shader::BindingSet& slotBi
|
|||
return inputsCount;
|
||||
}
|
||||
|
||||
int GLBackend::makeOutputSlots(GLuint glprogram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs) {
|
||||
int GLBackend::makeOutputSlots(const ShaderObject& shaderProgram, const Shader::BindingSet& slotBindings, Shader::SlotSet& outputs) {
|
||||
/* GLint outputsCount = 0;
|
||||
|
||||
glGetProgramiv(glprogram, GL_ACTIVE_, &outputsCount);
|
||||
|
@ -525,67 +593,19 @@ void GLBackend::makeProgramBindings(ShaderObject& shaderObject) {
|
|||
if (!shaderObject.glprogram) {
|
||||
return;
|
||||
}
|
||||
GLuint glprogram = shaderObject.glprogram;
|
||||
GLint loc = -1;
|
||||
|
||||
//Check for gpu specific attribute slotBindings
|
||||
loc = glGetAttribLocation(glprogram, "inPosition");
|
||||
if (loc >= 0 && loc != gpu::Stream::POSITION) {
|
||||
glBindAttribLocation(glprogram, gpu::Stream::POSITION, "inPosition");
|
||||
}
|
||||
|
||||
loc = glGetAttribLocation(glprogram, "inNormal");
|
||||
if (loc >= 0 && loc != gpu::Stream::NORMAL) {
|
||||
glBindAttribLocation(glprogram, gpu::Stream::NORMAL, "inNormal");
|
||||
}
|
||||
|
||||
loc = glGetAttribLocation(glprogram, "inColor");
|
||||
if (loc >= 0 && loc != gpu::Stream::COLOR) {
|
||||
glBindAttribLocation(glprogram, gpu::Stream::COLOR, "inColor");
|
||||
}
|
||||
|
||||
loc = glGetAttribLocation(glprogram, "inTexCoord0");
|
||||
if (loc >= 0 && loc != gpu::Stream::TEXCOORD) {
|
||||
glBindAttribLocation(glprogram, gpu::Stream::TEXCOORD, "inTexCoord0");
|
||||
}
|
||||
|
||||
loc = glGetAttribLocation(glprogram, "inTangent");
|
||||
if (loc >= 0 && loc != gpu::Stream::TANGENT) {
|
||||
glBindAttribLocation(glprogram, gpu::Stream::TANGENT, "inTangent");
|
||||
}
|
||||
|
||||
char attribName[] = "inTexCoordn";
|
||||
for (auto i = 0; i < 4; i++) {
|
||||
auto streamId = gpu::Stream::TEXCOORD1 + i;
|
||||
|
||||
attribName[strlen(attribName) - 1] = '1' + i;
|
||||
loc = glGetAttribLocation(glprogram, attribName);
|
||||
if (loc >= 0 && loc != streamId) {
|
||||
glBindAttribLocation(glprogram, streamId, attribName);
|
||||
}
|
||||
}
|
||||
|
||||
loc = glGetAttribLocation(glprogram, "inSkinClusterIndex");
|
||||
if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_INDEX) {
|
||||
glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_INDEX, "inSkinClusterIndex");
|
||||
}
|
||||
|
||||
loc = glGetAttribLocation(glprogram, "inSkinClusterWeight");
|
||||
if (loc >= 0 && loc != gpu::Stream::SKIN_CLUSTER_WEIGHT) {
|
||||
glBindAttribLocation(glprogram, gpu::Stream::SKIN_CLUSTER_WEIGHT, "inSkinClusterWeight");
|
||||
}
|
||||
|
||||
loc = glGetAttribLocation(glprogram, "_drawCallInfo");
|
||||
if (loc >= 0 && loc != gpu::Stream::DRAW_CALL_INFO) {
|
||||
glBindAttribLocation(glprogram, gpu::Stream::DRAW_CALL_INFO, "_drawCallInfo");
|
||||
}
|
||||
|
||||
// Link again to take into account the assigned attrib location
|
||||
glLinkProgram(glprogram);
|
||||
|
||||
GLint linked = 0;
|
||||
glGetProgramiv(glprogram, GL_LINK_STATUS, &linked);
|
||||
if (!linked) {
|
||||
qCWarning(gpugllogging) << "GLShader::makeBindings - failed to link after assigning slotBindings?";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GLBackend::initShaderBinaryCache() {
|
||||
GLint numBinFormats = 0;
|
||||
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &numBinFormats);
|
||||
if (numBinFormats > 0) {
|
||||
_shaderBinaryCache._formats.resize(numBinFormats);
|
||||
glGetIntegerv(GL_PROGRAM_BINARY_FORMATS, _shaderBinaryCache._formats.data());
|
||||
}
|
||||
::gl::loadShaderCache(_shaderBinaryCache._binaries);
|
||||
}
|
||||
|
||||
void GLBackend::killShaderBinaryCache() {
|
||||
::gl::saveShaderCache(_shaderBinaryCache._binaries);
|
||||
}
|
||||
|
|
|
@ -68,22 +68,23 @@ bool GLShader::makeProgram(GLBackend& backend, Shader& shader, const Shader::Bin
|
|||
for (int version = 0; version < GLShader::NumVersions; version++) {
|
||||
auto& shaderObject = object->_shaderObjects[version];
|
||||
if (shaderObject.glprogram) {
|
||||
shaderObject.uniforms = ::gl::loadUniforms(shaderObject.glprogram);
|
||||
Shader::SlotSet buffers;
|
||||
backend.makeUniformBlockSlots(shaderObject.glprogram, slotBindings, buffers);
|
||||
backend.makeUniformBlockSlots(shaderObject, slotBindings, buffers);
|
||||
|
||||
Shader::SlotSet uniforms;
|
||||
Shader::SlotSet textures;
|
||||
Shader::SlotSet samplers;
|
||||
backend.makeUniformSlots(shaderObject.glprogram, slotBindings, uniforms, textures, samplers);
|
||||
backend.makeUniformSlots(shaderObject, slotBindings, uniforms, textures, samplers);
|
||||
|
||||
Shader::SlotSet resourceBuffers;
|
||||
backend.makeResourceBufferSlots(shaderObject.glprogram, slotBindings, resourceBuffers);
|
||||
backend.makeResourceBufferSlots(shaderObject, slotBindings, resourceBuffers);
|
||||
|
||||
Shader::SlotSet inputs;
|
||||
backend.makeInputSlots(shaderObject.glprogram, slotBindings, inputs);
|
||||
backend.makeInputSlots(shaderObject, slotBindings, inputs);
|
||||
|
||||
Shader::SlotSet outputs;
|
||||
backend.makeOutputSlots(shaderObject.glprogram, slotBindings, outputs);
|
||||
backend.makeOutputSlots(shaderObject, slotBindings, outputs);
|
||||
|
||||
// Define the public slots only from the default version
|
||||
if (version == 0) {
|
||||
|
|
|
@ -9,14 +9,17 @@
|
|||
#define hifi_gpu_gl_GLShader_h
|
||||
|
||||
#include "GLShared.h"
|
||||
#include <gl/GLShaders.h>
|
||||
|
||||
namespace gpu { namespace gl {
|
||||
|
||||
struct ShaderObject {
|
||||
using Uniforms = ::gl::Uniforms;
|
||||
GLuint glshader { 0 };
|
||||
GLuint glprogram { 0 };
|
||||
GLint transformCameraSlot { -1 };
|
||||
GLint transformObjectSlot { -1 };
|
||||
Uniforms uniforms;
|
||||
};
|
||||
|
||||
class GLShader : public GPUObject {
|
||||
|
|
|
@ -173,7 +173,7 @@ protected:
|
|||
|
||||
std::string getBackendShaderHeader() const override;
|
||||
void makeProgramBindings(ShaderObject& shaderObject) override;
|
||||
int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) override;
|
||||
int makeResourceBufferSlots(const ShaderObject& shaderProgram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) override;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -22,20 +22,13 @@ std::string GL41Backend::getBackendShaderHeader() const {
|
|||
return header;
|
||||
}
|
||||
|
||||
int GL41Backend::makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) {
|
||||
int GL41Backend::makeResourceBufferSlots(const ShaderObject& shaderProgram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) {
|
||||
GLint ssboCount = 0;
|
||||
GLint uniformsCount = 0;
|
||||
|
||||
glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount);
|
||||
|
||||
for (int i = 0; i < uniformsCount; i++) {
|
||||
const GLint NAME_LENGTH = 256;
|
||||
GLchar name[NAME_LENGTH];
|
||||
GLint length = 0;
|
||||
GLint size = 0;
|
||||
GLenum type = 0;
|
||||
glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name);
|
||||
GLint location = glGetUniformLocation(glprogram, name);
|
||||
const auto& glprogram = shaderProgram.glprogram;
|
||||
for (const auto& uniform : shaderProgram.uniforms) {
|
||||
const auto& name = uniform.name;
|
||||
const auto& type = uniform.type;
|
||||
const auto& location = uniform.location;
|
||||
const GLint INVALID_UNIFORM_LOCATION = -1;
|
||||
|
||||
// Try to make sense of the gltype
|
||||
|
|
|
@ -274,7 +274,7 @@ protected:
|
|||
// Shader Stage
|
||||
std::string getBackendShaderHeader() const override;
|
||||
void makeProgramBindings(ShaderObject& shaderObject) override;
|
||||
int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) override;
|
||||
int makeResourceBufferSlots(const ShaderObject& shaderProgram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) override;
|
||||
|
||||
// Texture Management Stage
|
||||
void initTextureManagementStage() override;
|
||||
|
|
|
@ -27,7 +27,8 @@ std::string GL45Backend::getBackendShaderHeader() const {
|
|||
return header;
|
||||
}
|
||||
|
||||
int GL45Backend::makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) {
|
||||
int GL45Backend::makeResourceBufferSlots(const ShaderObject& shaderProgram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) {
|
||||
const auto& glprogram = shaderProgram.glprogram;
|
||||
GLint buffersCount = 0;
|
||||
glGetProgramInterfaceiv(glprogram, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &buffersCount);
|
||||
|
||||
|
|
|
@ -164,7 +164,7 @@ protected:
|
|||
|
||||
std::string getBackendShaderHeader() const override;
|
||||
void makeProgramBindings(ShaderObject& shaderObject) override;
|
||||
int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) override;
|
||||
int makeResourceBufferSlots(const ShaderObject& shaderObject, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) override;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -25,20 +25,15 @@ std::string GLESBackend::getBackendShaderHeader() const {
|
|||
return header;
|
||||
}
|
||||
|
||||
int GLESBackend::makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) {
|
||||
int GLESBackend::makeResourceBufferSlots(const ShaderObject& shaderObject, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) {
|
||||
GLint ssboCount = 0;
|
||||
GLint uniformsCount = 0;
|
||||
GLint uniformsCount = 0;
|
||||
const auto& glprogram = shaderObject.glprogram;
|
||||
|
||||
glGetProgramiv(glprogram, GL_ACTIVE_UNIFORMS, &uniformsCount);
|
||||
|
||||
for (int i = 0; i < uniformsCount; i++) {
|
||||
const GLint NAME_LENGTH = 256;
|
||||
GLchar name[NAME_LENGTH];
|
||||
GLint length = 0;
|
||||
GLint size = 0;
|
||||
GLenum type = 0;
|
||||
glGetActiveUniform(glprogram, i, NAME_LENGTH, &length, &size, &type, name);
|
||||
GLint location = glGetUniformLocation(glprogram, name);
|
||||
for (const auto& uniform : shaderObject.uniforms) {
|
||||
const auto& type = uniform.type;
|
||||
const auto& location = uniform.location;
|
||||
const auto& name = uniform.name;
|
||||
const GLint INVALID_UNIFORM_LOCATION = -1;
|
||||
|
||||
// Try to make sense of the gltype
|
||||
|
|
|
@ -53,6 +53,13 @@ Context::~Context() {
|
|||
_batchPool.clear();
|
||||
}
|
||||
|
||||
void Context::shutdown() {
|
||||
if (_backend) {
|
||||
_backend->shutdown();
|
||||
_backend.reset();
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& Context::getBackendVersion() const {
|
||||
return _backend->getVersion();
|
||||
}
|
||||
|
|
|
@ -54,6 +54,7 @@ class Backend {
|
|||
public:
|
||||
virtual ~Backend(){};
|
||||
|
||||
virtual void shutdown() {}
|
||||
virtual const std::string& getVersion() const = 0;
|
||||
|
||||
void setStereoState(const StereoState& stereo) { _stereo = stereo; }
|
||||
|
@ -154,6 +155,7 @@ public:
|
|||
Context();
|
||||
~Context();
|
||||
|
||||
void shutdown();
|
||||
const std::string& getBackendVersion() const;
|
||||
|
||||
void beginFrame(const glm::mat4& renderView = glm::mat4(), const glm::mat4& renderPose = glm::mat4());
|
||||
|
|
|
@ -54,13 +54,11 @@ public:
|
|||
|
||||
struct CompilationLog {
|
||||
std::string message;
|
||||
std::vector<char> binary;
|
||||
bool compiled{ false };
|
||||
|
||||
CompilationLog() {}
|
||||
CompilationLog(const CompilationLog& src) :
|
||||
message(src.message),
|
||||
binary(src.binary),
|
||||
compiled(src.compiled) {}
|
||||
};
|
||||
using CompilationLogs = std::vector<CompilationLog>;
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <QtCore/QStringList>
|
||||
#include <QtCore/QStandardPaths>
|
||||
#include <QtCore/QUrlQuery>
|
||||
#include <QtCore/QThreadPool>
|
||||
#include <QtNetwork/QHttpMultiPart>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
#include <qthread.h>
|
||||
|
@ -743,6 +744,9 @@ void AccountManager::generateNewKeypair(bool isUserKeypair, const QUuid& domainI
|
|||
return;
|
||||
}
|
||||
|
||||
// Ensure openssl/Qt config is set up.
|
||||
QSslConfiguration::defaultConfiguration();
|
||||
|
||||
// make sure we don't already have an outbound keypair generation request
|
||||
if (!_isWaitingForKeypairResponse) {
|
||||
_isWaitingForKeypairResponse = true;
|
||||
|
@ -751,94 +755,75 @@ void AccountManager::generateNewKeypair(bool isUserKeypair, const QUuid& domainI
|
|||
qCDebug(networking) << "Clearing current private key in DataServerAccountInfo";
|
||||
_accountInfo.setPrivateKey(QByteArray());
|
||||
|
||||
// setup a new QThread to generate the keypair on, in case it takes a while
|
||||
QThread* generateThread = new QThread(this);
|
||||
generateThread->setObjectName("Account Manager Generator Thread");
|
||||
|
||||
// setup a keypair generator
|
||||
// Create a runnable keypair generated to create an RSA pair and exit.
|
||||
RSAKeypairGenerator* keypairGenerator = new RSAKeypairGenerator;
|
||||
|
||||
if (!isUserKeypair) {
|
||||
keypairGenerator->setDomainID(domainID);
|
||||
_accountInfo.setDomainID(domainID);
|
||||
}
|
||||
|
||||
// start keypair generation when the thread starts
|
||||
connect(generateThread, &QThread::started, keypairGenerator, &RSAKeypairGenerator::generateKeypair);
|
||||
|
||||
// handle success or failure of keypair generation
|
||||
connect(keypairGenerator, &RSAKeypairGenerator::generatedKeypair, this, &AccountManager::processGeneratedKeypair);
|
||||
connect(keypairGenerator, &RSAKeypairGenerator::errorGeneratingKeypair,
|
||||
this, &AccountManager::handleKeypairGenerationError);
|
||||
|
||||
connect(keypairGenerator, &QObject::destroyed, generateThread, &QThread::quit);
|
||||
connect(generateThread, &QThread::finished, generateThread, &QThread::deleteLater);
|
||||
|
||||
keypairGenerator->moveToThread(generateThread);
|
||||
connect(keypairGenerator, &RSAKeypairGenerator::generatedKeypair, this,
|
||||
&AccountManager::processGeneratedKeypair);
|
||||
connect(keypairGenerator, &RSAKeypairGenerator::errorGeneratingKeypair, this,
|
||||
&AccountManager::handleKeypairGenerationError);
|
||||
|
||||
qCDebug(networking) << "Starting worker thread to generate 2048-bit RSA keypair.";
|
||||
generateThread->start();
|
||||
// Start on Qt's global thread pool.
|
||||
QThreadPool::globalInstance()->start(keypairGenerator);
|
||||
}
|
||||
}
|
||||
|
||||
void AccountManager::processGeneratedKeypair() {
|
||||
void AccountManager::processGeneratedKeypair(QByteArray publicKey, QByteArray privateKey) {
|
||||
|
||||
qCDebug(networking) << "Generated 2048-bit RSA keypair. Uploading public key now.";
|
||||
|
||||
RSAKeypairGenerator* keypairGenerator = qobject_cast<RSAKeypairGenerator*>(sender());
|
||||
// hold the private key to later set our metaverse API account info if upload succeeds
|
||||
_pendingPrivateKey = privateKey;
|
||||
|
||||
if (keypairGenerator) {
|
||||
// hold the private key to later set our metaverse API account info if upload succeeds
|
||||
_pendingPrivateKey = keypairGenerator->getPrivateKey();
|
||||
// upload the public key so data-web has an up-to-date key
|
||||
const QString USER_PUBLIC_KEY_UPDATE_PATH = "api/v1/user/public_key";
|
||||
const QString DOMAIN_PUBLIC_KEY_UPDATE_PATH = "api/v1/domains/%1/public_key";
|
||||
|
||||
// upload the public key so data-web has an up-to-date key
|
||||
const QString USER_PUBLIC_KEY_UPDATE_PATH = "api/v1/user/public_key";
|
||||
const QString DOMAIN_PUBLIC_KEY_UPDATE_PATH = "api/v1/domains/%1/public_key";
|
||||
|
||||
QString uploadPath;
|
||||
const auto& domainID = keypairGenerator->getDomainID();
|
||||
if (domainID.isNull()) {
|
||||
uploadPath = USER_PUBLIC_KEY_UPDATE_PATH;
|
||||
} else {
|
||||
uploadPath = DOMAIN_PUBLIC_KEY_UPDATE_PATH.arg(uuidStringWithoutCurlyBraces(domainID));
|
||||
}
|
||||
|
||||
// setup a multipart upload to send up the public key
|
||||
QHttpMultiPart* requestMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
||||
|
||||
QHttpPart publicKeyPart;
|
||||
publicKeyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
|
||||
|
||||
publicKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader,
|
||||
QVariant("form-data; name=\"public_key\"; filename=\"public_key\""));
|
||||
publicKeyPart.setBody(keypairGenerator->getPublicKey());
|
||||
requestMultiPart->append(publicKeyPart);
|
||||
|
||||
if (!domainID.isNull()) {
|
||||
const auto& key = getTemporaryDomainKey(domainID);
|
||||
QHttpPart apiKeyPart;
|
||||
publicKeyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
|
||||
apiKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader,
|
||||
QVariant("form-data; name=\"api_key\""));
|
||||
apiKeyPart.setBody(key.toUtf8());
|
||||
requestMultiPart->append(apiKeyPart);
|
||||
}
|
||||
|
||||
// setup callback parameters so we know once the keypair upload has succeeded or failed
|
||||
JSONCallbackParameters callbackParameters;
|
||||
callbackParameters.jsonCallbackReceiver = this;
|
||||
callbackParameters.jsonCallbackMethod = "publicKeyUploadSucceeded";
|
||||
callbackParameters.errorCallbackReceiver = this;
|
||||
callbackParameters.errorCallbackMethod = "publicKeyUploadFailed";
|
||||
|
||||
sendRequest(uploadPath, AccountManagerAuth::Optional, QNetworkAccessManager::PutOperation,
|
||||
callbackParameters, QByteArray(), requestMultiPart);
|
||||
|
||||
keypairGenerator->deleteLater();
|
||||
QString uploadPath;
|
||||
const auto& domainID = _accountInfo.getDomainID();
|
||||
if (domainID.isNull()) {
|
||||
uploadPath = USER_PUBLIC_KEY_UPDATE_PATH;
|
||||
} else {
|
||||
qCWarning(networking) << "Expected processGeneratedKeypair to be called by a live RSAKeypairGenerator"
|
||||
<< "but the casted sender is NULL. Will not process generated keypair.";
|
||||
uploadPath = DOMAIN_PUBLIC_KEY_UPDATE_PATH.arg(uuidStringWithoutCurlyBraces(domainID));
|
||||
}
|
||||
|
||||
// setup a multipart upload to send up the public key
|
||||
QHttpMultiPart* requestMultiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
||||
|
||||
QHttpPart publicKeyPart;
|
||||
publicKeyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
|
||||
|
||||
publicKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader,
|
||||
QVariant("form-data; name=\"public_key\"; filename=\"public_key\""));
|
||||
publicKeyPart.setBody(publicKey);
|
||||
requestMultiPart->append(publicKeyPart);
|
||||
|
||||
// Currently broken? We don't have the temporary domain key.
|
||||
if (!domainID.isNull()) {
|
||||
const auto& key = getTemporaryDomainKey(domainID);
|
||||
QHttpPart apiKeyPart;
|
||||
publicKeyPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
|
||||
apiKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader,
|
||||
QVariant("form-data; name=\"api_key\""));
|
||||
apiKeyPart.setBody(key.toUtf8());
|
||||
requestMultiPart->append(apiKeyPart);
|
||||
}
|
||||
|
||||
// setup callback parameters so we know once the keypair upload has succeeded or failed
|
||||
JSONCallbackParameters callbackParameters;
|
||||
callbackParameters.jsonCallbackReceiver = this;
|
||||
callbackParameters.jsonCallbackMethod = "publicKeyUploadSucceeded";
|
||||
callbackParameters.errorCallbackReceiver = this;
|
||||
callbackParameters.errorCallbackMethod = "publicKeyUploadFailed";
|
||||
|
||||
sendRequest(uploadPath, AccountManagerAuth::Optional, QNetworkAccessManager::PutOperation,
|
||||
callbackParameters, QByteArray(), requestMultiPart);
|
||||
}
|
||||
|
||||
void AccountManager::publicKeyUploadSucceeded(QNetworkReply& reply) {
|
||||
|
@ -877,6 +862,4 @@ void AccountManager::handleKeypairGenerationError() {
|
|||
|
||||
// reset our waiting state for keypair response
|
||||
_isWaitingForKeypairResponse = false;
|
||||
|
||||
sender()->deleteLater();
|
||||
}
|
||||
|
|
|
@ -128,7 +128,7 @@ signals:
|
|||
private slots:
|
||||
void processReply();
|
||||
void handleKeypairGenerationError();
|
||||
void processGeneratedKeypair();
|
||||
void processGeneratedKeypair(QByteArray publicKey, QByteArray privateKey);
|
||||
void publicKeyUploadSucceeded(QNetworkReply& reply);
|
||||
void publicKeyUploadFailed(QNetworkReply& reply);
|
||||
void generateNewKeypair(bool isUserKeypair = true, const QUuid& domainID = QUuid());
|
||||
|
|
|
@ -25,7 +25,10 @@
|
|||
RSAKeypairGenerator::RSAKeypairGenerator(QObject* parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void RSAKeypairGenerator::run() {
|
||||
generateKeypair();
|
||||
}
|
||||
|
||||
void RSAKeypairGenerator::generateKeypair() {
|
||||
|
@ -92,5 +95,5 @@ void RSAKeypairGenerator::generateKeypair() {
|
|||
OPENSSL_free(publicKeyDER);
|
||||
OPENSSL_free(privateKeyDER);
|
||||
|
||||
emit generatedKeypair();
|
||||
emit generatedKeypair(_publicKey, _privateKey);
|
||||
}
|
||||
|
|
|
@ -13,25 +13,20 @@
|
|||
#define hifi_RSAKeypairGenerator_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QRunnable>
|
||||
#include <QtCore/QUuid>
|
||||
|
||||
class RSAKeypairGenerator : public QObject {
|
||||
class RSAKeypairGenerator : public QObject, public QRunnable {
|
||||
Q_OBJECT
|
||||
public:
|
||||
RSAKeypairGenerator(QObject* parent = 0);
|
||||
RSAKeypairGenerator(QObject* parent = nullptr);
|
||||
|
||||
void setDomainID(const QUuid& domainID) { _domainID = domainID; }
|
||||
const QUuid& getDomainID() const { return _domainID; }
|
||||
|
||||
const QByteArray& getPublicKey() const { return _publicKey; }
|
||||
const QByteArray& getPrivateKey() const { return _privateKey; }
|
||||
|
||||
public slots:
|
||||
virtual void run() override;
|
||||
void generateKeypair();
|
||||
|
||||
signals:
|
||||
void errorGeneratingKeypair();
|
||||
void generatedKeypair();
|
||||
void generatedKeypair(QByteArray publicKey, QByteArray privateKey);
|
||||
|
||||
private:
|
||||
QUuid _domainID;
|
||||
|
|
|
@ -218,8 +218,8 @@ ScriptableResource* ResourceCache::prefetch(const QUrl& url, void* extra) {
|
|||
}
|
||||
|
||||
ResourceCache::ResourceCache(QObject* parent) : QObject(parent) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
if (nodeList) {
|
||||
if (DependencyManager::isSet<NodeList>()) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
auto& domainHandler = nodeList->getDomainHandler();
|
||||
connect(&domainHandler, &DomainHandler::disconnectedFromDomain,
|
||||
this, &ResourceCache::clearATPAssets, Qt::DirectConnection);
|
||||
|
|
|
@ -159,6 +159,13 @@ void ObjectDynamic::removeFromSimulation(EntitySimulationPointer simulation) con
|
|||
simulation->removeDynamic(myID);
|
||||
}
|
||||
|
||||
void ObjectDynamic::setOwnerEntity(const EntityItemPointer ownerEntity) {
|
||||
if (!ownerEntity) {
|
||||
activateBody();
|
||||
}
|
||||
_ownerEntity = ownerEntity;
|
||||
}
|
||||
|
||||
EntityItemPointer ObjectDynamic::getEntityByID(EntityItemID entityID) const {
|
||||
EntityItemPointer ownerEntity;
|
||||
withReadLock([&]{
|
||||
|
|
|
@ -33,7 +33,7 @@ public:
|
|||
|
||||
virtual void removeFromSimulation(EntitySimulationPointer simulation) const override;
|
||||
virtual EntityItemWeakPointer getOwnerEntity() const override { return _ownerEntity; }
|
||||
virtual void setOwnerEntity(const EntityItemPointer ownerEntity) override { _ownerEntity = ownerEntity; }
|
||||
virtual void setOwnerEntity(const EntityItemPointer ownerEntity) override;
|
||||
|
||||
virtual void invalidate() {};
|
||||
|
||||
|
|
|
@ -215,10 +215,7 @@ void CauterizedModel::updateRenderItems() {
|
|||
modelTransform.setRotation(self->getRotation());
|
||||
|
||||
bool isWireframe = self->isWireframe();
|
||||
bool isVisible = self->isVisible();
|
||||
bool canCastShadow = self->canCastShadow();
|
||||
bool isLayeredInFront = self->isLayeredInFront();
|
||||
bool isLayeredInHUD = self->isLayeredInHUD();
|
||||
auto renderItemKeyGlobalFlags = self->getRenderItemKeyGlobalFlags();
|
||||
bool enableCauterization = self->getEnableCauterization();
|
||||
|
||||
render::Transaction transaction;
|
||||
|
@ -234,7 +231,7 @@ void CauterizedModel::updateRenderItems() {
|
|||
bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning();
|
||||
|
||||
transaction.updateItem<CauterizedMeshPartPayload>(itemID, [modelTransform, meshState, useDualQuaternionSkinning, cauterizedMeshState, invalidatePayloadShapeKey,
|
||||
isWireframe, isVisible, isLayeredInFront, isLayeredInHUD, canCastShadow, enableCauterization](CauterizedMeshPartPayload& data) {
|
||||
isWireframe, renderItemKeyGlobalFlags, enableCauterization](CauterizedMeshPartPayload& data) {
|
||||
if (useDualQuaternionSkinning) {
|
||||
data.updateClusterBuffer(meshState.clusterDualQuaternions,
|
||||
cauterizedMeshState.clusterDualQuaternions);
|
||||
|
@ -276,8 +273,7 @@ void CauterizedModel::updateRenderItems() {
|
|||
data.updateTransformForCauterizedMesh(renderTransform);
|
||||
|
||||
data.setEnableCauterization(enableCauterization);
|
||||
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, render::ItemKey::TAG_BITS_ALL);
|
||||
data.setLayer(isLayeredInFront, isLayeredInHUD);
|
||||
data.updateKey(renderItemKeyGlobalFlags);
|
||||
data.setShapeKey(invalidatePayloadShapeKey, isWireframe, useDualQuaternionSkinning);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -79,28 +79,10 @@ void MeshPartPayload::removeMaterial(graphics::MaterialPointer material) {
|
|||
_drawMaterials.remove(material);
|
||||
}
|
||||
|
||||
void MeshPartPayload::updateKey(bool isVisible, bool isLayered, bool canCastShadow, uint8_t tagBits, bool isGroupCulled) {
|
||||
ItemKey::Builder builder;
|
||||
void MeshPartPayload::updateKey(const render::ItemKey& key) {
|
||||
ItemKey::Builder builder(key);
|
||||
builder.withTypeShape();
|
||||
|
||||
if (!isVisible) {
|
||||
builder.withInvisible();
|
||||
}
|
||||
|
||||
builder.withTagBits(tagBits);
|
||||
|
||||
if (isLayered) {
|
||||
builder.withLayered();
|
||||
}
|
||||
|
||||
if (canCastShadow) {
|
||||
builder.withShadowCaster();
|
||||
}
|
||||
|
||||
if (isGroupCulled) {
|
||||
builder.withSubMetaCulled();
|
||||
}
|
||||
|
||||
if (topMaterialExists()) {
|
||||
auto matKey = _drawMaterials.top().material->getKey();
|
||||
if (matKey.isTranslucent()) {
|
||||
|
@ -200,12 +182,6 @@ template <> const Item::Bound payloadGetBound(const ModelMeshPartPayload::Pointe
|
|||
}
|
||||
return Item::Bound();
|
||||
}
|
||||
template <> int payloadGetLayer(const ModelMeshPartPayload::Pointer& payload) {
|
||||
if (payload) {
|
||||
return payload->getLayer();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <> const ShapeKey shapeGetShapeKey(const ModelMeshPartPayload::Pointer& payload) {
|
||||
if (payload) {
|
||||
|
@ -332,28 +308,10 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& render
|
|||
}
|
||||
|
||||
// Note that this method is called for models but not for shapes
|
||||
void ModelMeshPartPayload::updateKey(bool isVisible, bool isLayered, bool canCastShadow, uint8_t tagBits, bool isGroupCulled) {
|
||||
ItemKey::Builder builder;
|
||||
void ModelMeshPartPayload::updateKey(const render::ItemKey& key) {
|
||||
ItemKey::Builder builder(key);
|
||||
builder.withTypeShape();
|
||||
|
||||
if (!isVisible) {
|
||||
builder.withInvisible();
|
||||
}
|
||||
|
||||
builder.withTagBits(tagBits);
|
||||
|
||||
if (isLayered) {
|
||||
builder.withLayered();
|
||||
}
|
||||
|
||||
if (canCastShadow) {
|
||||
builder.withShadowCaster();
|
||||
}
|
||||
|
||||
if (isGroupCulled) {
|
||||
builder.withSubMetaCulled();
|
||||
}
|
||||
|
||||
if (_isBlendShaped || _isSkinned) {
|
||||
builder.withDeformed();
|
||||
}
|
||||
|
@ -368,20 +326,6 @@ void ModelMeshPartPayload::updateKey(bool isVisible, bool isLayered, bool canCas
|
|||
_itemKey = builder.build();
|
||||
}
|
||||
|
||||
void ModelMeshPartPayload::setLayer(bool isLayeredInFront, bool isLayeredInHUD) {
|
||||
if (isLayeredInFront) {
|
||||
_layer = Item::LAYER_3D_FRONT;
|
||||
} else if (isLayeredInHUD) {
|
||||
_layer = Item::LAYER_3D_HUD;
|
||||
} else {
|
||||
_layer = Item::LAYER_3D;
|
||||
}
|
||||
}
|
||||
|
||||
int ModelMeshPartPayload::getLayer() const {
|
||||
return _layer;
|
||||
}
|
||||
|
||||
void ModelMeshPartPayload::setShapeKey(bool invalidateShapeKey, bool isWireframe, bool useDualQuaternionSkinning) {
|
||||
if (invalidateShapeKey) {
|
||||
_shapeKey = ShapeKey::Builder::invalid();
|
||||
|
|
|
@ -32,7 +32,7 @@ public:
|
|||
typedef render::Payload<MeshPartPayload> Payload;
|
||||
typedef Payload::DataPointer Pointer;
|
||||
|
||||
virtual void updateKey(bool isVisible, bool isLayered, bool canCastShadow, uint8_t tagBits, bool isGroupCulled = false);
|
||||
virtual void updateKey(const render::ItemKey& key);
|
||||
|
||||
virtual void updateMeshPart(const std::shared_ptr<const graphics::Mesh>& drawMesh, int partIndex);
|
||||
|
||||
|
@ -95,7 +95,7 @@ public:
|
|||
|
||||
void notifyLocationChanged() override;
|
||||
|
||||
void updateKey(bool isVisible, bool isLayered, bool canCastShadow, uint8_t tagBits, bool isGroupCulled = false) override;
|
||||
void updateKey(const render::ItemKey& key) override;
|
||||
|
||||
// matrix palette skinning
|
||||
void updateClusterBuffer(const std::vector<glm::mat4>& clusterMatrices);
|
||||
|
@ -105,11 +105,9 @@ public:
|
|||
void updateTransformForSkinnedMesh(const Transform& renderTransform, const Transform& boundTransform);
|
||||
|
||||
// Render Item interface
|
||||
int getLayer() const;
|
||||
render::ShapeKey getShapeKey() const override; // shape interface
|
||||
void render(RenderArgs* args) override;
|
||||
|
||||
void setLayer(bool isLayeredInFront, bool isLayeredInHUD);
|
||||
void setShapeKey(bool invalidateShapeKey, bool isWireframe, bool useDualQuaternionSkinning);
|
||||
|
||||
// ModelMeshPartPayload functions to perform render
|
||||
|
@ -139,13 +137,11 @@ private:
|
|||
|
||||
gpu::BufferPointer _blendedVertexBuffer;
|
||||
render::ShapeKey _shapeKey { render::ShapeKey::Builder::invalid() };
|
||||
int _layer { render::Item::LAYER_3D };
|
||||
};
|
||||
|
||||
namespace render {
|
||||
template <> const ItemKey payloadGetKey(const ModelMeshPartPayload::Pointer& payload);
|
||||
template <> const Item::Bound payloadGetBound(const ModelMeshPartPayload::Pointer& payload);
|
||||
template <> int payloadGetLayer(const ModelMeshPartPayload::Pointer& payload);
|
||||
template <> const ShapeKey shapeGetShapeKey(const ModelMeshPartPayload::Pointer& payload);
|
||||
template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, RenderArgs* args);
|
||||
}
|
||||
|
|
|
@ -103,11 +103,10 @@ Model::Model(QObject* parent, SpatiallyNestable* spatiallyNestableOverride) :
|
|||
_snapModelToRegistrationPoint(false),
|
||||
_snappedToRegistrationPoint(false),
|
||||
_url(HTTP_INVALID_COM),
|
||||
_isVisible(true),
|
||||
_canCastShadow(false),
|
||||
_blendNumber(0),
|
||||
_appliedBlendNumber(0),
|
||||
_isWireframe(false)
|
||||
_isWireframe(false),
|
||||
_renderItemKeyGlobalFlags(render::ItemKey::Builder().withVisible().withTagBits(render::hifi::TAG_ALL_VIEWS).build())
|
||||
{
|
||||
// we may have been created in the network thread, but we live in the main thread
|
||||
if (_viewState) {
|
||||
|
@ -268,12 +267,7 @@ void Model::updateRenderItems() {
|
|||
modelTransform.setScale(glm::vec3(1.0f));
|
||||
|
||||
bool isWireframe = self->isWireframe();
|
||||
bool isVisible = self->isVisible();
|
||||
bool canCastShadow = self->canCastShadow();
|
||||
uint8_t viewTagBits = self->getViewTagBits();
|
||||
bool isLayeredInFront = self->isLayeredInFront();
|
||||
bool isLayeredInHUD = self->isLayeredInHUD();
|
||||
bool isGroupCulled = self->isGroupCulled();
|
||||
auto renderItemKeyGlobalFlags = self->getRenderItemKeyGlobalFlags();
|
||||
|
||||
render::Transaction transaction;
|
||||
for (int i = 0; i < (int) self->_modelMeshRenderItemIDs.size(); i++) {
|
||||
|
@ -287,9 +281,7 @@ void Model::updateRenderItems() {
|
|||
bool useDualQuaternionSkinning = self->getUseDualQuaternionSkinning();
|
||||
|
||||
transaction.updateItem<ModelMeshPartPayload>(itemID, [modelTransform, meshState, useDualQuaternionSkinning,
|
||||
invalidatePayloadShapeKey, isWireframe, isVisible,
|
||||
canCastShadow, viewTagBits, isLayeredInFront,
|
||||
isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
|
||||
invalidatePayloadShapeKey, isWireframe, renderItemKeyGlobalFlags](ModelMeshPartPayload& data) {
|
||||
if (useDualQuaternionSkinning) {
|
||||
data.updateClusterBuffer(meshState.clusterDualQuaternions);
|
||||
} else {
|
||||
|
@ -313,8 +305,7 @@ void Model::updateRenderItems() {
|
|||
}
|
||||
data.updateTransformForSkinnedMesh(renderTransform, modelTransform);
|
||||
|
||||
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled);
|
||||
data.setLayer(isLayeredInFront, isLayeredInHUD);
|
||||
data.updateKey(renderItemKeyGlobalFlags);
|
||||
data.setShapeKey(invalidatePayloadShapeKey, isWireframe, useDualQuaternionSkinning);
|
||||
});
|
||||
}
|
||||
|
@ -322,8 +313,9 @@ void Model::updateRenderItems() {
|
|||
Transform collisionMeshOffset;
|
||||
collisionMeshOffset.setIdentity();
|
||||
foreach(auto itemID, self->_collisionRenderItemsMap.keys()) {
|
||||
transaction.updateItem<MeshPartPayload>(itemID, [modelTransform, collisionMeshOffset](MeshPartPayload& data) {
|
||||
transaction.updateItem<MeshPartPayload>(itemID, [renderItemKeyGlobalFlags, modelTransform, collisionMeshOffset](MeshPartPayload& data) {
|
||||
// update the model transform for this render item.
|
||||
data.updateKey(renderItemKeyGlobalFlags);
|
||||
data.updateTransform(modelTransform, collisionMeshOffset);
|
||||
});
|
||||
}
|
||||
|
@ -773,110 +765,100 @@ void Model::calculateTriangleSets(const FBXGeometry& geometry) {
|
|||
}
|
||||
}
|
||||
|
||||
void Model::setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t viewTagBits, bool isGroupCulled) {
|
||||
if (_isVisible != isVisible || _viewTagBits != viewTagBits || _isGroupCulled != isGroupCulled) {
|
||||
_isVisible = isVisible;
|
||||
_viewTagBits = viewTagBits;
|
||||
_isGroupCulled = isGroupCulled;
|
||||
void Model::updateRenderItemsKey(const render::ScenePointer& scene) {
|
||||
if (!scene) {
|
||||
_needsFixupInScene = true;
|
||||
return;
|
||||
}
|
||||
auto renderItemsKey = _renderItemKeyGlobalFlags;
|
||||
render::Transaction transaction;
|
||||
foreach(auto item, _modelMeshRenderItemsMap.keys()) {
|
||||
transaction.updateItem<ModelMeshPartPayload>(item, [renderItemsKey](ModelMeshPartPayload& data) {
|
||||
data.updateKey(renderItemsKey);
|
||||
});
|
||||
}
|
||||
foreach(auto item, _collisionRenderItemsMap.keys()) {
|
||||
transaction.updateItem<ModelMeshPartPayload>(item, [renderItemsKey](ModelMeshPartPayload& data) {
|
||||
data.updateKey(renderItemsKey);
|
||||
});
|
||||
}
|
||||
scene->enqueueTransaction(transaction);
|
||||
}
|
||||
|
||||
bool isLayeredInFront = _isLayeredInFront;
|
||||
bool isLayeredInHUD = _isLayeredInHUD;
|
||||
bool canCastShadow = _canCastShadow;
|
||||
render::Transaction transaction;
|
||||
foreach (auto item, _modelMeshRenderItemsMap.keys()) {
|
||||
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow,
|
||||
isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
|
||||
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled);
|
||||
});
|
||||
}
|
||||
foreach(auto item, _collisionRenderItemsMap.keys()) {
|
||||
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow,
|
||||
isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
|
||||
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled);
|
||||
});
|
||||
}
|
||||
scene->enqueueTransaction(transaction);
|
||||
void Model::setVisibleInScene(bool visible, const render::ScenePointer& scene) {
|
||||
if (Model::isVisible() != visible) {
|
||||
auto keyBuilder = render::ItemKey::Builder(_renderItemKeyGlobalFlags);
|
||||
_renderItemKeyGlobalFlags = (visible ? keyBuilder.withVisible() : keyBuilder.withInvisible());
|
||||
updateRenderItemsKey(scene);
|
||||
}
|
||||
}
|
||||
|
||||
void Model::setCanCastShadow(bool canCastShadow, const render::ScenePointer& scene, uint8_t viewTagBits, bool isGroupCulled) {
|
||||
if (_canCastShadow != canCastShadow) {
|
||||
_canCastShadow = canCastShadow;
|
||||
bool Model::isVisible() const {
|
||||
return _renderItemKeyGlobalFlags.isVisible();
|
||||
}
|
||||
|
||||
bool isVisible = _isVisible;
|
||||
bool isLayeredInFront = _isLayeredInFront;
|
||||
bool isLayeredInHUD = _isLayeredInHUD;
|
||||
|
||||
render::Transaction transaction;
|
||||
foreach (auto item, _modelMeshRenderItemsMap.keys()) {
|
||||
transaction.updateItem<ModelMeshPartPayload>(item,
|
||||
[isVisible, viewTagBits, canCastShadow, isLayeredInFront, isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
|
||||
data.updateKey(isVisible, viewTagBits, canCastShadow, isLayeredInFront || isLayeredInHUD, isGroupCulled);
|
||||
});
|
||||
}
|
||||
|
||||
scene->enqueueTransaction(transaction);
|
||||
void Model::setCanCastShadow(bool castShadow, const render::ScenePointer& scene) {
|
||||
if (Model::canCastShadow() != castShadow) {
|
||||
auto keyBuilder = render::ItemKey::Builder(_renderItemKeyGlobalFlags);
|
||||
_renderItemKeyGlobalFlags = (castShadow ? keyBuilder.withShadowCaster() : keyBuilder.withoutShadowCaster());
|
||||
updateRenderItemsKey(scene);
|
||||
}
|
||||
}
|
||||
|
||||
void Model::setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& scene) {
|
||||
if (_isLayeredInFront != isLayeredInFront) {
|
||||
_isLayeredInFront = isLayeredInFront;
|
||||
bool Model::canCastShadow() const {
|
||||
return _renderItemKeyGlobalFlags.isShadowCaster();
|
||||
}
|
||||
|
||||
bool isVisible = _isVisible;
|
||||
bool canCastShadow = _canCastShadow;
|
||||
uint8_t viewTagBits = _viewTagBits;
|
||||
bool isLayeredInHUD = _isLayeredInHUD;
|
||||
bool isGroupCulled = _isGroupCulled;
|
||||
|
||||
render::Transaction transaction;
|
||||
foreach(auto item, _modelMeshRenderItemsMap.keys()) {
|
||||
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow,
|
||||
isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
|
||||
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled);
|
||||
data.setLayer(isLayeredInFront, isLayeredInHUD);
|
||||
});
|
||||
}
|
||||
foreach(auto item, _collisionRenderItemsMap.keys()) {
|
||||
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow,
|
||||
isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
|
||||
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled);
|
||||
data.setLayer(isLayeredInFront, isLayeredInHUD);
|
||||
});
|
||||
}
|
||||
scene->enqueueTransaction(transaction);
|
||||
void Model::setLayeredInFront(bool layeredInFront, const render::ScenePointer& scene) {
|
||||
if (Model::isLayeredInFront() != layeredInFront) {
|
||||
auto keyBuilder = render::ItemKey::Builder(_renderItemKeyGlobalFlags);
|
||||
_renderItemKeyGlobalFlags = (layeredInFront ? keyBuilder.withLayer(render::hifi::LAYER_3D_FRONT) : keyBuilder.withoutLayer());
|
||||
updateRenderItemsKey(scene);
|
||||
}
|
||||
}
|
||||
|
||||
void Model::setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& scene) {
|
||||
if (_isLayeredInHUD != isLayeredInHUD) {
|
||||
_isLayeredInHUD = isLayeredInHUD;
|
||||
bool Model::isLayeredInFront() const {
|
||||
return _renderItemKeyGlobalFlags.isLayer(render::hifi::LAYER_3D_FRONT);
|
||||
}
|
||||
|
||||
bool isVisible = _isVisible;
|
||||
bool canCastShadow = _canCastShadow;
|
||||
uint8_t viewTagBits = _viewTagBits;
|
||||
bool isLayeredInFront = _isLayeredInFront;
|
||||
bool isGroupCulled = _isGroupCulled;
|
||||
|
||||
render::Transaction transaction;
|
||||
foreach(auto item, _modelMeshRenderItemsMap.keys()) {
|
||||
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow,
|
||||
isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
|
||||
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled);
|
||||
data.setLayer(isLayeredInFront, isLayeredInHUD);
|
||||
});
|
||||
}
|
||||
foreach(auto item, _collisionRenderItemsMap.keys()) {
|
||||
transaction.updateItem<ModelMeshPartPayload>(item, [isVisible, viewTagBits, isLayeredInFront, canCastShadow,
|
||||
isLayeredInHUD, isGroupCulled](ModelMeshPartPayload& data) {
|
||||
data.updateKey(isVisible, isLayeredInFront || isLayeredInHUD, canCastShadow, viewTagBits, isGroupCulled);
|
||||
data.setLayer(isLayeredInFront, isLayeredInHUD);
|
||||
});
|
||||
}
|
||||
scene->enqueueTransaction(transaction);
|
||||
void Model::setLayeredInHUD(bool layeredInHUD, const render::ScenePointer& scene) {
|
||||
if (Model::isLayeredInHUD() != layeredInHUD) {
|
||||
auto keyBuilder = render::ItemKey::Builder(_renderItemKeyGlobalFlags);
|
||||
_renderItemKeyGlobalFlags = (layeredInHUD ? keyBuilder.withLayer(render::hifi::LAYER_3D_HUD) : keyBuilder.withoutLayer());
|
||||
updateRenderItemsKey(scene);
|
||||
}
|
||||
}
|
||||
|
||||
bool Model::isLayeredInHUD() const {
|
||||
return _renderItemKeyGlobalFlags.isLayer(render::hifi::LAYER_3D_HUD);
|
||||
}
|
||||
|
||||
void Model::setTagMask(uint8_t mask, const render::ScenePointer& scene) {
|
||||
if (Model::getTagMask() != mask) {
|
||||
auto keyBuilder = render::ItemKey::Builder(_renderItemKeyGlobalFlags);
|
||||
_renderItemKeyGlobalFlags = keyBuilder.withTagBits(mask);
|
||||
updateRenderItemsKey(scene);
|
||||
}
|
||||
}
|
||||
render::hifi::Tag Model::getTagMask() const {
|
||||
return (render::hifi::Tag) _renderItemKeyGlobalFlags.getTagBits();
|
||||
}
|
||||
|
||||
void Model::setGroupCulled(bool groupCulled, const render::ScenePointer& scene) {
|
||||
if (Model::isGroupCulled() != groupCulled) {
|
||||
auto keyBuilder = render::ItemKey::Builder(_renderItemKeyGlobalFlags);
|
||||
_renderItemKeyGlobalFlags = (groupCulled ? keyBuilder.withSubMetaCulled() : keyBuilder.withoutSubMetaCulled());
|
||||
updateRenderItemsKey(scene);
|
||||
}
|
||||
}
|
||||
bool Model::isGroupCulled() const {
|
||||
return _renderItemKeyGlobalFlags.isSubMetaCulled();
|
||||
}
|
||||
|
||||
const render::ItemKey Model::getRenderItemKeyGlobalFlags() const {
|
||||
return _renderItemKeyGlobalFlags;
|
||||
}
|
||||
|
||||
bool Model::addToScene(const render::ScenePointer& scene,
|
||||
render::Transaction& transaction,
|
||||
render::Item::Status::Getters& statusGetters) {
|
||||
|
@ -1676,20 +1658,16 @@ void Model::addMaterial(graphics::MaterialLayer material, const std::string& par
|
|||
for (auto shapeID : shapeIDs) {
|
||||
if (shapeID < _modelMeshRenderItemIDs.size()) {
|
||||
auto itemID = _modelMeshRenderItemIDs[shapeID];
|
||||
bool visible = isVisible();
|
||||
uint8_t viewTagBits = getViewTagBits();
|
||||
bool layeredInFront = isLayeredInFront();
|
||||
bool layeredInHUD = isLayeredInHUD();
|
||||
bool canCastShadow = _canCastShadow;
|
||||
auto renderItemsKey = _renderItemKeyGlobalFlags;
|
||||
bool wireframe = isWireframe();
|
||||
auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex;
|
||||
bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex);
|
||||
bool useDualQuaternionSkinning = _useDualQuaternionSkinning;
|
||||
transaction.updateItem<ModelMeshPartPayload>(itemID, [material, visible, layeredInFront, layeredInHUD, viewTagBits, canCastShadow,
|
||||
transaction.updateItem<ModelMeshPartPayload>(itemID, [material, renderItemsKey,
|
||||
invalidatePayloadShapeKey, wireframe, useDualQuaternionSkinning](ModelMeshPartPayload& data) {
|
||||
data.addMaterial(material);
|
||||
// if the material changed, we might need to update our item key or shape key
|
||||
data.updateKey(visible, layeredInFront || layeredInHUD, canCastShadow, viewTagBits);
|
||||
data.updateKey(renderItemsKey);
|
||||
data.setShapeKey(invalidatePayloadShapeKey, wireframe, useDualQuaternionSkinning);
|
||||
});
|
||||
}
|
||||
|
@ -1704,19 +1682,16 @@ void Model::removeMaterial(graphics::MaterialPointer material, const std::string
|
|||
if (shapeID < _modelMeshRenderItemIDs.size()) {
|
||||
auto itemID = _modelMeshRenderItemIDs[shapeID];
|
||||
bool visible = isVisible();
|
||||
uint8_t viewTagBits = getViewTagBits();
|
||||
bool layeredInFront = isLayeredInFront();
|
||||
bool layeredInHUD = isLayeredInHUD();
|
||||
bool canCastShadow = _canCastShadow;
|
||||
auto renderItemsKey = _renderItemKeyGlobalFlags;
|
||||
bool wireframe = isWireframe();
|
||||
auto meshIndex = _modelMeshRenderItemShapes[shapeID].meshIndex;
|
||||
bool invalidatePayloadShapeKey = shouldInvalidatePayloadShapeKey(meshIndex);
|
||||
bool useDualQuaternionSkinning = _useDualQuaternionSkinning;
|
||||
transaction.updateItem<ModelMeshPartPayload>(itemID, [material, visible, layeredInFront, layeredInHUD, viewTagBits, canCastShadow,
|
||||
transaction.updateItem<ModelMeshPartPayload>(itemID, [material, visible, renderItemsKey,
|
||||
invalidatePayloadShapeKey, wireframe, useDualQuaternionSkinning](ModelMeshPartPayload& data) {
|
||||
data.removeMaterial(material);
|
||||
// if the material changed, we might need to update our item key or shape key
|
||||
data.updateKey(visible, layeredInFront || layeredInHUD, canCastShadow, viewTagBits);
|
||||
data.updateKey(renderItemsKey);
|
||||
data.setShapeKey(invalidatePayloadShapeKey, wireframe, useDualQuaternionSkinning);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <TriangleSet.h>
|
||||
#include <DualQuaternion.h>
|
||||
|
||||
#include "RenderHifi.h"
|
||||
#include "GeometryCache.h"
|
||||
#include "TextureCache.h"
|
||||
#include "Rig.h"
|
||||
|
@ -87,13 +88,27 @@ public:
|
|||
const QUrl& getURL() const { return _url; }
|
||||
|
||||
// new Scene/Engine rendering support
|
||||
void setVisibleInScene(bool isVisible, const render::ScenePointer& scene, uint8_t viewTagBits, bool isGroupCulled);
|
||||
void setVisibleInScene(bool isVisible, const render::ScenePointer& scene = nullptr);
|
||||
bool isVisible() const;
|
||||
|
||||
bool canCastShadow() const { return _canCastShadow; }
|
||||
void setCanCastShadow(bool canCastShadow, const render::ScenePointer& scene, uint8_t viewTagBits, bool isGroupCulled);
|
||||
render::hifi::Tag getTagMask() const;
|
||||
void setTagMask(uint8_t mask, const render::ScenePointer& scene = nullptr);
|
||||
|
||||
bool isGroupCulled() const;
|
||||
void setGroupCulled(bool isGroupCulled, const render::ScenePointer& scene = nullptr);
|
||||
|
||||
bool canCastShadow() const;
|
||||
void setCanCastShadow(bool canCastShadow, const render::ScenePointer& scene = nullptr);
|
||||
|
||||
void setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& scene = nullptr);
|
||||
void setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& scene = nullptr);
|
||||
|
||||
bool isLayeredInFront() const;
|
||||
bool isLayeredInHUD() const;
|
||||
|
||||
// Access the current RenderItemKey Global Flags used by the model and applied to the render items representing the parts of the model.
|
||||
const render::ItemKey getRenderItemKeyGlobalFlags() const;
|
||||
|
||||
void setLayeredInFront(bool isLayeredInFront, const render::ScenePointer& scene);
|
||||
void setLayeredInHUD(bool isLayeredInHUD, const render::ScenePointer& scene);
|
||||
bool needsFixupInScene() const;
|
||||
|
||||
bool needsReload() const { return _needsReload; }
|
||||
|
@ -108,13 +123,7 @@ public:
|
|||
void removeFromScene(const render::ScenePointer& scene, render::Transaction& transaction);
|
||||
bool isRenderable() const;
|
||||
|
||||
bool isVisible() const { return _isVisible; }
|
||||
uint8_t getViewTagBits() const { return _viewTagBits; }
|
||||
|
||||
bool isLayeredInFront() const { return _isLayeredInFront; }
|
||||
bool isLayeredInHUD() const { return _isLayeredInHUD; }
|
||||
|
||||
bool isGroupCulled() const { return _isGroupCulled; }
|
||||
void updateRenderItemsKey(const render::ScenePointer& scene);
|
||||
|
||||
virtual void updateRenderItems();
|
||||
void setRenderItemsNeedUpdate();
|
||||
|
@ -404,10 +413,6 @@ protected:
|
|||
QVector<float> _blendshapeCoefficients;
|
||||
|
||||
QUrl _url;
|
||||
bool _isVisible;
|
||||
uint8_t _viewTagBits{ render::ItemKey::TAG_BITS_ALL };
|
||||
|
||||
bool _canCastShadow;
|
||||
|
||||
gpu::Buffers _blendedVertexBuffers;
|
||||
|
||||
|
@ -471,10 +476,16 @@ protected:
|
|||
int _renderInfoDrawCalls { 0 };
|
||||
int _renderInfoHasTransparent { false };
|
||||
|
||||
bool _isLayeredInFront { false };
|
||||
bool _isLayeredInHUD { false };
|
||||
|
||||
bool _isGroupCulled{ false };
|
||||
// This Render ItemKey Global Flags capture the Model wide global set of flags that should be communicated to all the render items representing the Model.
|
||||
// The flags concerned are:
|
||||
// - isVisible: if true the Model is visible globally in the scene, regardless of the other flags in the item keys (tags or layer or shadow caster).
|
||||
// - TagBits: the view mask defined through the TagBits telling in which view the Model is rendered if visible.
|
||||
// - Layer: In which Layer this Model lives.
|
||||
// - CastShadow: if true and visible and rendered in the view, the Model cast shadows if in a Light volume casting shadows.
|
||||
// - CullGroup: if true, the render items representing the parts of the Model are culled by a single Meta render item that knows about them, they are not culled individually.
|
||||
// For this to work, a Meta RI must exists and knows about the RIs of this Model.
|
||||
//
|
||||
render::ItemKey _renderItemKeyGlobalFlags;
|
||||
|
||||
bool shouldInvalidatePayloadShapeKey(int meshIndex);
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <render/DrawSceneOctree.h>
|
||||
#include <render/BlurTask.h>
|
||||
|
||||
#include "RenderHifi.h"
|
||||
#include "RenderCommonTask.h"
|
||||
#include "LightingModel.h"
|
||||
#include "StencilMaskPass.h"
|
||||
|
@ -200,8 +201,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
|
|||
const auto overlaysInFrontRangeTimer = task.addJob<BeginGPURangeTimer>("BeginOverlaysInFrontRangeTimer", "BeginOverlaysInFrontRangeTimer");
|
||||
|
||||
// Layered Overlays
|
||||
const auto filteredOverlaysOpaque = task.addJob<FilterLayeredItems>("FilterOverlaysLayeredOpaque", overlayOpaques, Item::LAYER_3D_FRONT);
|
||||
const auto filteredOverlaysTransparent = task.addJob<FilterLayeredItems>("FilterOverlaysLayeredTransparent", overlayTransparents, Item::LAYER_3D_FRONT);
|
||||
const auto filteredOverlaysOpaque = task.addJob<FilterLayeredItems>("FilterOverlaysLayeredOpaque", overlayOpaques, render::hifi::LAYER_3D_FRONT);
|
||||
const auto filteredOverlaysTransparent = task.addJob<FilterLayeredItems>("FilterOverlaysLayeredTransparent", overlayTransparents, render::hifi::LAYER_3D_FRONT);
|
||||
const auto overlaysInFrontOpaque = filteredOverlaysOpaque.getN<FilterLayeredItems::Outputs>(0);
|
||||
const auto overlaysInFrontTransparent = filteredOverlaysTransparent.getN<FilterLayeredItems::Outputs>(0);
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include <render/FilterTask.h>
|
||||
|
||||
#include "RenderHifi.h"
|
||||
#include "StencilMaskPass.h"
|
||||
#include "ZoneRenderer.h"
|
||||
#include "FadeEffect.h"
|
||||
|
@ -79,8 +80,8 @@ void RenderForwardTask::build(JobModel& task, const render::Varying& input, rend
|
|||
task.addJob<PrepareStencil>("PrepareStencil", framebuffer);
|
||||
|
||||
// Layered Overlays
|
||||
const auto filteredOverlaysOpaque = task.addJob<FilterLayeredItems>("FilterOverlaysLayeredOpaque", overlayOpaques, Item::LAYER_3D_FRONT);
|
||||
const auto filteredOverlaysTransparent = task.addJob<FilterLayeredItems>("FilterOverlaysLayeredTransparent", overlayTransparents, Item::LAYER_3D_FRONT);
|
||||
const auto filteredOverlaysOpaque = task.addJob<FilterLayeredItems>("FilterOverlaysLayeredOpaque", overlayOpaques, render::hifi::LAYER_3D_FRONT);
|
||||
const auto filteredOverlaysTransparent = task.addJob<FilterLayeredItems>("FilterOverlaysLayeredTransparent", overlayTransparents, render::hifi::LAYER_3D_FRONT);
|
||||
const auto overlaysInFrontOpaque = filteredOverlaysOpaque.getN<FilterLayeredItems::Outputs>(0);
|
||||
const auto overlaysInFrontTransparent = filteredOverlaysTransparent.getN<FilterLayeredItems::Outputs>(0);
|
||||
|
||||
|
|
43
libraries/render-utils/src/RenderHifi.h
Normal file
43
libraries/render-utils/src/RenderHifi.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// RenderHifi.h
|
||||
// libraries/render-utils/src
|
||||
//
|
||||
// Created by Sam Gateau on 5/30/2018.
|
||||
// Copyright 2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
#ifndef hifi_RenderHifi_h
|
||||
#define hifi_RenderHifi_h
|
||||
|
||||
#include <render/Item.h>
|
||||
|
||||
|
||||
|
||||
// In the library render-utils we are specializing the generic components of the render library to create the custom hifi render engine
|
||||
// Objects and types serving this goal are define in the namespace render.hifi
|
||||
// TODO: extend the namespace to all the classes where it make sense in render-utils
|
||||
namespace render {
|
||||
namespace hifi {
|
||||
|
||||
// Tag is the alias names of render::ItemKey::Tag combinations used in the Hifi Render Engine
|
||||
enum Tag : uint8_t {
|
||||
TAG_NONE = render::ItemKey::TAG_BITS_NONE, // No Tags at all
|
||||
TAG_MAIN_VIEW = render::ItemKey::TAG_BITS_0, // Main view
|
||||
TAG_SECONDARY_VIEW = render::ItemKey::TAG_BITS_1, // Secondary View
|
||||
TAG_ALL_VIEWS = TAG_MAIN_VIEW | TAG_SECONDARY_VIEW, // All views
|
||||
};
|
||||
|
||||
// Layer is the alias names of the render::ItemKey::Layer used in the Hifi Render Engine
|
||||
enum Layer : uint8_t {
|
||||
LAYER_3D = render::ItemKey::LAYER_DEFAULT,
|
||||
LAYER_3D_FRONT = render::ItemKey::LAYER_1,
|
||||
LAYER_3D_HUD = render::ItemKey::LAYER_2,
|
||||
LAYER_2D = render::ItemKey::LAYER_3,
|
||||
LAYER_BACKGROUND = render::ItemKey::LAYER_BACKGROUND,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // hifi_RenderHifi_h
|
|
@ -368,9 +368,9 @@ void CullShapeBounds::run(const RenderContextPointer& renderContext, const Input
|
|||
RenderArgs* args = renderContext->args;
|
||||
|
||||
const auto& inShapes = inputs.get0();
|
||||
const auto& cullFilter = inputs.get1();
|
||||
const auto& boundsFilter = inputs.get2();
|
||||
const auto& antiFrustum = inputs.get3();
|
||||
const auto& cullFilter = inputs.get1();
|
||||
const auto& boundsFilter = inputs.get2();
|
||||
const auto& antiFrustum = inputs.get3();
|
||||
auto& outShapes = outputs.edit0();
|
||||
auto& outBounds = outputs.edit1();
|
||||
|
||||
|
@ -380,7 +380,7 @@ void CullShapeBounds::run(const RenderContextPointer& renderContext, const Input
|
|||
if (!cullFilter.selectsNothing() || !boundsFilter.selectsNothing()) {
|
||||
auto& details = args->_details.edit(_detailType);
|
||||
Test test(_cullFunctor, args, details, antiFrustum);
|
||||
auto scene = args->_scene;
|
||||
auto scene = args->_scene;
|
||||
|
||||
for (auto& inItems : inShapes) {
|
||||
auto key = inItems.first;
|
||||
|
@ -395,26 +395,26 @@ void CullShapeBounds::run(const RenderContextPointer& renderContext, const Input
|
|||
if (antiFrustum == nullptr) {
|
||||
for (auto& item : inItems.second) {
|
||||
if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound)) {
|
||||
const auto shapeKey = scene->getItem(item.id).getKey();
|
||||
if (cullFilter.test(shapeKey)) {
|
||||
outItems->second.emplace_back(item);
|
||||
}
|
||||
if (boundsFilter.test(shapeKey)) {
|
||||
outBounds += item.bound;
|
||||
}
|
||||
const auto shapeKey = scene->getItem(item.id).getKey();
|
||||
if (cullFilter.test(shapeKey)) {
|
||||
outItems->second.emplace_back(item);
|
||||
}
|
||||
if (boundsFilter.test(shapeKey)) {
|
||||
outBounds += item.bound;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (auto& item : inItems.second) {
|
||||
if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound) && test.antiFrustumTest(item.bound)) {
|
||||
const auto shapeKey = scene->getItem(item.id).getKey();
|
||||
if (cullFilter.test(shapeKey)) {
|
||||
outItems->second.emplace_back(item);
|
||||
}
|
||||
if (boundsFilter.test(shapeKey)) {
|
||||
outBounds += item.bound;
|
||||
}
|
||||
}
|
||||
const auto shapeKey = scene->getItem(item.id).getKey();
|
||||
if (cullFilter.test(shapeKey)) {
|
||||
outItems->second.emplace_back(item);
|
||||
}
|
||||
if (boundsFilter.test(shapeKey)) {
|
||||
outBounds += item.bound;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
details._rendered += (int)outItems->second.size();
|
||||
|
@ -487,6 +487,7 @@ void FetchSpatialSelection::run(const RenderContextPointer& renderContext,
|
|||
if (filter.test(item.getKey())) {
|
||||
ItemBound itemBound(id, item.getBound());
|
||||
outItems.emplace_back(itemBound);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,25 +29,9 @@ const float Item::Status::Value::CYAN = 180.0f;
|
|||
const float Item::Status::Value::BLUE = 240.0f;
|
||||
const float Item::Status::Value::MAGENTA = 300.0f;
|
||||
|
||||
const int Item::LAYER_2D = 0;
|
||||
const int Item::LAYER_3D = 1;
|
||||
const int Item::LAYER_3D_FRONT = 2;
|
||||
const int Item::LAYER_3D_HUD = 3;
|
||||
|
||||
const uint8_t ItemKey::TAG_BITS_ALL { 0xFF };
|
||||
const uint8_t ItemKey::TAG_BITS_NONE { 0x00 };
|
||||
const uint8_t ItemKey::TAG_BITS_0 { 0x01 };
|
||||
const uint8_t ItemKey::TAG_BITS_1 { 0x02 };
|
||||
const uint8_t ItemKey::TAG_BITS_2 { 0x04 };
|
||||
const uint8_t ItemKey::TAG_BITS_3 { 0x08 };
|
||||
const uint8_t ItemKey::TAG_BITS_4 { 0x10 };
|
||||
const uint8_t ItemKey::TAG_BITS_5 { 0x20 };
|
||||
const uint8_t ItemKey::TAG_BITS_6 { 0x40 };
|
||||
const uint8_t ItemKey::TAG_BITS_7 { 0x80 };
|
||||
|
||||
const uint32_t ItemKey::KEY_TAG_BITS_MASK = ((uint32_t) ItemKey::TAG_BITS_ALL) << FIRST_TAG_BIT;
|
||||
|
||||
|
||||
const uint32_t ItemKey::KEY_LAYER_BITS_MASK = ((uint32_t)ItemKey::LAYER_BITS_ALL) << FIRST_LAYER_BIT;
|
||||
|
||||
void Item::Status::Value::setScale(float scale) {
|
||||
_scale = (std::numeric_limits<unsigned short>::max() -1) * 0.5f * (1.0f + std::max(std::min(scale, 1.0f), 0.0f));
|
||||
|
|
|
@ -52,23 +52,45 @@ public:
|
|||
TAG_6,
|
||||
TAG_7,
|
||||
|
||||
NUM_TAGS
|
||||
NUM_TAGS,
|
||||
|
||||
// Tag bits are derived from the Tag enum
|
||||
TAG_BITS_ALL = 0xFF,
|
||||
TAG_BITS_NONE = 0x00,
|
||||
TAG_BITS_0 = 0x01,
|
||||
TAG_BITS_1 = 0x02,
|
||||
TAG_BITS_2 = 0x04,
|
||||
TAG_BITS_3 = 0x08,
|
||||
TAG_BITS_4 = 0x10,
|
||||
TAG_BITS_5 = 0x20,
|
||||
TAG_BITS_6 = 0x40,
|
||||
TAG_BITS_7 = 0x80,
|
||||
};
|
||||
|
||||
// Items are organized in layers, an item belongs to one of the 8 Layers available.
|
||||
// By default an item is in the 'LAYER_DEFAULT' meaning that it is NOT layered.
|
||||
// THere is NO ordering relationship between layers.
|
||||
enum Layer : uint8_t {
|
||||
LAYER_DEFAULT = 0, // layer 0 aka Default is a 'NOT' layer, items are not considered layered, this is the default value
|
||||
LAYER_1,
|
||||
LAYER_2,
|
||||
LAYER_3,
|
||||
LAYER_4,
|
||||
LAYER_5,
|
||||
LAYER_6,
|
||||
LAYER_BACKGROUND, // Last Layer is the background by convention
|
||||
|
||||
NUM_LAYERS,
|
||||
|
||||
// Layer bits are derived from the Layer enum, the number of bits needed to represent integer 0 to NUM_LAYERS
|
||||
NUM_LAYER_BITS = 3,
|
||||
LAYER_BITS_ALL = 0x07,
|
||||
};
|
||||
// Tag bits are derived from the Tag enum
|
||||
const static uint8_t TAG_BITS_ALL;
|
||||
const static uint8_t TAG_BITS_NONE;
|
||||
const static uint8_t TAG_BITS_0;
|
||||
const static uint8_t TAG_BITS_1;
|
||||
const static uint8_t TAG_BITS_2;
|
||||
const static uint8_t TAG_BITS_3;
|
||||
const static uint8_t TAG_BITS_4;
|
||||
const static uint8_t TAG_BITS_5;
|
||||
const static uint8_t TAG_BITS_6;
|
||||
const static uint8_t TAG_BITS_7;
|
||||
|
||||
enum FlagBit : uint32_t {
|
||||
TYPE_SHAPE = 0, // Item is a Shape
|
||||
TYPE_LIGHT, // Item is a Light
|
||||
TYPE_SHAPE = 0, // Item is a Shape: Implements the Shape Interface that draw a Geometry rendered with a Material
|
||||
TYPE_LIGHT, // Item is a Light: Implements the Light Interface that
|
||||
TYPE_CAMERA, // Item is a Camera: Implements the Camera Interface
|
||||
TYPE_META, // Item is a Meta: meanning it s used to represent a higher level object, potentially represented by other render items
|
||||
|
||||
TRANSLUCENT, // Transparent and not opaque, for some odd reason TRANSPARENCY doesn't work...
|
||||
|
@ -77,13 +99,15 @@ public:
|
|||
DEFORMED, // Deformed within bound, not solid
|
||||
INVISIBLE, // Visible or not in the scene?
|
||||
SHADOW_CASTER, // Item cast shadows
|
||||
LAYERED, // Item belongs to one of the layers different from the default layer
|
||||
META_CULL_GROUP, // As a meta item, the culling of my sub items is based solely on my bounding box and my visibility in the view
|
||||
SUB_META_CULLED, // As a sub item of a meta render item set as cull group, need to be set to my culling to the meta render it
|
||||
|
||||
FIRST_TAG_BIT, // 8 Tags available to organize the items and filter them against
|
||||
LAST_TAG_BIT = FIRST_TAG_BIT + NUM_TAGS,
|
||||
|
||||
FIRST_LAYER_BIT, // 8 Exclusive Layers (encoded in 3 bits) available to organize the items in layers, an item can only belong to ONE layer
|
||||
LAST_LAYER_BIT = FIRST_LAYER_BIT + NUM_LAYER_BITS,
|
||||
|
||||
__SMALLER, // Reserved bit for spatialized item to indicate that it is smaller than expected in the cell in which it belongs (probably because it overlaps over several smaller cells)
|
||||
|
||||
NUM_FLAGS, // Not a valid flag
|
||||
|
@ -96,6 +120,12 @@ public:
|
|||
return (keyBits & ~KEY_TAG_BITS_MASK) | (((uint32_t)tagBits) << FIRST_TAG_BIT);
|
||||
}
|
||||
|
||||
// All the bits touching layer bits sets to true
|
||||
const static uint32_t KEY_LAYER_BITS_MASK;
|
||||
static uint32_t evalLayerBitsWithKeyBits(uint8_t layer, const uint32_t keyBits) {
|
||||
return (keyBits & ~KEY_LAYER_BITS_MASK) | (((uint32_t)layer & LAYER_BITS_ALL) << FIRST_LAYER_BIT);
|
||||
}
|
||||
|
||||
// The key is the Flags
|
||||
Flags _flags;
|
||||
|
||||
|
@ -124,19 +154,24 @@ public:
|
|||
Builder& withInvisible() { _flags.set(INVISIBLE); return (*this); }
|
||||
Builder& withVisible() { _flags.reset(INVISIBLE); return (*this); }
|
||||
Builder& withShadowCaster() { _flags.set(SHADOW_CASTER); return (*this); }
|
||||
Builder& withLayered() { _flags.set(LAYERED); return (*this); }
|
||||
Builder& withoutShadowCaster() { _flags.reset(SHADOW_CASTER); return (*this); }
|
||||
Builder& withMetaCullGroup() { _flags.set(META_CULL_GROUP); return (*this); }
|
||||
Builder& withoutMetaCullGroup() { _flags.reset(META_CULL_GROUP); return (*this); }
|
||||
Builder& withSubMetaCulled() { _flags.set(SUB_META_CULLED); return (*this); }
|
||||
Builder& withoutSubMetaCulled() { _flags.reset(SUB_META_CULLED); return (*this); }
|
||||
|
||||
Builder& withTag(Tag tag) { _flags.set(FIRST_TAG_BIT + tag); return (*this); }
|
||||
// Set ALL the tags in one call using the Tag bits
|
||||
Builder& withTagBits(uint8_t tagBits) { _flags = evalTagBitsWithKeyBits(tagBits, _flags.to_ulong()); return (*this); }
|
||||
|
||||
Builder& withLayer(uint8_t layer) { _flags = evalLayerBitsWithKeyBits(layer, _flags.to_ulong()); return (*this); }
|
||||
Builder& withoutLayer() { return withLayer(LAYER_DEFAULT); }
|
||||
|
||||
// Convenient standard keys that we will keep on using all over the place
|
||||
static Builder opaqueShape() { return Builder().withTypeShape(); }
|
||||
static Builder transparentShape() { return Builder().withTypeShape().withTransparent(); }
|
||||
static Builder light() { return Builder().withTypeLight(); }
|
||||
static Builder background() { return Builder().withViewSpace().withLayered(); }
|
||||
static Builder background() { return Builder().withViewSpace().withLayer(LAYER_BACKGROUND); }
|
||||
};
|
||||
ItemKey(const Builder& builder) : ItemKey(builder._flags) {}
|
||||
|
||||
|
@ -161,9 +196,6 @@ public:
|
|||
|
||||
bool isShadowCaster() const { return _flags[SHADOW_CASTER]; }
|
||||
|
||||
bool isLayered() const { return _flags[LAYERED]; }
|
||||
bool isSpatial() const { return !isLayered(); }
|
||||
|
||||
bool isMetaCullGroup() const { return _flags[META_CULL_GROUP]; }
|
||||
void setMetaCullGroup(bool cullGroup) { (cullGroup ? _flags.set(META_CULL_GROUP) : _flags.reset(META_CULL_GROUP)); }
|
||||
|
||||
|
@ -173,6 +205,11 @@ public:
|
|||
bool isTag(Tag tag) const { return _flags[FIRST_TAG_BIT + tag]; }
|
||||
uint8_t getTagBits() const { return ((_flags.to_ulong() & KEY_TAG_BITS_MASK) >> FIRST_TAG_BIT); }
|
||||
|
||||
uint8_t getLayer() const { return ((_flags.to_ulong() & KEY_LAYER_BITS_MASK) >> FIRST_LAYER_BIT); }
|
||||
bool isLayer(uint8_t layer) const { return getLayer() == layer; }
|
||||
bool isLayered() const { return getLayer() != LAYER_DEFAULT; }
|
||||
bool isSpatial() const { return !isLayered(); }
|
||||
|
||||
// Probably not public, flags used by the scene
|
||||
bool isSmall() const { return _flags[__SMALLER]; }
|
||||
void setSmaller(bool smaller) { (smaller ? _flags.set(__SMALLER) : _flags.reset(__SMALLER)); }
|
||||
|
@ -230,9 +267,6 @@ public:
|
|||
Builder& withNoShadowCaster() { _value.reset(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); }
|
||||
Builder& withShadowCaster() { _value.set(ItemKey::SHADOW_CASTER); _mask.set(ItemKey::SHADOW_CASTER); return (*this); }
|
||||
|
||||
Builder& withoutLayered() { _value.reset(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); }
|
||||
Builder& withLayered() { _value.set(ItemKey::LAYERED); _mask.set(ItemKey::LAYERED); return (*this); }
|
||||
|
||||
Builder& withoutMetaCullGroup() { _value.reset(ItemKey::META_CULL_GROUP); _mask.set(ItemKey::META_CULL_GROUP); return (*this); }
|
||||
Builder& withMetaCullGroup() { _value.set(ItemKey::META_CULL_GROUP); _mask.set(ItemKey::META_CULL_GROUP); return (*this); }
|
||||
|
||||
|
@ -244,6 +278,9 @@ public:
|
|||
// Set ALL the tags in one call using the Tag bits and the Tag bits touched
|
||||
Builder& withTagBits(uint8_t tagBits, uint8_t tagMask) { _value = ItemKey::evalTagBitsWithKeyBits(tagBits, _value.to_ulong()); _mask = ItemKey::evalTagBitsWithKeyBits(tagMask, _mask.to_ulong()); return (*this); }
|
||||
|
||||
Builder& withoutLayered() { _value = ItemKey::evalLayerBitsWithKeyBits(ItemKey::LAYER_DEFAULT, _value.to_ulong()); _mask |= ItemKey::KEY_LAYER_BITS_MASK; return (*this); }
|
||||
Builder& withLayer(uint8_t layer) { _value = ItemKey::evalLayerBitsWithKeyBits(layer, _value.to_ulong()); _mask |= ItemKey::KEY_LAYER_BITS_MASK; return (*this); }
|
||||
|
||||
Builder& withNothing() { _value.reset(); _mask.reset(); return (*this); }
|
||||
|
||||
// Convenient standard keys that we will keep on using all over the place
|
||||
|
@ -252,9 +289,7 @@ public:
|
|||
static Builder transparentShape() { return Builder().withTypeShape().withTransparent().withWorldSpace(); }
|
||||
static Builder light() { return Builder().withTypeLight(); }
|
||||
static Builder meta() { return Builder().withTypeMeta(); }
|
||||
static Builder background() { return Builder().withViewSpace().withLayered(); }
|
||||
static Builder opaqueShapeLayered() { return Builder().withTypeShape().withOpaque().withWorldSpace().withLayered(); }
|
||||
static Builder transparentShapeLayered() { return Builder().withTypeShape().withTransparent().withWorldSpace().withLayered(); }
|
||||
static Builder background() { return Builder().withViewSpace().withLayer(ItemKey::LAYER_BACKGROUND); }
|
||||
static Builder nothing() { return Builder().withNothing(); }
|
||||
};
|
||||
|
||||
|
@ -377,7 +412,6 @@ public:
|
|||
public:
|
||||
virtual const ItemKey getKey() const = 0;
|
||||
virtual const Bound getBound() const = 0;
|
||||
virtual int getLayer() const = 0;
|
||||
virtual void render(RenderArgs* args) = 0;
|
||||
|
||||
virtual const ShapeKey getShapeKey() const = 0;
|
||||
|
@ -422,13 +456,8 @@ public:
|
|||
// Get the bound of the item expressed in world space (or eye space depending on the key.isWorldSpace())
|
||||
const Bound getBound() const { return _payload->getBound(); }
|
||||
|
||||
// Get the layer where the item belongs.
|
||||
int getLayer() const { return _payload->getLayer(); }
|
||||
|
||||
static const int LAYER_2D;
|
||||
static const int LAYER_3D;
|
||||
static const int LAYER_3D_FRONT;
|
||||
static const int LAYER_3D_HUD;
|
||||
// Get the layer where the item belongs, simply reflecting the key.
|
||||
int getLayer() const { return _key.getLayer(); }
|
||||
|
||||
// Render call for the item
|
||||
void render(RenderArgs* args) const { _payload->render(args); }
|
||||
|
@ -478,7 +507,6 @@ inline QDebug operator<<(QDebug debug, const Item& item) {
|
|||
// Item shared interface supported by the payload
|
||||
template <class T> const ItemKey payloadGetKey(const std::shared_ptr<T>& payloadData) { return ItemKey(); }
|
||||
template <class T> const Item::Bound payloadGetBound(const std::shared_ptr<T>& payloadData) { return Item::Bound(); }
|
||||
template <class T> int payloadGetLayer(const std::shared_ptr<T>& payloadData) { return 0; }
|
||||
template <class T> void payloadRender(const std::shared_ptr<T>& payloadData, RenderArgs* args) { }
|
||||
|
||||
// Shape type interface
|
||||
|
@ -505,7 +533,6 @@ public:
|
|||
// Payload general interface
|
||||
virtual const ItemKey getKey() const override { return payloadGetKey<T>(_data); }
|
||||
virtual const Item::Bound getBound() const override { return payloadGetBound<T>(_data); }
|
||||
virtual int getLayer() const override { return payloadGetLayer<T>(_data); }
|
||||
|
||||
virtual void render(RenderArgs* args) override { payloadRender<T>(_data, args); }
|
||||
|
||||
|
|
|
@ -237,6 +237,14 @@ QString ScriptEngine::getContext() const {
|
|||
return "unknown";
|
||||
}
|
||||
|
||||
bool ScriptEngine::isDebugMode() const {
|
||||
#if defined(DEBUG)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
ScriptEngine::~ScriptEngine() {
|
||||
auto scriptEngines = DependencyManager::get<ScriptEngines>();
|
||||
if (scriptEngines) {
|
||||
|
|
|
@ -232,6 +232,12 @@ public:
|
|||
*/
|
||||
Q_INVOKABLE bool isClientScript() const { return _context == CLIENT_SCRIPT; }
|
||||
|
||||
/**jsdoc
|
||||
* @function Script.isDebugMode
|
||||
* @returns {boolean}
|
||||
*/
|
||||
Q_INVOKABLE bool isDebugMode() const;
|
||||
|
||||
/**jsdoc
|
||||
* @function Script.isEntityClientScript
|
||||
* @returns {boolean}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <QtCore/QCoreApplication>
|
||||
#include <QUuid>
|
||||
|
||||
#include "NumericalConstants.h"
|
||||
// When writing out avatarEntities to a QByteArray, if the parentID is the ID of MyAvatar, use this ID instead. This allows
|
||||
// the value to be reset when the sessionID changes.
|
||||
const QUuid AVATAR_SELF_ID = QUuid("{00000000-0000-0000-0000-000000000001}");
|
||||
|
@ -122,6 +123,27 @@ const QByteArray HIGH_FIDELITY_USER_AGENT = "Mozilla/5.0 (HighFidelityInterface)
|
|||
quint64 usecTimestampNow(bool wantDebug = false);
|
||||
void usecTimestampNowForceClockSkew(qint64 clockSkew);
|
||||
|
||||
inline bool afterUsecs(quint64& startUsecs, quint64 maxIntervalUecs) {
|
||||
auto now = usecTimestampNow();
|
||||
auto interval = now - startUsecs;
|
||||
if (interval > maxIntervalUecs) {
|
||||
startUsecs = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool afterSecs(quint64& startUsecs, quint64 maxIntervalSecs) {
|
||||
return afterUsecs(startUsecs, maxIntervalSecs * USECS_PER_SECOND);
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void doEvery(quint64& lastReportUsecs, quint64 secs, F lamdba) {
|
||||
if (afterSecs(lastReportUsecs, secs)) {
|
||||
lamdba();
|
||||
}
|
||||
}
|
||||
|
||||
// Number of seconds expressed since the first call to this function, expressed as a float
|
||||
// Maximum accuracy in msecs
|
||||
float secTimestampNow();
|
||||
|
|
|
@ -48,6 +48,6 @@ bool blockingInvokeMethod(
|
|||
} }
|
||||
|
||||
#define BLOCKING_INVOKE_METHOD(obj, member, ...) \
|
||||
hifi::qt::blockingInvokeMethod(__FUNCTION__, obj, member, ##__VA_ARGS__)
|
||||
::hifi::qt::blockingInvokeMethod(__FUNCTION__, obj, member, ##__VA_ARGS__)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
set(TARGET_NAME test-utils)
|
||||
setup_hifi_library(Network Gui)
|
||||
|
||||
link_hifi_libraries(shared)
|
||||
|
|
|
@ -313,27 +313,6 @@ inline QString getTestResource(const QString& relativePath) {
|
|||
return QDir::cleanPath(dir.absoluteFilePath(relativePath));
|
||||
}
|
||||
|
||||
inline bool afterUsecs(quint64& startUsecs, quint64 maxIntervalUecs) {
|
||||
auto now = usecTimestampNow();
|
||||
auto interval = now - startUsecs;
|
||||
if (interval > maxIntervalUecs) {
|
||||
startUsecs = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool afterSecs(quint64& startUsecs, quint64 maxIntervalSecs) {
|
||||
return afterUsecs(startUsecs, maxIntervalSecs * USECS_PER_SECOND);
|
||||
}
|
||||
|
||||
template <typename F>
|
||||
void doEvery(quint64& lastReportUsecs, quint64 secs, F lamdba) {
|
||||
if (afterSecs(lastReportUsecs, secs)) {
|
||||
lamdba();
|
||||
}
|
||||
}
|
||||
|
||||
inline void failAfter(quint64 startUsecs, quint64 secs, const char* message) {
|
||||
if (afterSecs(startUsecs, secs)) {
|
||||
QFAIL(message);
|
|
@ -198,9 +198,9 @@ public:
|
|||
std::string fsSource = HMD_REPROJECTION_FRAG;
|
||||
GLuint vertexShader { 0 }, fragmentShader { 0 };
|
||||
std::string error;
|
||||
std::vector<char> binary;
|
||||
::gl::compileShader(GL_VERTEX_SHADER, vsSource, "", vertexShader, error);
|
||||
::gl::compileShader(GL_FRAGMENT_SHADER, fsSource, "", fragmentShader, error);
|
||||
::gl::CachedShader binary;
|
||||
::gl::compileShader(GL_VERTEX_SHADER, vsSource, vertexShader, error);
|
||||
::gl::compileShader(GL_FRAGMENT_SHADER, fsSource, fragmentShader, error);
|
||||
_program = ::gl::compileProgram({ { vertexShader, fragmentShader } }, error, binary);
|
||||
glDeleteShader(vertexShader);
|
||||
glDeleteShader(fragmentShader);
|
||||
|
|
|
@ -16,8 +16,7 @@ var DEFAULT_SCRIPTS_COMBINED = [
|
|||
"system/+android/touchscreenvirtualpad.js",
|
||||
"system/+android/actionbar.js",
|
||||
"system/+android/audio.js" ,
|
||||
"system/+android/modes.js",
|
||||
"system/+android/stats.js"/*,
|
||||
"system/+android/modes.js"/*,
|
||||
"system/away.js",
|
||||
"system/controllers/controllerDisplayManager.js",
|
||||
"system/controllers/handControllerGrabAndroid.js",
|
||||
|
@ -33,6 +32,10 @@ var DEFAULT_SCRIPTS_COMBINED = [
|
|||
"developer/debugging/debugAndroidMouse.js"*/
|
||||
];
|
||||
|
||||
var DEBUG_SCRIPTS = [
|
||||
"system/+android/stats.js"
|
||||
];
|
||||
|
||||
var DEFAULT_SCRIPTS_SEPARATE = [ ];
|
||||
|
||||
// add a menu item for debugging
|
||||
|
@ -70,6 +73,11 @@ function runDefaultsTogether() {
|
|||
for (var i in DEFAULT_SCRIPTS_COMBINED) {
|
||||
Script.include(DEFAULT_SCRIPTS_COMBINED[i]);
|
||||
}
|
||||
if (Script.isDebugMode()) {
|
||||
for (var i in DEBUG_SCRIPTS) {
|
||||
Script.include(DEBUG_SCRIPTS[i]);
|
||||
}
|
||||
}
|
||||
loadSeparateDefaults();
|
||||
}
|
||||
|
||||
|
@ -77,6 +85,11 @@ function runDefaultsSeparately() {
|
|||
for (var i in DEFAULT_SCRIPTS_COMBINED) {
|
||||
Script.load(DEFAULT_SCRIPTS_COMBINED[i]);
|
||||
}
|
||||
if (Script.isDebugMode()) {
|
||||
for (var i in DEBUG_SCRIPTS) {
|
||||
Script.load(DEBUG_SCRIPTS[i]);
|
||||
}
|
||||
}
|
||||
loadSeparateDefaults();
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ var DEFAULT_SCRIPTS_COMBINED = [
|
|||
"system/makeUserConnection.js",
|
||||
"system/tablet-goto.js",
|
||||
"system/marketplaces/marketplaces.js",
|
||||
"system/notifications.js",
|
||||
"system/commerce/wallet.js",
|
||||
"system/edit.js",
|
||||
"system/dialTone.js",
|
||||
|
|
|
@ -30,7 +30,7 @@ function init() {
|
|||
text: "STATS"
|
||||
});
|
||||
statsButton.clicked.connect(function() {
|
||||
Menu.triggerOption("Stats");
|
||||
Menu.triggerOption("Show Statistics");
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
var isDisabled = false;
|
||||
|
||||
var previousFlyingState = MyAvatar.getFlyingEnabled();
|
||||
var previousDrivingState = MyAvatar.useAdvancedMovementControls;
|
||||
var previousDrivingState = false;
|
||||
|
||||
function rotate180() {
|
||||
var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.angleAxis(180, {
|
||||
|
@ -100,7 +100,7 @@
|
|||
Controller.enableMapping(DRIVING_MAPPING_NAME);
|
||||
}
|
||||
|
||||
if (MyAvatar.getFyingEnabled()) {
|
||||
if (MyAvatar.getFlyingEnabled()) {
|
||||
Controller.disableMapping(FLYING_MAPPING_NAME);
|
||||
} else {
|
||||
Controller.enableMapping(FLYING_MAPPING_NAME);
|
||||
|
@ -171,25 +171,4 @@
|
|||
Messages.subscribe(HIFI_ADVANCED_MOVEMENT_DISABLER_CHANNEL);
|
||||
Messages.messageReceived.connect(handleMessage);
|
||||
|
||||
function initializeControls() {
|
||||
if(HMD.active) {
|
||||
if (Controller.Hardware.Vive !== undefined || Controller.Hardware.OculusTouch !== undefined) {
|
||||
if (MyAvatar.useAdvancedMovementControls) {
|
||||
Controller.disableMapping(DRIVING_MAPPING_NAME);
|
||||
} else {
|
||||
Controller.enableMapping(DRIVING_MAPPING_NAME);
|
||||
}
|
||||
|
||||
if (MyAvatar.getFlyingEnabled()) {
|
||||
Controller.disableMapping(FLYING_MAPPING_NAME);
|
||||
} else {
|
||||
Controller.enableMapping(FLYING_MAPPING_NAME);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
initializeControls();
|
||||
|
||||
}()); // END LOCAL_SCOPE
|
||||
|
|
|
@ -79,11 +79,7 @@
|
|||
var frame = 0;
|
||||
var ctrlIsPressed = false;
|
||||
var ready = true;
|
||||
var MENU_NAME = 'Tools > Notifications';
|
||||
var PLAY_NOTIFICATION_SOUNDS_MENU_ITEM = "Play Notification Sounds";
|
||||
var NOTIFICATION_MENU_ITEM_POST = " Notifications";
|
||||
var PLAY_NOTIFICATION_SOUNDS_SETTING = "play_notification_sounds";
|
||||
var PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE = "play_notification_sounds_type_";
|
||||
var NOTIFICATIONS_MESSAGE_CHANNEL = "Hifi-Notifications";
|
||||
|
||||
var NotificationType = {
|
||||
|
@ -401,11 +397,6 @@
|
|||
alpha: backgroundAlpha
|
||||
};
|
||||
|
||||
if (Menu.isOptionChecked(PLAY_NOTIFICATION_SOUNDS_MENU_ITEM) &&
|
||||
Menu.isOptionChecked(NotificationType.getMenuString(notificationType))) {
|
||||
randomSounds.playRandom();
|
||||
}
|
||||
|
||||
return notify(noticeProperties, buttonProperties, height, imageProperties);
|
||||
}
|
||||
|
||||
|
@ -618,30 +609,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
function setup() {
|
||||
var type;
|
||||
Menu.addMenu(MENU_NAME);
|
||||
var checked = Settings.getValue(PLAY_NOTIFICATION_SOUNDS_SETTING);
|
||||
checked = checked === '' ? true : checked;
|
||||
Menu.addMenuItem({
|
||||
menuName: MENU_NAME,
|
||||
menuItemName: PLAY_NOTIFICATION_SOUNDS_MENU_ITEM,
|
||||
isCheckable: true,
|
||||
isChecked: Settings.getValue(PLAY_NOTIFICATION_SOUNDS_SETTING)
|
||||
});
|
||||
Menu.addSeparator(MENU_NAME, "Play sounds for:");
|
||||
for (type in NotificationType.properties) {
|
||||
checked = Settings.getValue(PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE + (parseInt(type, 10) + 1));
|
||||
checked = checked === '' ? true : checked;
|
||||
Menu.addMenuItem({
|
||||
menuName: MENU_NAME,
|
||||
menuItemName: NotificationType.properties[type].text + NOTIFICATION_MENU_ITEM_POST,
|
||||
isCheckable: true,
|
||||
isChecked: checked
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// When our script shuts down, we should clean up all of our overlays
|
||||
function scriptEnding() {
|
||||
var notificationIndex;
|
||||
|
@ -649,27 +616,14 @@
|
|||
Overlays.deleteOverlay(notifications[notificationIndex]);
|
||||
Overlays.deleteOverlay(buttons[notificationIndex]);
|
||||
}
|
||||
Menu.removeMenu(MENU_NAME);
|
||||
Messages.unsubscribe(NOTIFICATIONS_MESSAGE_CHANNEL);
|
||||
}
|
||||
|
||||
function menuItemEvent(menuItem) {
|
||||
if (menuItem === PLAY_NOTIFICATION_SOUNDS_MENU_ITEM) {
|
||||
Settings.setValue(PLAY_NOTIFICATION_SOUNDS_SETTING, Menu.isOptionChecked(PLAY_NOTIFICATION_SOUNDS_MENU_ITEM));
|
||||
return;
|
||||
}
|
||||
var notificationType = NotificationType.getTypeFromMenuItem(menuItem);
|
||||
if (notificationType !== notificationType.UNKNOWN) {
|
||||
Settings.setValue(PLAY_NOTIFICATION_SOUNDS_TYPE_SETTING_PRE + notificationType, Menu.isOptionChecked(menuItem));
|
||||
}
|
||||
}
|
||||
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Controller.mousePressEvent.connect(mousePressEvent);
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
||||
Script.update.connect(update);
|
||||
Script.scriptEnding.connect(scriptEnding);
|
||||
Menu.menuItemEvent.connect(menuItemEvent);
|
||||
Window.domainConnectionRefused.connect(onDomainConnectionRefused);
|
||||
Window.stillSnapshotTaken.connect(onSnapshotTaken);
|
||||
Window.snapshot360Taken.connect(onSnapshotTaken);
|
||||
|
@ -684,7 +638,4 @@
|
|||
|
||||
Messages.subscribe(NOTIFICATIONS_MESSAGE_CHANNEL);
|
||||
Messages.messageReceived.connect(onMessageReceived);
|
||||
|
||||
setup();
|
||||
|
||||
}()); // END LOCAL_SCOPE
|
||||
|
|
|
@ -134,12 +134,12 @@ const std::string PIXEL_SHADER_DEFINES{ R"GLSL(
|
|||
|
||||
void testShaderBuild(const std::string& vs_src, const std::string& fs_src) {
|
||||
std::string error;
|
||||
std::vector<char> binary;
|
||||
GLuint vs, fs;
|
||||
if (!gl::compileShader(GL_VERTEX_SHADER, vs_src, VERTEX_SHADER_DEFINES, vs, error) ||
|
||||
!gl::compileShader(GL_FRAGMENT_SHADER, fs_src, PIXEL_SHADER_DEFINES, fs, error)) {
|
||||
if (!gl::compileShader(GL_VERTEX_SHADER, VERTEX_SHADER_DEFINES + vs_src, vs, error) ||
|
||||
!gl::compileShader(GL_FRAGMENT_SHADER, PIXEL_SHADER_DEFINES + fs_src, fs, error)) {
|
||||
throw std::runtime_error("Failed to compile shader");
|
||||
}
|
||||
gl::CachedShader binary;
|
||||
auto pr = gl::compileProgram({ vs, fs }, error, binary);
|
||||
if (!pr) {
|
||||
throw std::runtime_error("Failed to link shader");
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Declare dependencies
|
||||
macro (setup_testcase_dependencies)
|
||||
# link in the shared libraries
|
||||
link_hifi_libraries(shared animation gpu fbx graphics networking)
|
||||
link_hifi_libraries(shared animation gpu fbx graphics networking test-utils)
|
||||
|
||||
package_libraries_for_deployment()
|
||||
endmacro ()
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
#include <AnimationLogging.h>
|
||||
#include <NumericalConstants.h>
|
||||
|
||||
#include "../QTestExtensions.h"
|
||||
#include <test-utils/QTestExtensions.h>
|
||||
|
||||
QTEST_MAIN(AnimInverseKinematicsTests)
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include <AccountManager.h>
|
||||
#include <ResourceManager.h>
|
||||
#include <StatTracker.h>
|
||||
#include <../QTestExtensions.h>
|
||||
#include <test-utils/QTestExtensions.h>
|
||||
|
||||
QTEST_MAIN(AnimTests)
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include <NumericalConstants.h>
|
||||
#include <SwingTwistConstraint.h>
|
||||
|
||||
#include "../QTestExtensions.h"
|
||||
#include <test-utils/QTestExtensions.h>
|
||||
|
||||
|
||||
QTEST_MAIN(RotationConstraintTests)
|
||||
|
@ -47,7 +47,6 @@ void RotationConstraintTests::testElbowConstraint() {
|
|||
glm::quat inputRotation = referenceRotation;
|
||||
glm::quat outputRotation = inputRotation;
|
||||
bool updated = elbow.apply(outputRotation);
|
||||
QVERIFY(updated == false);
|
||||
glm::quat expectedRotation = referenceRotation;
|
||||
QCOMPARE_WITH_ABS_ERROR(expectedRotation, outputRotation, EPSILON);
|
||||
}
|
||||
|
@ -62,7 +61,6 @@ void RotationConstraintTests::testElbowConstraint() {
|
|||
glm::quat inputRotation = glm::angleAxis(angle, hingeAxis) * referenceRotation;
|
||||
glm::quat outputRotation = inputRotation;
|
||||
bool updated = elbow.apply(outputRotation);
|
||||
QVERIFY(updated == false);
|
||||
QCOMPARE_WITH_ABS_ERROR(inputRotation, outputRotation, EPSILON);
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +70,6 @@ void RotationConstraintTests::testElbowConstraint() {
|
|||
glm::quat inputRotation = glm::angleAxis(angle, hingeAxis) * referenceRotation;
|
||||
glm::quat outputRotation = inputRotation;
|
||||
bool updated = elbow.apply(outputRotation);
|
||||
QVERIFY(updated == true);
|
||||
glm::quat expectedRotation = glm::angleAxis(minAngle, hingeAxis) * referenceRotation;
|
||||
QCOMPARE_WITH_ABS_ERROR(expectedRotation, outputRotation, EPSILON);
|
||||
}
|
||||
|
@ -82,7 +79,6 @@ void RotationConstraintTests::testElbowConstraint() {
|
|||
glm::quat inputRotation = glm::angleAxis(angle, hingeAxis) * referenceRotation;
|
||||
glm::quat outputRotation = inputRotation;
|
||||
bool updated = elbow.apply(outputRotation);
|
||||
QVERIFY(updated == true);
|
||||
glm::quat expectedRotation = glm::angleAxis(maxAngle, hingeAxis) * referenceRotation;
|
||||
QCOMPARE_WITH_ABS_ERROR(expectedRotation, outputRotation, EPSILON);
|
||||
}
|
||||
|
@ -94,7 +90,6 @@ void RotationConstraintTests::testElbowConstraint() {
|
|||
glm::quat inputRotation = glm::angleAxis(someAngle, twistVector) * referenceRotation;
|
||||
glm::quat outputRotation = inputRotation;
|
||||
bool updated = elbow.apply(outputRotation);
|
||||
QVERIFY(updated == true);
|
||||
glm::quat expectedRotation = referenceRotation;
|
||||
QCOMPARE_WITH_ABS_ERROR(expectedRotation, outputRotation, EPSILON);
|
||||
}
|
||||
|
|
287
tests/gpu/src/ShaderLoadTest.cpp
Normal file
287
tests/gpu/src/ShaderLoadTest.cpp
Normal file
|
@ -0,0 +1,287 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2018/01/11
|
||||
// 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 "ShaderLoadTest.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <QtCore/QTemporaryFile>
|
||||
|
||||
#include <NumericalConstants.h>
|
||||
#include <gpu/Forward.h>
|
||||
#include <gl/Config.h>
|
||||
#include <gl/GLHelpers.h>
|
||||
#include <gl/GLShaders.h>
|
||||
#include <gpu/gl/GLShader.h>
|
||||
#include <gpu/gl/GLBackend.h>
|
||||
#include <shared/FileUtils.h>
|
||||
#include <SettingManager.h>
|
||||
|
||||
#include <test-utils/QTestExtensions.h>
|
||||
|
||||
QTEST_MAIN(ShaderLoadTest)
|
||||
|
||||
extern std::atomic<size_t> gpuBinaryShadersLoaded;
|
||||
|
||||
extern const QString& getShaderCacheFile();
|
||||
|
||||
|
||||
QtMessageHandler originalHandler;
|
||||
|
||||
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
|
||||
#if defined(Q_OS_WIN)
|
||||
OutputDebugStringA(message.toStdString().c_str());
|
||||
OutputDebugStringA("\n");
|
||||
#endif
|
||||
originalHandler(type, context, message);
|
||||
}
|
||||
|
||||
std::pair<int, std::vector<std::pair<QString, QString>>> parseCachedShaderString(const QString& cachedShaderString) {
|
||||
|
||||
std::pair<int, std::vector<std::pair<QString, QString>>> result;
|
||||
{
|
||||
static const QRegularExpression versionRegex("^// VERSION (\\d+)");
|
||||
auto match = versionRegex.match(cachedShaderString);
|
||||
result.first = match.captured(1).toInt();
|
||||
}
|
||||
|
||||
|
||||
int rangeStart = 0;
|
||||
QString type;
|
||||
static const QRegularExpression regex("//-------- (\\w+)");
|
||||
auto match = regex.match(cachedShaderString, rangeStart);
|
||||
while (match.hasMatch()) {
|
||||
auto newType = match.captured(1);
|
||||
auto start = match.capturedStart(0);
|
||||
auto end = match.capturedEnd(0);
|
||||
if (rangeStart != 0) {
|
||||
QString subString = cachedShaderString.mid(rangeStart, start - rangeStart);
|
||||
result.second.emplace_back(type, subString);
|
||||
}
|
||||
rangeStart = end;
|
||||
type = newType;
|
||||
match = regex.match(cachedShaderString, rangeStart);
|
||||
}
|
||||
|
||||
if (rangeStart != 0) {
|
||||
QString subString = cachedShaderString.mid(rangeStart);
|
||||
result.second.emplace_back(type, subString);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string getShaderName(const QString& shader) {
|
||||
static const QRegularExpression nameExp("//\\s+(\\w+\\.(?:vert|frag))");
|
||||
auto match = nameExp.match(shader);
|
||||
if (!match.hasMatch()) {
|
||||
return (QCryptographicHash::hash(shader.toUtf8(), QCryptographicHash::Md5).toHex() + ".shader").toStdString();
|
||||
}
|
||||
return match.captured(1).trimmed().toStdString();
|
||||
}
|
||||
|
||||
void ShaderLoadTest::randomizeShaderSources() {
|
||||
for (auto& entry : _shaderSources) {
|
||||
entry.second += ("\n//" + QUuid::createUuid().toString()).toStdString();
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_LOCAL_SHADERS
|
||||
const QString SHADER_CACHE_FILENAME = "c:/Users/bdavi/AppData/Local/High Fidelity - dev/Interface/shaders/cache.json";
|
||||
static const QString SHADER_FOLDER = "D:/shaders/";
|
||||
void ShaderLoadTest::parseCacheDirectory() {
|
||||
for (const auto& shaderFile : QDir(SHADER_FOLDER).entryList(QDir::Files)) {
|
||||
QString shaderSource = FileUtils::readFile(SHADER_FOLDER + "/" + shaderFile);
|
||||
_shaderSources[shaderFile.trimmed().toStdString()] = shaderSource.toStdString();
|
||||
}
|
||||
|
||||
auto programsDoc = QJsonDocument::fromJson(FileUtils::readFile(SHADER_FOLDER + "programs.json").toUtf8());
|
||||
for (const auto& programElement : programsDoc.array()) {
|
||||
auto programObj = programElement.toObject();
|
||||
QString vertexSource = programObj["vertex"].toString();
|
||||
QString pixelSource = programObj["pixel"].toString();
|
||||
_programs.insert({ vertexSource.toStdString(), pixelSource.toStdString() });
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderLoadTest::persistCacheDirectory() {
|
||||
for (const auto& shaderFile : QDir(SHADER_FOLDER).entryList(QDir::Files)) {
|
||||
QFile(SHADER_FOLDER + "/" + shaderFile).remove();
|
||||
}
|
||||
|
||||
// Write the shader source files
|
||||
for (const auto& entry : _shaderSources) {
|
||||
const QString name = entry.first.c_str();
|
||||
const QString shader = entry.second.c_str();
|
||||
QString fullFile = SHADER_FOLDER + name;
|
||||
QVERIFY(!QFileInfo(fullFile).exists());
|
||||
QFile shaderFile(fullFile);
|
||||
shaderFile.open(QIODevice::WriteOnly);
|
||||
shaderFile.write(shader.toUtf8());
|
||||
shaderFile.close();
|
||||
}
|
||||
|
||||
// Write the list of programs
|
||||
{
|
||||
QVariantList programsList;
|
||||
for (const auto& program : _programs) {
|
||||
QVariantMap programMap;
|
||||
programMap["vertex"] = program.first.c_str();
|
||||
programMap["pixel"] = program.second.c_str();
|
||||
programsList.push_back(programMap);
|
||||
}
|
||||
|
||||
QFile saveFile(SHADER_FOLDER + "programs.json");
|
||||
saveFile.open(QFile::WriteOnly | QFile::Text | QFile::Truncate);
|
||||
saveFile.write(QJsonDocument::fromVariant(programsList).toJson(QJsonDocument::Indented));
|
||||
saveFile.close();
|
||||
}
|
||||
}
|
||||
#else
|
||||
const QString SHADER_CACHE_FILENAME = ":cache.json";
|
||||
#endif
|
||||
|
||||
void ShaderLoadTest::parseCacheFile() {
|
||||
QString json = FileUtils::readFile(SHADER_CACHE_FILENAME);
|
||||
auto root = QJsonDocument::fromJson(json.toUtf8()).object();
|
||||
_programs.clear();
|
||||
_programs.reserve(root.size());
|
||||
|
||||
const auto keys = root.keys();
|
||||
Program program;
|
||||
for (auto shaderKey : keys) {
|
||||
auto cacheEntry = root[shaderKey].toObject();
|
||||
auto source = cacheEntry["source"].toString();
|
||||
auto shaders = parseCachedShaderString(source);
|
||||
for (const auto& entry : shaders.second) {
|
||||
const auto& type = entry.first;
|
||||
const auto& source = entry.second;
|
||||
const auto name = getShaderName(source);
|
||||
if (name.empty()) {
|
||||
continue;
|
||||
}
|
||||
if (0 == _shaderSources.count(name)) {
|
||||
_shaderSources[name] = source.toStdString();
|
||||
}
|
||||
if (type == "vertex") {
|
||||
program.first = name;
|
||||
} else if (type == "pixel") {
|
||||
program.second = name;
|
||||
}
|
||||
}
|
||||
// FIXME support geometry / tesselation shaders eventually
|
||||
if (program.first.empty() || program.second.empty()) {
|
||||
qFatal("Bad Shader Setup");
|
||||
}
|
||||
_programs.insert(program);
|
||||
}
|
||||
}
|
||||
|
||||
bool ShaderLoadTest::buildProgram(const Program& programFiles) {
|
||||
const auto& vertexName = programFiles.first;
|
||||
const auto& vertexSource = _shaderSources[vertexName];
|
||||
auto vertexShader = gpu::Shader::createVertex({ vertexSource });
|
||||
|
||||
const auto& pixelName = programFiles.second;
|
||||
const auto& pixelSource = _shaderSources[pixelName];
|
||||
auto pixelShader = gpu::Shader::createPixel({ pixelSource });
|
||||
|
||||
auto program = gpu::Shader::createProgram(vertexShader, pixelShader);
|
||||
return gpu::gl::GLBackend::makeProgram(*program, {}, {});
|
||||
}
|
||||
|
||||
void ShaderLoadTest::initTestCase() {
|
||||
originalHandler = qInstallMessageHandler(messageHandler);
|
||||
DependencyManager::set<Setting::Manager>();
|
||||
{
|
||||
const auto& shaderCacheFile = getShaderCacheFile();
|
||||
if (QFileInfo(shaderCacheFile).exists()) {
|
||||
QFile(shaderCacheFile).remove();
|
||||
}
|
||||
}
|
||||
|
||||
// For local debugging
|
||||
#if USE_LOCAL_SHADERS
|
||||
parseCacheFile();
|
||||
persistCacheDirectory();
|
||||
parseCacheDirectory();
|
||||
#else
|
||||
parseCacheFile();
|
||||
#endif
|
||||
|
||||
// We use this to defeat shader caching both by the GPU backend
|
||||
// and the OpenGL driver
|
||||
randomizeShaderSources();
|
||||
|
||||
QVERIFY(!_programs.empty());
|
||||
for (const auto& program : _programs) {
|
||||
QVERIFY(_shaderSources.count(program.first) == 1);
|
||||
QVERIFY(_shaderSources.count(program.second) == 1);
|
||||
}
|
||||
|
||||
getDefaultOpenGLSurfaceFormat();
|
||||
_canvas.create();
|
||||
if (!_canvas.makeCurrent()) {
|
||||
qFatal("Unable to make test GL context current");
|
||||
}
|
||||
gl::initModuleGl();
|
||||
gpu::Context::init<gpu::gl::GLBackend>();
|
||||
_canvas.makeCurrent();
|
||||
}
|
||||
|
||||
void ShaderLoadTest::cleanupTestCase() {
|
||||
DependencyManager::destroy<Setting::Manager>();
|
||||
}
|
||||
|
||||
void ShaderLoadTest::testShaderLoad() {
|
||||
auto gpuContext = std::make_shared<gpu::Context>();
|
||||
QVERIFY(gpuBinaryShadersLoaded == 0);
|
||||
|
||||
QElapsedTimer timer;
|
||||
|
||||
// Initial load of all the shaders
|
||||
// No caching
|
||||
{
|
||||
timer.start();
|
||||
for (const auto& program : _programs) {
|
||||
QVERIFY(buildProgram(program));
|
||||
}
|
||||
qDebug() << "Uncached shader load took" << timer.elapsed() << "ms";
|
||||
QVERIFY(gpuBinaryShadersLoaded == 0);
|
||||
}
|
||||
gpuContext->recycle();
|
||||
glFinish();
|
||||
|
||||
// Reload the shaders within the same GPU context lifetime.
|
||||
// Shaders will use the cached binaries in memory
|
||||
{
|
||||
timer.start();
|
||||
for (const auto& program : _programs) {
|
||||
QVERIFY(buildProgram(program));
|
||||
}
|
||||
qDebug() << "Cached shader load took" << timer.elapsed() << "ms";
|
||||
QVERIFY(gpuBinaryShadersLoaded == _programs.size() * gpu::gl::GLShader::NumVersions);
|
||||
}
|
||||
|
||||
// Simulate reloading the shader cache from disk by destroying and recreating the gpu context
|
||||
// Shaders will use the cached binaries from disk
|
||||
{
|
||||
gpuBinaryShadersLoaded = 0;
|
||||
gpuContext->recycle();
|
||||
gpuContext->shutdown();
|
||||
gpuContext.reset();
|
||||
gpuContext = std::make_shared<gpu::Context>();
|
||||
_canvas.makeCurrent();
|
||||
timer.start();
|
||||
for (const auto& program : _programs) {
|
||||
QVERIFY(buildProgram(program));
|
||||
}
|
||||
qDebug() << "Cached shader load took" << timer.elapsed() << "ms";
|
||||
QVERIFY(gpuBinaryShadersLoaded == _programs.size() * gpu::gl::GLShader::NumVersions);
|
||||
}
|
||||
|
||||
}
|
||||
|
63
tests/gpu/src/ShaderLoadTest.h
Normal file
63
tests/gpu/src/ShaderLoadTest.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
//
|
||||
// Created by Bradley Austin Davis on 2018/05/08
|
||||
// Copyright 2013-2018 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
#include <QtCore/QTemporaryDir>
|
||||
|
||||
#include <gpu/Forward.h>
|
||||
#include <gl/OffscreenGLCanvas.h>
|
||||
|
||||
#define USE_LOCAL_SHADERS 0
|
||||
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<std::pair<std::string, std::string>> {
|
||||
size_t operator()(const std::pair<std::string, std::string>& a) const {
|
||||
std::hash<std::string> hasher;
|
||||
return hasher(a.first) + hasher(a.second);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using ShadersByName = std::unordered_map<std::string, std::string>;
|
||||
using Program = std::pair<std::string, std::string>;
|
||||
using Programs = std::unordered_set<Program>;
|
||||
|
||||
class ShaderLoadTest : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
|
||||
void parseCacheFile();
|
||||
#if USE_LOCAL_SHADERS
|
||||
void parseCacheDirectory();
|
||||
void persistCacheDirectory();
|
||||
#endif
|
||||
bool buildProgram(const Program& program);
|
||||
void randomizeShaderSources();
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
void testShaderLoad();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
ShadersByName _shaderSources;
|
||||
Programs _programs;
|
||||
QString _resourcesPath;
|
||||
OffscreenGLCanvas _canvas;
|
||||
const glm::uvec2 _size{ 640, 480 };
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue