3
0
Fork 0
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:
David Rowe 2018-06-05 09:13:54 +12:00
commit b5028a9626
132 changed files with 2805 additions and 1411 deletions
android/app
build.gradle
src/main
AndroidManifest.xml
java/io/highfidelity/hifiinterface
cmake/macros
interface
libraries
plugins/openvr/src
scripts
tests-manual/shaders/src
tests

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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-&lt;user name&gt-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-&lt;user name&gt;-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&deg; 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-&lt;user name&gt-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&deg; 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-&lt;user name&gt;-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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -35,7 +35,7 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) :
_useDualQuaternionSkinning = true;
// Avatars all cast shadow
_canCastShadow = true;
setCanCastShadow(true);
assert(_owningAvatar);
}

View file

@ -46,6 +46,8 @@
const char* SRGB_TO_LINEAR_FRAG = R"SCRIBE(
// OpenGLDisplayPlugin_present.frag
uniform sampler2D colorMap;
in vec2 varTexCoord0;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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([&] {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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([&]{

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,3 +1,3 @@
set(TARGET_NAME test-utils)
setup_hifi_library(Network Gui)
link_hifi_libraries(shared)

View file

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

View file

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

View file

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

View file

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

View file

@ -30,7 +30,7 @@ function init() {
text: "STATS"
});
statsButton.clicked.connect(function() {
Menu.triggerOption("Stats");
Menu.triggerOption("Show Statistics");
});
}

View file

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

View file

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

View file

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

View file

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

View file

@ -16,7 +16,7 @@
#include <AnimationLogging.h>
#include <NumericalConstants.h>
#include "../QTestExtensions.h"
#include <test-utils/QTestExtensions.h>
QTEST_MAIN(AnimInverseKinematicsTests)

View file

@ -20,7 +20,7 @@
#include <AccountManager.h>
#include <ResourceManager.h>
#include <StatTracker.h>
#include <../QTestExtensions.h>
#include <test-utils/QTestExtensions.h>
QTEST_MAIN(AnimTests)

View file

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

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

View 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