diff --git a/.gitignore b/.gitignore index 1ffb93fe80..df91e0ca7b 100644 --- a/.gitignore +++ b/.gitignore @@ -78,6 +78,8 @@ TAGS node_modules npm-debug.log +# ignore qmlc files generated from qml as cache +*.qmlc # Android studio files *___jb_old___ @@ -88,4 +90,4 @@ android/app/src/main/assets interface/compiledResources # GPUCache -interface/resources/GPUCache/* \ No newline at end of file +interface/resources/GPUCache/* diff --git a/BUILD_ANDROID.md b/BUILD_ANDROID.md index 3d6d056d79..8c9263b6e7 100644 --- a/BUILD_ANDROID.md +++ b/BUILD_ANDROID.md @@ -53,7 +53,7 @@ Enter the repository `android` directory Execute a gradle pre-build setup. This step should only need to be done once -`gradle setupDepedencies` +`gradle setupDependencies` # Building & Running diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 5e93bdffa3..b3a8c87649 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -19,9 +19,9 @@ android:allowBackup="true" android:screenOrientation="unspecified" android:theme="@style/NoSystemUI" - android:icon="@mipmap/ic_launcher" + android:icon="@drawable/ic_launcher" android:launchMode="singleTop" - android:roundIcon="@mipmap/ic_launcher_round"> + android:roundIcon="@drawable/ic_launcher"> diff --git a/android/app/src/main/res/drawable/ic_launcher.xml b/android/app/src/main/res/drawable/ic_launcher.xml new file mode 100644 index 0000000000..03b1edc4e9 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_launcher.xml @@ -0,0 +1,17 @@ + + + + + + + \ No newline at end of file diff --git a/android/app/src/main/res/drawable/icon.png b/android/app/src/main/res/drawable/icon.png deleted file mode 100644 index 70aaf9b4ed..0000000000 Binary files a/android/app/src/main/res/drawable/icon.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index d376d7af88..0000000000 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index 81a137957d..0000000000 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 88b1be5903..0000000000 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index d032c30014..0000000000 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index 4a018d4ed9..0000000000 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index 3cccf1037b..0000000000 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index e97062e0ee..0000000000 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index 8d142881da..0000000000 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index f01203c738..0000000000 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index 211e298961..0000000000 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/android/build.gradle b/android/build.gradle index 22273f9059..1dfef97f91 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -137,6 +137,13 @@ def packages = [ checksum: '20768f298f53b195e71b414b0ae240c4', sharedLibFolder: 'lib/release', includeLibs: ['libtbb.so', 'libtbbmalloc.so'], + ], + hifiAC: [ + file: 'libplugins_libhifiCodec.zip', + versionId: 'mzKhsRCgVmloqq5bvE.0IwYK1NjGQc_G', + checksum: '9412a8e12c88a4096c1fc843bb9fe52d', + sharedLibFolder: '', + includeLibs: ['libplugins_libhifiCodec.so'] ] ] @@ -353,6 +360,7 @@ task verifyGvr(type: Verify) { def p = packages['gvr']; src new File(baseFolder, task verifyOpenSSL(type: Verify) { def p = packages['openssl']; src new File(baseFolder, p['file']); checksum p['checksum'] } task verifyPolyvox(type: Verify) { def p = packages['polyvox']; src new File(baseFolder, p['file']); checksum p['checksum'] } task verifyTBB(type: Verify) { def p = packages['tbb']; src new File(baseFolder, p['file']); checksum p['checksum'] } +task verifyHifiAC(type: Verify) { def p = packages['hifiAC']; src new File(baseFolder, p['file']); checksum p['checksum'] } task verifyDependencyDownloads(dependsOn: downloadDependencies) { } verifyDependencyDownloads.dependsOn verifyQt @@ -362,6 +370,7 @@ verifyDependencyDownloads.dependsOn verifyGvr verifyDependencyDownloads.dependsOn verifyOpenSSL verifyDependencyDownloads.dependsOn verifyPolyvox verifyDependencyDownloads.dependsOn verifyTBB +verifyDependencyDownloads.dependsOn verifyHifiAC task extractDependencies(dependsOn: verifyDependencyDownloads) { doLast { diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 3ed5445493..49877cacc3 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -23,12 +23,22 @@ set(RESOURCES_QRC ${CMAKE_CURRENT_BINARY_DIR}/resources.qrc) set(RESOURCES_RCC ${CMAKE_CURRENT_SOURCE_DIR}/compiledResources/resources.rcc) generate_qrc(OUTPUT ${RESOURCES_QRC} PATH ${CMAKE_CURRENT_SOURCE_DIR}/resources CUSTOM_PATHS ${CUSTOM_INTERFACE_QRC_PATHS} GLOBS *) -add_custom_command( - OUTPUT ${RESOURCES_RCC} - DEPENDS ${RESOURCES_QRC} ${GENERATE_QRC_DEPENDS} - COMMAND "${QT_DIR}/bin/rcc" - ARGS ${RESOURCES_QRC} -binary -o ${RESOURCES_RCC} -) +if (ANDROID) + # on Android, don't compress the rcc binary + add_custom_command( + OUTPUT ${RESOURCES_RCC} + DEPENDS ${RESOURCES_QRC} ${GENERATE_QRC_DEPENDS} + COMMAND "${QT_DIR}/bin/rcc" + ARGS ${RESOURCES_QRC} -no-compress -binary -o ${RESOURCES_RCC} + ) +else () + add_custom_command( + OUTPUT ${RESOURCES_RCC} + DEPENDS ${RESOURCES_QRC} ${GENERATE_QRC_DEPENDS} + COMMAND "${QT_DIR}/bin/rcc" + ARGS ${RESOURCES_QRC} -binary -o ${RESOURCES_RCC} + ) +endif() list(APPEND GENERATE_QRC_DEPENDS ${RESOURCES_RCC}) add_custom_target(resources ALL DEPENDS ${GENERATE_QRC_DEPENDS}) diff --git a/interface/resources/qml/+android/AddressBarDialog.qml b/interface/resources/qml/+android/AddressBarDialog.qml index 50552f0d3b..b8d6b5e270 100644 --- a/interface/resources/qml/+android/AddressBarDialog.qml +++ b/interface/resources/qml/+android/AddressBarDialog.qml @@ -76,33 +76,33 @@ Item { HifiStyles.RalewayRegular { id: notice text: "YOUR LOCATION" - font.pixelSize: (hifi.fonts.pixelSize * 2.15)*(android.dimen.atLeast1440p?1:.75); + font.pixelSize: (hifi.fonts.pixelSize * 2.15) * (android.dimen.atLeast1440p ? 1 : .75); color: "#2CD7FF" anchors { bottom: addressBackground.top - bottomMargin: android.dimen.atLeast1440p?45:34 + bottomMargin: android.dimen.atLeast1440p ? 45 : 34 left: addressBackground.left - leftMargin: android.dimen.atLeast1440p?60:45 + leftMargin: android.dimen.atLeast1440p ? 60 : 45 } } - property int inputAreaHeight: android.dimen.atLeast1440p?210:156 + property int inputAreaHeight: android.dimen.atLeast1440p ? 210 : 156 property int inputAreaStep: (height - inputAreaHeight) / 2 ToolbarButton { id: homeButton - y: android.dimen.atLeast1440p?280:210 + y: android.dimen.atLeast1440p ? 280 : 210 imageURL: "../../icons/home.svg" onClicked: { addressBarDialog.loadHome(); bar.shown = false; } anchors { - leftMargin: android.dimen.atLeast1440p?75:56 + leftMargin: android.dimen.atLeast1440p ? 75 : 56 left: parent.left } - size: android.dimen.atLeast1440p?150:150//112 + size: android.dimen.atLeast1440p ? 150 : 150//112 } ToolbarButton { @@ -111,10 +111,10 @@ Item { onClicked: addressBarDialog.loadBack(); anchors { left: homeButton.right - leftMargin: android.dimen.atLeast1440p?70:52 + leftMargin: android.dimen.atLeast1440p ? 70 : 52 verticalCenter: homeButton.verticalCenter } - size: android.dimen.atLeast1440p?150:150 + size: android.dimen.atLeast1440p ? 150 : 150 } ToolbarButton { id: forwardArrow; @@ -122,10 +122,10 @@ Item { onClicked: addressBarDialog.loadForward(); anchors { left: backArrow.right - leftMargin: android.dimen.atLeast1440p?60:45 + leftMargin: android.dimen.atLeast1440p ? 60 : 45 verticalCenter: homeButton.verticalCenter } - size: android.dimen.atLeast1440p?150:150 + size: android.dimen.atLeast1440p ? 150 : 150 } HifiStyles.FiraSansRegular { @@ -140,25 +140,22 @@ Item { Rectangle { id: addressBackground - x: android.dimen.atLeast1440p?780:585 - y: android.dimen.atLeast1440p?280:235 // tweaking by hand - width: android.dimen.atLeast1440p?1270:952 - height: android.dimen.atLeast1440p?150:112 + x: android.dimen.atLeast1440p ? 780 : 585 + y: android.dimen.atLeast1440p ? 280 : 235 // tweaking by hand + width: android.dimen.atLeast1440p ? 1270 : 952 + height: android.dimen.atLeast1440p ? 150 : 112 color: "#FFFFFF" } TextInput { id: addressLine focus: true - x: android.dimen.atLeast1440p?870:652 - y: android.dimen.atLeast1440p?300:245 // tweaking by hand - width: android.dimen.atLeast1440p?1200:900 - height: android.dimen.atLeast1440p?120:90 + x: android.dimen.atLeast1440p ? 870 : 652 + y: android.dimen.atLeast1440p ? 300 : 245 // tweaking by hand + width: android.dimen.atLeast1440p ? 1200 : 900 + height: android.dimen.atLeast1440p ? 120 : 90 inputMethodHints: Qt.ImhNoPredictiveText //helperText: "Hint is here" - anchors { - //verticalCenter: addressBackground.verticalCenter - } font.pixelSize: hifi.fonts.pixelSize * 3.75 onTextChanged: { //filterChoicesByText(); @@ -228,4 +225,4 @@ Item { } } -} \ No newline at end of file +} diff --git a/interface/resources/qml/controls-uit/CheckBox.qml b/interface/resources/qml/controls-uit/CheckBox.qml index e60f646327..973bbee60e 100644 --- a/interface/resources/qml/controls-uit/CheckBox.qml +++ b/interface/resources/qml/controls-uit/CheckBox.qml @@ -42,8 +42,8 @@ Original.CheckBox { style: CheckBoxStyle { indicator: Rectangle { id: box - width: boxSize - height: boxSize + implicitWidth: boxSize + implicitHeight: boxSize radius: boxRadius border.width: 1 border.color: pressed || hovered @@ -101,8 +101,8 @@ Original.CheckBox { } label: Label { - text: control.text - color: control.color + text: checkBox.text + color: checkBox.color x: 2 wrapMode: checkBox.wrap ? Text.Wrap : Text.NoWrap elide: checkBox.wrap ? Text.ElideNone : Text.ElideRight diff --git a/interface/resources/qml/hifi/+android/AvatarOption.qml b/interface/resources/qml/hifi/+android/AvatarOption.qml index e7056baa36..85d7e52eb2 100644 --- a/interface/resources/qml/hifi/+android/AvatarOption.qml +++ b/interface/resources/qml/hifi/+android/AvatarOption.qml @@ -26,13 +26,13 @@ ColumnLayout { property string methodName: ""; property string actionText: ""; - spacing: 4*3 + spacing: 4 * 3 signal sendToParentQml(var message); Image { id: itemImage - Layout.preferredWidth: 250*3 - Layout.preferredHeight: 140*3 + Layout.preferredWidth: 250 * 3 + Layout.preferredHeight: 140 * 3 source: thumbnailUrl asynchronous: true fillMode: Image.PreserveAspectFit @@ -81,7 +81,7 @@ ColumnLayout { HifiControlsUit.ImageButton { width: 140*3 height: 35*3 - text: type=="extra"? actionText: "CHOOSE" + text: type=="extra" ? actionText: "CHOOSE" source: "../../../../icons/button.svg" hoverSource: "../../../../icons/button-a.svg" fontSize: 18*3 @@ -102,8 +102,8 @@ ColumnLayout { Image { id: tickImage - width: 35*3 - height: 35*3 + width: 35 * 3 + height: 35 * 3 source: "../../../icons/tick.svg" anchors { horizontalCenter: itemName.horizontalCenter @@ -114,4 +114,4 @@ ColumnLayout { Component.onCompleted:{ sendToParentQml.connect(sendToScript); } -} \ No newline at end of file +} diff --git a/interface/resources/qml/hifi/+android/HifiConstants.qml b/interface/resources/qml/hifi/+android/HifiConstants.qml index fbdf60dcb0..4c161da259 100644 --- a/interface/resources/qml/hifi/+android/HifiConstants.qml +++ b/interface/resources/qml/hifi/+android/HifiConstants.qml @@ -22,26 +22,26 @@ Item { Item { id: dimen readonly property bool atLeast1440p: Screen.width >= 2560 && Screen.height >= 1440 - readonly property real windowLessWidth: atLeast1440p?378:284 - readonly property real windowLessHeight: atLeast1440p?192:144 + readonly property real windowLessWidth: atLeast1440p ? 378 : 284 + readonly property real windowLessHeight: atLeast1440p ? 192 : 144 readonly property real windowZ: 100 - readonly property real headerHeight: atLeast1440p?276:207 + readonly property real headerHeight: atLeast1440p ? 276 : 207 - readonly property real headerIconPosX: atLeast1440p?90:67 - readonly property real headerIconPosY: atLeast1440p?108:81 - readonly property real headerIconWidth: atLeast1440p?111:83 - readonly property real headerIconHeight: atLeast1440p?111:83 - readonly property real headerIconTitleDistance: atLeast1440p?151:113 + readonly property real headerIconPosX: atLeast1440p ? 90 : 67 + readonly property real headerIconPosY: atLeast1440p ? 108 : 81 + readonly property real headerIconWidth: atLeast1440p ? 111 : 83 + readonly property real headerIconHeight: atLeast1440p ? 111 : 83 + readonly property real headerIconTitleDistance: atLeast1440p ? 151 : 113 - readonly property real headerHideWidth: atLeast1440p?150:112 - readonly property real headerHideHeight: atLeast1440p?150:112 - readonly property real headerHideRightMargin: atLeast1440p?110:82 - readonly property real headerHideTopMargin: atLeast1440p?90:67 - readonly property real headerHideIconWidth: atLeast1440p?70:52 - readonly property real headerHideIconHeight: atLeast1440p?45:33 - readonly property real headerHideTextTopMargin: atLeast1440p?36:27 + readonly property real headerHideWidth: atLeast1440p ? 150 : 112 + readonly property real headerHideHeight: atLeast1440p ? 150 : 112 + readonly property real headerHideRightMargin: atLeast1440p ? 110 : 82 + readonly property real headerHideTopMargin: atLeast1440p ? 90 : 67 + readonly property real headerHideIconWidth: atLeast1440p ? 70 : 52 + readonly property real headerHideIconHeight: atLeast1440p ? 45 : 33 + readonly property real headerHideTextTopMargin: atLeast1440p ? 36 : 27 readonly property real botomHudWidth: 366 readonly property real botomHudHeight: 180 diff --git a/interface/resources/qml/windows/Frame.qml b/interface/resources/qml/windows/Frame.qml index 210a3c76d0..98bfb52c38 100644 --- a/interface/resources/qml/windows/Frame.qml +++ b/interface/resources/qml/windows/Frame.qml @@ -16,10 +16,11 @@ import "../js/Utils.js" as Utils Item { id: frame + objectName: "Frame" HifiConstants { id: hifi } default property var decoration - + property string qmlFile: "N/A" property bool gradientsSupported: desktop.gradientsSupported readonly property int frameMarginLeft: frame.decoration ? frame.decoration.frameMarginLeft : 0 @@ -44,7 +45,7 @@ Item { id: debugZ visible: DebugQML color: "red" - text: (window ? "Z: " + window.z : "") + text: (window ? "Z: " + window.z : "") + " " + qmlFile y: window ? window.height + 4 : 0 } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index fd0a7a65d7..165deaf6bd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2032,20 +2032,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(_window, SIGNAL(windowMinimizedChanged(bool)), this, SLOT(windowMinimizedChanged(bool))); qCDebug(interfaceapp, "Startup time: %4.2f seconds.", (double)startupTimer.elapsed() / 1000.0); - { - PROFILE_RANGE(render, "Process Default Skybox"); - auto textureCache = DependencyManager::get(); - - QFileSelector fileSelector; - fileSelector.setExtraSelectors(FileUtils::getFileSelectors()); - auto skyboxUrl = fileSelector.select(PathUtils::resourcesPath() + "images/Default-Sky-9-cubemap.ktx"); - - _defaultSkyboxTexture = gpu::Texture::unserialize(skyboxUrl.toStdString()); - _defaultSkyboxAmbientTexture = _defaultSkyboxTexture; - - _defaultSkybox->setCubemap(_defaultSkyboxTexture); - } - EntityTreeRenderer::setEntitiesShouldFadeFunction([this]() { SharedNodePointer entityServerNode = DependencyManager::get()->soloNodeOfType(NodeType::EntityServer); return entityServerNode && !isPhysicsEnabled(); @@ -2495,7 +2481,6 @@ void Application::initializeGL() { DeadlockWatchdogThread::withPause([&] { // Set up the render engine render::CullFunctor cullFunctor = LODManager::shouldRender; - static const QString RENDER_FORWARD = "HIFI_RENDER_FORWARD"; _renderEngine->addJob("UpdateScene"); #ifndef Q_OS_ANDROID _renderEngine->addJob("SecondaryCameraJob", cullFunctor, !DISABLE_DEFERRED); @@ -5527,7 +5512,7 @@ void Application::update(float deltaTime) { editRenderArgs([this, deltaTime](AppRenderArgs& appRenderArgs) { PerformanceTimer perfTimer("editRenderArgs"); - appRenderArgs._headPose= getHMDSensorPose(); + appRenderArgs._headPose = getHMDSensorPose(); auto myAvatar = getMyAvatar(); @@ -5547,10 +5532,10 @@ void Application::update(float deltaTime) { { QMutexLocker viewLocker(&_viewMutex); // adjust near clip plane to account for sensor scaling. - auto adjustedProjection = glm::perspective(_viewFrustum.getFieldOfView(), - _viewFrustum.getAspectRatio(), - DEFAULT_NEAR_CLIP * sensorToWorldScale, - _viewFrustum.getFarClip()); + auto adjustedProjection = glm::perspective(glm::radians(_fieldOfView.get()), + getActiveDisplayPlugin()->getRecommendedAspectRatio(), + DEFAULT_NEAR_CLIP * sensorToWorldScale, + DEFAULT_FAR_CLIP); _viewFrustum.setProjection(adjustedProjection); _viewFrustum.calculate(); } @@ -5632,6 +5617,7 @@ void Application::update(float deltaTime) { { QMutexLocker viewLocker(&_viewMutex); _myCamera.loadViewFrustum(_displayViewFrustum); + appRenderArgs._view = glm::inverse(_displayViewFrustum.getView()); } { diff --git a/interface/src/Application.h b/interface/src/Application.h index 2e0e5b3db5..4c31eb0074 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -272,10 +272,6 @@ public: void shareSnapshot(const QString& filename, const QUrl& href = QUrl("")); - graphics::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; } - gpu::TexturePointer getDefaultSkyboxTexture() const { return _defaultSkyboxTexture; } - gpu::TexturePointer getDefaultSkyboxAmbientTexture() const { return _defaultSkyboxAmbientTexture; } - OverlayID getTabletScreenID() const; OverlayID getTabletHomeButtonID() const; QUuid getTabletFrameID() const; // may be an entity or an overlay @@ -627,6 +623,7 @@ private: struct AppRenderArgs { render::Args _renderArgs; glm::mat4 _eyeToWorld; + glm::mat4 _view; glm::mat4 _eyeOffsets[2]; glm::mat4 _eyeProjections[2]; glm::mat4 _headPose; @@ -682,10 +679,6 @@ private: ConnectionMonitor _connectionMonitor; - graphics::SkyboxPointer _defaultSkybox { new ProceduralSkybox() } ; - gpu::TexturePointer _defaultSkyboxTexture; - gpu::TexturePointer _defaultSkyboxAmbientTexture; - QTimer _addAssetToWorldResizeTimer; QHash _addAssetToWorldResizeList; diff --git a/interface/src/Application_render.cpp b/interface/src/Application_render.cpp index 5cc072df37..e1015ca5d1 100644 --- a/interface/src/Application_render.cpp +++ b/interface/src/Application_render.cpp @@ -90,10 +90,10 @@ void Application::paintGL() { { PROFILE_RANGE(render, "/gpuContextReset"); - _gpuContext->beginFrame(HMDSensorPose); + _gpuContext->beginFrame(_appRenderArgs._view, HMDSensorPose); // Reset the gpu::Context Stages // Back to the default framebuffer; - gpu::doInBatch(_gpuContext, [&](gpu::Batch& batch) { + gpu::doInBatch("Application_render::gpuContextReset", _gpuContext, [&](gpu::Batch& batch) { batch.resetStages(); }); } @@ -216,7 +216,7 @@ void Application::runRenderFrame(RenderArgs* renderArgs) { // Make sure the WorldBox is in the scene // For the record, this one RenderItem is the first one we created and added to the scene. - // We could meoee that code elsewhere but you know... + // We could move that code elsewhere but you know... if (!render::Item::isValidID(WorldBoxRenderData::_item)) { auto worldBoxRenderData = std::make_shared(); auto worldBoxRenderPayload = std::make_shared(worldBoxRenderData); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index ca6f7a31d1..fa0e8087f0 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -45,6 +45,9 @@ #include "LocationBookmarks.h" #include "DeferredLightingEffect.h" +#include "AmbientOcclusionEffect.h" +#include "RenderShadowTask.h" + #if defined(Q_OS_MAC) || defined(Q_OS_WIN) #include "SpeechRecognizer.h" #endif @@ -361,18 +364,6 @@ Menu::Menu() { // Developer menu ---------------------------------- MenuWrapper* developerMenu = addMenu("Developer", "Developer"); - // Developer > Graphics - MenuWrapper* graphicsOptionsMenu = developerMenu->addMenu("Render"); - action = addCheckableActionToQMenuAndActionHash(graphicsOptionsMenu, MenuOption::Shadows, 0, true); - connect(action, &QAction::triggered, [action] { - DependencyManager::get()->setShadowMapEnabled(action->isChecked()); - }); - - action = addCheckableActionToQMenuAndActionHash(graphicsOptionsMenu, MenuOption::AmbientOcclusion, 0, false); - connect(action, &QAction::triggered, [action] { - DependencyManager::get()->setAmbientOcclusionEnabled(action->isChecked()); - }); - // Developer > UI >>> MenuWrapper* uiOptionsMenu = developerMenu->addMenu("UI"); action = addCheckableActionToQMenuAndActionHash(uiOptionsMenu, MenuOption::DesktopTabletToToolbar, 0, @@ -389,6 +380,36 @@ Menu::Menu() { // Developer > Render >>> MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render"); + action = addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Shadows, 0, true); + connect(action, &QAction::triggered, [action] { + auto renderConfig = qApp->getRenderEngine()->getConfiguration(); + if (renderConfig) { + auto mainViewShadowTaskConfig = renderConfig->getConfig("RenderMainView.RenderShadowTask"); + if (mainViewShadowTaskConfig) { + if (action->isChecked()) { + mainViewShadowTaskConfig->setPreset("Enabled"); + } else { + mainViewShadowTaskConfig->setPreset("None"); + } + } + } + }); + + action = addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::AmbientOcclusion, 0, false); + connect(action, &QAction::triggered, [action] { + auto renderConfig = qApp->getRenderEngine()->getConfiguration(); + if (renderConfig) { + auto mainViewAmbientOcclusionConfig = renderConfig->getConfig("RenderMainView.AmbientOcclusion"); + if (mainViewAmbientOcclusionConfig) { + if (action->isChecked()) { + mainViewAmbientOcclusionConfig->setPreset("Enabled"); + } else { + mainViewAmbientOcclusionConfig->setPreset("None"); + } + } + } + }); + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::WorldAxes); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DefaultSkybox, 0, true); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 1d37b74ffe..cf9eed1a27 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -205,7 +205,7 @@ namespace MenuOption { const QString DesktopTabletToToolbar = "Desktop Tablet Becomes Toolbar"; const QString HMDTabletToToolbar = "HMD Tablet Becomes Toolbar"; const QString Shadows = "Shadows"; - const QString AmbientOcclusion = "AmbientOcclusion"; + const QString AmbientOcclusion = "Ambient Occlusion"; } #endif // hifi_Menu_h diff --git a/interface/src/SecondaryCamera.cpp b/interface/src/SecondaryCamera.cpp index 45f1756eb7..a4bf0bcefe 100644 --- a/interface/src/SecondaryCamera.cpp +++ b/interface/src/SecondaryCamera.cpp @@ -107,7 +107,7 @@ public: args->_displayMode = RenderArgs::MONO; args->_renderMode = RenderArgs::RenderMode::SECONDARY_CAMERA_RENDER_MODE; - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("SecondaryCameraJob::run", args->_context, [&](gpu::Batch& batch) { batch.disableContextStereo(); batch.disableContextViewCorrection(); }); @@ -196,7 +196,7 @@ public: args->_displayMode = cachedArgs->_displayMode; args->_renderMode = cachedArgs->_renderMode; - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("EndSecondaryCameraFrame::run", args->_context, [&](gpu::Batch& batch) { batch.restoreContextStereo(); batch.restoreContextViewCorrection(); }); diff --git a/interface/src/avatar/AvatarActionFarGrab.h b/interface/src/avatar/AvatarActionFarGrab.h index bcaf7f2f3c..97d4a6bb03 100644 --- a/interface/src/avatar/AvatarActionFarGrab.h +++ b/interface/src/avatar/AvatarActionFarGrab.h @@ -15,6 +15,24 @@ #include #include +/**jsdoc + * The "far-grab" {@link Entities.ActionType|ActionType} moves and rotates an entity to a target position and + * orientation, optionally relative to another entity. Collisions between the entity and the user's avatar are disabled during + * the far-grab. + * It has arguments in addition to the common {@link Entities.ActionArguments|ActionArguments}. + * + * @typedef {object} Entities.ActionArguments-FarGrab + * @property {Vec3} targetPosition=0,0,0 - The target position. + * @property {Quat} targetRotation=0,0,0,1 - The target rotation. + * @property {Uuid} otherID=null - If an entity ID, the targetPosition and targetRotation are + * relative to this entity's position and rotation. + * @property {number} linearTimeScale=3.4e+38 - Controls how long it takes for the entity's position to catch up with the + * target position. The value is the time for the action to catch up to 1/e = 0.368 of the target value, where the action + * is applied using an exponential decay. + * @property {number} angularTimeScale=3.4e+38 - Controls how long it takes for the entity's orientation to catch up with the + * target orientation. The value is the time for the action to catch up to 1/e = 0.368 of the target value, where the + * action is applied using an exponential decay. + */ class AvatarActionFarGrab : public ObjectActionTractor { public: AvatarActionFarGrab(const QUuid& id, EntityItemPointer ownerEntity); diff --git a/interface/src/avatar/AvatarActionHold.cpp b/interface/src/avatar/AvatarActionHold.cpp index d4fe8574ca..9d568e6d73 100644 --- a/interface/src/avatar/AvatarActionHold.cpp +++ b/interface/src/avatar/AvatarActionHold.cpp @@ -416,6 +416,26 @@ bool AvatarActionHold::updateArguments(QVariantMap arguments) { return true; } +/**jsdoc + * The "hold" {@link Entities.ActionType|ActionType} positions and rotates an entity relative to an avatar's hand. + * Collisions between the entity and the user's avatar are disabled during the hold. + * It has arguments in addition to the common {@link Entities.ActionArguments|ActionArguments}. + * + * @typedef {object} Entities.ActionArguments-Hold + * @property {Uuid} holderID=MyAvatar.sessionUUID - The ID of the avatar holding the entity. + * @property {Vec3} relativePosition=0,0,0 - The target position relative to the avatar's hand. + * @property {Vec3} relativeRotation=0,0,0,1 - The target rotation relative to the avatar's hand. + * @property {number} timeScale=3.4e+38 - Controls how long it takes for the entity's position and rotation to catch up with + * the target. The value is the time for the action to catch up to 1/e = 0.368 of the target value, where the action is + * applied using an exponential decay. + * @property {string} hand=right - The hand holding the entity: "left" or "right". + * @property {boolean} kinematic=false - If true, the entity is made kinematic during the action; the entity won't + * lag behind the hand but constraint actions such as "hinge" won't act properly. + * @property {boolean} kinematicSetVelocity=false - If true and kinematic is true, the + * entity's velocity property will be set during the action, e.g., so that other scripts may use the value. + * @property {boolean} ignoreIK=false - If true, the entity follows the HMD controller rather than the avatar's + * hand. + */ QVariantMap AvatarActionHold::getArguments() { QVariantMap arguments = ObjectDynamic::getArguments(); withReadLock([&]{ diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index a5e24b8707..9620a2dcec 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1115,7 +1115,6 @@ void MyAvatar::setEnableDebugDrawIKChains(bool isEnabled) { void MyAvatar::setEnableMeshVisible(bool isEnabled) { _skeletonModel->setVisibleInScene(isEnabled, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true); - _skeletonModel->setCanCastShadow(isEnabled, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true); } void MyAvatar::setEnableInverseKinematics(bool isEnabled) { @@ -1468,7 +1467,6 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { int skeletonModelChangeCount = _skeletonModelChangeCount; Avatar::setSkeletonModelURL(skeletonModelURL); _skeletonModel->setVisibleInScene(true, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true); - _skeletonModel->setCanCastShadow(true, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true); _headBoneSet.clear(); _cauterizationNeedsUpdate = true; @@ -2043,8 +2041,8 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) { _attachmentModels[i]->setVisibleInScene(shouldDrawHead, qApp->getMain3DScene(), render::ItemKey::TAG_BITS_NONE, true); - _attachmentModels[i]->setCanCastShadow(shouldDrawHead, qApp->getMain3DScene(), - render::ItemKey::TAG_BITS_NONE, true); + _attachmentModels[i]->setCanCastShadow(shouldDrawHead, qApp->getMain3DScene(), + render::ItemKey::TAG_BITS_NONE, true); } } } diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 7cce12c8e0..9a7ebdf784 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -64,7 +64,7 @@ void ApplicationOverlay::renderOverlay(RenderArgs* renderArgs) { } // Execute the batch into our framebuffer - doInBatch(renderArgs->_context, [&](gpu::Batch& batch) { + doInBatch("ApplicationOverlay::render", renderArgs->_context, [&](gpu::Batch& batch) { PROFILE_RANGE_BATCH(batch, "ApplicationOverlayRender"); renderArgs->_batch = &batch; batch.enableStereo(false); diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 48b56c7ced..4c233b986c 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -24,10 +24,6 @@ #include "SnapshotAnimated.h" #include "UserActivityLogger.h" -#include "AmbientOcclusionEffect.h" -#include "AntialiasingEffect.h" -#include "RenderShadowTask.h" - void setupPreferences() { auto preferences = DependencyManager::get(); auto nodeList = DependencyManager::get(); @@ -295,30 +291,6 @@ void setupPreferences() { } #endif - - { - static const QString RENDER("Graphics"); - auto renderConfig = qApp->getRenderEngine()->getConfiguration(); - if (renderConfig) { - auto mainViewAmbientOcclusionConfig = renderConfig->getConfig("RenderMainView.AmbientOcclusion"); - if (mainViewAmbientOcclusionConfig) { - auto getter = [mainViewAmbientOcclusionConfig]()->QString { return mainViewAmbientOcclusionConfig->getPreset(); }; - auto setter = [mainViewAmbientOcclusionConfig](QString preset) { mainViewAmbientOcclusionConfig->setPreset(preset); }; - auto preference = new ComboBoxPreference(RENDER, "Ambient occlusion", getter, setter); - preference->setItems(mainViewAmbientOcclusionConfig->getPresetList()); - preferences->addPreference(preference); - } - - auto mainViewShadowConfig = renderConfig->getConfig("RenderMainView.RenderShadowTask"); - if (mainViewShadowConfig) { - auto getter = [mainViewShadowConfig]()->QString { return mainViewShadowConfig->getPreset(); }; - auto setter = [mainViewShadowConfig](QString preset) { mainViewShadowConfig->setPreset(preset); }; - auto preference = new ComboBoxPreference(RENDER, "Shadows", getter, setter); - preference->setItems(mainViewShadowConfig->getPresetList()); - preferences->addPreference(preference); - } - } - } { static const QString NETWORKING("Networking"); diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 3efe94c206..c3d87642f1 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -44,6 +44,8 @@ void OverlayPropertyResultFromScriptValue(const QScriptValue& object, OverlayPro const OverlayID UNKNOWN_OVERLAY_ID = OverlayID(); /**jsdoc + * The result of a {@link PickRay} search using {@link Overlays.findRayIntersection|findRayIntersection} or + * {@link Overlays.findRayIntersectionVector|findRayIntersectionVector}. * @typedef {object} Overlays.RayToOverlayIntersectionResult * @property {boolean} intersects - true if the {@link PickRay} intersected with a 3D overlay, otherwise * false. @@ -75,7 +77,8 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R * yourself and that aren't persisted to the domain. They are used for UI. * @namespace Overlays * @property {Uuid} keyboardFocusOverlay - Get or set the {@link Overlays.OverlayType|web3d} overlay that has keyboard focus. - * If no overlay is set, get returns null; set to null to clear keyboard focus. + * If no overlay has keyboard focus, get returns null; set to null or {@link Uuid|Uuid.NULL} to + * clear keyboard focus. */ class Overlays : public QObject { @@ -116,7 +119,7 @@ public slots: * @function Overlays.addOverlay * @param {Overlays.OverlayType} type - The type of the overlay to add. * @param {Overlays.OverlayProperties} properties - The properties of the overlay to add. - * @returns {Uuid} The ID of the newly created overlay. + * @returns {Uuid} The ID of the newly created overlay if successful, otherwise {@link Uuid|Uuid.NULL}. * @example Add a cube overlay in front of your avatar. * var overlay = Overlays.addOverlay("cube", { * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })), @@ -131,7 +134,7 @@ public slots: * Create a clone of an existing overlay. * @function Overlays.cloneOverlay * @param {Uuid} overlayID - The ID of the overlay to clone. - * @returns {Uuid} The ID of the new overlay. + * @returns {Uuid} The ID of the new overlay if successful, otherwise {@link Uuid|Uuid.NULL}. * @example Add an overlay in front of your avatar, clone it, and move the clone to be above the * original. * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -3 })); @@ -322,10 +325,8 @@ public slots: * @function Overlays.findRayIntersection * @param {PickRay} pickRay - The PickRay to use for finding overlays. * @param {boolean} [precisionPicking=false] - Unused; exists to match Entity API. - * @param {Array.} [overlayIDsToInclude=[]] - Whitelist for intersection test. If empty then the result isn't limited - * to overlays in the list. - * @param {Array.} [overlayIDsToExclude=[]] - Blacklist for intersection test. If empty then the result doesn't - * exclude overlays in the list. + * @param {Array.} [overlayIDsToInclude=[]] - If not empty then the search is restricted to these overlays. + * @param {Array.} [overlayIDsToExclude=[]] - Overlays to ignore during the search. * @param {boolean} [visibleOnly=false] - Unused; exists to match Entity API. * @param {boolean} [collidableOnly=false] - Unused; exists to match Entity API. * @returns {Overlays.RayToOverlayIntersectionResult} The closest 3D overlay intersected by pickRay, taking @@ -531,7 +532,7 @@ public slots: * Set the Web3D overlay that has keyboard focus. * @function Overlays.setKeyboardFocusOverlay * @param {Uuid} overlayID - The ID of the {@link Overlays.OverlayType|web3d} overlay to set keyboard focus to. Use - * {@link Uuid|Uuid.NULL} or null to unset keyboard focus from an overlay. + * null or {@link Uuid|Uuid.NULL} to unset keyboard focus from an overlay. */ void setKeyboardFocusOverlay(const OverlayID& id); diff --git a/interface/src/ui/overlays/Shape3DOverlay.cpp b/interface/src/ui/overlays/Shape3DOverlay.cpp index e9ec0d6cf4..c27faf6f0f 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.cpp +++ b/interface/src/ui/overlays/Shape3DOverlay.cpp @@ -67,6 +67,32 @@ Shape3DOverlay* Shape3DOverlay::createClone() const { } +/**jsdoc + *

A shape {@link Overlays.OverlayType|OverlayType} may display as one of the following geometrical shapes:

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ValueDimensionsDescription
"Circle"2DA circle oriented in 3D.
"Cone"3D
"Cube"3D
"Cylinder"3D
"Dodecahedron"3D
"Hexagon"3DA hexagonal prism.
"Icosahedron"3D
"Line"1DA line oriented in 3D.
"Octagon"3DAn octagonal prism.
"Octahedron"3D
"Quad"2DA square oriented in 3D.
"Sphere"3D
"Tetrahedron"3D
"Torus"3DNot implemented.
"Triangle"3DA triangular prism.
+ * @typedef {string} Overlays.Shape + */ static const std::array shapeStrings { { "Line", "Triangle", @@ -80,7 +106,7 @@ static const std::array shapeStrings "Octahedron", "Dodecahedron", "Icosahedron", - "Torus", + "Torus", // Not implemented yet. "Cone", "Cylinder" } }; @@ -145,7 +171,7 @@ void Shape3DOverlay::setProperties(const QVariantMap& properties) { * * @property {Vec3} dimensions - The dimensions of the overlay. Synonyms: scale, size. * - * @property {Shape} shape=Hexagon - The geometrical shape of the overlay. + * @property {Overlays.Shape} shape=Hexagon - The geometrical shape of the overlay. */ QVariant Shape3DOverlay::getProperty(const QString& property) { if (property == "shape") { diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 2c37d2d089..e00cad9bc7 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -78,6 +78,20 @@ int AnimSkeleton::getParentIndex(int jointIndex) const { return _joints[jointIndex].parentIndex; } +std::vector AnimSkeleton::getChildrenOfJoint(int jointIndex) const { + // Children and grandchildren, etc. + std::vector result; + if (jointIndex != -1) { + for (int i = jointIndex + 1; i < (int)_joints.size(); i++) { + if (_joints[i].parentIndex == jointIndex + || (std::find(result.begin(), result.end(), _joints[i].parentIndex) != result.end())) { + result.push_back(i); + } + } + } + return result; +} + const QString& AnimSkeleton::getJointName(int jointIndex) const { return _joints[jointIndex].name; } diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index 664358f414..27dbf5ea92 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -43,6 +43,7 @@ public: const AnimPose& getPostRotationPose(int jointIndex) const; int getParentIndex(int jointIndex) const; + std::vector getChildrenOfJoint(int jointIndex) const; AnimPose getAbsolutePose(int jointIndex, const AnimPoseVec& relativePoses) const; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index d6791ab0b8..848f384687 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -199,6 +199,8 @@ void Rig::destroyAnimGraph() { _internalPoseSet._overridePoses.clear(); _internalPoseSet._overrideFlags.clear(); _numOverrides = 0; + _leftEyeJointChildren.clear(); + _rightEyeJointChildren.clear(); } void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOffset) { @@ -225,12 +227,17 @@ void Rig::initJointStates(const FBXGeometry& geometry, const glm::mat4& modelOff buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses); _rootJointIndex = geometry.rootJointIndex; + _leftEyeJointIndex = geometry.leftEyeJointIndex; + _rightEyeJointIndex = geometry.rightEyeJointIndex; _leftHandJointIndex = geometry.leftHandJointIndex; _leftElbowJointIndex = _leftHandJointIndex >= 0 ? geometry.joints.at(_leftHandJointIndex).parentIndex : -1; _leftShoulderJointIndex = _leftElbowJointIndex >= 0 ? geometry.joints.at(_leftElbowJointIndex).parentIndex : -1; _rightHandJointIndex = geometry.rightHandJointIndex; _rightElbowJointIndex = _rightHandJointIndex >= 0 ? geometry.joints.at(_rightHandJointIndex).parentIndex : -1; _rightShoulderJointIndex = _rightElbowJointIndex >= 0 ? geometry.joints.at(_rightElbowJointIndex).parentIndex : -1; + + _leftEyeJointChildren = _animSkeleton->getChildrenOfJoint(geometry.leftEyeJointIndex); + _rightEyeJointChildren = _animSkeleton->getChildrenOfJoint(geometry.rightEyeJointIndex); } void Rig::reset(const FBXGeometry& geometry) { @@ -253,6 +260,8 @@ void Rig::reset(const FBXGeometry& geometry) { buildAbsoluteRigPoses(_animSkeleton->getRelativeDefaultPoses(), _absoluteDefaultPoses); _rootJointIndex = geometry.rootJointIndex; + _leftEyeJointIndex = geometry.leftEyeJointIndex; + _rightEyeJointIndex = geometry.rightEyeJointIndex; _leftHandJointIndex = geometry.leftHandJointIndex; _leftElbowJointIndex = _leftHandJointIndex >= 0 ? geometry.joints.at(_leftHandJointIndex).parentIndex : -1; _leftShoulderJointIndex = _leftElbowJointIndex >= 0 ? geometry.joints.at(_leftElbowJointIndex).parentIndex : -1; @@ -260,6 +269,9 @@ void Rig::reset(const FBXGeometry& geometry) { _rightElbowJointIndex = _rightHandJointIndex >= 0 ? geometry.joints.at(_rightHandJointIndex).parentIndex : -1; _rightShoulderJointIndex = _rightElbowJointIndex >= 0 ? geometry.joints.at(_rightElbowJointIndex).parentIndex : -1; + _leftEyeJointChildren = _animSkeleton->getChildrenOfJoint(geometry.leftEyeJointIndex); + _rightEyeJointChildren = _animSkeleton->getChildrenOfJoint(geometry.rightEyeJointIndex); + if (!_animGraphURL.isEmpty()) { _animNode.reset(); initAnimGraph(_animGraphURL); @@ -1430,6 +1442,15 @@ void Rig::updateEyeJoint(int index, const glm::vec3& modelTranslation, const glm // directly set absolutePose rotation _internalPoseSet._absolutePoses[index].rot() = deltaQuat * headQuat; + + // Update eye joint's children. + auto children = index == _leftEyeJointIndex ? _leftEyeJointChildren : _rightEyeJointChildren; + for (int i = 0; i < (int)children.size(); i++) { + int jointIndex = children[i]; + int parentIndex = _animSkeleton->getParentIndex(jointIndex); + _internalPoseSet._absolutePoses[jointIndex] = + _internalPoseSet._absolutePoses[parentIndex] * _internalPoseSet._relativePoses[jointIndex]; + } } } diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index 7230d05e2a..e30b5d655c 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -267,6 +267,11 @@ protected: int _rootJointIndex { -1 }; + int _leftEyeJointIndex { -1 }; + int _rightEyeJointIndex { -1 }; + std::vector _leftEyeJointChildren; + std::vector _rightEyeJointChildren; + int _leftHandJointIndex { -1 }; int _leftElbowJointIndex { -1 }; int _leftShoulderJointIndex { -1 }; diff --git a/libraries/audio-client/src/AudioPeakValues.cpp b/libraries/audio-client/src/AudioPeakValues.cpp index 3df469b830..0b8921a117 100644 --- a/libraries/audio-client/src/AudioPeakValues.cpp +++ b/libraries/audio-client/src/AudioPeakValues.cpp @@ -43,14 +43,19 @@ void AudioClient::checkPeakValues() { // prepare the windows environment CoInitialize(NULL); + std::unique_lock lock(_deviceMutex, std::defer_lock); + // if disabled, clean up active clients if (!_enablePeakValues) { - activeClients.clear(); + if (lock.try_lock()) { + // deferred, if timer callbacks overlap + activeClients.clear(); + } return; } // lock the devices so the _inputDevices list is static - std::unique_lock lock(_deviceMutex); + lock.lock(); HRESULT result; // initialize the payload diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index b2a494230b..b25df633c0 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -33,6 +33,10 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent) : { // SkeletonModels, and by extention Avatars, use Dual Quaternion skinning. _useDualQuaternionSkinning = true; + + // Avatars all cast shadow + _canCastShadow = true; + assert(_owningAvatar); } diff --git a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp index 56cbcdadf8..97f74fa24e 100644 --- a/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/Basic2DWindowOpenGLDisplayPlugin.cpp @@ -28,7 +28,7 @@ void Basic2DWindowOpenGLDisplayPlugin::customizeContext() { auto iconPath = PathUtils::resourcesPath() + "images/analog_stick.png"; auto image = QImage(iconPath); qreal dpi = getFullscreenTarget()->physicalDotsPerInch(); - _virtualPadPixelSize = dpi * 512 / 534; // 534 dpi for Pixel XL and Mate 9 Pro + _virtualPadPixelSize = dpi * VirtualPad::Manager::PIXEL_SIZE / VirtualPad::Manager::DPI; if (image.format() != QImage::Format_ARGB32) { image = image.convertToFormat(QImage::Format_ARGB32); diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.h b/libraries/display-plugins/src/display-plugins/CompositorHelper.h index 234818c740..de115b0554 100644 --- a/libraries/display-plugins/src/display-plugins/CompositorHelper.h +++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.h @@ -90,7 +90,7 @@ public: glm::vec2 getReticleMaximumPosition() const; glm::mat4 getReticleTransform(const glm::mat4& eyePose = glm::mat4(), const glm::vec3& headPosition = glm::vec3()) const; - glm::mat4 getPoint2DTransform(const glm::vec2& point = glm::vec2(), float sizeX = 512.0f, float sizeY = 512.0f) const; + glm::mat4 getPoint2DTransform(const glm::vec2& point, float sizeX , float sizeY) const; ReticleInterface* getReticleInterface() { return _reticleInterface; } diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp index 9bd7d89634..db9b86b9dd 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.cpp @@ -361,7 +361,7 @@ void OpenGLDisplayPlugin::customizeContext() { auto presentThread = DependencyManager::get(); Q_ASSERT(thread() == presentThread->thread()); - getGLBackend()->setCameraCorrection(mat4()); + getGLBackend()->setCameraCorrection(mat4(), mat4(), true); for (auto& cursorValue : _cursorsData) { auto& cursorData = cursorValue.second; @@ -692,6 +692,9 @@ void OpenGLDisplayPlugin::present() { incrementPresentCount(); if (_currentFrame) { + auto correction = getViewCorrection(); + getGLBackend()->setCameraCorrection(correction, _prevRenderView); + _prevRenderView = correction * _currentFrame->view; { withPresentThreadLock([&] { _renderRate.increment(); diff --git a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h index bf06486095..bde7984ec0 100644 --- a/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/OpenGLDisplayPlugin.h @@ -118,6 +118,7 @@ protected: void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor, gpu::FramebufferPointer fbo); void renderFromTexture(gpu::Batch& batch, const gpu::TexturePointer texture, glm::ivec4 viewport, const glm::ivec4 scissor); virtual void updateFrameData(); + virtual glm::mat4 getViewCorrection() { return glm::mat4(); } void withOtherThreadContext(std::function f) const; @@ -137,6 +138,7 @@ protected: gpu::FramePointer _currentFrame; gpu::Frame* _lastFrame { nullptr }; + mat4 _prevRenderView; gpu::FramebufferPointer _compositeFramebuffer; gpu::PipelinePointer _hudPipeline; gpu::PipelinePointer _mirrorHUDPipeline; diff --git a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp index 6e397efbe5..40063652c8 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.cpp @@ -7,6 +7,8 @@ // #include "DebugHmdDisplayPlugin.h" +#include + #include #include @@ -41,7 +43,15 @@ bool DebugHmdDisplayPlugin::beginFrameRender(uint32_t frameIndex) { } bool DebugHmdDisplayPlugin::internalActivate() { + _isAutoRotateEnabled = _container->getBoolSetting("autoRotate", true); + _container->addMenuItem(PluginType::DISPLAY_PLUGIN, MENU_PATH(), tr("Auto Rotate"), + [this](bool clicked) { + _isAutoRotateEnabled = clicked; + _container->setBoolSetting("autoRotate", _isAutoRotateEnabled); + }, true, _isAutoRotateEnabled); + _ipd = 0.0327499993f * 2.0f; + // Would be nice to know why the left and right projection matrices are slightly dissymetrical _eyeProjections[0][0] = vec4{ 0.759056330, 0.000000000, 0.000000000, 0.000000000 }; _eyeProjections[0][1] = vec4{ 0.000000000, 0.682773232, 0.000000000, 0.000000000 }; _eyeProjections[0][2] = vec4{ -0.0580431037, -0.00619550655, -1.00000489, -1.00000000 }; @@ -50,10 +60,15 @@ bool DebugHmdDisplayPlugin::internalActivate() { _eyeProjections[1][1] = vec4{ 0.000000000, 0.678060353, 0.000000000, 0.000000000 }; _eyeProjections[1][2] = vec4{ 0.0578232110, -0.00669418881, -1.00000489, -1.000000000 }; _eyeProjections[1][3] = vec4{ 0.000000000, 0.000000000, -0.0800003856, 0.000000000 }; - _eyeInverseProjections[0] = glm::inverse(_eyeProjections[0]); - _eyeInverseProjections[1] = glm::inverse(_eyeProjections[1]); + // No need to do so here as this will done in Parent::internalActivate + //_eyeInverseProjections[0] = glm::inverse(_eyeProjections[0]); + //_eyeInverseProjections[1] = glm::inverse(_eyeProjections[1]); _eyeOffsets[0][3] = vec4{ -0.0327499993, 0.0, 0.0149999997, 1.0 }; _eyeOffsets[1][3] = vec4{ 0.0327499993, 0.0, 0.0149999997, 1.0 }; + _eyeInverseProjections[0] = glm::inverse(_eyeProjections[0]); + _eyeInverseProjections[1] = glm::inverse(_eyeProjections[1]); + _eyeOffsets[0][3] = vec4{ -0.0327499993, 0.0, -0.0149999997, 1.0 }; + _eyeOffsets[1][3] = vec4{ 0.0327499993, 0.0, -0.0149999997, 1.0 }; _renderTargetSize = { 3024, 1680 }; _cullingProjection = _eyeProjections[0]; // This must come after the initialization, so that the values calculated @@ -63,10 +78,13 @@ bool DebugHmdDisplayPlugin::internalActivate() { } void DebugHmdDisplayPlugin::updatePresentPose() { - float yaw = sinf(secTimestampNow()) * 0.25f; - float pitch = cosf(secTimestampNow()) * 0.25f; - // Simulates head pose latency correction - _currentPresentFrameInfo.presentPose = - glm::mat4_cast(glm::angleAxis(yaw, Vectors::UP)) * - glm::mat4_cast(glm::angleAxis(pitch, Vectors::RIGHT)); + Parent::updatePresentPose(); + if (_isAutoRotateEnabled) { + float yaw = sinf(secTimestampNow()) * 0.25f; + float pitch = cosf(secTimestampNow()) * 0.25f; + // Simulates head pose latency correction + _currentPresentFrameInfo.presentPose = + glm::mat4_cast(glm::angleAxis(yaw, Vectors::UP)) * + glm::mat4_cast(glm::angleAxis(pitch, Vectors::RIGHT)) ; + } } diff --git a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h index cd6fdd44b9..f2b1f36419 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/DebugHmdDisplayPlugin.h @@ -28,5 +28,7 @@ protected: bool isHmdMounted() const override { return true; } bool internalActivate() override; private: + static const QString NAME; + bool _isAutoRotateEnabled{ true }; }; diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp index 8b255d9974..2a32a7d5ce 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.cpp @@ -58,6 +58,18 @@ QRect HmdDisplayPlugin::getRecommendedHUDRect() const { return CompositorHelper::VIRTUAL_SCREEN_RECOMMENDED_OVERLAY_RECT; } +glm::mat4 HmdDisplayPlugin::getEyeToHeadTransform(Eye eye) const { + return _eyeOffsets[eye]; +} + +glm::mat4 HmdDisplayPlugin::getEyeProjection(Eye eye, const glm::mat4& baseProjection) const { + return _eyeProjections[eye]; +} + +glm::mat4 HmdDisplayPlugin::getCullingProjection(const glm::mat4& baseProjection) const { + return _cullingProjection; +} + #define DISABLE_PREVIEW_MENU_ITEM_DELAY_MS 500 bool HmdDisplayPlugin::internalActivate() { @@ -324,12 +336,14 @@ void HmdDisplayPlugin::updateFrameData() { } updatePresentPose(); +} +glm::mat4 HmdDisplayPlugin::getViewCorrection() { if (_currentFrame) { auto batchPose = _currentFrame->pose; - auto currentPose = _currentPresentFrameInfo.presentPose; - auto correction = glm::inverse(batchPose) * currentPose; - getGLBackend()->setCameraCorrection(correction); + return glm::inverse(_currentPresentFrameInfo.presentPose) * batchPose; + } else { + return glm::mat4(); } } diff --git a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h index d2d30df093..3639952524 100644 --- a/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/hmd/HmdDisplayPlugin.h @@ -26,9 +26,9 @@ public: ~HmdDisplayPlugin(); bool isHmd() const override final { return true; } float getIPD() const override final { return _ipd; } - glm::mat4 getEyeToHeadTransform(Eye eye) const override final { return _eyeOffsets[eye]; } - glm::mat4 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const override { return _eyeProjections[eye]; } - glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override { return _cullingProjection; } + glm::mat4 getEyeToHeadTransform(Eye eye) const override final; + glm::mat4 getEyeProjection(Eye eye, const glm::mat4& baseProjection) const override; + glm::mat4 getCullingProjection(const glm::mat4& baseProjection) const override; glm::uvec2 getRecommendedUiSize() const override final; glm::uvec2 getRecommendedRenderSize() const override final { return _renderTargetSize; } bool isDisplayVisible() const override { return isHmdMounted(); } @@ -59,6 +59,7 @@ protected: void customizeContext() override; void uncustomizeContext() override; void updateFrameData() override; + glm::mat4 getViewCorrection() override; std::array _eyeOffsets; std::array _eyeProjections; diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp index ae8f9ec039..cfdfb1fc21 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.cpp @@ -101,3 +101,4 @@ void StereoDisplayPlugin::internalDeactivate() { float StereoDisplayPlugin::getRecommendedAspectRatio() const { return aspect(Parent::getRecommendedRenderSize()); } + diff --git a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h index 3b481dce97..c4205ea1db 100644 --- a/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h +++ b/libraries/display-plugins/src/display-plugins/stereo/StereoDisplayPlugin.h @@ -26,7 +26,7 @@ public: // the IPD at the Application level, the way we now allow with HMDs. // If that becomes an issue then we'll need to break up the functionality similar // to the HMD plugins. - // virtual glm::mat4 getEyeToHeadTransform(Eye eye) const override; + //virtual glm::mat4 getEyeToHeadTransform(Eye eye) const override; protected: virtual bool internalActivate() override; diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index 322c91e3d3..5a3caa55fe 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -433,7 +433,7 @@ void ZoneEntityRenderer::setAmbientURL(const QString& ambientUrl) { _ambientTexture = textureCache->getTexture(_ambientTextureURL, image::TextureUsage::CUBE_TEXTURE); // keep whatever is assigned on the ambient map/sphere until texture is loaded -} + } } void ZoneEntityRenderer::updateAmbientMap() { diff --git a/libraries/entities-renderer/src/paintStroke.slv b/libraries/entities-renderer/src/paintStroke.slv index 769b87f2a9..0cf9596cce 100644 --- a/libraries/entities-renderer/src/paintStroke.slv +++ b/libraries/entities-renderer/src/paintStroke.slv @@ -30,7 +30,7 @@ void main(void) { varTexcoord = inTexCoord0.st; // pass along the diffuse color - varColor = colorToLinearRGBA(inColor); + varColor = color_sRGBAToLinear(inColor); // standard transform diff --git a/libraries/entities-renderer/src/paintStroke_fade.slv b/libraries/entities-renderer/src/paintStroke_fade.slv index 9f10fa5d91..b6075caaf8 100644 --- a/libraries/entities-renderer/src/paintStroke_fade.slv +++ b/libraries/entities-renderer/src/paintStroke_fade.slv @@ -31,7 +31,7 @@ void main(void) { varTexcoord = inTexCoord0.st; // pass along the diffuse color - varColor = colorToLinearRGBA(inColor); + varColor = color_sRGBAToLinear(inColor); // standard transform diff --git a/libraries/entities/src/AmbientLightPropertyGroup.h b/libraries/entities/src/AmbientLightPropertyGroup.h index fbbc7c9900..591ea6a6fa 100644 --- a/libraries/entities/src/AmbientLightPropertyGroup.h +++ b/libraries/entities/src/AmbientLightPropertyGroup.h @@ -27,6 +27,14 @@ class OctreePacketData; class EntityTreeElementExtraEncodeData; class ReadBitstreamToTreeParams; +/**jsdoc + * Ambient light is defined by the following properties. + * @typedef {object} Entities.AmbientLight + * @property {number} ambientIntensity=0.5 - The intensity of the light. + * @property {string} ambientURL="" - A cube map image that defines the color of the light coming from each direction. If + * "" then the entity's {@link Entities.Skybox|Skybox} url property value is used, unless that also is "" in which + * case the entity's ambientLightMode property is set to "inherit". + */ class AmbientLightPropertyGroup : public PropertyGroup { public: // EntityItemProperty related helpers diff --git a/libraries/entities/src/AnimationPropertyGroup.cpp b/libraries/entities/src/AnimationPropertyGroup.cpp index 2af56fb6b2..82af60ed1a 100644 --- a/libraries/entities/src/AnimationPropertyGroup.cpp +++ b/libraries/entities/src/AnimationPropertyGroup.cpp @@ -44,6 +44,19 @@ bool operator!=(const AnimationPropertyGroup& a, const AnimationPropertyGroup& b } +/**jsdoc + * The AnimationProperties are used to configure an animation. + * @typedef Entities.AnimationProperties + * @property {string} url="" - The URL of the FBX file that has the animation. + * @property {number} fps=30 - The speed in frames/s that the animation is played at. + * @property {number} firstFrame=0 - The first frame to play in the animation. + * @property {number} lastFrame=100000 - The last frame to play in the animation. + * @property {number} currentFrame=0 - The current frame being played in the animation. + * @property {boolean} running=false - If true then the animation should play. + * @property {boolean} loop=true - If true then the animation should be continuously repeated in a loop. + * @property {boolean} hold=false - If true then the rotations and translations of the last frame played should be + * maintained when the animation stops playing. + */ void AnimationPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_URL, Animation, animation, URL, url); COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_ANIMATION_ALLOW_TRANSLATION, Animation, animation, AllowTranslation, allowTranslation); diff --git a/libraries/entities/src/EntityDynamicInterface.cpp b/libraries/entities/src/EntityDynamicInterface.cpp index f424c02e6e..d43bdd7b51 100644 --- a/libraries/entities/src/EntityDynamicInterface.cpp +++ b/libraries/entities/src/EntityDynamicInterface.cpp @@ -94,6 +94,49 @@ variables. These argument variables are used by the code which is run when bull #include "EntityDynamicInterface.h" +/**jsdoc +*

An entity action may be one of the following types:

+* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +* +*
ValueTypeDescriptionArguments
"far-grab"Avatar actionMoves and rotates an entity to a target position and orientation, optionally relative to another entity. Collisions +* between the entity and the user's avatar are disabled during the far-grab.{@link Entities.ActionArguments-FarGrab}
"hold"Avatar actionPositions and rotates an entity relative to an avatar's hand. Collisions between the entity and the user's avatar +* are disabled during the hold.{@link Entities.ActionArguments-Hold}
"offset"Object actionMoves an entity so that it is a set distance away from a target point.{@link Entities.ActionArguments-Offset}
"tractor"Object actionMoves and rotates an entity to a target position and orientation, optionally relative to another entity.{@link Entities.ActionArguments-Tractor}
"travel-oriented"Object actionOrients an entity to align with its direction of travel.{@link Entities.ActionArguments-TravelOriented}
"hinge"Object constraintLets an entity pivot about an axis or connects two entities with a hinge joint.{@link Entities.ActionArguments-Hinge}
"slider"Object constraintLets an entity slide and rotate along an axis, or connects two entities that slide and rotate along a shared +* axis.{@link Entities.ActionArguments-Slider|ActionArguments-Slider}
"cone-twist"Object constraintConnects two entities with a joint that can move through a cone and can twist.{@link Entities.ActionArguments-ConeTwist}
"ball-socket"Object constraintConnects two entities with a ball and socket joint.{@link Entities.ActionArguments-BallSocket}
"spring"Synonym for "tractor". Legacy value.
+* @typedef {string} Entities.ActionType +*/ +// Note: The "none" action type is not listed because it's an internal "uninitialized" value and not useful for scripts. EntityDynamicType EntityDynamicInterface::dynamicTypeFromString(QString dynamicTypeString) { QString normalizedDynamicTypeString = dynamicTypeString.toLower().remove('-').remove('_'); if (normalizedDynamicTypeString == "none") { diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index c244fdf4d9..828430e2ca 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -440,6 +440,707 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { return changedProperties; } +/**jsdoc + * Different entity types have different properties: some common to all entities (listed below) and some specific to each + * {@link Entities.EntityType|EntityType} (linked to below). The properties are accessed as an object of property names and + * values. + * + * @typedef {object} Entities.EntityProperties + * @property {Uuid} id - The ID of the entity. Read-only. + * @property {string} name="" - A name for the entity. Need not be unique. + * @property {Entities.EntityType} type - The entity type. You cannot change the type of an entity after it's created. (Though + * its value may switch among "Box", "Shape", and "Sphere" depending on changes to + * the shape property set for entities of these types.) Read-only. + * @property {boolean} clientOnly=false - If true then the entity is an avatar entity, otherwise it is a server + * entity. Read-only. + * @property {Uuid} owningAvatarID=Uuid.NULL - The session ID of the owning avatar if clientOnly is + * true, otherwise {@link Uuid|Uuid.NULL}. Read-only. + * + * @property {string} created - The UTC date and time that the entity was created, in ISO 8601 format as + * yyyy-MM-ddTHH:mm:ssZ. Read-only. + * @property {number} age - The age of the entity in seconds since it was created. Read-only. + * @property {string} ageAsText - The age of the entity since it was created, formatted as h hours m minutes s + * seconds. + * @property {number} lifetime=-1 - How long an entity lives for, in seconds, before being automatically deleted. A value of + * -1 means that the entity lives for ever. + * @property {number} lastEdited - When the entity was last edited, expressed as the number of microseconds since + * 1970-01-01T00:00:00 UTC. Read-only. + * @property {Uuid} lastEditedBy - The session ID of the avatar or agent that most recently created or edited the entity. + * Read-only. + * + * @property {boolean} locked=false - Whether or not the entity can be edited or deleted. If true then the + * entity's properties other than locked cannot be changed, and the entity cannot be deleted. + * @property {boolean} visible=true - Whether or not the entity is rendered. If true then the entity is rendered. + * @property {boolean} canCastShadows=true - Whether or not the entity casts shadows. Currently applicable only to + * {@link Entities.EntityType|Model} and {@link Entities.EntityType|Shape} entities. Shadows are cast if inside a + * {@link Entities.EntityType|Zone} entity with castShadows enabled in its {@link Entities.EntityProperties-Zone|keyLight} property. + * + * @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. + * @property {Vec3} registrationPoint=0.5,0.5,0.5 - The point in the entity that is set to the entity's position and is rotated + * about, {@link Vec3|Vec3.ZERO} – {@link Vec3|Vec3.ONE}. A value of {@link Vec3|Vec3.ZERO} is the entity's + * minimum x, y, z corner; a value of {@link Vec3|Vec3.ONE} is the entity's maximum x, y, z corner. + * + * @property {Vec3} naturalPosition=0,0,0 - The center of the entity's unscaled mesh model if it has one, otherwise + * {@link Vec3|Vec3.ZERO}. Read-only. + * @property {Vec3} naturalDimensions - The dimensions of the entity's unscaled mesh model if it has one, otherwise + * {@link Vec3|Vec3.ONE}. Read-only. + * + * @property {Vec3} velocity=0,0,0 - The linear velocity of the entity in m/s with respect to world coordinates. + * @property {number} damping=0.39347 - How much to slow down the linear velocity of an entity over time, 0.0 + * – 1.0. A higher damping value slows down the entity more quickly. The default value is for an + * exponential decay timescale of 2.0s, where it takes 2.0s for the movement to slow to 1/e = 0.368 of its + * initial value. + * @property {Vec3} angularVelocity=0,0,0 - The angular velocity of the entity in rad/s with respect to its axes, about its + * registration point. + * @property {number} angularDamping=0.39347 - How much to slow down the angular velocity of an entity over time, + * 0.01.0. A higher damping value slows down the entity more quickly. The default value + * is for an exponential decay timescale of 2.0s, where it takes 2.0s for the movement to slow to 1/e = 0.368 + * of its initial value. + * + * @property {Vec3} gravity=0,0,0 - The acceleration due to gravity in m/s2 that the entity should move with, in + * world coordinates. Set to { x: 0, y: -9.8, z: 0 } to simulate Earth's gravity. Gravity is applied to an + * entity's motion only if its dynamic property is true. If changing an entity's + * gravity from {@link Vec3|Vec3.ZERO}, you need to give it a small velocity in order to kick off + * physics simulation. + * The gravity value is applied in addition to the acceleration value. + * @property {Vec3} acceleration=0,0,0 - A general acceleration in m/s2 that the entity should move with, in world + * coordinates. The acceleration is applied to an entity's motion only if its dynamic property is + * true. If changing an entity's acceleration from {@link Vec3|Vec3.ZERO}, you need to give it a + * small velocity in order to kick off physics simulation. + * The acceleration value is applied in addition to the gravity value. + * @property {number} restitution=0.5 - The "bounciness" of an entity when it collides, 0.0 – + * 0.99. The higher the value, the more bouncy. + * @property {number} friction=0.5 - How much to slow down an entity when it's moving against another, 0.0 – + * 10.0. The higher the value, the more quickly it slows down. Examples: 0.1 for ice, + * 0.9 for sandpaper. + * @property {number} density=1000 - The density of the entity in kg/m3, 100 for balsa wood – + * 10000 for silver. The density is used in conjunction with the entity's bounding box volume to work out its + * mass in the application of physics. + * + * @property {boolean} collisionless=false - Whether or not the entity should collide with items per its + * collisionMask property. If true then the entity does not collide. + * @property {boolean} ignoreForCollisions=false - Synonym for collisionless. + * @property {Entities.CollisionMask} collisionMask=31 - What types of items the entity should collide with. + * @property {string} collidesWith="static,dynamic,kinematic,myAvatar,otherAvatar," - Synonym for collisionMask, + * in text format. + * @property {string} collisionSoundURL="" - The sound to play when the entity experiences a collision. Valid file formats are + * as per the {@link SoundCache} object. + * @property {boolean} dynamic=false - Whether or not the entity should be affected by collisions. If true then + * the entity's movement is affected by collisions. + * @property {boolean} collisionsWillMove=false - Synonym for dynamic. + * + * @property {string} href="" - A "hifi://" metaverse address that a user is taken to when they click on the entity. + * @property {string} description="" - A description of the href property value. + * + * @property {string} userData="" - Used to store extra data about the entity in JSON format. WARNING: Other apps such as the + * Create app can also use this property, so make sure you handle data stored by other apps — edit only your bit and + * leave the rest of the data intact. You can use JSON.parse() to parse the string into a JavaScript object + * which you can manipulate the properties of, and use JSON.stringify() to convert the object into a string to + * put in the property. + * + * @property {string} script="" - The URL of the client entity script, if any, that is attached to the entity. + * @property {number} scriptTimestamp=0 - Intended to be used to indicate when the client entity script was loaded. Should be + * an integer number of milliseconds since midnight GMT on January 1, 1970 (e.g., as supplied by Date.now(). + * If you update the property's value, the script is re-downloaded and reloaded. This is how the "reload" + * button beside the "script URL" field in properties tab of the Create app works. + * @property {string} serverScripts="" - The URL of the server entity script, if any, that is attached to the entity. + * + * @property {Uuid} parentID=Uuid.NULL - The ID of the entity or avatar that this entity is parented to. {@link Uuid|Uuid.NULL} + * if the entity is not parented. + * @property {number} parentJointIndex=65535 - The joint of the entity or avatar that this entity is parented to. Use + * 65535 or -1 to parent to the entity or avatar's position and orientation rather than a joint. + * @property {Vec3} localPosition=0,0,0 - The position of the entity relative to its parent if the entity is parented, + * otherwise the same value as position. If the entity is parented to an avatar and is clientOnly + * so that it scales with the avatar, this value remains the original local position value while the avatar scale changes. + * @property {Quat} localRotation=0,0,0,1 - The rotation of the entity relative to its parent if the entity is parented, + * otherwise the same value as rotation. + * @property {Vec3} localVelocity=0,0,0 - The velocity of the entity relative to its parent if the entity is parented, + * otherwise the same value as velocity. + * @property {Vec3} localAngularVelocity=0,0,0 - The angular velocity of the entity relative to its parent if the entity is + * parented, otherwise the same value as position. + * @property {Vec3} localDimensions - The dimensions of the entity. If the entity is parented to an avatar and is + * clientOnly so that it scales with the avatar, this value remains the original dimensions value while the + * avatar scale changes. + * + * @property {Entities.BoundingBox} boundingBox - The axis-aligned bounding box that tightly encloses the entity. + * Read-only. + * @property {AACube} queryAACube - The axis-aligned cube that determines where the entity lives in the entity server's octree. + * The cube may be considerably larger than the entity in some situations, e.g., when the entity is grabbed by an avatar: + * the position of the entity is determined through avatar mixer updates and so the AA cube is expanded in order to reduce + * unnecessary entity server updates. Scripts should not change this property's value. + * + * @property {string} actionData="" - Base-64 encoded compressed dump of the actions associated with the entity. This property + * is typically not used in scripts directly; rather, functions that manipulate an entity's actions update it. + * The size of this property increases with the number of actions. Because this property value has to fit within a High + * Fidelity datagram packet there is a limit to the number of actions that an entity can have, and edits which would result + * in overflow are rejected. + * Read-only. + * @property {Entities.RenderInfo} renderInfo - Information on the cost of rendering the entity. Currently information is only + * provided for Model entities. Read-only. + * + * @property {string} itemName="" - Certifiable name of the Marketplace item. + * @property {string} itemDescription="" - Certifiable description of the Marketplace item. + * @property {string} itemCategories="" - Certifiable category of the Marketplace item. + * @property {string} itemArtist="" - Certifiable artist that created the Marketplace item. + * @property {string} itemLicense="" - Certifiable license URL for the Marketplace item. + * @property {number} limitedRun=4294967295 - Certifiable maximum integer number of editions (copies) of the Marketplace item + * allowed to be sold. + * @property {number} editionNumber=0 - Certifiable integer edition (copy) number or the Marketplace item. Each copy sold in + * the Marketplace is numbered sequentially, starting at 1. + * @property {number} entityInstanceNumber=0 - Certifiable integer instance number for identical entities in a Marketplace + * item. A Marketplace item may have identical parts. If so, then each is numbered sequentially with an instance number. + * @property {string} marketplaceID="" - Certifiable UUID for the Marketplace item, as used in the URL of the item's download + * and its Marketplace Web page. + * @property {string} certificateID="" - Hash of the entity's static certificate JSON, signed by the artist's private key. + * @property {number} staticCertificateVersion=0 - The version of the method used to generate the certificateID. + * + * @see The different entity types have additional properties as follows: + * @see {@link Entities.EntityProperties-Box|EntityProperties-Box} + * @see {@link Entities.EntityProperties-Light|EntityProperties-Light} + * @see {@link Entities.EntityProperties-Line|EntityProperties-Line} + * @see {@link Entities.EntityProperties-Material|EntityProperties-Material} + * @see {@link Entities.EntityProperties-Model|EntityProperties-Model} + * @see {@link Entities.EntityProperties-ParticleEffect|EntityProperties-ParticleEffect} + * @see {@link Entities.EntityProperties-PolyLine|EntityProperties-PolyLine} + * @see {@link Entities.EntityProperties-PolyVox|EntityProperties-PolyVox} + * @see {@link Entities.EntityProperties-Shape|EntityProperties-Shape} + * @see {@link Entities.EntityProperties-Sphere|EntityProperties-Sphere} + * @see {@link Entities.EntityProperties-Text|EntityProperties-Text} + * @see {@link Entities.EntityProperties-Web|EntityProperties-Web} + * @see {@link Entities.EntityProperties-Zone|EntityProperties-Zone} + */ + +/**jsdoc + * The "Box" {@link Entities.EntityType|EntityType} is the same as the "Shape" + * {@link Entities.EntityType|EntityType} except that its shape value is always set to "Cube" + * when the entity is created. If its shape property value is subsequently changed then the entity's + * type will be reported as "Sphere" if the shape is set to "Sphere", + * otherwise it will be reported as "Shape". + * @typedef {object} Entities.EntityProperties-Box + */ + +/**jsdoc + * The "Light" {@link Entities.EntityType|EntityType} adds local lighting effects. + * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * @typedef {object} Entities.EntityProperties-Light + * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. Entity surface outside these dimensions are not lit + * by the light. + * @property {Color} color=255,255,255 - The color of the light emitted. + * @property {number} intensity=1 - The brightness of the light. + * @property {number} falloffRadius=0.1 - The distance from the light's center at which intensity is reduced by 25%. + * @property {boolean} isSpotlight=false - If true then the light is directional, emitting along the entity's + * local negative z-axis; otherwise the light is a point light which emanates in all directions. + * @property {number} exponent=0 - Affects the softness of the spotlight beam: the higher the value the softer the beam. + * @property {number} cutoff=1.57 - Affects the size of the spotlight beam: the higher the value the larger the beam. + * @example Create a spotlight pointing at the ground. + * Entities.addEntity({ + * type: "Light", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -4 })), + * rotation: Quat.fromPitchYawRollDegrees(-75, 0, 0), + * dimensions: { x: 5, y: 5, z: 5 }, + * intensity: 100, + * falloffRadius: 0.3, + * isSpotlight: true, + * exponent: 20, + * cutoff: 30, + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + +/**jsdoc + * The "Line" {@link Entities.EntityType|EntityType} draws thin, straight lines between a sequence of two or more + * points. + * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * @typedef {object} Entities.EntityProperties-Line + * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. Must be sufficient to contain all the + * linePoints. + * @property {Vec3[]} linePoints=[]] - The sequence of points to draw lines between. The values are relative to the entity's + * position. A maximum of 70 points can be specified. The property's value is set only if all the linePoints + * lie within the entity's dimensions. + * @property {number} lineWidth=2 - Currently not used. + * @property {Color} color=255,255,255 - The color of the line. + * @example Draw lines in a "V". + * var entity = Entities.addEntity({ + * type: "Line", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -5 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 2, y: 2, z: 1 }, + * linePoints: [ + * { x: -1, y: 1, z: 0 }, + * { x: 0, y: -1, z: 0 }, + * { x: 1, y: 1, z: 0 }, + * ], + * color: { red: 255, green: 0, blue: 0 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + +/**jsdoc + * The "Material" {@link Entities.EntityType|EntityType} modifies the existing materials on + * {@link Entities.EntityType|Model} entities, {@link Entities.EntityType|Shape} entities (albedo only), + * {@link Overlays.OverlayType|model overlays}, and avatars. + * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}.
+ * To apply a material to an entity or overlay, set the material entity's parentID property to the entity or + * overlay's ID. + * To apply a material to an avatar, set the material entity's parentID property to the avatar's session UUID. + * To apply a material to your avatar such that it persists across domains and log-ins, create the material as an avatar entity + * by setting the clientOnly parameter in {@link Entities.addEntity} to true. + * Material entities render as non-scalable spheres if they don't have their parent set. + * @typedef {object} Entities.EntityProperties-Material + * @property {string} materialURL="" - URL to a {@link MaterialResource}. If you append ?name to the URL, the + * material with that name in the {@link MaterialResource} will be applied to the entity.
+ * Alternatively, set the property value to "userData" to use the {@link Entities.EntityProperties|userData} + * entity property to live edit the material resource values. + * @property {number} priority=0 - The priority for applying the material to its parent. Only the highest priority material is + * applied, with materials of the same priority randomly assigned. Materials that come with the model have a priority of + * 0. + * @property {string|number} parentMaterialName="0" - Selects the submesh or submeshes within the parent to apply the material + * to. If in the format "mat::string", all submeshes with material name "string" are replaced. + * Otherwise the property value is parsed as an unsigned integer, specifying the mesh index to modify. Invalid values are + * parsed to 0. + * @property {string} materialMappingMode="uv" - How the material is mapped to the entity. Either "uv" or + * "projected". Currently, only "uv" is supported. + * @property {Vec2} materialMappingPos=0,0 - Offset position in UV-space of the top left of the material, range + * { x: 0, y: 0 }{ x: 1, y: 1 }. + * @property {Vec2} materialMappingScale=1,1 - How much to scale the material within the parent's UV-space. + * @property {number} materialMappingRot=0 - How much to rotate the material within the parent's UV-space, in degrees. + * @example Color a sphere using a Material entity. + * var entityID = Entities.addEntity({ + * type: "Sphere", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * dimensions: { x: 1, y: 1, z: 1 }, + * color: { red: 128, green: 128, blue: 128 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + * + * var materialID = Entities.addEntity({ + * type: "Material", + * parentID: entityID, + * materialURL: "userData", + * priority: 1, + * userData: JSON.stringify({ + * materials: { + * // Can only set albedo on a Shape entity. + * // Value overrides entity's "color" property. + * albedo: [1.0, 0, 0] + * } + * }), + * }); + */ + +/**jsdoc + * The "Model" {@link Entities.EntityType|EntityType} displays an FBX or OBJ model. + * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * @typedef {object} Entities.EntityProperties-Model + * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. When adding an entity, if no dimensions + * value is specified then the model is automatically sized to its + * {@link Entities.EntityProperties|naturalDimensions}. + * @property {Color} color=255,255,255 - Currently not used. + * @property {string} modelURL="" - The URL of the FBX of OBJ model. Baked FBX models' URLs end in ".baked.fbx".
+ * Note: If the name ends with "default-image-model.fbx" then the entity is considered to be an "Image" + * entity, in which case the textures property should be set per the example. + * @property {string} textures="" - A JSON string of texture name, URL pairs used when rendering the model in place of the + * model's original textures. Use a texture name from the originalTextures property to override that texture. + * Only the texture names and URLs to be overridden need be specified; original textures are used where there are no + * overrides. You can use JSON.stringify() to convert a JavaScript object of name, URL pairs into a JSON + * string. + * @property {string} originalTextures="{}" - A JSON string of texture name, URL pairs used in the model. The property value is + * filled in after the entity has finished rezzing (i.e., textures have loaded). You can use JSON.parse() to + * parse the JSON string into a JavaScript object of name, URL pairs. Read-only. + * + * @property {ShapeType} shapeType="none" - The shape of the collision hull used if collisions are enabled. + * @property {string} compoundShapeURL="" - The OBJ file to use for the compound shape if shapeType is + * "compound". + * + * @property {Entities.AnimationProperties} animation - An animation to play on the model. + * + * @property {Quat[]} jointRotations=[]] - Joint rotations applied to the model; [] if none are applied or the + * model hasn't loaded. The array indexes are per {@link Entities.getJointIndex|getJointIndex}. Rotations are relative to + * each joint's parent.
+ * Joint rotations can be set by {@link Entities.setLocalJointRotation|setLocalJointRotation} and similar functions, or by + * setting the value of this property. If you set a joint rotation using this property you also need to set the + * corresponding jointRotationsSet value to true. + * @property {boolean[]} jointRotationsSet=[]] - true values for joints that have had rotations applied, + * false otherwise; [] if none are applied or the model hasn't loaded. The array indexes are per + * {@link Entities.getJointIndex|getJointIndex}. + * @property {Vec3[]} jointTranslations=[]] - Joint translations applied to the model; [] if none are applied or + * the model hasn't loaded. The array indexes are per {@link Entities.getJointIndex|getJointIndex}. Rotations are relative + * to each joint's parent.
+ * Joint translations can be set by {@link Entities.setLocalJointTranslation|setLocalJointTranslation} and similar + * functions, or by setting the value of this property. If you set a joint translation using this property you also need to + * set the corresponding jointTranslationsSet value to true. + * @property {boolean[]} jointTranslationsSet=[]] - true values for joints that have had translations applied, + * false otherwise; [] if none are applied or the model hasn't loaded. The array indexes are per + * {@link Entities.getJointIndex|getJointIndex}. + * @property {boolean} relayParentJoints=false - If true and the entity is parented to an avatar, then the + * avatar's joint rotations are applied to the entity's joints. + * + * @example Rez a Vive tracker puck. + * var entity = Entities.addEntity({ + * type: "Model", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -2 })), + * rotation: MyAvatar.orientation, + * modelURL: "http://content.highfidelity.com/seefo/production/puck-attach/vive_tracker_puck.obj", + * dimensions: { x: 0.0945, y: 0.0921, z: 0.0423 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + * @example Create an "Image" entity like you can in the Create app. + * var IMAGE_MODEL = "https://hifi-content.s3.amazonaws.com/DomainContent/production/default-image-model.fbx"; + * var DEFAULT_IMAGE = "https://hifi-content.s3.amazonaws.com/DomainContent/production/no-image.jpg"; + * var entity = Entities.addEntity({ + * type: "Model", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -3 })), + * rotation: MyAvatar.orientation, + * dimensions: { + * x: 0.5385, + * y: 0.2819, + * z: 0.0092 + * }, + * shapeType: "box", + * collisionless: true, + * modelURL: IMAGE_MODEL, + * textures: JSON.stringify({ "tex.picture": DEFAULT_IMAGE }), + * lifetime: 300 // Delete after 5 minutes + * }); + */ + +/**jsdoc + * The "ParticleEffect" {@link Entities.EntityType|EntityType} displays a particle system that can be used to + * simulate things such as fire, smoke, snow, magic spells, etc. The particles emanate from an ellipsoid or part thereof. + * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * @typedef {object} Entities.EntityProperties-ParticleEffect + + * @property {boolean} isEmitting=true - If true then particles are emitted. + * @property {number} maxParticles=1000 - The maximum number of particles to render at one time. Older particles are deleted if + * necessary when new ones are created. + * @property {number} lifespan=3s - How long, in seconds, each particle lives. + * @property {number} emitRate=15 - The number of particles per second to emit. + * @property {number} emitSpeed=5 - The speed, in m/s, that each particle is emitted at. + * @property {number} speedSpread=1 - The spread in speeds at which particles are emitted at. If emitSpeed == 5 + * and speedSpread == 1, particles will be emitted with speeds in the range 4m/s – 6m/s. + * @property {vec3} emitAcceleration=0,-9.8,0 - The acceleration that is applied to each particle during its lifetime. The + * default is Earth's gravity value. + * @property {vec3} accelerationSpread=0,0,0 - The spread in accelerations that each particle is given. If + * emitAccelerations == {x: 0, y: -9.8, z: 0} and accelerationSpread == + * {x: 0, y: 1, z: 0}, each particle will have an acceleration in the range, {x: 0, y: -10.8, z: 0} + * – {x: 0, y: -8.8, z: 0}. + * @property {Vec3} dimensions - The dimensions of the particle effect, i.e., a bounding box containing all the particles + * during their lifetimes, assuming that emitterShouldTrail is false. Read-only. + * @property {boolean} emitterShouldTrail=false - If true then particles are "left behind" as the emitter moves, + * otherwise they stay with the entity's dimensions. + * + * @property {Quat} emitOrientation=-0.707,0,0,0.707 - The orientation of particle emission relative to the entity's axes. By + * default, particles emit along the entity's local z-axis, and azimuthStart and azimuthFinish + * are relative to the entity's local x-axis. The default value is a rotation of -90 degrees about the local x-axis, i.e., + * the particles emit vertically. + * @property {vec3} emitDimensions=0,0,0 - The dimensions of the ellipsoid from which particles are emitted. + * @property {number} emitRadiusStart=1 - The starting radius within the ellipsoid at which particles start being emitted; + * range 0.01.0 for the ellipsoid center to the ellipsoid surface, respectively. + * Particles are emitted from the portion of the ellipsoid that lies between emitRadiusStart and the + * ellipsoid's surface. + * @property {number} polarStart=0 - The angle in radians from the entity's local z-axis at which particles start being emitted + * within the ellipsoid; range 0Math.PI. Particles are emitted from the portion of the + * ellipsoid that lies between polarStart and polarFinish. + * @property {number} polarFinish=0 - The angle in radians from the entity's local z-axis at which particles stop being emitted + * within the ellipsoid; range 0Math.PI. Particles are emitted from the portion of the + * ellipsoid that lies between polarStart and polarFinish. + * @property {number} azimuthStart=-Math.PI - The angle in radians from the entity's local x-axis about the entity's local + * z-axis at which particles start being emitted; range -Math.PIMath.PI. Particles are + * emitted from the portion of the ellipsoid that lies between azimuthStart and azimuthFinish. + * @property {number} azimuthFinish=Math.PI - The angle in radians from the entity's local x-axis about the entity's local + * z-axis at which particles stop being emitted; range -Math.PIMath.PI. Particles are + * emitted from the portion of the ellipsoid that lies between azimuthStart and azimuthFinish. + * + * @property {string} textures="" - The URL of a JPG or PNG image file to display for each particle. If you want transparency, + * use PNG format. + * @property {number} particleRadius=0.025 - The radius of each particle at the middle of its life. + * @property {number} radiusStart=0.025 - The radius of each particle at the start of its life. If not explicitly set, the + * particleRadius value is used. + * @property {number} radiusFinish=0.025 - The radius of each particle at the end of its life. If not explicitly set, the + * particleRadius value is used. + * @property {number} radiusSpread=0 - Currently not used. + * @property {Color} color=255,255,255 - The color of each particle at the middle of its life. + * @property {Color} colorStart=255,255,255 - The color of each particle at the start of its life. If not explicitly set, the + * color value is used. + * @property {Color} colorFinish=255,255,255 - The color of each particle at the end of its life. If not explicitly set, the + * color value is used. + * @property {Color} colorSpread=0,0,0 - Currently not used. + * @property {number} alpha=1 - The alpha of each particle at the middle of its life. + * @property {number} alphaStart=1 - The alpha of each particle at the start of its life. If not explicitly set, the + * alpha value is used. + * @property {number} alphaFinish=1 - The alpha of each particle at the end of its life. If not explicitly set, the + * alpha value is used. + * @property {number} alphaSpread=0 - Currently not used. + * + * @property {ShapeType} shapeType="none" - Currently not used. Read-only. + * + * @example Create a ball of green smoke. + * particles = Entities.addEntity({ + * type: "ParticleEffect", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -4 })), + * lifespan: 5, + * emitRate: 10, + * emitSpeed: 0.02, + * speedSpread: 0.01, + * emitAcceleration: { x: 0, y: 0.02, z: 0 }, + * polarFinish: Math.PI, + * textures: "https://content.highfidelity.com/DomainContent/production/Particles/wispy-smoke.png", + * particleRadius: 0.1, + * color: { red: 0, green: 255, blue: 0 }, + * alphaFinish: 0, + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + +/**jsdoc + * The "PolyLine" {@link Entities.EntityType|EntityType} draws textured, straight lines between a sequence of + * points. + * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * @typedef {object} Entities.EntityProperties-PolyLine + * @property {Vec3} dimensions=1,1,1 - The dimensions of the entity, i.e., the size of the bounding box that contains the + * lines drawn. + * @property {Vec3[]} linePoints=[]] - The sequence of points to draw lines between. The values are relative to the entity's + * position. A maximum of 70 points can be specified. Must be specified in order for the entity to render. + * @property {Vec3[]} normals=[]] - The normal vectors for the line's surface at the linePoints. The values are + * relative to the entity's orientation. Must be specified in order for the entity to render. + * @property {number[]} strokeWidths=[]] - The widths, in m, of the line at the linePoints. Must be specified in + * order for the entity to render. + * @property {number} lineWidth=2 - Currently not used. + * @property {Vec3[]} strokeColors=[]] - Currently not used. + * @property {Color} color=255,255,255 - The base color of the line, which is multiplied with the color of the texture for + * rendering. + * @property {string} textures="" - The URL of a JPG or PNG texture to use for the lines. If you want transparency, use PNG + * format. + * @property {boolean} isUVModeStretch=true - If true, the texture is stretched to fill the whole line, otherwise + * the texture repeats along the line. + * @example Draw a textured "V". + * var entity = Entities.addEntity({ + * type: "PolyLine", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -5 })), + * rotation: MyAvatar.orientation, + * linePoints: [ + * { x: -1, y: 0.5, z: 0 }, + * { x: 0, y: 0, z: 0 }, + * { x: 1, y: 0.5, z: 0 } + * ], + * normals: [ + * { x: 0, y: 0, z: 1 }, + * { x: 0, y: 0, z: 1 }, + * { x: 0, y: 0, z: 1 } + * ], + * strokeWidths: [ 0.1, 0.1, 0.1 ], + * color: { red: 255, green: 0, blue: 0 }, // Use just the red channel from the image. + * textures: "http://hifi-production.s3.amazonaws.com/DomainContent/Toybox/flowArts/trails.png", + * isUVModeStretch: true, + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + +/**jsdoc + * The "PolyVox" {@link Entities.EntityType|EntityType} displays a set of textured voxels. + * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * If you have two or more neighboring PolyVox entities of the same size abutting each other, you can display them as joined by + * configuring their voxelSurfaceStyle and neighbor ID properties.
+ * PolyVox entities uses a library from Volumes of Fun. Their + * library documentation may be useful to read. + * @typedef {object} Entities.EntityProperties-PolyVox + * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. + * @property {Vec3} voxelVolumeSize=32,32,32 - Integer number of voxels along each axis of the entity, in the range + * 1,1,1 to 128,128,128. The dimensions of each voxel is + * dimensions / voxelVolumesize. + * @property {string} voxelData="ABAAEAAQAAAAHgAAEAB42u3BAQ0AAADCoPdPbQ8HFAAAAPBuEAAAAQ==" - Base-64 encoded compressed dump of + * the PolyVox data. This property is typically not used in scripts directly; rather, functions that manipulate a PolyVox + * entity update it.
+ * The size of this property increases with the size and complexity of the PolyVox entity, with the size depending on how + * the particular entity's voxels compress. Because this property value has to fit within a High Fidelity datagram packet + * there is a limit to the size and complexity of a PolyVox entity, and edits which would result in an overflow are + * rejected. + * @property {Entities.PolyVoxSurfaceStyle} voxelSurfaceStyle=2 - The style of rendering the voxels' surface and how + * neighboring PolyVox entities are joined. + * @property {string} xTextureURL="" - URL of the texture to map to surfaces perpendicular to the entity's local x-axis. JPG or + * PNG format. If no texture is specified the surfaces display white. + * @property {string} yTextureURL="" - URL of the texture to map to surfaces perpendicular to the entity's local y-axis. JPG or + * PNG format. If no texture is specified the surfaces display white. + * @property {string} zTextureURL="" - URL of the texture to map to surfaces perpendicular to the entity's local z-axis. JPG or + * PNG format. If no texture is specified the surfaces display white. + * @property {Uuid} xNNeighborID=Uuid.NULL - ID of the neighboring PolyVox entity in the entity's -ve local x-axis direction, + * if you want them joined. Set to {@link Uuid|Uuid.NULL} if there is none or you don't want to join them. + * @property {Uuid} yNNeighborID=Uuid.NULL - ID of the neighboring PolyVox entity in the entity's -ve local y-axis direction, + * if you want them joined. Set to {@link Uuid|Uuid.NULL} if there is none or you don't want to join them. + * @property {Uuid} zNNeighborID=Uuid.NULL - ID of the neighboring PolyVox entity in the entity's -ve local z-axis direction, + * if you want them joined. Set to {@link Uuid|Uuid.NULL} if there is none or you don't want to join them. + * @property {Uuid} xPNeighborID=Uuid.NULL - ID of the neighboring PolyVox entity in the entity's +ve local x-axis direction, + * if you want them joined. Set to {@link Uuid|Uuid.NULL} if there is none or you don't want to join them. + * @property {Uuid} yPNeighborID=Uuid.NULL - ID of the neighboring PolyVox entity in the entity's +ve local y-axis direction, + * if you want them joined. Set to {@link Uuid|Uuid.NULL} if there is none or you don't want to join them. + * @property {Uuid} zPNeighborID=Uuid.NULL - ID of the neighboring PolyVox entity in the entity's +ve local z-axis direction, + * if you want them joined. Set to {@link Uuid|Uuid.NULL} if there is none or you don't want to join them. + * @example Create a textured PolyVox sphere. + * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -8 })); + * var texture = "http://public.highfidelity.com/cozza13/tuscany/Concrete2.jpg"; + * var polyVox = Entities.addEntity({ + * type: "PolyVox", + * position: position, + * dimensions: { x: 2, y: 2, z: 2 }, + * xTextureURL: texture, + * yTextureURL: texture, + * zTextureURL: texture, + * lifetime: 300 // Delete after 5 minutes. + * }); + * Entities.setVoxelSphere(polyVox, position, 0.8, 255); + */ + +/**jsdoc + * The "Shape" {@link Entities.EntityType|EntityType} displays an entity of a specified shape. + * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * @typedef {object} Entities.EntityProperties-Shape + * @property {Entities.Shape} shape="Sphere" - The shape of the entity. + * @property {Vec3} dimensions=0.1,0.1,0.1 - The dimensions of the entity. + * @property {Color} color=255,255,255 - The color of the entity. + * @example Create a cylinder. + * var shape = Entities.addEntity({ + * type: "Shape", + * shape: "Cylinder", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * dimensions: { x: 0.4, y: 0.6, z: 0.4 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + +/**jsdoc + * The "Sphere" {@link Entities.EntityType|EntityType} is the same as the "Shape" + * {@link Entities.EntityType|EntityType} except that its shape value is always set to "Sphere" + * when the entity is created. If its shape property value is subsequently changed then the entity's + * type will be reported as "Box" if the shape is set to "Cube", + * otherwise it will be reported as "Shape". + * @typedef {object} Entities.EntityProperties-Sphere + */ + +/**jsdoc + * The "Text" {@link Entities.EntityType|EntityType} displays a 2D rectangle of text in the domain. + * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * @typedef {object} Entities.EntityProperties-Text + * @property {Vec3} dimensions=0.1,0.1,0.01 - The dimensions of the entity. + * @property {string} text="" - The text to display on the face of the entity. Text wraps if necessary to fit. New lines can be + * created using \n. Overflowing lines are not displayed. + * @property {number} lineHeight=0.1 - The height of each line of text (thus determining the font size). + * @property {Color} textColor=255,255,255 - The color of the text. + * @property {Color} backgroundColor=0,0,0 - The color of the background rectangle. + * @property {boolean} faceCamera=false - If true, the entity is oriented to face each user's camera (i.e., it + * differs for each user present). + * @example Create a text entity. + * var text = Entities.addEntity({ + * type: "Text", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * dimensions: { x: 0.6, y: 0.3, z: 0.01 }, + * lineHeight: 0.12, + * text: "Hello\nthere!", + * faceCamera: true, + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + +/**jsdoc + * The "Web" {@link Entities.EntityType|EntityType} displays a browsable Web page. Each user views their own copy + * of the Web page: if one user navigates to another page on the entity, other users do not see the change; if a video is being + * played, users don't see it in sync. + * The entity has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * @typedef {object} Entities.EntityProperties-Web + * @property {Vec3} dimensions=0.1,0.1,0.01 - The dimensions of the entity. + * @property {string} sourceUrl="" - The URL of the Web page to display. This value does not change as you or others navigate + * on the Web entity. + * @property {number} dpi=30 - The resolution to display the page at, in dots per inch. If you convert this to dots per meter + * (multiply by 1 / 0.0254 = 39.3701) then multiply dimensions.x and dimensions.y by that value + * you get the resolution in pixels. + * @example Create a Web entity displaying at 1920 x 1080 resolution. + * var METERS_TO_INCHES = 39.3701; + * var entity = Entities.addEntity({ + * type: "Web", + * sourceUrl: "https://highfidelity.com/", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -4 })), + * rotation: MyAvatar.orientation, + * dimensions: { + * x: 3, + * y: 3 * 1080 / 1920, + * z: 0.01 + * }, + * dpi: 1920 / (3 * METERS_TO_INCHES), + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + +/**jsdoc + * The "Zone" {@link Entities.EntityType|EntityType} is a volume of lighting effects and avatar permissions. + * Avatar interaction events such as {@link Entities.enterEntity} are also often used with a Zone entity. + * It has properties in addition to the common {@link Entities.EntityProperties|EntityProperties}. + * @typedef {object} Entities.EntityProperties-Zone + * @property {Vec3} dimensions=0.1,0.1,0.1 - The size of the volume in which the zone's lighting effects and avatar permissions + * have effect. + * + * @property {ShapeType} shapeType="box" - The shape of the volume in which the zone's lighting effects and avatar + * permissions have effect. Reverts to the default value if set to "none", or set to "compound" + * and compoundShapeURL is "". + * @property {string} compoundShapeURL="" - The OBJ file to use for the compound shape if shapeType is + * "compound". + * + * @property {string} keyLightMode="inherit" - Configures the key light in the zone. Possible values:
+ * "inherit": The key light from any enclosing zone continues into this zone.
+ * "disabled": The key light from any enclosing zone and the key light of this zone are disabled in this + * zone.
+ * "enabled": The key light properties of this zone are enabled, overriding the key light of from any + * enclosing zone. + * @property {Entities.KeyLight} keyLight - The key light properties of the zone. + * + * @property {string} ambientLightMode="inherit" - Configures the ambient light in the zone. Possible values:
+ * "inherit": The ambient light from any enclosing zone continues into this zone.
+ * "disabled": The ambient light from any enclosing zone and the ambient light of this zone are disabled in + * this zone.
+ * "enabled": The ambient light properties of this zone are enabled, overriding the ambient light from any + * enclosing zone. + * @property {Entities.AmbientLight} ambientLight - The ambient light properties of the zone. + * + * @property {string} skyboxMode="inherit" - Configures the skybox displayed in the zone. Possible values:
+ * "inherit": The skybox from any enclosing zone is dislayed in this zone.
+ * "disabled": The skybox from any enclosing zone and the skybox of this zone are disabled in this zone.
+ * "enabled": The skybox properties of this zone are enabled, overriding the skybox from any enclosing zone. + * @property {Entities.Skybox} skybox - The skybox properties of the zone. + * + * @property {string} hazeMode="inherit" - Configures the haze in the zone. Possible values:
+ * "inherit": The haze from any enclosing zone continues into this zone.
+ * "disabled": The haze from any enclosing zone and the haze of this zone are disabled in this zone.
+ * "enabled": The haze properties of this zone are enabled, overriding the haze from any enclosing zone. + * @property {Entities.Haze} haze - The haze properties of the zone. + * + * @property {boolean} flyingAllowed=true - If true then visitors can fly in the zone; otherwise they cannot. + * @property {boolean} ghostingAllowed=true - If true then visitors with avatar collisions turned off will not + * collide with content in the zone; otherwise visitors will always collide with content in the zone. + + * @property {string} filterURL="" - The URL of a JavaScript file that filters changes to properties of entities within the + * zone. It is periodically executed for each entity in the zone. It can, for example, be used to not allow changes to + * certain properties.
+ *
+ * function filter(properties) {
+ *     // Test and edit properties object values,
+ *     // e.g., properties.modelURL, as required.
+ *     return properties;
+ * }
+ * 
+ * + * @example Create a zone that casts a red key light along the x-axis. + * var zone = Entities.addEntity({ + * type: "Zone", + * position: MyAvatar.position, + * dimensions: { x: 100, y: 100, z: 100 }, + * keyLightMode: "enabled", + * keyLight: { + * "color": { "red": 255, "green": 0, "blue": 0 }, + * "direction": { "x": 1, "y": 0, "z": 0 } + * }, + * lifetime: 300 // Delete after 5 minutes. + * }); + */ + QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool skipDefaults, bool allowUnknownCreateTime, bool strictSemantics) const { // If strictSemantics is true and skipDefaults is false, then all and only those properties are copied for which the property flag // is included in _desiredProperties, or is one of the specially enumerated ALWAYS properties below. @@ -491,7 +1192,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ANGULAR_VELOCITY, angularVelocity); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ANGULAR_DAMPING, angularDamping); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_VISIBLE, visible); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CAN_CAST_SHADOW, canCastShadow); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CAN_CAST_SHADOW, canCastShadow); // Relevant to Shape and Model entities only. COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISIONLESS, collisionless); COPY_PROXY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_COLLISIONLESS, collisionless, ignoreForCollisions, getCollisionless()); // legacy support COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISION_MASK, collisionMask); @@ -500,7 +1201,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROXY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_DYNAMIC, dynamic, collisionsWillMove, getDynamic()); // legacy support COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_HREF, href); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_DESCRIPTION, description); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FACE_CAMERA, faceCamera); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_FACE_CAMERA, faceCamera); // Text only. COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_ACTION_DATA, actionData); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCKED, locked); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_USER_DATA, userData); @@ -521,7 +1222,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NAME, name); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLLISION_SOUND_URL, collisionSoundURL); - // Boxes, Spheres, Light, Line, Model(??), Particle, PolyLine + // Light, Line, Model, ParticleEffect, PolyLine, Shape COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_COLOR, color); // Particles only @@ -569,12 +1270,15 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool if (_type == EntityTypes::Model || _type == EntityTypes::Zone || _type == EntityTypes::ParticleEffect) { COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, getShapeTypeAsString()); } + + // FIXME: Shouldn't provide a shapeType property for Box and Sphere entities. if (_type == EntityTypes::Box) { COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, QString("Box")); } if (_type == EntityTypes::Sphere) { COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SHAPE_TYPE, shapeType, QString("Sphere")); } + if (_type == EntityTypes::Box || _type == EntityTypes::Sphere || _type == EntityTypes::Shape) { COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_SHAPE, shape); } @@ -653,11 +1357,11 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool if (_type == EntityTypes::Line || _type == EntityTypes::PolyLine) { COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_WIDTH, lineWidth); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LINE_POINTS, linePoints); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NORMALS, normals); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_STROKE_COLORS, strokeColors); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_STROKE_WIDTHS, strokeWidths); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IS_UV_MODE_STRETCH, isUVModeStretch); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_NORMALS, normals); // Polyline only. + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_STROKE_COLORS, strokeColors); // Polyline only. + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_STROKE_WIDTHS, strokeWidths); // Polyline only. + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_TEXTURES, textures); // Polyline only. + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_IS_UV_MODE_STRETCH, isUVModeStretch); // Polyline only. } // Materials @@ -671,6 +1375,14 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_MATERIAL_MAPPING_ROT, materialMappingRot); } + /**jsdoc + * The axis-aligned bounding box of an entity. + * @typedef Entities.BoundingBox + * @property {Vec3} brn - The bottom right near (minimum axes values) corner of the AA box. + * @property {Vec3} tfl - The top far left (maximum axes values) corner of the AA box. + * @property {Vec3} center - The center of the AA box. + * @property {Vec3} dimensions - The dimensions of the AA box. + */ if (!skipDefaults && !strictSemantics) { AABox aaBox = getAABox(); QScriptValue boundingBox = engine->newObject(); @@ -692,6 +1404,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARENT_ID, parentID); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_PARENT_JOINT_INDEX, parentJointIndex); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_QUERY_AA_CUBE, queryAACube); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_POSITION, localPosition); @@ -700,13 +1413,23 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_ANGULAR_VELOCITY, localAngularVelocity); COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_LOCAL_DIMENSIONS, localDimensions); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLIENT_ONLY, clientOnly); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_OWNING_AVATAR_ID, owningAvatarID); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLIENT_ONLY, clientOnly); // Gettable but not settable + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_OWNING_AVATAR_ID, owningAvatarID); // Gettable but not settable // Rendering info if (!skipDefaults && !strictSemantics) { QScriptValue renderInfo = engine->newObject(); + /**jsdoc + * Information on how an entity is rendered. Properties are only filled in for Model entities; other + * entity types have an empty object, {}. + * @typedef {object} Entities.RenderInfo + * @property {number} verticesCount - The number of vertices in the entity. + * @property {number} texturesCount - The number of textures in the entity. + * @property {number} textureSize - The total size of the textures in the entity, in bytes. + * @property {boolean} hasTransparent - Is true if any of the textures has transparency. + * @property {number} drawCalls - The number of draw calls required to render the entity. + */ // currently only supported by models if (_type == EntityTypes::Model) { renderInfo.setProperty("verticesCount", (int)getRenderInfoVertexCount()); // FIXME - theoretically the number of vertex could be > max int @@ -719,6 +1442,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER_NO_SKIP(renderInfo, renderInfo); // Gettable but not settable } + // FIXME: These properties should already have been set above. properties.setProperty("clientOnly", convertScriptValue(engine, getClientOnly())); properties.setProperty("owningAvatarID", convertScriptValue(engine, getOwningAvatarID())); diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 0838bb937a..9e5d6ddc79 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -208,7 +208,7 @@ public: DEFINE_PROPERTY(PROP_NORMALS, Normals, normals, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); DEFINE_PROPERTY(PROP_STROKE_COLORS, StrokeColors, strokeColors, QVector, ENTITY_ITEM_DEFAULT_EMPTY_VEC3_QVEC); DEFINE_PROPERTY(PROP_STROKE_WIDTHS, StrokeWidths, strokeWidths, QVector, QVector()); - DEFINE_PROPERTY(PROP_IS_UV_MODE_STRETCH, IsUVModeStretch, isUVModeStretch, bool, true); + DEFINE_PROPERTY(PROP_IS_UV_MODE_STRETCH, IsUVModeStretch, isUVModeStretch, bool, true); DEFINE_PROPERTY_REF(PROP_X_TEXTURE_URL, XTextureURL, xTextureURL, QString, ""); DEFINE_PROPERTY_REF(PROP_Y_TEXTURE_URL, YTextureURL, yTextureURL, QString, ""); DEFINE_PROPERTY_REF(PROP_Z_TEXTURE_URL, ZTextureURL, zTextureURL, QString, ""); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 4af0b7232d..7e15e78624 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -538,7 +538,7 @@ QUuid EntityScriptingInterface::editEntity(QUuid id, const EntityItemProperties& NestableType nestableType = nestable->getNestableType(); if (nestableType == NestableType::Overlay || nestableType == NestableType::Avatar) { qCWarning(entities) << "attempted edit on non-entity: " << id << nestable->getName(); - return QUuid(); // null UUID to indicate failure + return QUuid(); // null script value to indicate failure } } } @@ -1017,6 +1017,25 @@ QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, c QString faceName = ""; // handle BoxFace + /**jsdoc + *

A BoxFace specifies the face of an axis-aligned (AA) box. + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ValueDescription
"MIN_X_FACE"The minimum x-axis face.
"MAX_X_FACE"The maximum x-axis face.
"MIN_Y_FACE"The minimum y-axis face.
"MAX_Y_FACE"The maximum y-axis face.
"MIN_Z_FACE"The minimum z-axis face.
"MAX_Z_FACE"The maximum z-axis face.
"UNKNOWN_FACE"Unknown value.
+ * @typedef {string} BoxFace + */ + // FIXME: Move enum to string function to BoxBase.cpp. switch (value.face) { case MIN_X_FACE: faceName = "MIN_X_FACE"; diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 6b4cd81e44..b483225390 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -52,6 +52,23 @@ private: QPointer _engine; }; +/**jsdoc + * The result of a {@link PickRay} search using {@link Entities.findRayIntersection|findRayIntersection} or + * {@link Entities.findRayIntersectionBlocking|findRayIntersectionBlocking}. + * @typedef {object} Entities.RayToEntityIntersectionResult + * @property {boolean} intersects - true if the {@link PickRay} intersected an entity, otherwise + * false. + * @property {boolean} accurate - Is always true. + * @property {Uuid} entityID - The ID if the entity intersected, if any, otherwise null. + * @property {number} distance - The distance from the {@link PickRay} origin to the intersection point. + * @property {Vec3} intersection - The intersection point. + * @property {Vec3} surfaceNormal - The surface normal of the entity at the intersection point. + * @property {BoxFace} face - The face of the entity's axis-aligned box that the ray intersects. + * @property {object} extraInfo - Extra information depending on the entity intersected. Currently, only Model + * entities provide extra information, and the information provided depends on the precisionPicking parameter + * value that the search function was called with. + */ +// "accurate" is currently always true because the ray intersection is always performed with an Octree::Lock. class RayToEntityIntersectionResult { public: RayToEntityIntersectionResult(); @@ -72,7 +89,16 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra /**jsdoc + * The Entities API provides facilities to create and interact with entities. Entities are 2D and 3D objects that are visible + * to everyone and typically are persisted to the domain. For Interface scripts, the entities available are those that + * Interface has displayed and so knows about. + * * @namespace Entities + * @property {number} currentAvatarEnergy - Deprecated + * @property {number} costMultiplier - Deprecated + * @property {Uuid} keyboardFocusEntity - Get or set the {@link Entities.EntityType|Web} entity that has keyboard focus. + * If no entity has keyboard focus, get returns null; set to null or {@link Uuid|Uuid.NULL} to + * clear keyboard focus. */ /// handles scripting of Entity commands from JS passed to assigned clients class EntityScriptingInterface : public OctreeScriptingInterface, public Dependency { @@ -107,58 +133,88 @@ public: public slots: /**jsdoc - * Returns `true` if the DomainServer will allow this Node/Avatar to make changes - * + * Check whether or not you can change the locked property of entities. Locked entities have their + * locked property set to true and cannot be edited or deleted. Whether or not you can change + * entities' locked properties is configured in the domain server's permissions. * @function Entities.canAdjustLocks - * @return {bool} `true` if the client can adjust locks, `false` if not. + * @returns {boolean} true if the client can change the locked property of entities, + * otherwise false. + * @example Set an entity's locked property to true if you can. + * if (Entities.canAdjustLocks()) { + * Entities.editEntity(entityID, { locked: true }); + * } else { + * Window.alert("You do not have the permissions to set an entity locked!"); + * } */ Q_INVOKABLE bool canAdjustLocks(); /**jsdoc + * Check whether or not you can rez (create) new entities in the domain. * @function Entities.canRez - * @return {bool} `true` if the DomainServer will allow this Node/Avatar to rez new entities + * @returns {boolean} true if the domain server will allow the script to rez (create) new entities, + * otherwise false. */ Q_INVOKABLE bool canRez(); /**jsdoc + * Check whether or not you can rez (create) new temporary entities in the domain. Temporary entities are entities with a + * finite lifetime property value set. * @function Entities.canRezTmp - * @return {bool} `true` if the DomainServer will allow this Node/Avatar to rez new temporary entities + * @returns {boolean} true if the domain server will allow the script to rez (create) new temporary + * entities, otherwise false. */ Q_INVOKABLE bool canRezTmp(); /**jsdoc - * @function Entities.canRezCertified - * @return {bool} `true` if the DomainServer will allow this Node/Avatar to rez new certified entities - */ + * Check whether or not you can rez (create) new certified entities in the domain. Certified entities are entities that have + * PoP certificates. + * @function Entities.canRezCertified + * @returns {boolean} true if the domain server will allow the script to rez (create) new certified + * entities, otherwise false. + */ Q_INVOKABLE bool canRezCertified(); /**jsdoc - * @function Entities.canRezTmpCertified - * @return {bool} `true` if the DomainServer will allow this Node/Avatar to rez new temporary certified entities - */ + * Check whether or not you can rez (create) new temporary certified entities in the domain. Temporary entities are entities + * with a finite lifetime property value set. Certified entities are entities that have PoP certificates. + * @function Entities.canRezTmpCertified + * @returns {boolean} true if the domain server will allow the script to rez (create) new temporary + * certified entities, otherwise false. + */ Q_INVOKABLE bool canRezTmpCertified(); /**jsdoc - * @function Entities.canWriteAssets - * @return {bool} `true` if the DomainServer will allow this Node/Avatar to write to the asset server - */ + * Check whether or not you can make changes to the asset server's assets. + * @function Entities.canWriteAssets + * @returns {boolean} true if the domain server will allow the script to make changes to the asset server's + * assets, otherwise false. + */ Q_INVOKABLE bool canWriteAssets(); /**jsdoc - * @function Entities.canReplaceContent - * @return {bool} `true` if the DomainServer will allow this Node/Avatar to replace the domain's content set - */ + * Check whether or not you can replace the domain's content set. + * @function Entities.canReplaceContent + * @returns {boolean} true if the domain server will allow the script to replace the domain's content set, + * otherwise false. + */ Q_INVOKABLE bool canReplaceContent(); /**jsdoc - * Add a new entity with the specified properties. If `clientOnly` is true, the entity will - * not be sent to the server and will only be visible/accessible on the local client. - * + * Add a new entity with specified properties. * @function Entities.addEntity - * @param {EntityItemProperties} properties Properties of the entity to create. - * @param {bool} [clientOnly=false] Whether the entity should only exist locally or not. - * @return {EntityID} The entity ID of the newly created entity. The ID will be a null - * UUID (`{00000000-0000-0000-0000-000000000000}`) if the entity could not be created. + * @param {Entities.EntityProperties} properties - The properties of the entity to create. + * @param {boolean} [clientOnly=false] - If true, the entity is created as an avatar entity, otherwise it + * is created on the server. An avatar entity follows you to each domain you visit, rendering at the same world + * coordinates unless it's parented to your avatar. + * @returns {Uuid} The ID of the entity if successfully created, otherwise {@link Uuid|Uuid.NULL}. + * @example Create a box entity in front of your avatar. + * var entityID = Entities.addEntity({ + * type: "Box", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.5, y: 0.5, z: 0.5 } + * }); + * print("Entity created: " + entityID); */ Q_INVOKABLE QUuid addEntity(const EntityItemProperties& properties, bool clientOnly = false); @@ -168,115 +224,202 @@ public slots: bool collisionless, const glm::vec3& position, const glm::vec3& gravity); /**jsdoc - * Return the properties for the specified {EntityID}. - * not be sent to the server and will only be visible/accessible on the local client. - * @param {EntityItemProperties} properties Properties of the entity to create. - * @param {EntityPropertyFlags} [desiredProperties=[]] Array containing the names of the properties you - * would like to get. If the array is empty, all properties will be returned. - * @return {EntityItemProperties} The entity properties for the specified entity. + * Get the properties of an entity. + * @function Entities.getEntityProperties + * @param {Uuid} entityID - The ID of the entity to get the properties of. + * @param {string[]} [desiredProperties=[]] - Array of the names of the properties to get. If the array is empty, + * all properties are returned. + * @returns {Entities.EntityProperties} The properties of the entity if the entity can be found, otherwise an empty object. + * @example Report the color of a new box entity. + * var entityID = Entities.addEntity({ + * type: "Box", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.5, y: 0.5, z: 0.5 } + * }); + * var properties = Entities.getEntityProperties(entityID, ["color"]); + * print("Entity color: " + JSON.stringify(properties.color)); */ Q_INVOKABLE EntityItemProperties getEntityProperties(QUuid entityID); Q_INVOKABLE EntityItemProperties getEntityProperties(QUuid identity, EntityPropertyFlags desiredProperties); /**jsdoc - * Updates an entity with the specified properties. - * + * Update an entity with specified properties. * @function Entities.editEntity - * @return {EntityID} The EntityID of the entity if the edit was successful, otherwise the null {EntityID}. + * @param {Uuid} entityID - The ID of the entity to edit. + * @param {Entities.EntityProperties} properties - The properties to update the entity with. + * @returns {Uuid} The ID of the entity if the edit was successful, otherwise null. + * @example Change the color of an entity. + * var entityID = Entities.addEntity({ + * type: "Box", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.5, y: 0.5, z: 0.5 } + * }); + * var properties = Entities.getEntityProperties(entityID, ["color"]); + * print("Entity color: " + JSON.stringify(properties.color)); + * + * Entities.editEntity(entityID, { + * color: { red: 255, green: 0, blue: 0 } + * }); + * properties = Entities.getEntityProperties(entityID, ["color"]); + * print("Entity color: " + JSON.stringify(properties.color)); */ Q_INVOKABLE QUuid editEntity(QUuid entityID, const EntityItemProperties& properties); /**jsdoc - * Deletes an entity. - * + * Delete an entity. * @function Entities.deleteEntity - * @param {EntityID} entityID The ID of the entity to delete. + * @param {Uuid} entityID - The ID of the entity to delete. + * @example Delete an entity a few seconds after creating it. + * var entityID = Entities.addEntity({ + * type: "Box", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 0.5, y: 0.5, z: 0.5 } + * }); + * + * Script.setTimeout(function () { + * Entities.deleteEntity(entityID); + * }, 3000); */ Q_INVOKABLE void deleteEntity(QUuid entityID); + /**jsdoc - * Call a method on an entity in the same context as this function is called. Allows a script - * to call a method on an entity's script. The method will execute in the entity script engine. - * If the entity does not have an entity script or the method does not exist, this call will - * have no effect. If it is running an entity script (specified by the `script` property) - * and it exposes a property with the specified name `method`, it will be called - * using `params` as the list of arguments. If this is called within an entity script, the - * method will be executed on the client in the entity script engine in which it was called. If - * this is called in an entity server script, the method will be executed on the entity server - * script engine. - * + * Call a method in a client entity script from a client script or client entity script, or call a method in a server + * entity script from a server entity script. The entity script method must be exposed as a property in the target client + * entity script. Additionally, if calling a server entity script, the server entity script must include the method's name + * in an exposed property called remotelyCallable that is an array of method names that can be called. * @function Entities.callEntityMethod - * @param {EntityID} entityID The ID of the entity to call the method on. - * @param {string} method The name of the method to call. - * @param {string[]} params The list of parameters to call the specified method with. + * @param {Uuid} entityID - The ID of the entity to call the method in. + * @param {string} method - The name of the method to call. + * @param {string[]} [parameters=[]] - The parameters to call the specified method with. */ Q_INVOKABLE void callEntityMethod(QUuid entityID, const QString& method, const QStringList& params = QStringList()); /**jsdoc - * Call a server method on an entity. Allows a client entity script to call a method on an - * entity's server script. The method will execute in the entity server script engine. If - * the entity does not have an entity server script or the method does not exist, this call will - * have no effect. If the entity is running an entity script (specified by the `serverScripts` property) - * and it exposes a property with the specified name `method`, it will be called using `params` as - * the list of arguments. - * - * @function Entities.callEntityServerMethod - * @param {EntityID} entityID The ID of the entity to call the method on. - * @param {string} method The name of the method to call. - * @param {string[]} params The list of parameters to call the specified method with. - */ + * Call a method in a server entity script from a client script or client entity script. The entity script method must be + * exposed as a property in the target server entity script. Additionally, the target server entity script must include the + * method's name in an exposed property called remotelyCallable that is an array of method names that can be + * called. + * @function Entities.callEntityServerMethod + * @param {Uuid} entityID - The ID of the entity to call the method in. + * @param {string} method - The name of the method to call. + * @param {string[]} [parameters=[]] - The parameters to call the specified method with. + */ Q_INVOKABLE void callEntityServerMethod(QUuid entityID, const QString& method, const QStringList& params = QStringList()); /**jsdoc - * Call a client method on an entity on a specific client node. Allows a server entity script to call a - * method on an entity's client script for a particular client. The method will execute in the entity script - * engine on that single client. If the entity does not have an entity script or the method does not exist, or - * the client is not connected to the domain, or you attempt to make this call outside of the entity server - * script, this call will have no effect. - * - * @function Entities.callEntityClientMethod - * @param {SessionID} clientSessionID The session ID of the client to call the method on. - * @param {EntityID} entityID The ID of the entity to call the method on. - * @param {string} method The name of the method to call. - * @param {string[]} params The list of parameters to call the specified method with. - */ - Q_INVOKABLE void callEntityClientMethod(QUuid clientSessionID, QUuid entityID, const QString& method, const QStringList& params = QStringList()); + * Call a method in a specific user's client entity script from a server entity script. The entity script method must be + * exposed as a property in the target client entity script. + * @function Entities.callEntityClientMethod + * @param {Uuid} clientSessionID - The session ID of the user to call the method in. + * @param {Uuid} entityID - The ID of the entity to call the method in. + * @param {string} method - The name of the method to call. + * @param {string[]} [parameters=[]] - The parameters to call the specified method with. + */ + Q_INVOKABLE void callEntityClientMethod(QUuid clientSessionID, QUuid entityID, const QString& method, + const QStringList& params = QStringList()); + /**jsdoc - * finds the closest model to the center point, within the radius - * will return a EntityItemID.isKnownID = false if no models are in the radius - * this function will not find any models in script engine contexts which don't have access to models + * Find the entity with a position closest to a specified point and within a specified radius. * @function Entities.findClosestEntity - * @param {vec3} center point - * @param {float} radius to search - * @return {EntityID} The EntityID of the entity that is closest and in the radius. + * @param {Vec3} center - The point about which to search. + * @param {number} radius - The radius within which to search. + * @returns {Uuid} The ID of the entity that is closest to the center and within the radius if + * there is one, otherwise null. + * @example Find the closest entity within 10m of your avatar. + * var entityID = Entities.findClosestEntity(MyAvatar.position, 10); + * print("Closest entity: " + entityID); */ + /// this function will not find any models in script engine contexts which don't have access to models Q_INVOKABLE QUuid findClosestEntity(const glm::vec3& center, float radius) const; - /// finds models within the search sphere specified by the center point and radius + /**jsdoc + * Find all entities that intersect a sphere defined by a center point and radius. + * @function Entities.findEntities + * @param {Vec3} center - The point about which to search. + * @param {number} radius - The radius within which to search. + * @returns {Uuid[]} An array of entity IDs that were found that intersect the search sphere. The array is empty if no + * entities could be found. + * @example Report how many entities are within 10m of your avatar. + * var entityIDs = Entities.findEntities(MyAvatar.position, 10); + * print("Number of entities within 10m: " + entityIDs.length); + */ /// this function will not find any models in script engine contexts which don't have access to models Q_INVOKABLE QVector findEntities(const glm::vec3& center, float radius) const; - /// finds models within the box specified by the corner and dimensions + /**jsdoc + * Find all entities whose axis-aligned boxes intersect a search axis-aligned box defined by its minimum coordinates corner + * and dimensions. + * @function Entities.findEntitiesInBox + * @param {Vec3} corner - The corner of the search AA box with minimum co-ordinate values. + * @param {Vec3} dimensions - The dimensions of the search AA box. + * @returns {Uuid[]} An array of entity IDs whose AA boxes intersect the search AA box. The array is empty if no entities + * could be found. + */ /// this function will not find any models in script engine contexts which don't have access to models Q_INVOKABLE QVector findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const; - /// finds models within the frustum - /// the frustum must have the following properties: - /// - position - /// - orientation - /// - projection - /// - centerRadius + /**jsdoc + * Find all entities whose axis-aligned boxes intersect a search frustum. + * @function Entities.findEntitiesInFrustum + * @param {ViewFrustum} frustum - The frustum to search in. The position, orientation, + * projection, and centerRadius properties must be specified. + * @returns {Uuid[]} An array of entity IDs axis-aligned boxes intersect the frustum. The array is empty if no entities + * could be found. + * @example Report the number of entities in view. + * var entityIDs = Entities.findEntitiesInFrustum(Camera.frustum); + * print("Number of entities in view: " + entityIDs.length); + */ /// this function will not find any models in script engine contexts which don't have access to entities Q_INVOKABLE QVector findEntitiesInFrustum(QVariantMap frustum) const; - /// finds entities of the indicated type within a sphere given by the center point and radius - /// @param {QString} string representation of entity type - /// @param {vec3} center point - /// @param {float} radius to search + /**jsdoc + * Find all entities of a particular type that intersect a sphere defined by a center point and radius. + * @function Entities.findEntitiesByType + * @param {Entities.EntityType} entityType - The type of entity to search for. + * @param {Vec3} center - The point about which to search. + * @param {number} radius - The radius within which to search. + * @returns {Uuid[]} An array of entity IDs of the specified type that intersect the search sphere. The array is empty if + * no entities could be found. + * @example Report the number of Model entities within 10m of your avatar. + * var entityIDs = Entities.findEntitiesByType("Model", MyAvatar.position, 10); + * print("Number of Model entities within 10m: " + entityIDs.length); + */ /// this function will not find any entities in script engine contexts which don't have access to entities Q_INVOKABLE QVector findEntitiesByType(const QString entityType, const glm::vec3& center, float radius) const; + /**jsdoc + * Find the first entity intersected by a {@link PickRay}. Light and Zone entities are not + * intersected unless they've been configured as pickable using {@link Entities.setLightsArePickable|setLightsArePickable} + * and {@link Entities.setZonesArePickable|setZonesArePickable}, respectively.
+ * @function Entities.findRayIntersection + * @param {PickRay} pickRay - The PickRay to use for finding entities. + * @param {boolean} [precisionPicking=false] - If true and the intersected entity is a Model + * entity, the result's extraInfo property includes more information than it otherwise would. + * @param {Uuid[]} [entitiesToInclude=[]] - If not empty then the search is restricted to these entities. + * @param {Uuid[]} [entitiesToDiscard=[]] - Entities to ignore during the search. + * @param {boolean} [visibleOnly=false] - If true then only entities that are + * {@link Entities.EntityProperties|visible} are searched. + * @param {boolean} [collideableOnly=false] - If true then only entities that are not + * {@link Entities.EntityProperties|collisionless} are searched. + * @returns {Entities.RayToEntityIntersectionResult} The result of the search for the first intersected entity. + * @example Find the entity directly in front of your avatar. + * var pickRay = { + * origin: MyAvatar.position, + * direction: Quat.getFront(MyAvatar.orientation) + * }; + * + * var intersection = Entities.findRayIntersection(pickRay, true); + * if (intersection.intersects) { + * print("Entity in front of avatar: " + intersection.entityID); + * } else { + * print("No entity in front of avatar."); + * } + */ /// If the scripting context has visible entities, this will determine a ray intersection, the results /// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate /// will be false. @@ -289,187 +432,1424 @@ public slots: const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly); + /**jsdoc + * Find the first entity intersected by a {@link PickRay}. Light and Zone entities are not + * intersected unless they've been configured as pickable using {@link Entities.setLightsArePickable|setLightsArePickable} + * and {@link Entities.setZonesArePickable|setZonesArePickable}, respectively.
+ * This is a synonym for {@link Entities.findRayIntersection|findRayIntersection}. + * @function Entities.findRayIntersectionBlocking + * @param {PickRay} pickRay - The PickRay to use for finding entities. + * @param {boolean} [precisionPicking=false] - If true and the intersected entity is a Model + * entity, the result's extraInfo property includes more information than it otherwise would. + * @param {Uuid[]} [entitiesToInclude=[]] - If not empty then the search is restricted to these entities. + * @param {Uuid[]} [entitiesToDiscard=[]] - Entities to ignore during the search. + * @deprecated This function is deprecated and will soon be removed. Use + * {@link Entities.findRayIntersection|findRayIntersection} instead; it blocks and performs the same function. + */ /// If the scripting context has visible entities, this will determine a ray intersection, and will block in /// order to return an accurate result - Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue()); + Q_INVOKABLE RayToEntityIntersectionResult findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking = false, + const QScriptValue& entityIdsToInclude = QScriptValue(), const QScriptValue& entityIdsToDiscard = QScriptValue()); + + /**jsdoc + * Reloads an entity's server entity script such that the latest version re-downloaded. + * @function Entities.reloadServerScripts + * @param {Uuid} entityID - The ID of the entity to reload the server entity script of. + * @returns {boolean} true if the reload request was successfully sent to the server, otherwise + * false. + */ Q_INVOKABLE bool reloadServerScripts(QUuid entityID); /**jsdoc - * Query additional metadata for "magic" Entity properties like `script` and `serverScripts`. - * - * @function Entities.queryPropertyMetadata - * @param {EntityID} entityID The ID of the entity. - * @param {string} property The name of the property extended metadata is wanted for. - * @param {ResultCallback} callback Executes callback(err, result) with the query results. + * Gets the status of server entity script attached to an entity + * @function Entities.getServerScriptStatus + * @property {Uuid} entityID - The ID of the entity to get the server entity script status for. + * @property {Entities~getServerScriptStatusCallback} callback - The function to call upon completion. + * @returns {boolean} true always. */ /**jsdoc - * Query additional metadata for "magic" Entity properties like `script` and `serverScripts`. - * - * @function Entities.queryPropertyMetadata - * @param {EntityID} entityID The ID of the entity. - * @param {string} property The name of the property extended metadata is wanted for. - * @param {Object} thisObject The scoping "this" context that callback will be executed within. - * @param {ResultCallback} callbackOrMethodName Executes thisObject[callbackOrMethodName](err, result) with the query results. + * Called when {@link Entities.getServerScriptStatus} is complete. + * @callback Entities~getServerScriptStatusCallback + * @param {boolean} success - true if the server entity script status could be obtained, otherwise + * false. + * @param {boolean} isRunning - true if there is a server entity script running, otherwise false. + * @param {string} status - "running" if there is a server entity script running, otherwise an error string. + * @param {string} errorInfo - "" if there is a server entity script running, otherwise it may contain extra + * information on the error. */ - Q_INVOKABLE bool queryPropertyMetadata(QUuid entityID, QScriptValue property, QScriptValue scopeOrCallback, QScriptValue methodOrName = QScriptValue()); - Q_INVOKABLE bool getServerScriptStatus(QUuid entityID, QScriptValue callback); + /**jsdoc + * Get metadata for certain entity properties such as script and serverScripts. + * @function Entities.queryPropertyMetadata + * @param {Uuid} entityID - The ID of the entity to get the metadata for. + * @param {string} property - The property name to get the metadata for. + * @param {Entities~queryPropertyMetadataCallback} callback - The function to call upon completion. + * @returns {boolean} true if the request for metadata was successfully sent to the server, otherwise + * false. + * @throws Throws an error if property is not handled yet or callback is not a function. + */ + /**jsdoc + * Get metadata for certain entity properties such as script and serverScripts. + * @function Entities.queryPropertyMetadata + * @param {Uuid} entityID - The ID of the entity to get the metadata for. + * @param {string} property - The property name to get the metadata for. + * @param {object} scope - The "this" context that the callback will be executed within. + * @param {Entities~queryPropertyMetadataCallback} callback - The function to call upon completion. + * @returns {boolean} true if the request for metadata was successfully sent to the server, otherwise + * false. + * @throws Throws an error if property is not handled yet or callback is not a function. + */ + /**jsdoc + * Called when {@link Entities.queryPropertyMetadata} is complete. + * @callback Entities~queryPropertyMetadataCallback + * @param {string} error - undefined if there was no error, otherwise an error message. + * @param {object} result - The metadata for the requested entity property if there was no error, otherwise + * undefined. + */ + Q_INVOKABLE bool queryPropertyMetadata(QUuid entityID, QScriptValue property, QScriptValue scopeOrCallback, + QScriptValue methodOrName = QScriptValue()); + + + /**jsdoc + * Set whether or not ray picks intersect the bounding box of {@link Entities.EntityType|Light} entities. By default, Light + * entities are not intersected. The setting lasts for the Interface session. Ray picks are done using + * {@link Entities.findRayIntersection|findRayIntersection} or + * {@link Entities.findRayIntersectionBlocking|findRayIntersectionBlocking}, or the {@link Picks} and {@link RayPick} + * APIs. + * @function Entities.setLightsArePickable + * @param {boolean} value - Set true to make ray picks intersect the bounding box of + * {@link Entities.EntityType|Light} entities, otherwise false. + */ // FIXME move to a renderable entity interface Q_INVOKABLE void setLightsArePickable(bool value); + + /**jsdoc + * Get whether or not ray picks intersect the bounding box of {@link Entities.EntityType|Light} entities. Ray picks are + * done using {@link Entities.findRayIntersection|findRayIntersection} or + * {@link Entities.findRayIntersectionBlocking|findRayIntersectionBlocking}, or the {@link Picks} and {@link RayPick} + * APIs. + * @function Entities.getLightsArePickable + * @returns {boolean} true if ray picks intersect the bounding box of {@link Entities.EntityType|Light} + * entities, otherwise false. + */ + // FIXME move to a renderable entity interface Q_INVOKABLE bool getLightsArePickable() const; + /**jsdoc + * Set whether or not ray picks intersect the bounding box of {@link Entities.EntityType|Zone} entities. By default, Light + * entities are not intersected. The setting lasts for the Interface session. Ray picks are done using + * {@link Entities.findRayIntersection|findRayIntersection} or + * {@link Entities.findRayIntersectionBlocking|findRayIntersectionBlocking}, or the {@link Picks} and {@link RayPick} + * APIs. + * @function Entities.setZonesArePickable + * @param {boolean} value - Set true to make ray picks intersect the bounding box of + * {@link Entities.EntityType|Zone} entities, otherwise false. + */ // FIXME move to a renderable entity interface Q_INVOKABLE void setZonesArePickable(bool value); + + /**jsdoc + * Get whether or not ray picks intersect the bounding box of {@link Entities.EntityType|Zone} entities. Ray picks are + * done using {@link Entities.findRayIntersection|findRayIntersection} or + * {@link Entities.findRayIntersectionBlocking|findRayIntersectionBlocking}, or the {@link Picks} and {@link RayPick} + * APIs. + * @function Entities.getZonesArePickable + * @returns {boolean} true if ray picks intersect the bounding box of {@link Entities.EntityType|Zone} + * entities, otherwise false. + */ + // FIXME move to a renderable entity interface Q_INVOKABLE bool getZonesArePickable() const; + /**jsdoc + * Set whether or not {@link Entities.EntityType|Zone} entities' boundaries should be drawn. Currently not used. + * @function Entities.setDrawZoneBoundaries + * @param {boolean} value - Set to true if {@link Entities.EntityType|Zone} entities' boundaries should be + * drawn, otherwise false. + */ // FIXME move to a renderable entity interface Q_INVOKABLE void setDrawZoneBoundaries(bool value); + + /**jsdoc + * Get whether or not {@link Entities.EntityType|Zone} entities' boundaries should be drawn. Currently not used. + * @function Entities.getDrawZoneBoundaries + * @returns {boolean} true if {@link Entities.EntityType|Zone} entities' boundaries should be drawn, + * otherwise false. + */ + // FIXME move to a renderable entity interface Q_INVOKABLE bool getDrawZoneBoundaries() const; + + /**jsdoc + * Set the values of all voxels in a spherical portion of a {@link Entities.EntityType|PolyVox} entity. + * @function Entities.setVoxelSphere + * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|PolyVox} entity. + * @param {Vec3} center - The center of the sphere of voxels to set, in world coordinates. + * @param {number} radius - The radius of the sphere of voxels to set, in world coordinates. + * @param {number} value - If value % 256 == 0 then each voxel is cleared, otherwise each voxel is set. + * @example Create a PolyVox sphere. + * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -8 })); + * var polyVox = Entities.addEntity({ + * type: "PolyVox", + * position: position, + * dimensions: { x: 2, y: 2, z: 2 }, + * voxelVolumeSize: { x: 32, y: 32, z: 32 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + * Entities.setVoxelSphere(polyVox, position, 0.9, 255); + */ // FIXME move to a renderable entity interface Q_INVOKABLE bool setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value); + + /**jsdoc + * Set the values of all voxels in a capsule-shaped portion of a {@link Entities.EntityType|PolyVox} entity. + * @function Entities.setVoxelCapsule + * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|PolyVox} entity. + * @param {Vec3} start - The center of the sphere of voxels to set, in world coordinates. + * @param {Vec3} end - The center of the sphere of voxels to set, in world coordinates. + * @param {number} radius - The radius of the capsule cylinder and spherical ends, in world coordinates. + * @param {number} value - If value % 256 == 0 then each voxel is cleared, otherwise each voxel is set. + * @example Create a PolyVox capsule shape. + * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -8 })); + * var polyVox = Entities.addEntity({ + * type: "PolyVox", + * position: position, + * dimensions: { x: 2, y: 2, z: 2 }, + * voxelVolumeSize: { x: 32, y: 32, z: 32 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + * var startPosition = Vec3.sum({ x: -0.5, y: 0, z: 0 }, position); + * var endPosition = Vec3.sum({ x: 0.5, y: 0, z: 0 }, position); + * Entities.setVoxelCapsule(polyVox, startPosition, endPosition, 0.5, 255); + */ + // FIXME move to a renderable entity interface Q_INVOKABLE bool setVoxelCapsule(QUuid entityID, const glm::vec3& start, const glm::vec3& end, float radius, int value); + /**jsdoc + * Set the value of a particular voxels in a {@link Entities.EntityType|PolyVox} entity. + * @function Entities.setVoxel + * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|PolyVox} entity. + * @param {Vec3} position - The position relative to the minimum axes values corner of the entity. The + * position coordinates are rounded to the nearest integer to get the voxel coordinate. The minimum axes + * corner voxel is { x: 0, y: 0, z: 0 }. + * @param {number} value - If value % 256 == 0 then voxel is cleared, otherwise the voxel is set. + * @example Create a cube PolyVox entity and clear the minimum axes corner voxel. + * var entity = Entities.addEntity({ + * type: "PolyVox", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -8 })), + * dimensions: { x: 2, y: 2, z: 2 }, + * voxelVolumeSize: { x: 16, y: 16, z: 16 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + * Entities.setAllVoxels(entity, 1); + * Entities.setVoxel(entity, { x: 0, y: 0, z: 0 }, 0); + */ // FIXME move to a renderable entity interface Q_INVOKABLE bool setVoxel(QUuid entityID, const glm::vec3& position, int value); + + /**jsdoc + * Set the values of all voxels in a {@link Entities.EntityType|PolyVox} entity. + * @function Entities.setAllVoxels + * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|PolyVox} entity. + * @param {number} value - If value % 256 == 0 then each voxel is cleared, otherwise each voxel is set. + * @example Create a PolyVox cube. + * var entity = Entities.addEntity({ + * type: "PolyVox", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -8 })), + * dimensions: { x: 2, y: 2, z: 2 }, + * voxelVolumeSize: { x: 16, y: 16, z: 16 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + * Entities.setAllVoxels(entity, 1); + */ + // FIXME move to a renderable entity interface Q_INVOKABLE bool setAllVoxels(QUuid entityID, int value); - Q_INVOKABLE bool setVoxelsInCuboid(QUuid entityID, const glm::vec3& lowPosition, - const glm::vec3& cuboidSize, int value); - Q_INVOKABLE bool setAllPoints(QUuid entityID, const QVector& points); - Q_INVOKABLE bool appendPoint(QUuid entityID, const glm::vec3& point); - - Q_INVOKABLE void dumpTree() const; - - Q_INVOKABLE QUuid addAction(const QString& actionTypeString, const QUuid& entityID, const QVariantMap& arguments); - Q_INVOKABLE bool updateAction(const QUuid& entityID, const QUuid& actionID, const QVariantMap& arguments); - Q_INVOKABLE bool deleteAction(const QUuid& entityID, const QUuid& actionID); - Q_INVOKABLE QVector getActionIDs(const QUuid& entityID); - Q_INVOKABLE QVariantMap getActionArguments(const QUuid& entityID, const QUuid& actionID); + /**jsdoc + * Set the values of all voxels in a cubic portion of a {@link Entities.EntityType|PolyVox} entity. + * @function Entities.setVoxelsInCuboid + * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|PolyVox} entity. + * @param {Vec3} lowPosition - The position of the minimum axes value corner of the cube of voxels to set, in voxel + * coordinates. + * @param {Vec3} cuboidSize - The size of the cube of voxels to set, in voxel coordinates. + * @param {number} value - If value % 256 == 0 then each voxel is cleared, otherwise each voxel is set. + * @example Create a PolyVox cube and clear the voxels in one corner. + * var polyVox = Entities.addEntity({ + * type: "PolyVox", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -8 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 2, y: 2, z: 2 }, + * voxelVolumeSize: { x: 16, y: 16, z: 16 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + * Entities.setAllVoxels(polyVox, 1); + * var cuboidPosition = { x: 12, y: 12, z: 12 }; + * var cuboidSize = { x: 4, y: 4, z: 4 }; + * Entities.setVoxelsInCuboid(polyVox, cuboidPosition, cuboidSize, 0); + */ + // FIXME move to a renderable entity interface + Q_INVOKABLE bool setVoxelsInCuboid(QUuid entityID, const glm::vec3& lowPosition, const glm::vec3& cuboidSize, int value); + /**jsdoc + * Convert voxel coordinates in a {@link Entities.EntityType|PolyVox} entity to world coordinates. Voxel coordinates are + * relative to the minimum axes values corner of the entity with a scale of Vec3.ONE being the dimensions of + * each voxel. + * @function Entities.voxelCoordsToWorldCoords + * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|PolyVox} entity. + * @param {Vec3} voxelCoords - The voxel coordinates. May be fractional and outside the entity's bounding box. + * @returns {Vec3} The world coordinates of the voxelCoords if the entityID is a + * {@link Entities.EntityType|PolyVox} entity, otherwise {@link Vec3|Vec3.ZERO}. + * @example Create a PolyVox cube with the 0,0,0 voxel replaced by a sphere. + * // Cube PolyVox with 0,0,0 voxel missing. + * var polyVox = Entities.addEntity({ + * type: "PolyVox", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -8 })), + * dimensions: { x: 2, y: 2, z: 2 }, + * voxelVolumeSize: { x: 16, y: 16, z: 16 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + * Entities.setAllVoxels(polyVox, 1); + * Entities.setVoxel(polyVox, { x: 0, y: 0, z: 0 }, 0); + * + * // Red sphere in 0,0,0 corner position. + * var cornerPosition = Entities.voxelCoordsToWorldCoords(polyVox, { x: 0, y: 0, z: 0 }); + * var voxelDimensions = Vec3.multiply(2 / 16, Vec3.ONE); + * var sphere = Entities.addEntity({ + * type: "Sphere", + * position: Vec3.sum(cornerPosition, Vec3.multiply(0.5, voxelDimensions)), + * dimensions: voxelDimensions, + * color: { red: 255, green: 0, blue: 0 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + */ // FIXME move to a renderable entity interface Q_INVOKABLE glm::vec3 voxelCoordsToWorldCoords(const QUuid& entityID, glm::vec3 voxelCoords); + + /**jsdoc + * Convert world coordinates to voxel coordinates in a {@link Entities.EntityType|PolyVox} entity. Voxel coordinates are + * relative to the minimum axes values corner of the entity, with a scale of Vec3.ONE being the dimensions of + * each voxel. + * @function Entities.worldCoordsToVoxelCoords + * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|PolyVox} entity. + * @param {Vec3} worldCoords - The world coordinates. May be outside the entity's bounding box. + * @returns {Vec3} The voxel coordinates of the worldCoords if the entityID is a + * {@link Entities.EntityType|PolyVox} entity, otherwise {@link Vec3|Vec3.ZERO}. The value may be fractional. + */ + // FIXME move to a renderable entity interface Q_INVOKABLE glm::vec3 worldCoordsToVoxelCoords(const QUuid& entityID, glm::vec3 worldCoords); + + /**jsdoc + * Convert voxel coordinates in a {@link Entities.EntityType|PolyVox} entity to local coordinates relative to the minimum + * axes value corner of the entity, with the scale being the same as world coordinates. + * @function Entities.voxelCoordsToLocalCoords + * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|PolyVox} entity. + * @param {Vec3} voxelCoords - The voxel coordinates. May be fractional and outside the entity's bounding box. + * @returns {Vec3} The local coordinates of the voxelCoords if the entityID is a + * {@link Entities.EntityType|PolyVox} entity, otherwise {@link Vec3|Vec3.ZERO}. + * @example Get the world dimensions of a voxel in a PolyVox entity. + * var polyVox = Entities.addEntity({ + * type: "PolyVox", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.5, z: -8 })), + * dimensions: { x: 2, y: 2, z: 2 }, + * voxelVolumeSize: { x: 16, y: 16, z: 16 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + * var voxelDimensions = Entities.voxelCoordsToLocalCoords(polyVox, Vec3.ONE); + * print("Voxel dimensions: " + JSON.stringify(voxelDimensions)); + */ + // FIXME move to a renderable entity interface Q_INVOKABLE glm::vec3 voxelCoordsToLocalCoords(const QUuid& entityID, glm::vec3 voxelCoords); + + /**jsdoc + * Convert local coordinates to voxel coordinates in a {@link Entities.EntityType|PolyVox} entity. Local coordinates are + * relative to the minimum axes value corner of the entity, with the scale being the same as world coordinates. + * @function Entities.localCoordsToVoxelCoords + * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|PolyVox} entity. + * @param {Vec3} localCoords - The local coordinates. May be outside the entity's bounding box. + * @returns {Vec3} The voxel coordinates of the worldCoords if the entityID is a + * {@link Entities.EntityType|PolyVox} entity, otherwise {@link Vec3|Vec3.ZERO}. The value may be fractional. + */ + // FIXME move to a renderable entity interface Q_INVOKABLE glm::vec3 localCoordsToVoxelCoords(const QUuid& entityID, glm::vec3 localCoords); + /**jsdoc + * Set the linePoints property of a {@link Entities.EntityType|Line} entity. + * @function Entities.setAllPoints + * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|Line} entity. + * @param {Vec3[]} points - The array of points to set the entity's linePoints property to. + * @returns {boolean} true if the entity's property was updated, otherwise false. The property + * may fail to be updated if the entity does not exist, the entity is not a {@link Entities.EntityType|Line} entity, + * one of the points is outside the entity's dimensions, or the number of points is greater than the maximum allowed. + * @example Change the shape of a Line entity. + * // Draw a horizontal line between two points. + * var entity = Entities.addEntity({ + * type: "Line", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -5 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 2, y: 2, z: 1 }, + * linePoints: [ + * { x: -1, y: 0, z: 0 }, + * { x:1, y: -0, z: 0 } + * ], + * color: { red: 255, green: 0, blue: 0 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + * + * // Change the line to be a "V". + * Script.setTimeout(function () { + * Entities.setAllPoints(entity, [ + * { x: -1, y: 1, z: 0 }, + * { x: 0, y: -1, z: 0 }, + * { x: 1, y: 1, z: 0 }, + * ]); + * }, 2000); + */ + Q_INVOKABLE bool setAllPoints(QUuid entityID, const QVector& points); + + /**jsdoc + * Append a point to a {@link Entities.EntityType|Line} entity. + * @function Entities.appendPoint + * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|Line} entity. + * @param {Vec3} point - The point to add to the line. The coordinates are relative to the entity's position. + * @returns {boolean} true if the point was added to the line, otherwise false. The point may + * fail to be added if the entity does not exist, the entity is not a {@link Entities.EntityType|Line} entity, the + * point is outside the entity's dimensions, or the maximum number of points has been reached. + * @example Append a point to a Line entity. + * // Draw a line between two points. + * var entity = Entities.addEntity({ + * type: "Line", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -5 })), + * rotation: MyAvatar.orientation, + * dimensions: { x: 2, y: 2, z: 1 }, + * linePoints: [ + * { x: -1, y: 1, z: 0 }, + * { x: 0, y: -1, z: 0 } + * ], + * color: { red: 255, green: 0, blue: 0 }, + * lifetime: 300 // Delete after 5 minutes. + * }); + * + * // Add a third point to create a "V". + * Entities.appendPoint(entity, { x: 1, y: 1, z: 0 }); + */ + Q_INVOKABLE bool appendPoint(QUuid entityID, const glm::vec3& point); + + /**jsdoc + * Dumps debug information about all entities in Interface's local in-memory tree of entities it knows about — domain + * and client-only — to the program log. + * @function Entities.dumpTree + */ + Q_INVOKABLE void dumpTree() const; + + + /**jsdoc + * Add an action to an entity. An action is registered with the physics engine and is applied every physics simulation + * step. Any entity may have more than one action associated with it, but only as many as will fit in an entity's + * actionData property. + * @function Entities.addAction + * @param {Entities.ActionType} actionType - The type of action. + * @param {Uuid} entityID - The ID of the entity to add the action to. + * @param {Entities.ActionArguments} arguments - Configure the action. + * @returns {Uuid} The ID of the action added if successfully added, otherwise null. + * @example Constrain a cube to move along a vertical line. + * var entityID = Entities.addEntity({ + * type: "Box", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0.75, z: -5 })), + * dimensions: { x: 0.5, y: 0.5, z: 0.5 }, + * dynamic: true, + * collisionless: false, + * userData: "{ \"grabbableKey\": { \"grabbable\": true, \"kinematic\": false } }", + * lifetime: 300 // Delete after 5 minutes. + * }); + * + * var actionID = Entities.addAction("slider", entityID, { + * axis: { x: 0, y: 1, z: 0 }, + * linearLow: 0, + * linearHigh: 0.6 + * }); + */ + Q_INVOKABLE QUuid addAction(const QString& actionTypeString, const QUuid& entityID, const QVariantMap& arguments); + + /**jsdoc + * Update an entity action. + * @function Entities.updateAction + * @param {Uuid} entityID - The ID of the entity with the action to update. + * @param {Uuid} actionID - The ID of the action to update. + * @param {Entities.ActionArguments} arguments - The arguments to update. + * @returns {boolean} true if the update was successful, otherwise false. + */ + Q_INVOKABLE bool updateAction(const QUuid& entityID, const QUuid& actionID, const QVariantMap& arguments); + + /**jsdoc + * Delete an action from an entity. + * @function Entities.deleteAction + * @param {Uuid} entityID - The ID of entity to delete the action from. + * @param {Uuid} actionID - The ID of the action to delete. + * @returns {boolean} true if the update was successful, otherwise false. + */ + Q_INVOKABLE bool deleteAction(const QUuid& entityID, const QUuid& actionID); + + /**jsdoc + * Get the IDs of the actions that are associated with an entity. + * @function Entities.getActionIDs + * @param {Uuid} entityID - The entity to get the action IDs for. + * @returns {Uuid[]} An array of action IDs if any are found, otherwise an empty array. + */ + Q_INVOKABLE QVector getActionIDs(const QUuid& entityID); + + /**jsdoc + * Get the arguments of an action. + * @function Entities.getActionArguments + * @param {Uuid} entityID - The ID of the entity with the action. + * @param {Uuid} actionID - The ID of the action to get the arguments of. + * @returns {Entities.ActionArguments} The arguments of the requested action if found, otherwise an empty object. + */ + Q_INVOKABLE QVariantMap getActionArguments(const QUuid& entityID, const QUuid& actionID); + + + /**jsdoc + * Get the translation of a joint in a {@link Entities.EntityType|Model} entity relative to the entity's position and + * orientation. + * @function Entities.getAbsoluteJointTranslationInObjectFrame + * @param {Uuid} entityID - The ID of the entity. + * @param {number} jointIndex - The integer index of the joint. + * @returns {Vec3} The translation of the joint relative to the entity's position and orientation if the entity is a + * {@link Entities.EntityType|Model} entity, the entity is loaded, and the joint index is valid; otherwise + * {@link Vec3(0)|Vec3.ZERO}. + */ // FIXME move to a renderable entity interface Q_INVOKABLE glm::vec3 getAbsoluteJointTranslationInObjectFrame(const QUuid& entityID, int jointIndex); + + /**jsdoc + * Get the translation of a joint in a {@link Entities.EntityType|Model} entity relative to the entity's position and + * orientation. + * @function Entities.getAbsoluteJointRotationInObjectFrame + * @param {Uuid} entityID - The ID of the entity. + * @param {number} jointIndex - The integer index of the joint. + * @returns {Quat} The rotation of the joint relative to the entity's orientation if the entity is a + * {@link Entities.EntityType|Model} entity, the entity is loaded, and the joint index is valid; otherwise + * {@link Quat(0)|Quat.IDENTITY}. + * @example Compare the local and absolute rotations of an avatar model's left hand joint. + * entityID = Entities.addEntity({ + * type: "Model", + * modelURL: "https://hifi-content.s3.amazonaws.com/milad/production/Examples/Models/Avatars/blue_suited.fbx", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * rotation: MyAvatar.orientation, + * lifetime: 300 // Delete after 5 minutes. + * }); + * + * Script.setTimeout(function () { + * // Joint data aren't available until after the model has loaded. + * var index = Entities.getJointIndex(entityID, "LeftHand"); + * var localRotation = Entities.getLocalJointRotation(entityID, index); + * var absoluteRotation = Entities.getAbsoluteJointRotationInObjectFrame(entityID, index); + * print("Left hand local rotation: " + JSON.stringify(Quat.safeEulerAngles(localRotation))); + * print("Left hand absolute rotation: " + JSON.stringify(Quat.safeEulerAngles(absoluteRotation))); + * }, 2000); + */ + // FIXME move to a renderable entity interface Q_INVOKABLE glm::quat getAbsoluteJointRotationInObjectFrame(const QUuid& entityID, int jointIndex); + + /**jsdoc + * Set the translation of a joint in a {@link Entities.EntityType|Model} entity relative to the entity's position and + * orientation. + * @function Entities.setAbsoluteJointTranslationInObjectFrame + * @param {Uuid} entityID - The ID of the entity. + * @param {number} jointIndex - The integer index of the joint. + * @param {Vec3} translation - The translation to set the joint to relative to the entity's position and orientation. + * @returns {boolean} trueif the entity is a {@link Entities.EntityType|Model} entity, the entity is loaded, + * the joint index is valid, and the translation is different to the joint's current translation; otherwise + * false. + */ + // FIXME move to a renderable entity interface Q_INVOKABLE bool setAbsoluteJointTranslationInObjectFrame(const QUuid& entityID, int jointIndex, glm::vec3 translation); + + /**jsdoc + * Set the rotation of a joint in a {@link Entities.EntityType|Model} entity relative to the entity's position and + * orientation. + * @function Entities.setAbsoluteJointRotationInObjectFrame + * @param {Uuid} entityID - The ID of the entity. + * @param {number} jointIndex - The integer index of the joint. + * @param {Quat} rotation - The rotation to set the joint to relative to the entity's orientation. + * @returns {boolean} true if the entity is a {@link Entities.EntityType|Model} entity, the entity is loaded, + * the joint index is valid, and the rotation is different to the joint's current rotation; otherwise false. + * @example Raise an avatar model's left palm. + * entityID = Entities.addEntity({ + * type: "Model", + * modelURL: "https://hifi-content.s3.amazonaws.com/milad/production/Examples/Models/Avatars/blue_suited.fbx", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * rotation: MyAvatar.orientation, + * lifetime: 300 // Delete after 5 minutes. + * }); + * + * Script.setTimeout(function () { + * // Joint data aren't available until after the model has loaded. + * var index = Entities.getJointIndex(entityID, "LeftHand"); + * var absoluteRotation = Entities.getAbsoluteJointRotationInObjectFrame(entityID, index); + * absoluteRotation = Quat.multiply(Quat.fromPitchYawRollDegrees(0, 0, 90), absoluteRotation); + * var success = Entities.setAbsoluteJointRotationInObjectFrame(entityID, index, absoluteRotation); + * print("Success: " + success); + * }, 2000); + */ + // FIXME move to a renderable entity interface Q_INVOKABLE bool setAbsoluteJointRotationInObjectFrame(const QUuid& entityID, int jointIndex, glm::quat rotation); + + /**jsdoc + * Get the local translation of a joint in a {@link Entities.EntityType|Model} entity. + * @function Entities.getLocalJointTranslation + * @param {Uuid} entityID - The ID of the entity. + * @param {number} jointIndex - The integer index of the joint. + * @returns {Vec3} The local translation of the joint if the entity is a {@link Entities.EntityType|Model} entity, the + * entity is loaded, and the joint index is valid; otherwise {@link Vec3(0)|Vec3.ZERO}. + */ // FIXME move to a renderable entity interface Q_INVOKABLE glm::vec3 getLocalJointTranslation(const QUuid& entityID, int jointIndex); + + /**jsdoc + * Get the local rotation of a joint in a {@link Entities.EntityType|Model} entity. + * @function Entities.getLocalJointRotation + * @param {Uuid} entityID - The ID of the entity. + * @param {number} jointIndex - The integer index of the joint. + * @returns {Quat} The local rotation of the joint if the entity is a {@link Entities.EntityType|Model} entity, the entity + * is loaded, and the joint index is valid; otherwise {@link Quat(0)|Quat.IDENTITY}. + * @example Report the local rotation of an avatar model's head joint. + * entityID = Entities.addEntity({ + * type: "Model", + * modelURL: "https://hifi-content.s3.amazonaws.com/milad/production/Examples/Models/Avatars/blue_suited.fbx", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * rotation: MyAvatar.orientation, + * lifetime: 300 // Delete after 5 minutes. + * }); + * + * Script.setTimeout(function () { + * // Joint data aren't available until after the model has loaded. + * var index = Entities.getJointIndex(entityID, "Head"); + * var rotation = Entities.getLocalJointRotation(entityID, index); + * print("Head local rotation: " + JSON.stringify(Quat.safeEulerAngles(rotation))); + * }, 2000); + */ + // FIXME move to a renderable entity interface Q_INVOKABLE glm::quat getLocalJointRotation(const QUuid& entityID, int jointIndex); + + /**jsdoc + * Set the local translation of a joint in a {@link Entities.EntityType|Model} entity. + * @function Entities.setLocalJointTranslation + * @param {Uuid} entityID - The ID of the entity. + * @param {number} jointIndex - The integer index of the joint. + * @param {Vec3} translation - The local translation to set the joint to. + * @returns {boolean} trueif the entity is a {@link Entities.EntityType|Model} entity, the entity is loaded, + * the joint index is valid, and the translation is different to the joint's current translation; otherwise + * false. + */ + // FIXME move to a renderable entity interface Q_INVOKABLE bool setLocalJointTranslation(const QUuid& entityID, int jointIndex, glm::vec3 translation); + + /**jsdoc + * Set the local rotation of a joint in a {@link Entities.EntityType|Model} entity. + * @function Entities.setLocalJointRotation + * @param {Uuid} entityID - The ID of the entity. + * @param {number} jointIndex - The integer index of the joint. + * @param {Quat} rotation - The local rotation to set the joint to. + * @returns {boolean} true if the entity is a {@link Entities.EntityType|Model} entity, the entity is loaded, + * the joint index is valid, and the rotation is different to the joint's current rotation; otherwise false. + * @example Make an avatar model turn its head left. + * entityID = Entities.addEntity({ + * type: "Model", + * modelURL: "https://hifi-content.s3.amazonaws.com/milad/production/Examples/Models/Avatars/blue_suited.fbx", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * rotation: MyAvatar.orientation, + * lifetime: 300 // Delete after 5 minutes. + * }); + * + * Script.setTimeout(function () { + * // Joint data aren't available until after the model has loaded. + * var index = Entities.getJointIndex(entityID, "Head"); + * var rotation = Quat.fromPitchYawRollDegrees(0, 60, 0); + * var success = Entities.setLocalJointRotation(entityID, index, rotation); + * print("Success: " + success); + * }, 2000); + */ + // FIXME move to a renderable entity interface Q_INVOKABLE bool setLocalJointRotation(const QUuid& entityID, int jointIndex, glm::quat rotation); + + /**jsdoc + * Set the local translations of joints in a {@link Entities.EntityType|Model} entity. + * @function Entities.setLocalJointTranslations + * @param {Uuid} entityID - The ID of the entity. + * @param {Vec3[]} translations - The local translations to set the joints to. + * @returns {boolean} trueif the entity is a {@link Entities.EntityType|Model} entity, the entity is loaded, + * the model has joints, and at least one of the translations is different to the model's current translations; + * otherwise false. + */ + // FIXME move to a renderable entity interface + Q_INVOKABLE bool setLocalJointTranslations(const QUuid& entityID, const QVector& translations); + + /**jsdoc + * Set the local rotations of joints in a {@link Entities.EntityType|Model} entity. + * @function Entities.setLocalJointRotations + * @param {Uuid} entityID - The ID of the entity. + * @param {Quat[]} rotations - The local rotations to set the joints to. + * @returns {boolean} true if the entity is a {@link Entities.EntityType|Model} entity, the entity is loaded, + * the model has joints, and at least one of the rotations is different to the model's current rotations; otherwise + * false. + * @example Raise both palms of an avatar model. + * entityID = Entities.addEntity({ + * type: "Model", + * modelURL: "https://hifi-content.s3.amazonaws.com/milad/production/Examples/Models/Avatars/blue_suited.fbx", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * rotation: MyAvatar.orientation, + * lifetime: 300 // Delete after 5 minutes. + * }); + * + * Script.setTimeout(function () { + * // Joint data aren't available until after the model has loaded. + * + * // Get all the joint rotations. + * var jointNames = Entities.getJointNames(entityID); + * var jointRotations = []; + * for (var i = 0, length = jointNames.length; i < length; i++) { + * var index = Entities.getJointIndex(entityID, jointNames[i]); + * jointRotations.push(Entities.getLocalJointRotation(entityID, index)); + * } + * + * // Raise both palms. + * var index = jointNames.indexOf("LeftHand"); + * jointRotations[index] = Quat.multiply(Quat.fromPitchYawRollDegrees(-90, 0, 0), jointRotations[index]); + * index = jointNames.indexOf("RightHand"); + * jointRotations[index] = Quat.multiply(Quat.fromPitchYawRollDegrees(-90, 0, 0), jointRotations[index]); + * + * // Update all the joint rotations. + * var success = Entities.setLocalJointRotations(entityID, jointRotations); + * print("Success: " + success); + * }, 2000); + */ // FIXME move to a renderable entity interface Q_INVOKABLE bool setLocalJointRotations(const QUuid& entityID, const QVector& rotations); - Q_INVOKABLE bool setLocalJointTranslations(const QUuid& entityID, const QVector& translations); + + /**jsdoc + * Set the local rotations and translations of joints in a {@link Entities.EntityType|Model} entity. This is the same as + * calling both {@link Entities.setLocalJointRotations|setLocalJointRotations} and + * {@link Entities.setLocalJointTranslations|setLocalJointTranslations} at the same time. + * @function Entities.setLocalJointsData + * @param {Uuid} entityID - The ID of the entity. + * @param {Quat[]} rotations - The local rotations to set the joints to. + * @param {Vec3[]} translations - The local translations to set the joints to. + * @returns {boolean} true if the entity is a {@link Entities.EntityType|Model} entity, the entity is loaded, + * the model has joints, and at least one of the rotations or translations is different to the model's current values; + * otherwise false. + */ + // FIXME move to a renderable entity interface Q_INVOKABLE bool setLocalJointsData(const QUuid& entityID, const QVector& rotations, const QVector& translations); + + /**jsdoc + * Get the index of a named joint in a {@link Entities.EntityType|Model} entity. + * @function Entities.getJointIndex + * @param {Uuid} entityID - The ID of the entity. + * @param {string} name - The name of the joint. + * @returns {number} The integer index of the joint if the entity is a {@link Entities.EntityType|Model} entity, the entity + * is loaded, and the joint is present; otherwise -1. The joint indexes are in order per + * {@link Entities.getJointNames|getJointNames}. + * @example Report the index of a model's head joint. + * entityID = Entities.addEntity({ + * type: "Model", + * modelURL: "https://hifi-content.s3.amazonaws.com/milad/production/Examples/Models/Avatars/blue_suited.fbx", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * rotation: MyAvatar.orientation, + * lifetime: 300 // Delete after 5 minutes. + * }); + * + * Script.setTimeout(function () { + * // Joint data aren't available until after the model has loaded. + * var index = Entities.getJointIndex(entityID, "Head"); + * print("Head joint index: " + index); + * }, 2000); + */ // FIXME move to a renderable entity interface Q_INVOKABLE int getJointIndex(const QUuid& entityID, const QString& name); + + /**jsdoc + * Get the names of all the joints in a {@link Entities.EntityType|Model} entity. + * @function Entities.getJointNames + * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|Model} entity. + * @returns {string[]} The names of all the joints in the entity if it is a {@link Entities.EntityType|Model} entity and + * is loaded, otherwise an empty array. The joint names are in order per {@link Entities.getJointIndex|getJointIndex}. + * @example Report a model's joint names. + * entityID = Entities.addEntity({ + * type: "Model", + * modelURL: "https://hifi-content.s3.amazonaws.com/milad/production/Examples/Models/Avatars/blue_suited.fbx", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * rotation: MyAvatar.orientation, + * lifetime: 300 // Delete after 5 minutes. + * }); + * + * Script.setTimeout(function () { + * // Joint data aren't available until after the model has loaded. + * var jointNames = Entities.getJointNames(entityID); + * print("Joint names: " + JSON.stringify(jointNames)); + * }, 2000); + */ + // FIXME move to a renderable entity interface Q_INVOKABLE QStringList getJointNames(const QUuid& entityID); + /**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. + * @function Entities.getChildrenIDs + * @param {Uuid} parentID - The ID of the entity to get the children IDs of. + * @returns {Uuid[]} An array of entity, overlay, and avatar IDs that are parented directly to the parentID + * entity. Does not include children's children, etc. The array is empty if no children can be found or + * parentID cannot be found. + * @example Report the children of an entity. + * function createEntity(description, position, parent) { + * var entity = Entities.addEntity({ + * type: "Sphere", + * position: position, + * dimensions: Vec3.HALF, + * parentID: parent, + * lifetime: 300 // Delete after 5 minutes. + * }); + * print(description + ": " + entity); + * return entity; + * } + * + * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 2, z: -5 })); + * var root = createEntity("Root", position, Uuid.NULL); + * var child = createEntity("Child", Vec3.sum(position, { x: 0, y: -1, z: 0 }), root); + * var grandChild = createEntity("Grandchild", Vec3.sum(position, { x: 0, y: -2, z: 0 }), child); + * + * var children = Entities.getChildrenIDs(root); + * print("Children of root: " + JSON.stringify(children)); // Only the child entity. + */ Q_INVOKABLE QVector getChildrenIDs(const QUuid& parentID); + + /**jsdoc + * Get the IDs of entities, overlays, and avatars that are directly parented to an entity, overlay, or avatar model's joint. + * @function Entities.getChildrenIDsOfJoint + * @param {Uuid} parentID - The ID of the entity, overlay, or avatar to get the children IDs of. + * @param {number} jointIndex - Integer number of the model joint to get the children IDs of. + * @returns {Uuid[]} An array of entity, overlay, and avatar IDs that are parented directly to the parentID + * entity, overlay, or avatar at the jointIndex joint. Does not include children's children, etc. The + * array is empty if no children can be found or parentID cannot be found. + * @example Report the children of your avatar's right hand. + * function createEntity(description, position, parent) { + * var entity = Entities.addEntity({ + * type: "Sphere", + * position: position, + * dimensions: Vec3.HALF, + * parentID: parent, + * lifetime: 300 // Delete after 5 minutes. + * }); + * print(description + ": " + entity); + * return entity; + * } + * + * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 2, z: -5 })); + * var root = createEntity("Root", position, Uuid.NULL); + * var child = createEntity("Child", Vec3.sum(position, { x: 0, y: -1, z: 0 }), root); + * + * Entities.editEntity(root, { + * parentID: MyAvatar.sessionUUID, + * parentJointIndex: MyAvatar.getJointIndex("RightHand") + * }); + * + * var children = Entities.getChildrenIDsOfJoint(MyAvatar.sessionUUID, MyAvatar.getJointIndex("RightHand")); + * print("Children of hand: " + JSON.stringify(children)); // Only the root entity. + */ Q_INVOKABLE QVector getChildrenIDsOfJoint(const QUuid& parentID, int jointIndex); + + /**jsdoc + * Check whether an entity or overlay has an entity as an ancestor (parent, parent's parent, etc.). + * @function Entities.isChildOfParent + * @param {Uuid} childID - The ID of the child entity or overlay to test for being a child, grandchild, etc. + * @param {Uuid} parentID - The ID of the parent entity to test for being a parent, grandparent, etc. + * @returns {boolean} true if the childID entity or overlay has the parentID entity + * as a parent or grandparent etc., otherwise false. + * @example Check that a grandchild entity is a child of its grandparent. + * function createEntity(description, position, parent) { + * var entity = Entities.addEntity({ + * type: "Sphere", + * position: position, + * dimensions: Vec3.HALF, + * parentID: parent, + * lifetime: 300 // Delete after 5 minutes. + * }); + * print(description + ": " + entity); + * return entity; + * } + * + * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 2, z: -5 })); + * var root = createEntity("Root", position, Uuid.NULL); + * var child = createEntity("Child", Vec3.sum(position, { x: 0, y: -1, z: 0 }), root); + * var grandChild = createEntity("Grandchild", Vec3.sum(position, { x: 0, y: -2, z: 0 }), child); + * + * print("grandChild has root as parent: " + Entities.isChildOfParent(grandChild, root)); // true + */ Q_INVOKABLE bool isChildOfParent(QUuid childID, QUuid parentID); - Q_INVOKABLE QString getNestableType(QUuid id); + /**jsdoc + * Get the type — entity, overlay, or avatar — of an in-world item. + * @function Entities.getNestableType + * @param {Uuid} entityID - The ID of the item to get the type of. + * @returns {string} The type of the item: "entity" if the item is an entity, "overlay" if the + * the item is an overlay, "avatar" if the item is an avatar; otherwise "unknown" if the item + * cannot be found. + * @example Print some nestable types. + * var entity = Entities.addEntity({ + * type: "Sphere", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 1, z: -2 })), + * lifetime: 300 // Delete after 5 minutes. + * }); + * + * print(Entities.getNestableType(entity)); // "entity" + * print(Entities.getNestableType(Uuid.generate())); // "unknown" + */ + Q_INVOKABLE QString getNestableType(QUuid entityID); + /**jsdoc + * Get the ID of the {@link Entities.EntityType|Web} entity that has keyboard focus. + * @function Entities.getKeyboardFocusEntity + * @returns {Uuid} The ID of the {@link Entities.EntityType|Web} entity that has focus, if any, otherwise null. + */ Q_INVOKABLE QUuid getKeyboardFocusEntity() const; + + /**jsdoc + * Set the {@link Entities.EntityType|Web} entity that has keyboard focus. + * @function Entities.setKeyboardFocusEntity + * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|Web} entity to set keyboard focus to. Use + * null or {@link Uuid|Uuid.NULL} to unset keyboard focus from an entity. + */ Q_INVOKABLE void setKeyboardFocusEntity(const EntityItemID& id); + + /**jsdoc + * Emit a {@link Entities.mousePressOnEntity|mousePressOnEntity} event. + * @function Entities.sendMousePressOnEntity + * @param {Uuid} entityID - The ID of the entity to emit the event for. + * @param {PointerEvent} event - The event details. + */ Q_INVOKABLE void sendMousePressOnEntity(const EntityItemID& id, const PointerEvent& event); + + /**jsdoc + * Emit a {@link Entities.mouseMoveOnEntity|mouseMoveOnEntity} event. + * @function Entities.sendMouseMoveOnEntity + * @param {Uuid} entityID - The ID of the entity to emit the event for. + * @param {PointerEvent} event - The event details. + */ Q_INVOKABLE void sendMouseMoveOnEntity(const EntityItemID& id, const PointerEvent& event); + + /**jsdoc + * Emit a {@link Entities.mouseReleaseOnEntity|mouseReleaseOnEntity} event. + * @function Entities.sendMouseReleaseOnEntity + * @param {Uuid} entityID - The ID of the entity to emit the event for. + * @param {PointerEvent} event - The event details. + */ Q_INVOKABLE void sendMouseReleaseOnEntity(const EntityItemID& id, const PointerEvent& event); + /**jsdoc + * Emit a {@link Entities.clickDownOnEntity|clickDownOnEntity} event. + * @function Entities.sendClickDownOnEntity + * @param {Uuid} entityID - The ID of the entity to emit the event for. + * @param {PointerEvent} event - The event details. + */ Q_INVOKABLE void sendClickDownOnEntity(const EntityItemID& id, const PointerEvent& event); + + /**jsdoc + * Emit a {@link Entities.holdingClickOnEntity|holdingClickOnEntity} event. + * @function Entities.sendHoldingClickOnEntity + * @param {Uuid} entityID - The ID of the entity to emit the event for. + * @param {PointerEvent} event - The event details. + */ Q_INVOKABLE void sendHoldingClickOnEntity(const EntityItemID& id, const PointerEvent& event); + + /**jsdoc + * Emit a {@link Entities.clickReleaseOnEntity|clickReleaseOnEntity} event. + * @function Entities.sendClickReleaseOnEntity + * @param {Uuid} entityID - The ID of the entity to emit the event for. + * @param {PointerEvent} event - The event details. + */ Q_INVOKABLE void sendClickReleaseOnEntity(const EntityItemID& id, const PointerEvent& event); + /**jsdoc + * Emit a {@link Entities.hoverEnterEntity|hoverEnterEntity} event. + * @function Entities.sendHoverEnterEntity + * @param {Uuid} entityID - The ID of the entity to emit the event for. + * @param {PointerEvent} event - The event details. + */ Q_INVOKABLE void sendHoverEnterEntity(const EntityItemID& id, const PointerEvent& event); + + /**jsdoc + * Emit a {@link Entities.hoverOverEntity|hoverOverEntity} event. + * @function Entities.sendHoverOverEntity + * @param {Uuid} entityID - The ID of the entity to emit the event for. + * @param {PointerEvent} event - The event details. + */ Q_INVOKABLE void sendHoverOverEntity(const EntityItemID& id, const PointerEvent& event); + + /**jsdoc + * Emit a {@link Entities.hoverLeaveEntity|hoverLeaveEntity} event. + * @function Entities.sendHoverLeaveEntity + * @param {Uuid} entityID - The ID of the entity to emit the event for. + * @param {PointerEvent} event - The event details. + */ Q_INVOKABLE void sendHoverLeaveEntity(const EntityItemID& id, const PointerEvent& event); + /**jsdoc + * Check whether an entity wants hand controller pointer events. For example, a {@link Entities.EntityType|Web} entity does + * but a {@link Entities.EntityType|Shape} entity doesn't. + * @function Entities.wantsHandControllerPointerEvents + * @param {Uuid} entityID - The ID of the entity. + * @returns {boolean} true if the entity can be found and it wants hand controller pointer events, otherwise + * false. + */ Q_INVOKABLE bool wantsHandControllerPointerEvents(QUuid id); + /**jsdoc + * Send a script event over a {@link Entities.EntityType|Web} entity's EventBridge to the Web page's scripts. + * @function Entities.emitScriptEvent + * @param {Uuid} entityID - The ID of the {@link Entities.EntityType|Web} entity. + * @param {string} message - The message to send. + * @todo This function is currently not implemented. + */ Q_INVOKABLE void emitScriptEvent(const EntityItemID& entityID, const QVariant& message); + /**jsdoc + * Check whether an axis-aligned box and a capsule intersect. + * @function Entities.AABoxIntersectsCapsule + * @param {Vec3} brn - The bottom right near (minimum axes values) corner of the AA box. + * @param {Vec3} dimensions - The dimensions of the AA box. + * @param {Vec3} start - One end of the capsule. + * @param {Vec3} end - The other end of the capsule. + * @param {number} radius - The radiues of the capsule. + * @returns {boolean} true if the AA box and capsule intersect, otherwise false. + */ Q_INVOKABLE bool AABoxIntersectsCapsule(const glm::vec3& low, const glm::vec3& dimensions, const glm::vec3& start, const glm::vec3& end, float radius); + /**jsdoc + * Get the meshes in a {@link Entities.EntityType|Model} or {@link Entities.EntityType|PolyVox} entity. + * @function Entities.getMeshes + * @param {Uuid} entityID - The ID of the Model or PolyVox entity to get the meshes of. + * @param {Entities~getMeshesCallback} callback - The function to call upon completion. + * @deprecated Use the {@link Graphics} API instead. + */ + /**jsdoc + * Called when {@link Entities.getMeshes} is complete. + * @callback Entities~getMeshesCallback + * @param {MeshProxy[]} meshes - If success< is true, a {@link MeshProxy} per mesh in the + * Model or PolyVox entity; otherwise undefined. + * @param {boolean} success - true if the {@link Entities.getMeshes} call was successful, false + * otherwise. The call may be unsuccessful if the requested entity could not be found. + * @deprecated Use the {@link Graphics} API instead. + */ // FIXME move to a renderable entity interface Q_INVOKABLE void getMeshes(QUuid entityID, QScriptValue callback); /**jsdoc - * Returns object to world transform, excluding scale - * + * Get the object to world transform, excluding scale, of an entity. * @function Entities.getEntityTransform - * @param {EntityID} entityID The ID of the entity whose transform is to be returned - * @return {Mat4} Entity's object to world transform, excluding scale + * @param {Uuid} entityID - The ID of the entity. + * @returns {Mat4} The entity's object to world transform excluding scale (i.e., translation and rotation, with scale of 1) + * if the entity can be found, otherwise a transform with zero translation and rotation and a scale of 1. + * @example Position and rotation in an entity's world transform. + * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 1, z: -2 })); + * var orientation = MyAvatar.orientation; + * print("Position: " + JSON.stringify(position)); + * print("Orientation: " + JSON.stringify(orientation)); + * + * var entityID = Entities.addEntity({ + * type: "Sphere", + * position: position, + * rotation: orientation, + * dimensions: Vec3.HALF, + * lifetime: 300 // Delete after 5 minutes. + * }); + * + * var transform = Entities.getEntityTransform(entityID); + * print("Transform: " + JSON.stringify(transform)); + * print("Translation: " + JSON.stringify(Mat4.extractTranslation(transform))); // Same as position. + * print("Rotation: " + JSON.stringify(Mat4.extractRotation(transform))); // Same as orientation. + * print("Scale: " + JSON.stringify(Mat4.extractScale(transform))); // { x: 1, y: 1, z: 1 } */ Q_INVOKABLE glm::mat4 getEntityTransform(const QUuid& entityID); - /**jsdoc - * Returns object to world transform, excluding scale - * + * Get the object to parent transform, excluding scale, of an entity. * @function Entities.getEntityLocalTransform - * @param {EntityID} entityID The ID of the entity whose local transform is to be returned - * @return {Mat4} Entity's object to parent transform, excluding scale - */ + * @param {Uuid} entityID - The ID of the entity. + * @returns {Mat4} The entity's object to parent transform excluding scale (i.e., translation and rotation, with scale of + * 1) if the entity can be found, otherwise a transform with zero translation and rotation and a scale of 1. + * @example Position and rotation in an entity's local transform. + * function createEntity(position, rotation, parent) { + * var entity = Entities.addEntity({ + * type: "Box", + * position: position, + * rotation: rotation, + * dimensions: Vec3.HALF, + * parentID: parent, + * lifetime: 300 // Delete after 5 minutes. + * }); + * return entity; + * } + * + * var position = Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 2, z: -5 })); + * + * var parent = createEntity(position, MyAvatar.orientation, Uuid.NULL); + * + * var childTranslation = { x: 0, y: -1.5, z: 0 }; + * var childRotation = Quat.fromPitchYawRollDegrees(0, 45, 0); + * var child = createEntity(Vec3.sum(position, childTranslation), Quat.multiply(childRotation, MyAvatar.orientation), parent); + * + * var transform = Entities.getEntityLocalTransform(child); + * print("Transform: " + JSON.stringify(transform)); + * print("Translation: " + JSON.stringify(Mat4.extractTranslation(transform))); // childTranslation + * print("Rotation: " + JSON.stringify(Quat.safeEulerAngles(Mat4.extractRotation(transform)))); // childRotation + * print("Scale: " + JSON.stringify(Mat4.extractScale(transform))); // { x: 1, y: 1, z: 1 } */ Q_INVOKABLE glm::mat4 getEntityLocalTransform(const QUuid& entityID); - /**jsdoc - * Return the Static Certificate JSON for the specified {EntityID}. - * @return {QByteArray} The Static Certificate JSON for the specified entity. + * Get the static certificate for an entity. The static certificate contains static properties of the item which cannot + * be altered. + * @function Entities.getStaticCertificateJSON + * @param {Uuid} entityID - The ID of the entity to get the static certificate for. + * @returns {string} The entity's static certificate as a JSON string if the entity can be found, otherwise an empty string. */ Q_INVOKABLE QString getStaticCertificateJSON(const QUuid& entityID); + + /**jsdoc + * Verify the entity's proof of provenance, i.e., that the entity's certificateID property was produced by + * High Fidelity signing the entity's static certificate JSON. + * @function Entities.verifyStaticCertificateProperties + * @param {Uuid} entityID - The ID of the entity to verify. + * @returns {boolean} true if the entity can be found an its certificateID property is present + * and its value matches the entity's static certificate JSON; otherwise false. + */ Q_INVOKABLE bool verifyStaticCertificateProperties(const QUuid& entityID); signals: + /**jsdoc + * Triggered on the client that is the physics simulation owner during the collision of two entities. Note: Isn't triggered + * for a collision with an avatar. + * @function Entities.collisionWithEntity + * @param {Uuid} idA - The ID of one entity in the collision. For an entity script, this is the ID of the entity containing + * the script. + * @param {Uuid} idB - The ID of the other entity in the collision. + * @param {Collision} collision - The details of the collision. + * @returns {Signal} + * @example Change the color of an entity when it collides with another entity. + * var entityScript = (function () { + * function randomInteger(min, max) { + * return Math.floor(Math.random() * (max - min + 1)) + min; + * } + * + * this.collisionWithEntity = function (myID, otherID, collision) { + * Entities.editEntity(myID, { + * color: { + * red: randomInteger(128, 255), + * green: randomInteger(128, 255), + * blue: randomInteger(128, 255) + * } + * }); + * }; + * }); + * + * var entityID = Entities.addEntity({ + * type: "Box", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * dimensions: { x: 0.5, y: 0.5, z: 0.5 }, + * color: { red: 128, green: 128, blue: 128 }, + * gravity: { x: 0, y: -9.8, z: 0 }, + * velocity: { x: 0, y: 0.1, z: 0 }, // Kick off physics. + * dynamic: true, + * collisionless: false, // So that collision events are generated. + * script: "(" + entityScript + ")", // Could host the script on a Web server instead. + * lifetime: 300 // Delete after 5 minutes. + * }); + */ void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); + /**jsdoc + * Triggered when your ability to change the locked property of entities changes. + * @function Entities.canAdjustLocksChanged + * @param {boolean} canAdjustLocks - true if the script can change the locked property of an + * entity, otherwise false. + * @returns {Signal} + * @example Report when your ability to change locks changes. + * function onCanAdjustLocksChanged(canAdjustLocks) { + * print("You can adjust entity locks: " + canAdjustLocks); + * } + * Entities.canAdjustLocksChanged.connect(onCanAdjustLocksChanged); + */ void canAdjustLocksChanged(bool canAdjustLocks); + + /**jsdoc + * Triggered when your ability to rez (create) entities changes. + * @function Entities.canRezChanged + * @param {boolean} canRez - true if the script can rez (create) entities, otherwise false. + * @returns {Signal} + */ void canRezChanged(bool canRez); - void canRezTmpChanged(bool canRez); - void canRezCertifiedChanged(bool canRez); - void canRezTmpCertifiedChanged(bool canRez); + + /**jsdoc + * Triggered when your ability to rez (create) temporary entities changes. Temporary entities are entities with a finite + * lifetime property value set. + * @function Entities.canRezTmpChanged + * @param {boolean} canRezTmp - true if the script can rez (create) temporary entities, otherwise + * false. + * @returns {Signal} + */ + void canRezTmpChanged(bool canRezTmp); + + /**jsdoc + * Triggered when your ability to rez (create) certified entities changes. Certified entities are entities that have PoP + * certificates. + * @function Entities.canRezCertifiedChanged + * @param {boolean} canRezCertified - true if the script can rez (create) certified entities, otherwise + * false. + * @returns {Signal} + */ + void canRezCertifiedChanged(bool canRezCertified); + + /**jsdoc + * Triggered when your ability to rez (create) temporary certified entities changes. Temporary entities are entities with a + * finite lifetime property value set. Certified entities are entities that have PoP certificates. + * @function Entities.canRezTmpCertifiedChanged + * @param {boolean} canRezTmpCertified - true if the script can rez (create) temporary certified entities, + * otherwise false. + * @returns {Signal} + */ + void canRezTmpCertifiedChanged(bool canRezTmpCertified); + + /**jsdoc + * Triggered when your ability to make changes to the asset server's assets changes. + * @function Entities.canWriteAssetsChanged + * @param {boolean} canWriteAssets - true if the script can change the ? property of an entity, + * otherwise false. + * @returns {Signal} + */ void canWriteAssetsChanged(bool canWriteAssets); + + /**jsdoc + * Triggered when a mouse button is clicked while the mouse cursor is on an entity, or a controller trigger is fully + * pressed while its laser is on an entity. + * @function Entities.mousePressOnEntity + * @param {Uuid} entityID - The ID of the entity that was pressed. + * @param {PointerEvent} event - Details of the event. + * @returns {Signal} + * @example Report when an entity is clicked with the mouse or laser. + * function onMousePressOnEntity(entityID, event) { + * print("Clicked on entity: " + entityID); + * } + * + * Entities.mousePressOnEntity.connect(onMousePressOnEntity); + */ void mousePressOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); + + /**jsdoc + * Triggered when a mouse button is double-clicked while the mouse cursor is on an entity. + * @function Entities.mousePressOnEntity + * @param {Uuid} entityID - The ID of the entity that was double-pressed. + * @param {PointerEvent} event - Details of the event. + * @returns {Signal} + */ void mouseDoublePressOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); + + /**jsdoc + * Repeatedly triggered while the mouse cursor or controller laser moves on an entity. + * @function Entities.mouseMoveOnEntity + * @param {Uuid} entityID - The ID of the entity that was moved on. + * @param {PointerEvent} event - Details of the event. + * @returns {Signal} + */ void mouseMoveOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); + + /**jsdoc + * Triggered when a mouse button is released after clicking on an entity or the controller trigger is partly or fully + * released after pressing on an entity, even if the mouse pointer or controller laser has moved off the entity. + * @function Entities.mouseReleaseOnEntity + * @param {Uuid} entityID - The ID of the entity that was originally pressed. + * @param {PointerEvent} event - Details of the event. + * @returns {Signal} + */ void mouseReleaseOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); + + /**jsdoc + * Triggered when a mouse button is clicked while the mouse cursor is not on an entity. + * @function Entities.mousePressOffEntity + * @param {PointerEvent} event - Details of the event. + * @returns {Signal} + */ void mousePressOffEntity(); + + /**jsdoc + * Triggered when a mouse button is double-clicked while the mouse cursor is not on an entity. + * @function Entities.mouseDoublePressOffEntity + * @param {PointerEvent} event - Details of the event. + * @returns {Signal} + */ void mouseDoublePressOffEntity(); + + /**jsdoc + * Triggered when a mouse button is clicked while the mouse cursor is on an entity. Note: Not triggered by controller. + * @function Entities.clickDownOnEntity + * @param {Uuid} entityID - The ID of the entity that was clicked. + * @param {PointerEvent} event - Details of the event. + * @returns {Signal} + */ void clickDownOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); + + /**jsdoc + * Repeatedly triggered while a mouse button continues to be held after clicking an entity, even if the mouse cursor has + * moved off the entity. Note: Not triggered by controller. + * @function Entities.holdingClickOnEntity + * @param {Uuid} entityID - The ID of the entity that was originally clicked. + * @param {PointerEvent} event - Details of the event. + * @returns {Signal} + */ void holdingClickOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); + + /**jsdoc + * Triggered when a mouse button is released after clicking on an entity, even if the mouse cursor has moved off the + * entity. Note: Not triggered by controller. + * @function Entities.clickReleaseOnEntity + * @param {Uuid} entityID - The ID of the entity that was originally clicked. + * @param {PointerEvent} event - Details of the event. + * @returns {Signal} + */ void clickReleaseOnEntity(const EntityItemID& entityItemID, const PointerEvent& event); + /**jsdoc + * Triggered when the mouse cursor or controller laser starts hovering on an entity. + * @function Entities.hoverEnterEntity + * @param {Uuid} entityID - The ID of the entity that is being hovered. + * @param {PointerEvent} event - Details of the event. + * @returns {Signal} + */ void hoverEnterEntity(const EntityItemID& entityItemID, const PointerEvent& event); + + /**jsdoc + * Repeatedly triggered when the mouse cursor or controller laser moves while hovering over an entity. + * @function Entities.hoverOverEntity + * @param {Uuid} entityID - The ID of the entity that is being hovered. + * @param {PointerEvent} event - Details of the event. + * @returns {Signal} + */ void hoverOverEntity(const EntityItemID& entityItemID, const PointerEvent& event); + + /**jsdoc + * Triggered when the mouse cursor or controller laser stops hovering over an entity. + * @function Entities.hoverLeaveEntity + * @param {Uuid} entityID - The ID of the entity that was being hovered. + * @param {PointerEvent} event - Details of the event. + * @returns {Signal} + */ void hoverLeaveEntity(const EntityItemID& entityItemID, const PointerEvent& event); + + /**jsdoc + * Triggered when an avatar enters an entity. + * @function Entities.enterEntity + * @param {Uuid} entityID - The ID of the entity that the avatar entered. + * @returns {Signal} + * @example Change the color of an entity when an avatar enters or leaves. + * var entityScript = (function () { + * this.enterEntity = function (entityID) { + * print("Enter entity"); + * Entities.editEntity(entityID, { + * color: { red: 255, green: 64, blue: 64 }, + * }); + * }; + * this.leaveEntity = function (entityID) { + * print("Leave entity"); + * Entities.editEntity(entityID, { + * color: { red: 128, green: 128, blue: 128 }, + * }); + * }; + * }); + * + * var entityID = Entities.addEntity({ + * type: "Sphere", + * position: Vec3.sum(MyAvatar.position, Vec3.multiplyQbyV(MyAvatar.orientation, { x: 0, y: 0, z: -5 })), + * dimensions: { x: 3, y: 3, z: 3 }, + * color: { red: 128, green: 128, blue: 128 }, + * collisionless: true, // So that avatar can walk through entity. + * script: "(" + entityScript + ")", // Could host the script on a Web server instead. + * lifetime: 300 // Delete after 5 minutes. + * }); + */ void enterEntity(const EntityItemID& entityItemID); + + /**jsdoc + * Triggered when an avatar leaves an entity. + * @function Entities.leaveEntity + * @param {Uuid} entityID - The ID of the entity that the avatar left. + * @returns {Signal} + */ void leaveEntity(const EntityItemID& entityItemID); + + /**jsdoc + * Triggered when an entity is deleted. + * @function Entities.deletingEntity + * @param {Uuid} entityID - The ID of the entity deleted. + * @returns {Signal} + * @example Report when an entity is deleted. + * Entities.deletingEntity.connect(function (entityID) { + * print("Deleted entity: " + entityID); + * }); + */ void deletingEntity(const EntityItemID& entityID); + + /**jsdoc + * Triggered when an entity is added to Interface's local in-memory tree of entities it knows about. This may occur when + * entities are loaded upon visiting a domain, when the user rotates their view so that more entities become visible, and + * when a domain or client-only entity is added (e.g., by {@Entities.addEntity|addEntity}). + * @function Entities.addingEntity + * @param {Uuid} entityID - The ID of the entity added. + * @returns {Signal} + * @example Report when an entity is added. + * Entities.addingEntity.connect(function (entityID) { + * print("Added entity: " + entityID); + * }); + */ void addingEntity(const EntityItemID& entityID); + + /**jsdoc + * Triggered when you disconnect from a domain, at which time Interface's local in-memory tree of entities it knows about + * is cleared. + * @function Entities.clearingEntities + * @returns {Signal} + * @example Report when Interfaces's entity tree is cleared. + * Entities.clearingEntities.connect(function () { + * print("Entities cleared"); + * }); + */ void clearingEntities(); + + /**jsdoc + * @function Entities.debitEnergySource + * @param {number} value - The amount to debit. + * @returns {Signal} + * @deprecated This function is deprecated and will soon be removed. + */ void debitEnergySource(float value); + /**jsdoc + * Triggered in when a script in a {@link Entities.EntityType|Web} entity's Web page script sends an event over the + * script's EventBridge. + * @function Entities.webEventReceived + * @param {Uuid} entityID - The ID of the entity that event was received from. + * @param {string} message - The message received. + * @returns {Signal} + */ void webEventReceived(const EntityItemID& entityItemID, const QVariant& message); protected: diff --git a/libraries/entities/src/EntityTypes.h b/libraries/entities/src/EntityTypes.h index 62011c6e26..0e2fca8180 100644 --- a/libraries/entities/src/EntityTypes.h +++ b/libraries/entities/src/EntityTypes.h @@ -35,6 +35,56 @@ typedef EntityItemPointer (*EntityTypeFactory)(const EntityItemID& entityID, con class EntityTypes { public: + /**jsdoc + *

An entity may be one of the following types:

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ValueDescriptionProperties
"Box"A rectangular prism. This is a synonym of "Shape" for the case + * where the entity's shape property value is "Cube".
+ * If an entity is created with its type + * set to "Box" it will always be created with a shape property value of + * "Cube". If an entity of type Shape or Sphere has its shape set + * to "Cube" then its type will be reported as "Box". + *
{@link Entities.EntityProperties-Box|EntityProperties-Box}
"Light"A local lighting effect.{@link Entities.EntityProperties-Light|EntityProperties-Light}
"Line"A sequence of one or more simple straight lines.{@link Entities.EntityProperties-Line|EntityProperties-Line}
"Material"Modifies the existing materials on Model entities, Shape entities (albedo + * only), {@link Overlays.OverlayType|model overlays}, and avatars.{@link Entities.EntityProperties-Material|EntityProperties-Material}
"Model"A mesh model from an FBX or OBJ file.{@link Entities.EntityProperties-Model|EntityProperties-Model}
"ParticleEffect"A particle system that can be used to simulate things such as fire, + * smoke, snow, magic spells, etc.{@link Entities.EntityProperties-ParticleEffect|EntityProperties-ParticleEffect}
"PolyLine"A sequence of one or more textured straight lines.{@link Entities.EntityProperties-PolyLine|EntityProperties-PolyLine}
"PolyVox"A set of textured voxels.{@link Entities.EntityProperties-PolyVox|EntityProperties-PolyVox}
"Shape"A basic entity such as a cube. + * See also, the "Box" and "Sphere" entity types.{@link Entities.EntityProperties-Shape|EntityProperties-Shape}
"Sphere"A sphere. This is a synonym of "Shape" for the case + * where the entity's shape property value is "Sphere".
+ * If an entity is created with its type + * set to "Sphere" it will always be created with a shape property value of + * "Sphere". If an entity of type Box or Shape has its shape set + * to "Sphere" then its type will be reported as "Sphere". + *
{@link Entities.EntityProperties-Sphere|EntityProperties-Sphere}
"Text"A pane of text oriented in space.{@link Entities.EntityProperties-Text|EntityProperties-Text}
"Web"A browsable Web page.{@link Entities.EntityProperties-Web|EntityProperties-Web}
"Zone"A volume of lighting effects and avatar permissions.{@link Entities.EntityProperties-Zone|EntityProperties-Zone}
+ * @typedef {string} Entities.EntityType + */ typedef enum EntityType_t { Unknown, Model, diff --git a/libraries/entities/src/HazePropertyGroup.h b/libraries/entities/src/HazePropertyGroup.h index 939391caf9..e992aefbf3 100644 --- a/libraries/entities/src/HazePropertyGroup.h +++ b/libraries/entities/src/HazePropertyGroup.h @@ -40,6 +40,35 @@ static const float INITIAL_HAZE_BACKGROUND_BLEND{ 0.0f }; static const float INITIAL_KEY_LIGHT_RANGE{ 1000.0f }; static const float INITIAL_KEY_LIGHT_ALTITUDE{ 200.0f }; +// FIXME: Document hazeAttenuationKeyLight, hazeKeyLightRange, and hazeKeyLightAltitude once they're working and are provided +// in the Create app's UI. +/**jsdoc + * Haze is defined by the following properties. + * @typedef {object} Entities.Haze + * + * @property {number} hazeRange=1000 - The horizontal distance at which visibility is reduced to 95%; i.e., 95% of each pixel's + * color is haze. + * @property {Color} hazeColor=128,154,179 - The color of the haze when looking away from the key light. + * @property {boolean} hazeEnableGlare=false - If true then the haze is colored with glare from the key light; + * hazeGlareColor and hazeGlareAngle are used. + * @property {Color} hazeGlareColor=255,299,179 - The color of the haze when looking towards the key light. + * @property {number} hazeGlareAngle=20 - The angle in degrees across the circle around the key light that the glare color and + * haze color are blended 50/50. + * + * @property {boolean} hazeAltitudeEffect=false - If true then haze decreases with altitude as defined by the + * entity's local coordinate system; hazeBaseRef and
hazeCeiling
are used. + * @property {number} hazeBaseRef=0 - The y-axis value in the entity's local coordinate system at which the haze density starts + * reducing with altitude. + * @property {number} hazeCeiling=200 - The y-axis value in the entity's local coordinate system at which the haze density has + * reduced to 5%. + * + * @property {number} hazeBackgroundBlend=0 - The proportion of the skybox image to show through the haze: 0.0 + * displays no skybox image; 1.0 displays no haze. + * + * @property {boolean} hazeAttenuateKeyLight=false - Currently not supported. + * @property {number} hazeKeyLightRange=1000 - Currently not supported. + * @property {number} hazeKeyLightAltitude=200 - Currently not supported. + */ class HazePropertyGroup : public PropertyGroup { public: // EntityItemProperty related helpers diff --git a/libraries/entities/src/KeyLightPropertyGroup.h b/libraries/entities/src/KeyLightPropertyGroup.h index 5e13a6afa6..2be33787de 100644 --- a/libraries/entities/src/KeyLightPropertyGroup.h +++ b/libraries/entities/src/KeyLightPropertyGroup.h @@ -27,6 +27,16 @@ class OctreePacketData; class EntityTreeElementExtraEncodeData; class ReadBitstreamToTreeParams; +/**jsdoc + * A key light is defined by the following properties. + * @typedef {object} Entities.KeyLight + * @property {Color} color=255,255,255 - The color of the light. + * @property {number} intensity=1 - The intensity of the light. + * @property {Vec3} direction=0,-1,0 - The direction the light is shining. + * @property {boolean} castShadows=false - If true then shadows are cast. Shadows are cast by avatars, plus + * {@link Entities.EntityType|Model} and {@link Entities.EntityType|Shape} entities that have their + * {@link Entities.EntityProperties|canCastShadows} property set to true. + */ class KeyLightPropertyGroup : public PropertyGroup { public: // EntityItemProperty related helpers diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index 90982fe448..0ddfe3e8cc 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -59,6 +59,25 @@ class PolyVoxEntityItem : public EntityItem { virtual int getOnCount() const { return 0; } + /**jsdoc + *

A PolyVoxSurfaceStyle may be one of the following:

+ * + * + * + * + * + * + * + * + * + * + *
ValueTypeDescription
0Marching cubes.Chamfered edges. Open volume. + * Joins neighboring PolyVox entities reasonably well.
1Cubic.Square edges. Open volume. + * Joins neighboring PolyVox entities cleanly.
2Edged cubic.Square edges. Enclosed volume. + * Joins neighboring PolyVox entities cleanly.
3Edged marching cubes.Chamfered edges. Enclosed volume. + * Doesn't join neighboring PolyVox entities.
+ * @typedef {number} Entities.PolyVoxSurfaceStyle + */ enum PolyVoxSurfaceStyle { SURFACE_MARCHING_CUBES, SURFACE_CUBIC, diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index db3d6798be..60ba429938 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -20,6 +20,33 @@ #include "ShapeEntityItem.h" namespace entity { + + /**jsdoc + *

A Shape, Box, or Sphere {@link Entities.EntityType|EntityType} may display as + * one of the following geometrical shapes:

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ValueDimensionsNotes
"Circle"2DA circle oriented in 3D.
"Cube"3D
"Cone"3D
"Cylinder"3D
"Dodecahedron"3D
"Hexagon"3DA hexagonal prism.
"Icosahedron"3D
"Octagon"3DAn octagonal prism.
"Octahedron"3D
"Quad"2DA square oriented in 3D.
"Sphere"3D
"Tetrahedron"3D
"Torus"3DNot implemented.
"Triangle"3DA triangular prism.
+ * @typedef {string} Entities.Shape + */ static const std::array shapeStrings { { "Triangle", "Quad", @@ -32,7 +59,7 @@ namespace entity { "Octahedron", "Dodecahedron", "Icosahedron", - "Torus", + "Torus", // Not implemented yet. "Cone", "Cylinder" } }; diff --git a/libraries/entities/src/ShapeEntityItem.h b/libraries/entities/src/ShapeEntityItem.h index 46d696f979..97202afcf7 100644 --- a/libraries/entities/src/ShapeEntityItem.h +++ b/libraries/entities/src/ShapeEntityItem.h @@ -105,7 +105,7 @@ public: protected: - float _alpha { 1 }; + float _alpha { 1 }; // FIXME: This property is not used. rgbColor _color; entity::Shape _shape { entity::Shape::Sphere }; diff --git a/libraries/entities/src/SkyboxPropertyGroup.h b/libraries/entities/src/SkyboxPropertyGroup.h index 8298a7b74e..d7b422bf11 100644 --- a/libraries/entities/src/SkyboxPropertyGroup.h +++ b/libraries/entities/src/SkyboxPropertyGroup.h @@ -27,6 +27,13 @@ class OctreePacketData; class EntityTreeElementExtraEncodeData; class ReadBitstreamToTreeParams; +/**jsdoc + * A skybox is defined by the following properties. + * @typedef {object} Entities.Skybox + * @property {Color} color=0,0,0 - Sets the color of the sky if url is "", otherwise modifies the + * color of the cube map image. + * @property {string} url="" - A cube map image that is used to render the sky. + */ class SkyboxPropertyGroup : public PropertyGroup { public: // EntityItemProperty related helpers diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp index 08bd20be66..b4e4656dc7 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.cpp @@ -99,12 +99,16 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] = (&::gpu::gl::GLBackend::do_setUniformBuffer), (&::gpu::gl::GLBackend::do_setResourceBuffer), (&::gpu::gl::GLBackend::do_setResourceTexture), + (&::gpu::gl::GLBackend::do_setResourceFramebufferSwapChainTexture), (&::gpu::gl::GLBackend::do_setFramebuffer), + (&::gpu::gl::GLBackend::do_setFramebufferSwapChain), (&::gpu::gl::GLBackend::do_clearFramebuffer), (&::gpu::gl::GLBackend::do_blit), (&::gpu::gl::GLBackend::do_generateTextureMips), + (&::gpu::gl::GLBackend::do_advance), + (&::gpu::gl::GLBackend::do_beginQuery), (&::gpu::gl::GLBackend::do_endQuery), (&::gpu::gl::GLBackend::do_getQuery), @@ -756,9 +760,13 @@ void GLBackend::recycle() const { Texture::KtxStorage::releaseOpenKtxFiles(); } -void GLBackend::setCameraCorrection(const Mat4& correction) { +void GLBackend::setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool reset) { + auto invCorrection = glm::inverse(correction); + auto invPrevView = glm::inverse(prevRenderView); + _transform._correction.prevView = (reset ? Mat4() : prevRenderView); + _transform._correction.prevViewInverse = (reset ? Mat4() : invPrevView); _transform._correction.correction = correction; - _transform._correction.correctionInverse = glm::inverse(correction); + _transform._correction.correctionInverse = invCorrection; _pipeline._cameraCorrectionBuffer._buffer->setSubData(0, _transform._correction); _pipeline._cameraCorrectionBuffer._buffer->flush(); } diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackend.h b/libraries/gpu-gl/src/gpu/gl/GLBackend.h index 18916ac18c..14a3d202f2 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gl/src/gpu/gl/GLBackend.h @@ -68,7 +68,7 @@ public: virtual ~GLBackend(); - void setCameraCorrection(const Mat4& correction); + void setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool reset = false); void render(const Batch& batch) final override; // This call synchronize the Full Backend cache with the current GLState @@ -126,15 +126,19 @@ public: // Resource Stage virtual void do_setResourceBuffer(const Batch& batch, size_t paramOffset) final; virtual void do_setResourceTexture(const Batch& batch, size_t paramOffset) final; + virtual void do_setResourceFramebufferSwapChainTexture(const Batch& batch, size_t paramOffset) final; // Pipeline Stage virtual void do_setPipeline(const Batch& batch, size_t paramOffset) final; // Output stage virtual void do_setFramebuffer(const Batch& batch, size_t paramOffset) final; + virtual void do_setFramebufferSwapChain(const Batch& batch, size_t paramOffset) final; virtual void do_clearFramebuffer(const Batch& batch, size_t paramOffset) final; virtual void do_blit(const Batch& batch, size_t paramOffset) = 0; + virtual void do_advance(const Batch& batch, size_t paramOffset) final; + // Query section virtual void do_beginQuery(const Batch& batch, size_t paramOffset) final; virtual void do_endQuery(const Batch& batch, size_t paramOffset) final; @@ -245,6 +249,8 @@ protected: void setupStereoSide(int side); #endif + virtual void setResourceTexture(unsigned int slot, const TexturePointer& resourceTexture); + virtual void setFramebuffer(const FramebufferPointer& framebuffer); virtual void initInput() final; virtual void killInput() final; virtual void syncInputStateCache() final; @@ -303,9 +309,12 @@ protected: // Allows for correction of the camera pose to account for changes // between the time when a was recorded and the time(s) when it is // executed + // Prev is the previous correction used at previous frame struct CameraCorrection { - Mat4 correction; - Mat4 correctionInverse; + mat4 correction; + mat4 correctionInverse; + mat4 prevView; + mat4 prevViewInverse; }; struct TransformStageState { diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp index 358b90ed8b..59e77ebe90 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendOutput.cpp @@ -37,6 +37,19 @@ void GLBackend::resetOutputStage() { void GLBackend::do_setFramebuffer(const Batch& batch, size_t paramOffset) { auto framebuffer = batch._framebuffers.get(batch._params[paramOffset]._uint); + setFramebuffer(framebuffer); +} + +void GLBackend::do_setFramebufferSwapChain(const Batch& batch, size_t paramOffset) { + auto swapChain = batch._swapChains.get(batch._params[paramOffset]._uint); + if (swapChain) { + auto index = batch._params[paramOffset + 1]._uint; + FramebufferPointer framebuffer = static_cast(swapChain.get())->get(index); + setFramebuffer(framebuffer); + } +} + +void GLBackend::setFramebuffer(const FramebufferPointer& framebuffer) { if (_output._framebuffer != framebuffer) { auto newFBO = getFramebufferID(framebuffer); if (_output._drawFBO != newFBO) { @@ -47,6 +60,13 @@ void GLBackend::do_setFramebuffer(const Batch& batch, size_t paramOffset) { } } +void GLBackend::do_advance(const Batch& batch, size_t paramOffset) { + auto ringbuffer = batch._swapChains.get(batch._params[paramOffset]._uint); + if (ringbuffer) { + ringbuffer->advance(); + } +} + void GLBackend::do_clearFramebuffer(const Batch& batch, size_t paramOffset) { if (_stereo.isStereo() && !_pipeline._stateCache.scissorEnable) { qWarning("Clear without scissor in stereo mode"); diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp index 7ef64343ea..237b8bc1e9 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendPipeline.cpp @@ -253,6 +253,31 @@ void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { releaseResourceTexture(slot); return; } + setResourceTexture(slot, resourceTexture); +} + +void GLBackend::do_setResourceFramebufferSwapChainTexture(const Batch& batch, size_t paramOffset) { + GLuint slot = batch._params[paramOffset + 1]._uint; + if (slot >= (GLuint)MAX_NUM_RESOURCE_TEXTURES) { + qCDebug(gpugllogging) << "GLBackend::do_setResourceFramebufferSwapChainTexture: Trying to set a resource Texture at slot #" << slot << " which doesn't exist. MaxNumResourceTextures = " << getMaxNumResourceTextures(); + return; + } + + SwapChainPointer swapChain = batch._swapChains.get(batch._params[paramOffset + 0]._uint); + + if (!swapChain) { + releaseResourceTexture(slot); + return; + } + auto index = batch._params[paramOffset + 2]._uint; + auto renderBufferSlot = batch._params[paramOffset + 3]._uint; + FramebufferPointer resourceFramebuffer = static_cast(swapChain.get())->get(index); + TexturePointer resourceTexture = resourceFramebuffer->getRenderBuffer(renderBufferSlot); + + setResourceTexture(slot, resourceTexture); +} + +void GLBackend::setResourceTexture(unsigned int slot, const TexturePointer& resourceTexture) { // check cache before thinking if (_resource._textures[slot] == resourceTexture) { return; @@ -269,11 +294,11 @@ void GLBackend::do_setResourceTexture(const Batch& batch, size_t paramOffset) { glActiveTexture(GL_TEXTURE0 + slot); glBindTexture(target, to); - (void) CHECK_GL_ERROR(); + (void)CHECK_GL_ERROR(); _resource._textures[slot] = resourceTexture; - _stats._RSAmountTextureMemoryBounded += (int) object->size(); + _stats._RSAmountTextureMemoryBounded += (int)object->size(); } else { releaseResourceTexture(slot); diff --git a/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp b/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp index f286a5cca9..72aaa5aa66 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLBackendTransform.cpp @@ -105,7 +105,7 @@ void GLBackend::TransformStageState::preUpdate(size_t commandIndex, const Stereo if (_viewIsCamera && (_viewCorrectionEnabled && _correction.correction != glm::mat4())) { // FIXME should I switch to using the camera correction buffer in Transform.slf and leave this out? Transform result; - _view.mult(result, _view, _correction.correction); + _view.mult(result, _view, _correction.correctionInverse); if (_skybox) { result.setTranslation(vec3()); } diff --git a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp index 1873034ac5..560b7337c7 100644 --- a/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp +++ b/libraries/gpu-gl/src/gpu/gl/GLTexelFormat.cpp @@ -146,7 +146,51 @@ GLenum GLTexelFormat::evalGLTexelFormatInternal(const gpu::Element& dstFormat) { case gpu::RGB: case gpu::RGBA: case gpu::XY: - result = GL_RG8; + switch (dstFormat.getType()) { + case gpu::UINT32: + result = GL_RG32UI; + break; + case gpu::INT32: + result = GL_RG32I; + break; + case gpu::FLOAT: + result = GL_RG32F; + break; + case gpu::UINT16: + result = GL_RG16UI; + break; + case gpu::INT16: + result = GL_RG16I; + break; + case gpu::NUINT16: + result = GL_RG16; + break; + case gpu::NINT16: + result = GL_RG16_SNORM; + break; + case gpu::HALF: + result = GL_RG16F; + break; + case gpu::UINT8: + result = GL_RG8UI; + break; + case gpu::INT8: + result = GL_RG8I; + break; + case gpu::NUINT8: + result = GL_RG8; + break; + case gpu::NINT8: + result = GL_RG8_SNORM; + break; + case gpu::NUINT32: + case gpu::NINT32: + case gpu::NUINT2: + case gpu::NINT2_10_10_10: + case gpu::COMPRESSED: + case gpu::NUM_TYPES: // quiet compiler + Q_UNREACHABLE(); + } break; default: qCWarning(gpugllogging) << "Unknown combination of texel format"; @@ -581,7 +625,52 @@ GLTexelFormat GLTexelFormat::evalGLTexelFormat(const Element& dstFormat, const E case gpu::RGB: case gpu::RGBA: case gpu::XY: - texel.internalFormat = GL_RG8; + switch (dstFormat.getType()) { + case gpu::UINT32: + texel.internalFormat = GL_RG32UI; + break; + case gpu::INT32: + texel.internalFormat = GL_RG32I; + break; + case gpu::FLOAT: + texel.internalFormat = GL_RG32F; + break; + case gpu::UINT16: + texel.internalFormat = GL_RG16UI; + break; + case gpu::INT16: + texel.internalFormat = GL_RG16I; + break; + case gpu::NUINT16: + texel.internalFormat = GL_RG16; + break; + case gpu::NINT16: + texel.internalFormat = GL_RG16_SNORM; + break; + case gpu::HALF: + texel.type = GL_FLOAT; + texel.internalFormat = GL_RG16F; + break; + case gpu::UINT8: + texel.internalFormat = GL_RG8UI; + break; + case gpu::INT8: + texel.internalFormat = GL_RG8I; + break; + case gpu::NUINT8: + texel.internalFormat = GL_RG8; + break; + case gpu::NINT8: + texel.internalFormat = GL_RG8_SNORM; + break; + case gpu::NUINT32: + case gpu::NINT32: + case gpu::NUINT2: + case gpu::NINT2_10_10_10: + case gpu::COMPRESSED: + case gpu::NUM_TYPES: // quiet compiler + Q_UNREACHABLE(); + } break; default: qCWarning(gpugllogging) << "Unknown combination of texel format"; diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp index fc1bc39929..e8e89a1da6 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gles/src/gpu/gl/GLBackend.cpp @@ -749,9 +749,13 @@ void GLBackend::recycle() const { Texture::KtxStorage::releaseOpenKtxFiles(); } -void GLBackend::setCameraCorrection(const Mat4& correction) { +void GLBackend::setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool reset) { + auto invCorrection = glm::inverse(correction); + auto invPrevView = glm::inverse(prevRenderView); + _transform._correction.prevView = (reset ? Mat4() : prevRenderView); + _transform._correction.prevViewInverse = (reset ? Mat4() : invPrevView); _transform._correction.correction = correction; - _transform._correction.correctionInverse = glm::inverse(correction); + _transform._correction.correctionInverse = invCorrection; _pipeline._cameraCorrectionBuffer._buffer->setSubData(0, _transform._correction); _pipeline._cameraCorrectionBuffer._buffer->flush(); } diff --git a/libraries/gpu-gles/src/gpu/gl/GLBackend.h b/libraries/gpu-gles/src/gpu/gl/GLBackend.h index 3681fc0492..33bec07854 100644 --- a/libraries/gpu-gles/src/gpu/gl/GLBackend.h +++ b/libraries/gpu-gles/src/gpu/gl/GLBackend.h @@ -65,7 +65,7 @@ public: virtual ~GLBackend(); - void setCameraCorrection(const Mat4& correction); + void setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool reset = false); void render(const Batch& batch) final override; // This call synchronize the Full Backend cache with the current GLState @@ -303,6 +303,8 @@ protected: struct CameraCorrection { Mat4 correction; Mat4 correctionInverse; + Mat4 prevView; + Mat4 prevViewInverse; }; struct TransformStageState { diff --git a/libraries/gpu/src/gpu/Batch.cpp b/libraries/gpu/src/gpu/Batch.cpp index 7141a7eac7..31bbfdd708 100644 --- a/libraries/gpu/src/gpu/Batch.cpp +++ b/libraries/gpu/src/gpu/Batch.cpp @@ -45,7 +45,12 @@ size_t Batch::_dataMax { BATCH_PREALLOCATE_MIN }; size_t Batch::_objectsMax { BATCH_PREALLOCATE_MIN }; size_t Batch::_drawCallInfosMax { BATCH_PREALLOCATE_MIN }; -Batch::Batch() { +Batch::Batch(const char* name) { +#ifdef DEBUG + if (name) { + _name = name; + } +#endif _commands.reserve(_commandsMax); _commandOffsets.reserve(_commandOffsetsMax); _params.reserve(_paramsMax); @@ -56,6 +61,9 @@ Batch::Batch() { Batch::Batch(const Batch& batch_) { Batch& batch = *const_cast(&batch_); +#ifdef DEBUG + _name = batch_._name; +#endif _commands.swap(batch._commands); _commandOffsets.swap(batch._commandOffsets); _params.swap(batch._params); @@ -71,6 +79,7 @@ Batch::Batch(const Batch& batch_) { _transforms._items.swap(batch._transforms._items); _pipelines._items.swap(batch._pipelines._items); _framebuffers._items.swap(batch._framebuffers._items); + _swapChains._items.swap(batch._swapChains._items); _drawCallInfos.swap(batch._drawCallInfos); _queries._items.swap(batch._queries._items); _lambdas._items.swap(batch._lambdas._items); @@ -108,6 +117,7 @@ void Batch::clear() { _transforms.clear(); _pipelines.clear(); _framebuffers.clear(); + _swapChains.clear(); _objects.clear(); _drawCallInfos.clear(); } @@ -327,6 +337,15 @@ void Batch::setResourceTexture(uint32 slot, const TextureView& view) { setResourceTexture(slot, view._texture); } +void Batch::setResourceFramebufferSwapChainTexture(uint32 slot, const FramebufferSwapChainPointer& framebuffer, unsigned int swapChainIndex, unsigned int renderBufferSlot) { + ADD_COMMAND(setResourceFramebufferSwapChainTexture); + + _params.emplace_back(_swapChains.cache(framebuffer)); + _params.emplace_back(slot); + _params.emplace_back(swapChainIndex); + _params.emplace_back(renderBufferSlot); +} + void Batch::setFramebuffer(const FramebufferPointer& framebuffer) { ADD_COMMAND(setFramebuffer); @@ -334,6 +353,19 @@ void Batch::setFramebuffer(const FramebufferPointer& framebuffer) { } +void Batch::setFramebufferSwapChain(const FramebufferSwapChainPointer& framebuffer, unsigned int swapChainIndex) { + ADD_COMMAND(setFramebufferSwapChain); + + _params.emplace_back(_swapChains.cache(framebuffer)); + _params.emplace_back(swapChainIndex); +} + +void Batch::advance(const SwapChainPointer& swapChain) { + ADD_COMMAND(advance); + + _params.emplace_back(_swapChains.cache(swapChain)); +} + void Batch::clearFramebuffer(Framebuffer::Masks targets, const Vec4& color, float depth, int stencil, bool enableScissor) { ADD_COMMAND(clearFramebuffer); diff --git a/libraries/gpu/src/gpu/Batch.h b/libraries/gpu/src/gpu/Batch.h index 79f08ea5cc..43fdb623e3 100644 --- a/libraries/gpu/src/gpu/Batch.h +++ b/libraries/gpu/src/gpu/Batch.h @@ -91,7 +91,7 @@ public: void captureDrawCallInfo(); void captureNamedDrawCallInfo(std::string name); - Batch(); + Batch(const char* name = nullptr); Batch(const Batch& batch); ~Batch(); @@ -187,11 +187,14 @@ public: void setResourceTexture(uint32 slot, const TexturePointer& texture); void setResourceTexture(uint32 slot, const TextureView& view); // not a command, just a shortcut from a TextureView - + void setResourceFramebufferSwapChainTexture(uint32 slot, const FramebufferSwapChainPointer& framebuffer, unsigned int swpaChainIndex, unsigned int renderBufferSlot = 0U); // not a command, just a shortcut from a TextureView // Ouput Stage void setFramebuffer(const FramebufferPointer& framebuffer); - + void setFramebufferSwapChain(const FramebufferSwapChainPointer& framebuffer, unsigned int swapChainIndex); + + void advance(const SwapChainPointer& swapChain); + // Clear framebuffer layers // Targets can be any of the render buffers contained in the currnetly bound Framebuffer // Optionally the scissor test can be enabled locally for this command and to restrict the clearing command to the pixels contained in the scissor rectangle @@ -299,12 +302,16 @@ public: COMMAND_setUniformBuffer, COMMAND_setResourceBuffer, COMMAND_setResourceTexture, + COMMAND_setResourceFramebufferSwapChainTexture, COMMAND_setFramebuffer, + COMMAND_setFramebufferSwapChain, COMMAND_clearFramebuffer, COMMAND_blit, COMMAND_generateTextureMips, + COMMAND_advance, + COMMAND_beginQuery, COMMAND_endQuery, COMMAND_getQuery, @@ -421,6 +428,7 @@ public: typedef Cache::Vector TransformCaches; typedef Cache::Vector PipelineCaches; typedef Cache::Vector FramebufferCaches; + typedef Cache::Vector SwapChainCaches; typedef Cache::Vector QueryCaches; typedef Cache::Vector StringCaches; typedef Cache>::Vector LambdaCache; @@ -475,6 +483,7 @@ public: TransformCaches _transforms; PipelineCaches _pipelines; FramebufferCaches _framebuffers; + SwapChainCaches _swapChains; QueryCaches _queries; LambdaCache _lambdas; StringCaches _profileRanges; @@ -486,6 +495,11 @@ public: bool _enableSkybox { false }; protected: + +#ifdef DEBUG + std::string _name; +#endif + friend class Context; friend class Frame; diff --git a/libraries/gpu/src/gpu/Buffer.cpp b/libraries/gpu/src/gpu/Buffer.cpp index 7ed2411c71..ebb768e597 100644 --- a/libraries/gpu/src/gpu/Buffer.cpp +++ b/libraries/gpu/src/gpu/Buffer.cpp @@ -98,8 +98,9 @@ Buffer::Update::Update(const Buffer& parent) : buffer(parent) { void Buffer::Update::apply() const { // Make sure we're loaded in order - ++buffer._applyUpdateCount; - assert(buffer._applyUpdateCount.load() == updateNumber); + buffer._applyUpdateCount++; + assert(buffer._applyUpdateCount == updateNumber); + const auto pageSize = buffer._pages._pageSize; buffer._renderSysmem.resize(size); buffer._renderPages.accommodate(size); diff --git a/libraries/gpu/src/gpu/Color.slh b/libraries/gpu/src/gpu/Color.slh index 526cc3dd46..d70e588f4d 100644 --- a/libraries/gpu/src/gpu/Color.slh +++ b/libraries/gpu/src/gpu/Color.slh @@ -11,18 +11,45 @@ <@if not GPU_COLOR_SLH@> <@def GPU_COLOR_SLH@> -float sRGBFloatToLinear(float value) { +// Linear ====> linear RGB +// sRGB ======> standard RGB with gamma of 2.2 +// YCoCg =====> Luma (Y) chrominance green (Cg) and chrominance orange (Co) +// https://software.intel.com/en-us/node/503873 + +float color_scalar_sRGBToLinear(float value) { const float SRGB_ELBOW = 0.04045; return (value <= SRGB_ELBOW) ? value / 12.92 : pow((value + 0.055) / 1.055, 2.4); } -vec3 colorToLinearRGB(vec3 srgb) { - return vec3(sRGBFloatToLinear(srgb.r), sRGBFloatToLinear(srgb.g), sRGBFloatToLinear(srgb.b)); +vec3 color_sRGBToLinear(vec3 srgb) { + return vec3(color_scalar_sRGBToLinear(srgb.r), color_scalar_sRGBToLinear(srgb.g), color_scalar_sRGBToLinear(srgb.b)); } -vec4 colorToLinearRGBA(vec4 srgba) { - return vec4(colorToLinearRGB(srgba.xyz), srgba.w); +vec4 color_sRGBAToLinear(vec4 srgba) { + return vec4(color_sRGBToLinear(srgba.xyz), srgba.w); +} + +vec3 color_LinearToYCoCg(vec3 rgb) { + // Y = R/4 + G/2 + B/4 + // Co = R/2 - B/2 + // Cg = -R/4 + G/2 - B/4 + return vec3( + rgb.x/4.0 + rgb.y/2.0 + rgb.z/4.0, + rgb.x/2.0 - rgb.z/2.0, + -rgb.x/4.0 + rgb.y/2.0 - rgb.z/4.0 + ); +} + +vec3 color_YCoCgToLinear(vec3 ycocg) { + // R = Y + Co - Cg + // G = Y + Cg + // B = Y - Co - Cg + return clamp(vec3( + ycocg.x + ycocg.y - ycocg.z, + ycocg.x + ycocg.z, + ycocg.x - ycocg.y - ycocg.z + ), vec3(0.0), vec3(1.0)); } <@func declareColorWheel()@> diff --git a/libraries/gpu/src/gpu/Context.cpp b/libraries/gpu/src/gpu/Context.cpp index d7d86c3ef7..e1b68c88ca 100644 --- a/libraries/gpu/src/gpu/Context.cpp +++ b/libraries/gpu/src/gpu/Context.cpp @@ -53,11 +53,12 @@ const std::string& Context::getBackendVersion() const { return _backend->getVersion(); } -void Context::beginFrame(const glm::mat4& renderPose) { +void Context::beginFrame(const glm::mat4& renderView, const glm::mat4& renderPose) { assert(!_frameActive); _frameActive = true; _currentFrame = std::make_shared(); _currentFrame->pose = renderPose; + _currentFrame->view = renderView; if (!_frameRangeTimer) { _frameRangeTimer = std::make_shared("gpu::Context::Frame"); @@ -108,7 +109,7 @@ void Context::executeFrame(const FramePointer& frame) const { consumeFrameUpdates(frame); _backend->setStereoState(frame->stereoState); { - Batch beginBatch; + Batch beginBatch("Context::executeFrame::begin"); _frameRangeTimer->begin(beginBatch); _backend->render(beginBatch); @@ -117,7 +118,7 @@ void Context::executeFrame(const FramePointer& frame) const { _backend->render(batch); } - Batch endBatch; + Batch endBatch("Context::executeFrame::end"); _frameRangeTimer->end(endBatch); _backend->render(endBatch); } diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h index 195565f438..2df7de2331 100644 --- a/libraries/gpu/src/gpu/Context.h +++ b/libraries/gpu/src/gpu/Context.h @@ -161,7 +161,7 @@ public: const std::string& getBackendVersion() const; - void beginFrame(const glm::mat4& renderPose = glm::mat4()); + void beginFrame(const glm::mat4& renderView = glm::mat4(), const glm::mat4& renderPose = glm::mat4()); void appendFrameBatch(Batch& batch); FramePointer endFrame(); @@ -274,8 +274,8 @@ protected: typedef std::shared_ptr ContextPointer; template -void doInBatch(std::shared_ptr context, F f) { - gpu::Batch batch; +void doInBatch(const char* name, std::shared_ptr context, F f) { + gpu::Batch batch(name); f(batch); context->appendFrameBatch(batch); } diff --git a/libraries/gpu/src/gpu/Frame.h b/libraries/gpu/src/gpu/Frame.h index 1ed77a26b7..441e3c5b5d 100644 --- a/libraries/gpu/src/gpu/Frame.h +++ b/libraries/gpu/src/gpu/Frame.h @@ -28,6 +28,8 @@ namespace gpu { StereoState stereoState; uint32_t frameIndex{ 0 }; + /// The view matrix used for rendering the frame, only applicable for HMDs + Mat4 view; /// The sensor pose used for rendering the frame, only applicable for HMDs Mat4 pose; /// The collection of batches which make up the frame diff --git a/libraries/gpu/src/gpu/Framebuffer.h b/libraries/gpu/src/gpu/Framebuffer.h index f470cc8aa9..fbbec50a28 100755 --- a/libraries/gpu/src/gpu/Framebuffer.h +++ b/libraries/gpu/src/gpu/Framebuffer.h @@ -12,6 +12,7 @@ #define hifi_gpu_Framebuffer_h #include "Texture.h" +#include "ResourceSwapChain.h" #include class Transform; // Texcood transform util @@ -177,6 +178,8 @@ protected: Framebuffer() {} }; typedef std::shared_ptr FramebufferPointer; +typedef ResourceSwapChain FramebufferSwapChain; +typedef std::shared_ptr FramebufferSwapChainPointer; } diff --git a/libraries/gpu/src/gpu/ResourceSwapChain.h b/libraries/gpu/src/gpu/ResourceSwapChain.h new file mode 100644 index 0000000000..7b46b35521 --- /dev/null +++ b/libraries/gpu/src/gpu/ResourceSwapChain.h @@ -0,0 +1,62 @@ +// +// Created by Olivier Prat on 2018/02/19 +// Copyright 2013-2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#ifndef hifi_gpu_ResourceSwapChain_h +#define hifi_gpu_ResourceSwapChain_h + +#include +#include + +namespace gpu { + class SwapChain { + public: + + SwapChain(unsigned int size = 2U) : _size{ size } {} + virtual ~SwapChain() {} + + void advance() { + _frontIndex = (_frontIndex + 1) % _size; + } + + unsigned int getSize() const { return _size; } + + protected: + unsigned int _size; + unsigned int _frontIndex{ 0U }; + + }; + typedef std::shared_ptr SwapChainPointer; + + template + class ResourceSwapChain : public SwapChain { + public: + + enum { + MAX_SIZE = 4 + }; + + using Type = R; + using TypePointer = std::shared_ptr; + + ResourceSwapChain(unsigned int size = 2U) : SwapChain{ size } {} + + void reset() { + for (auto& ptr : _resources) { + ptr.reset(); + } + } + + TypePointer& edit(unsigned int index) { return _resources[(index + _frontIndex) % _size]; } + const TypePointer& get(unsigned int index) const { return _resources[(index + _frontIndex) % _size]; } + + private: + + std::array _resources; + }; +} + +#endif diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h index 84c6cb6fa8..526352804b 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h @@ -38,7 +38,7 @@ public slots: * Returns a model reference object associated with the specified UUID ({@link EntityID}, {@link OverlayID}, or {@link AvatarID}). * * @function Graphics.getModel - * @param {UUID} The objectID of the model whose meshes are to be retrieved. + * @param {UUID} entityID - The objectID of the model whose meshes are to be retrieved. * @return {Graphics.Model} the resulting Model object */ scriptable::ScriptableModelPointer getModel(QUuid uuid); diff --git a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp index e323ed8fbc..c6ce179482 100644 --- a/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/TouchscreenVirtualPadDevice.cpp @@ -50,8 +50,8 @@ void TouchscreenVirtualPadDevice::init() { _screenDPIProvided = eventScreen->physicalDotsPerInch(); _screenDPI = eventScreen->physicalDotsPerInch(); - _fixedRadius = _screenDPI * 256 / 534; - _fixedRadiusForCalc = _fixedRadius - _screenDPI * 105 / 534; // 105 is the radius of the stick circle + _fixedRadius = _screenDPI * 0.5f * VirtualPad::Manager::PIXEL_SIZE / VirtualPad::Manager::DPI; + _fixedRadiusForCalc = _fixedRadius - _screenDPI * VirtualPad::Manager::STICK_RADIUS / VirtualPad::Manager::DPI; } auto& virtualPadManager = VirtualPad::Manager::instance(); @@ -69,7 +69,7 @@ void TouchscreenVirtualPadDevice::setupFixedCenter(VirtualPad::Manager& virtualP if (_extraBottomMargin == virtualPadManager.extraBottomMargin() && !force) return; // Our only criteria to decide a center change is the bottom margin _extraBottomMargin = virtualPadManager.extraBottomMargin(); - float margin = _screenDPI * 59 / 534; // 59px is for our 'base' of 534dpi (Pixel XL or Huawei Mate 9 Pro) + float margin = _screenDPI * VirtualPad::Manager::BASE_MARGIN / VirtualPad::Manager::DPI; QScreen* eventScreen = qApp->primaryScreen(); // do not call every time _fixedCenterPosition = glm::vec2( _fixedRadius + margin, eventScreen->size().height() - margin - _fixedRadius - _extraBottomMargin); @@ -82,10 +82,10 @@ float clip(float n, float lower, float upper) { } glm::vec2 TouchscreenVirtualPadDevice::clippedPointInCircle(float radius, glm::vec2 origin, glm::vec2 touchPoint) { - float deltaX = touchPoint.x-origin.x; - float deltaY = touchPoint.y-origin.y; + float deltaX = touchPoint.x - origin.x; + float deltaY = touchPoint.y - origin.y; - float distance = sqrt(pow(deltaX,2)+pow(deltaY,2)); + float distance = sqrt(pow(deltaX, 2) + pow(deltaY, 2)); // First case, inside the boundaires, just use the distance if (distance <= radius) { @@ -99,18 +99,18 @@ glm::vec2 TouchscreenVirtualPadDevice::clippedPointInCircle(float radius, glm::v // Third case, calculate point in circumference // line formula - float m = deltaY/deltaX; + float m = deltaY / deltaX; float b = touchPoint.y - m * touchPoint.x; // quadtratic coefs of circumference and line intersection - float qa = pow(m,2)+1; - float qb = 2 * ( m * b - origin.x - origin.y * m ); - float qc = powf(origin.x, 2) - powf(radius,2) + b * b - 2 * b * origin.y + powf(origin.y, 2); + float qa = powf(m, 2.0f) + 1.0f; + float qb = 2.0f * ( m * b - origin.x - origin.y * m); + float qc = powf(origin.x, 2.0f) - powf(radius, 2.0f) + b * b - 2.0f * b * origin.y + powf(origin.y, 2.0f); - float discr = qb * qb - 4 * qa * qc; - float discrSign = deltaX>0?1.0:-1.0; + float discr = qb * qb - 4.0f * qa * qc; + float discrSign = deltaX > 0.0f ? 1.0f : - 1.0f; - float finalX = (- qb + discrSign * sqrtf(discr)) / (2 * qa); + float finalX = (-qb + discrSign * sqrtf(discr)) / (2.0f * qa); float finalY = m * finalX + b; return vec2(finalX, finalY); diff --git a/libraries/model-networking/src/model-networking/MaterialCache.cpp b/libraries/model-networking/src/model-networking/MaterialCache.cpp index 85f2f43652..cf3e255e0c 100644 --- a/libraries/model-networking/src/model-networking/MaterialCache.cpp +++ b/libraries/model-networking/src/model-networking/MaterialCache.cpp @@ -28,6 +28,25 @@ void NetworkMaterialResource::downloadFinished(const QByteArray& data) { finishedLoading(true); } +/**jsdoc + *

An RGB or SRGB color value.

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
IndexTypeAttributesDefaultValue
0numberRed component value. Number in the range 0.01.0.
1numberGreen component value. Number in the range 0.01.0.
2numberBlue component value. Number in the range 0.01.0.
3boolean<optional>falseIf true then the color is an SRGB color.
+ * @typedef {array} RGBS + */ bool NetworkMaterialResource::parseJSONColor(const QJsonValue& array, glm::vec3& color, bool& isSRGB) { if (array.isArray()) { QJsonArray colorArray = array.toArray(); @@ -50,6 +69,12 @@ bool NetworkMaterialResource::parseJSONColor(const QJsonValue& array, glm::vec3& return false; } +/**jsdoc + * A material or set of materials such as may be used by a {@link Entities.EntityType|Material} entity. + * @typedef {object} MaterialResource + * @property {number} materialVersion=1 - The version of the material. Currently not used. + * @property {Material|Material[]} materials - The details of the material or materials. + */ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMaterials(const QJsonDocument& materialJSON) { ParsedMaterials toReturn; if (!materialJSON.isNull() && materialJSON.isObject()) { @@ -83,6 +108,36 @@ NetworkMaterialResource::ParsedMaterials NetworkMaterialResource::parseJSONMater return toReturn; } +/**jsdoc + * A material such as may be used by a {@link Entities.EntityType|Material} entity. + * @typedef {object} Material + * @property {string} name="" - A name for the material. + * @property {string} model="hifi_pbr" - Currently not used. + * @property {Vec3Color|RGBS} emissive - The emissive color, i.e., the color that the material emits. A {@link Vec3Color} value + * is treated as sRGB. A {@link RGBS} value can be either RGB or sRGB. + * @property {number} opacity=1.0 - The opacity, 0.01.0. + * @property {boolean} unlit=false - If true, the material is not lit. + * @property {Vec3Color|RGBS} albedo - The albedo color. A {@link Vec3Color} value is treated as sRGB. A {@link RGBS} value can + * be either RGB or sRGB. + * @property {number} roughness - The roughness, 0.01.0. + * @property {number} metallic - The metallicness, 0.01.0. + * @property {number} scattering - The scattering, 0.01.0. + * @property {string} emissiveMap - URL of emissive texture image. + * @property {string} albedoMap - URL of albedo texture image. + * @property {string} opacityMap - URL of opacity texture image. Set value the same as the albedoMap value for + * transparency. + * @property {string} roughnessMap - URL of roughness texture image. Can use this or glossMap, but not both. + * @property {string} glossMap - URL of gloss texture image. Can use this or roughnessMap, but not both. + * @property {string} metallicMap - URL of metallic texture image. Can use this or specularMap, but not both. + * @property {string} specularMap - URL of specular texture image. Can use this or metallicMap, but not both. + * @property {string} normalMap - URL of normal texture image. Can use this or bumpMap, but not both. + * @property {string} bumpMap - URL of bump texture image. Can use this or normalMap, but not both. + * @property {string} occlusionMap - URL of occlusion texture image. + * @property {string} scatteringMap - URL of scattering texture image. Only used if normalMap or + * bumpMap is specified. + * @property {string} lightMap - URL of light map texture image. Currently not used. + */ +// Note: See MaterialEntityItem.h for default values used in practice. std::pair> NetworkMaterialResource::parseJSONMaterial(const QJsonObject& materialJSON) { std::string name = ""; std::shared_ptr material = std::make_shared(); diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp index f2a2df6951..941d4795a5 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.cpp +++ b/libraries/model-networking/src/model-networking/TextureCache.cpp @@ -460,6 +460,10 @@ void NetworkTexture::makeRequest() { } +void NetworkTexture::handleLocalRequestCompleted() { + TextureCache::requestCompleted(_self); +} + void NetworkTexture::makeLocalRequest() { const QString scheme = _url.scheme(); QString path; @@ -469,6 +473,8 @@ void NetworkTexture::makeLocalRequest() { path = ":" + _url.path(); } + connect(this, &Resource::finished, this, &NetworkTexture::handleLocalRequestCompleted); + path = FileUtils::selectFile(path); auto storage = std::make_shared(path); diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h index 080c2704c6..b2740e2ca1 100644 --- a/libraries/model-networking/src/model-networking/TextureCache.h +++ b/libraries/model-networking/src/model-networking/TextureCache.h @@ -71,6 +71,7 @@ public slots: protected: void makeRequest() override; void makeLocalRequest(); + Q_INVOKABLE void handleLocalRequestCompleted(); virtual bool isCacheable() const override { return _loaded; } diff --git a/libraries/octree/src/OctreeScriptingInterface.h b/libraries/octree/src/OctreeScriptingInterface.h index c31da94532..e501dd166a 100644 --- a/libraries/octree/src/OctreeScriptingInterface.h +++ b/libraries/octree/src/OctreeScriptingInterface.h @@ -35,48 +35,126 @@ private slots: void cleanupManagedObjects(); public slots: + + /**jsdoc + * Set the maximum number of entity packets that the client can send per second. + * @function Entities.setPacketsPerSecond + * @param {number} packetsPerSecond - Integer maximum number of entity packets that the client can send per second. + */ /// set the max packets per second send rate void setPacketsPerSecond(int packetsPerSecond) { return _packetSender->setPacketsPerSecond(packetsPerSecond); } + /**jsdoc + * Get the maximum number of entity packets that the client can send per second. + * @function Entities.getPacketsPerSecond + * @returns {number} Integer maximum number of entity packets that the client can send per second. + */ /// get the max packets per second send rate int getPacketsPerSecond() const { return _packetSender->getPacketsPerSecond(); } - /// does a particle server exist to send to + /**jsdoc + * Check whether servers exist for the client to send entity packets to, i.e., whether you are connected to a domain and + * its entity server is working. + * @function Entities.serversExist + * @returns {boolean} true if servers exist for the client to send entity packets to, otherwise + * false. + */ + /// does a server exist to send to bool serversExist() const { return _packetSender->serversExist(); } + /**jsdoc + * Check whether the client has entity packets waiting to be sent. + * @function Entities.hasPacketsToSend + * @returns {boolean} true if the client has entity packets waiting to be sent, otherwise false. + */ /// are there packets waiting in the send queue to be sent bool hasPacketsToSend() const { return _packetSender->hasPacketsToSend(); } + /**jsdoc + * Get the number of entity packets the client has waiting to be sent. + * @function Entities.packetsToSendCount + * @returns {number} Integer number of entity packets the client has waiting to be sent. + */ /// how many packets are there in the send queue waiting to be sent int packetsToSendCount() const { return (int)_packetSender->packetsToSendCount(); } + /**jsdoc + * Get the entity packets per second send rate of the client over its lifetime. + * @function Entities.getLifetimePPS + * @returns {number} Entity packets per second send rate of the client over its lifetime. + */ /// returns the packets per second send rate of this object over its lifetime float getLifetimePPS() const { return _packetSender->getLifetimePPS(); } + /**jsdoc + * Get the entity bytes per second send rate of the client over its lifetime. + * @function Entities.getLifetimeBPS + * @returns {number} Entity bytes per second send rate of the client over its lifetime. + */ /// returns the bytes per second send rate of this object over its lifetime float getLifetimeBPS() const { return _packetSender->getLifetimeBPS(); } + /**jsdoc + * Get the entity packets per second queued rate of the client over its lifetime. + * @function Entities.getLifetimePPSQueued + * @returns {number} Entity packets per second queued rate of the client over its lifetime. + */ /// returns the packets per second queued rate of this object over its lifetime float getLifetimePPSQueued() const { return _packetSender->getLifetimePPSQueued(); } + /**jsdoc + * Get the entity bytes per second queued rate of the client over its lifetime. + * @function Entities.getLifetimeBPSQueued + * @returns {number} Entity bytes per second queued rate of the client over its lifetime. + */ /// returns the bytes per second queued rate of this object over its lifetime float getLifetimeBPSQueued() const { return _packetSender->getLifetimeBPSQueued(); } + /**jsdoc + * Get the lifetime of the client from the first entity packet sent until now, in microseconds. + * @function Entities.getLifetimeInUsecs + * @returns {number} Lifetime of the client from the first entity packet sent until now, in microseconds. + */ /// returns lifetime of this object from first packet sent to now in usecs long long unsigned int getLifetimeInUsecs() const { return _packetSender->getLifetimeInUsecs(); } - /// returns lifetime of this object from first packet sent to now in usecs + /**jsdoc + * Get the lifetime of the client from the first entity packet sent until now, in seconds. + * @function Entities.getLifetimeInSeconds + * @returns {number} Lifetime of the client from the first entity packet sent until now, in seconds. + */ + /// returns lifetime of this object from first packet sent to now in secs float getLifetimeInSeconds() const { return _packetSender->getLifetimeInSeconds(); } + /**jsdoc + * Get the total number of entity packets sent by the client over its lifetime. + * @function Entities.getLifetimePacketsSent + * @returns {number} The total number of entity packets sent by the client over its lifetime. + */ /// returns the total packets sent by this object over its lifetime long long unsigned int getLifetimePacketsSent() const { return _packetSender->getLifetimePacketsSent(); } + /**jsdoc + * Get the total bytes of entity packets sent by the client over its lifetime. + * @function Entities.getLifetimeBytesSent + * @returns {number} The total bytes of entity packets sent by the client over its lifetime. + */ /// returns the total bytes sent by this object over its lifetime long long unsigned int getLifetimeBytesSent() const { return _packetSender->getLifetimeBytesSent(); } + /**jsdoc + * Get the total number of entity packets queued by the client over its lifetime. + * @function Entities.getLifetimePacketsQueued + * @returns {number} The total number of entity packets queued by the client over its lifetime. + */ /// returns the total packets queued by this object over its lifetime long long unsigned int getLifetimePacketsQueued() const { return _packetSender->getLifetimePacketsQueued(); } + /**jsdoc + * Get the total bytes of entity packets queued by the client over its lifetime. + * @function Entities.getLifetimeBytesQueued + * @returns {number} The total bytes of entity packets queued by the client over its lifetime. + */ /// returns the total bytes queued by this object over its lifetime long long unsigned int getLifetimeBytesQueued() const { return _packetSender->getLifetimeBytesQueued(); } diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index d39930ab76..9b6e9fe7a0 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -756,6 +756,9 @@ void CharacterController::updateState() { SET_STATE(State::Hover, "double jump button"); } else if ((jumpButtonHeld || vertTargetSpeedIsNonZero) && (now - _jumpButtonDownStartTime) > JUMP_TO_HOVER_PERIOD) { SET_STATE(State::Hover, "jump button held"); + } else if (_floorDistance > _scaleFactor * DEFAULT_AVATAR_FALL_HEIGHT) { + // Transition to hover if we are above the fall threshold + SET_STATE(State::Hover, "above fall threshold"); } } break; diff --git a/libraries/physics/src/ObjectActionOffset.cpp b/libraries/physics/src/ObjectActionOffset.cpp index c1fb397e19..e90862266b 100644 --- a/libraries/physics/src/ObjectActionOffset.cpp +++ b/libraries/physics/src/ObjectActionOffset.cpp @@ -142,6 +142,18 @@ bool ObjectActionOffset::updateArguments(QVariantMap arguments) { return true; } +/**jsdoc + * The "offset" {@link Entities.ActionType|ActionType} moves an entity so that it is a set distance away from a + * target point. + * It has arguments in addition to the common {@link Entities.ActionArguments|ActionArguments}. + * + * @typedef {object} Entities.ActionArguments-Offset + * @property {Vec3} pointToOffsetFrom=0,0,0 - The target point to offset the entity from. + * @property {number} linearDistance=0 - The distance away from the target point to position the entity. + * @property {number} linearTimeScale=34e+38 - Controls how long it takes for the entity's position to catch up with the + * target offset. The value is the time for the action to catch up to 1/e = 0.368 of the target value, where the action + * is applied using an exponential decay. + */ QVariantMap ObjectActionOffset::getArguments() { QVariantMap arguments = ObjectDynamic::getArguments(); withReadLock([&] { diff --git a/libraries/physics/src/ObjectActionTractor.cpp b/libraries/physics/src/ObjectActionTractor.cpp index 03e6533c87..bc68d6de73 100644 --- a/libraries/physics/src/ObjectActionTractor.cpp +++ b/libraries/physics/src/ObjectActionTractor.cpp @@ -307,6 +307,23 @@ bool ObjectActionTractor::updateArguments(QVariantMap arguments) { return true; } +/**jsdoc + * The "tractor" {@link Entities.ActionType|ActionType} moves and rotates an entity to a target position and + * orientation, optionally relative to another entity. + * It has arguments in addition to the common {@link Entities.ActionArguments|ActionArguments}. + * + * @typedef {object} Entities.ActionArguments-Tractor + * @property {Vec3} targetPosition=0,0,0 - The target position. + * @property {Quat} targetRotation=0,0,0,1 - The target rotation. + * @property {Uuid} otherID=null - If an entity ID, the targetPosition and targetRotation are + * relative to this entity's position and rotation. + * @property {number} linearTimeScale=3.4e+38 - Controls how long it takes for the entity's position to catch up with the + * target position. The value is the time for the action to catch up to 1/e = 0.368 of the target value, where the action + * is applied using an exponential decay. + * @property {number} angularTimeScale=3.4e+38 - Controls how long it takes for the entity's orientation to catch up with the + * target orientation. The value is the time for the action to catch up to 1/e = 0.368 of the target value, where the + * action is applied using an exponential decay. + */ QVariantMap ObjectActionTractor::getArguments() { QVariantMap arguments = ObjectDynamic::getArguments(); withReadLock([&] { diff --git a/libraries/physics/src/ObjectActionTravelOriented.cpp b/libraries/physics/src/ObjectActionTravelOriented.cpp index 8ab24511d7..accade8695 100644 --- a/libraries/physics/src/ObjectActionTravelOriented.cpp +++ b/libraries/physics/src/ObjectActionTravelOriented.cpp @@ -146,6 +146,17 @@ bool ObjectActionTravelOriented::updateArguments(QVariantMap arguments) { return true; } +/**jsdoc + * The "travel-oriented" {@link Entities.ActionType|ActionType} orients an entity to align with its direction of + * travel. + * It has arguments in addition to the common {@link Entities.ActionArguments|ActionArguments}. + * + * @typedef {object} Entities.ActionArguments-TravelOriented + * @property {Vec3} forward=0,0,0 - The axis of the entity to align with the entity's direction of travel. + * @property {number} angularTimeScale=0.1 - Controls how long it takes for the entity's orientation to catch up with the + * direction of travel. The value is the time for the action to catch up to 1/e = 0.368 of the target value, where the + * action is applied using an exponential decay. + */ QVariantMap ObjectActionTravelOriented::getArguments() { QVariantMap arguments = ObjectDynamic::getArguments(); withReadLock([&] { diff --git a/libraries/physics/src/ObjectConstraintBallSocket.cpp b/libraries/physics/src/ObjectConstraintBallSocket.cpp index 9dd85954a3..70613d46ae 100644 --- a/libraries/physics/src/ObjectConstraintBallSocket.cpp +++ b/libraries/physics/src/ObjectConstraintBallSocket.cpp @@ -181,6 +181,15 @@ bool ObjectConstraintBallSocket::updateArguments(QVariantMap arguments) { return true; } +/**jsdoc + * The "ball-socket" {@link Entities.ActionType|ActionType} connects two entities with a ball and socket joint. + * It has arguments in addition to the common {@link Entities.ActionArguments|ActionArguments}. + * + * @typedef {object} Entities.ActionArguments-BallSocket + * @property {Vec3} pivot=0,0,0 - The local offset of the joint relative to the entity's position. + * @property {Uuid} otherEntityID=null - The ID of the other entity that is connected to the joint. + * @property {Vec3} otherPivot=0,0,0 - The local offset of the joint relative to the other entity's position. + */ QVariantMap ObjectConstraintBallSocket::getArguments() { QVariantMap arguments = ObjectDynamic::getArguments(); withReadLock([&] { diff --git a/libraries/physics/src/ObjectConstraintConeTwist.cpp b/libraries/physics/src/ObjectConstraintConeTwist.cpp index 49f926af81..86f1f21c63 100644 --- a/libraries/physics/src/ObjectConstraintConeTwist.cpp +++ b/libraries/physics/src/ObjectConstraintConeTwist.cpp @@ -261,6 +261,21 @@ bool ObjectConstraintConeTwist::updateArguments(QVariantMap arguments) { return true; } +/**jsdoc + * The "cone-twist" {@link Entities.ActionType|ActionType} connects two entities with a joint that can move + * through a cone and can twist. + * It has arguments in addition to the common {@link Entities.ActionArguments|ActionArguments}. + * + * @typedef {object} Entities.ActionArguments-ConeTwist + * @property {Vec3} pivot=0,0,0 - The local offset of the joint relative to the entity's position. + * @property {Vec3} axis=1,0,0 - The axis of the entity that moves through the cone. Must be a non-zero vector. + * @property {Uuid} otherEntityID=null - The ID of the other entity that is connected to the joint. + * @property {Vec3} otherPivot=0,0,0 - The local offset of the joint relative to the other entity's position. + * @property {Vec3} otherAxis=1,0,0 - The axis of the other entity that moves through the cone. Must be a non-zero vector. + * @property {number} swingSpan1=6.238 - The angle through which the joint can move in one axis of the cone, in radians. + * @property {number} swingSpan2=6.238 - The angle through which the joint can move in the other axis of the cone, in radians. + * @property {number} twistSpan=6.238 - The angle through with the joint can twist, in radians. + */ QVariantMap ObjectConstraintConeTwist::getArguments() { QVariantMap arguments = ObjectDynamic::getArguments(); withReadLock([&] { diff --git a/libraries/physics/src/ObjectConstraintHinge.cpp b/libraries/physics/src/ObjectConstraintHinge.cpp index 52be64796a..99ddd45abd 100644 --- a/libraries/physics/src/ObjectConstraintHinge.cpp +++ b/libraries/physics/src/ObjectConstraintHinge.cpp @@ -245,6 +245,22 @@ bool ObjectConstraintHinge::updateArguments(QVariantMap arguments) { return true; } +/**jsdoc + * The "hinge" {@link Entities.ActionType|ActionType} lets an entity pivot about an axis or connects two entities + * with a hinge joint. + * It has arguments in addition to the common {@link Entities.ActionArguments|ActionArguments}. + * + * @typedef {object} Entities.ActionArguments-Hinge + * @property {Vec3} pivot=0,0,0 - The local offset of the joint relative to the entity's position. + * @property {Vec3} axis=1,0,0 - The axis of the entity that it pivots about. Must be a non-zero vector. + * @property {Uuid} otherEntityID=null - The ID of the other entity that is connected to the joint, if any. If none is + * specified then the first entity simply pivots about its specified axis. + * @property {Vec3} otherPivot=0,0,0 - The local offset of the joint relative to the other entity's position. + * @property {Vec3} otherAxis=1,0,0 - The axis of the other entity that it pivots about. Must be a non-zero vector. + * @property {number} low=-6.283 - The most negative angle that the hinge can take, in radians. + * @property {number} high=6.283 - The most positive angle that the hinge can take, in radians. + * @property {number} angle=0 - The current angle of the hinge. Read-only. + */ QVariantMap ObjectConstraintHinge::getArguments() { QVariantMap arguments = ObjectDynamic::getArguments(); withReadLock([&] { diff --git a/libraries/physics/src/ObjectConstraintSlider.cpp b/libraries/physics/src/ObjectConstraintSlider.cpp index ded9ad47e6..c236afc10d 100644 --- a/libraries/physics/src/ObjectConstraintSlider.cpp +++ b/libraries/physics/src/ObjectConstraintSlider.cpp @@ -261,6 +261,31 @@ bool ObjectConstraintSlider::updateArguments(QVariantMap arguments) { return true; } +/**jsdoc + * The "slider" {@link Entities.ActionType|ActionType} lets an entity slide and rotate along an axis, or connects + * two entities that slide and rotate along a shared axis. + * It has arguments in addition to the common {@link Entities.ActionArguments|ActionArguments}. + * + * @typedef {object} Entities.ActionArguments-Slider + * @property {Vec3} point=0,0,0 - The local position of a point in the entity that slides along the axis. + * @property {Vec3} axis=1,0,0 - The axis of the entity that slides along the joint. Must be a non-zero vector. + * @property {Uuid} otherEntityID=null - The ID of the other entity that is connected to the joint, if any. If non is + * specified then the first entity simply slides and rotates about its specified axis. + * @property {Vec3} otherPoint=0,0,0 - The local position of a point in the other entity that slides along the axis. + * @property {Vec3} axis=1,0,0 - The axis of the other entity that slides along the joint. Must be a non-zero vector. + * @property {number} linearLow=1.17e-38 - The most negative linear offset from the entity's initial point that the entity can + * have along the slider. + * @property {number} linearHigh=3.40e+38 - The most positive linear offset from the entity's initial point that the entity can + * have along the slider. + * @property {number} angularLow=-6.283 - The most negative angle that the entity can rotate about the axis if the action + * involves only one entity, otherwise the most negative angle the rotation can be between the two entities. In radians. + * @property {number} angularHigh=6.283 - The most positive angle that the entity can rotate about the axis if the action + * involves only one entity, otherwise the most positive angle the rotation can be between the two entities. In radians. + * @property {number} linearPosition=0 - The current linear offset the entity is from its initial point if the action involves + * only one entity, otherwise the linear offset between the two entities' action points. Read-only. + * @property {number} angularPosition=0 - The current angular offset of the entity from its initial rotation if the action + * involves only one entity, otherwise the angular offset between the two entities. Read-only. + */ QVariantMap ObjectConstraintSlider::getArguments() { QVariantMap arguments = ObjectDynamic::getArguments(); withReadLock([&] { diff --git a/libraries/physics/src/ObjectDynamic.cpp b/libraries/physics/src/ObjectDynamic.cpp index 3deadd6468..5bbb5981d1 100644 --- a/libraries/physics/src/ObjectDynamic.cpp +++ b/libraries/physics/src/ObjectDynamic.cpp @@ -93,6 +93,38 @@ bool ObjectDynamic::updateArguments(QVariantMap arguments) { return somethingChanged; } +/**jsdoc +* Different entity action types have different arguments: some common to all actions (listed below) and some specific to each +* {@link Entities.ActionType|ActionType} (linked to below). The arguments are accessed as an object of property names and +* values. +* +* @typedef {object} Entities.ActionArguments +* @property {Entities.ActionType} type - The type of action. +* @property {string} tag="" - A string that a script can use for its own purposes. +* @property {number} ttl=0 - How long the action should exist, in seconds, before it is automatically deleted. A value of +* 0 means that the action should not be deleted. +* @property {boolean} isMine=true - Is true if you created the action during your current Interface session, +* false otherwise. Read-only. +* @property {boolean} ::no-motion-state - Is present when the entity hasn't been registered with the physics engine yet (e.g., +* if the action hasn't been properly configured), otherwise undefined. Read-only. +* @property {boolean} ::active - Is true when the action is modifying the entity's motion, false +* otherwise. Is present once the entity has been registered with the physics engine, otherwise undefined. +* Read-only. +* @property {Entities.PhysicsMotionType} ::motion-type - How the entity moves with the action. Is present once the entity has +* been registered with the physics engine, otherwise undefined. Read-only. +* +* @see The different action types have additional arguments as follows: +* @see {@link Entities.ActionArguments-FarGrab|ActionArguments-FarGrab} +* @see {@link Entities.ActionArguments-Hold|ActionArguments-Hold} +* @see {@link Entities.ActionArguments-Offset|ActionArguments-Offset} +* @see {@link Entities.ActionArguments-Tractor|ActionArguments-Tractor} +* @see {@link Entities.ActionArguments-TravelOriented|ActionArguments-TravelOriented} +* @see {@link Entities.ActionArguments-Hinge|ActionArguments-Hinge} +* @see {@link Entities.ActionArguments-Slider|ActionArguments-Slider} +* @see {@link Entities.ActionArguments-ConeTwist|ActionArguments-ConeTwist} +* @see {@link Entities.ActionArguments-BallSocket|ActionArguments-BallSocket} +*/ +// Note: The "type" property is set in EntityItem::getActionArguments(). QVariantMap ObjectDynamic::getArguments() { QVariantMap arguments; withReadLock([&]{ diff --git a/libraries/physics/src/ObjectMotionState.h b/libraries/physics/src/ObjectMotionState.h index 0b91ede574..7f583ca9ca 100644 --- a/libraries/physics/src/ObjectMotionState.h +++ b/libraries/physics/src/ObjectMotionState.h @@ -29,6 +29,23 @@ enum PhysicsMotionType { MOTION_TYPE_KINEMATIC // keyframed motion }; +/**jsdoc + *

An entity's physics motion type may be one of the following:

+ * + * + * + * + * + * + * + * + * + *
ValueDescription
"static"There is no motion because the entity is locked — its locked + * property is set to true.
"kinematic"Motion is applied without physical laws (e.g., damping) because the entity is + * not locked and has its dynamic property set to false.
"dynamic"Motion is applied according to physical laws (e.g., damping) because the entity + * is not locked and has its dynamic property set to true.
+ * @typedef {string} Entities.PhysicsMotionType + */ inline QString motionTypeToString(PhysicsMotionType motionType) { switch(motionType) { case MOTION_TYPE_STATIC: return QString("static"); diff --git a/libraries/plugins/src/plugins/DisplayPlugin.cpp b/libraries/plugins/src/plugins/DisplayPlugin.cpp index 2a8a72f594..47503e8f85 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.cpp +++ b/libraries/plugins/src/plugins/DisplayPlugin.cpp @@ -42,4 +42,9 @@ std::function Displa hudOperator = _hudOperator; } return hudOperator; -} \ No newline at end of file +} + +glm::mat4 HmdDisplay::getEyeToHeadTransform(Eye eye) const { + static const glm::mat4 xform; + return xform; +} diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index 2c717f629c..e95084df52 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -93,9 +93,7 @@ class HmdDisplay : public StereoDisplay { public: // HMD specific methods // TODO move these into another class? - virtual glm::mat4 getEyeToHeadTransform(Eye eye) const { - static const glm::mat4 transform; return transform; - } + virtual glm::mat4 getEyeToHeadTransform(Eye eye) const; // returns a copy of the most recent head pose, computed via updateHeadPose virtual glm::mat4 getHeadPose() const { diff --git a/libraries/plugins/src/plugins/PluginManager.cpp b/libraries/plugins/src/plugins/PluginManager.cpp index dbd6845676..e9c084e132 100644 --- a/libraries/plugins/src/plugins/PluginManager.cpp +++ b/libraries/plugins/src/plugins/PluginManager.cpp @@ -96,7 +96,9 @@ const LoaderList& getLoadedPlugins() { static std::once_flag once; static LoaderList loadedPlugins; std::call_once(once, [&] { -#ifdef Q_OS_MAC +#if defined(Q_OS_ANDROID) + QString pluginPath = QCoreApplication::applicationDirPath() + "/"; +#elif defined(Q_OS_MAC) QString pluginPath = QCoreApplication::applicationDirPath() + "/../PlugIns/"; #else QString pluginPath = QCoreApplication::applicationDirPath() + "/plugins/"; @@ -106,6 +108,10 @@ const LoaderList& getLoadedPlugins() { pluginDir.setFilter(QDir::Files); if (pluginDir.exists()) { qInfo() << "Loading runtime plugins from " << pluginPath; +#if defined(Q_OS_ANDROID) + // Can be a better filter and those libs may have a better name to destinguish them from qt plugins + pluginDir.setNameFilters(QStringList() << "libplugins_lib*.so"); +#endif auto candidates = pluginDir.entryList(); for (auto plugin : candidates) { qCDebug(plugins) << "Attempting plugin" << qPrintable(plugin); diff --git a/libraries/qml/src/qml/OffscreenSurface.cpp b/libraries/qml/src/qml/OffscreenSurface.cpp index e3e26bb7fa..704111762a 100644 --- a/libraries/qml/src/qml/OffscreenSurface.cpp +++ b/libraries/qml/src/qml/OffscreenSurface.cpp @@ -342,6 +342,11 @@ void OffscreenSurface::finishQmlLoad(QQmlComponent* qmlComponent, // Make sure we make items focusable (critical for // supporting keyboard shortcuts) newItem->setFlag(QQuickItem::ItemIsFocusScope, true); +#ifdef DEBUG + for (auto frame : newObject->findChildren("Frame")) { + frame->setProperty("qmlFile", qmlComponent->url()); + } +#endif } bool rootCreated = getRootItem() != nullptr; diff --git a/libraries/render-utils/src/AmbientOcclusionEffect.cpp b/libraries/render-utils/src/AmbientOcclusionEffect.cpp index 015f5678c8..c526f16b75 100644 --- a/libraries/render-utils/src/AmbientOcclusionEffect.cpp +++ b/libraries/render-utils/src/AmbientOcclusionEffect.cpp @@ -385,7 +385,7 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte auto firstHBlurPipeline = getHBlurPipeline(); auto lastVBlurPipeline = getVBlurPipeline(); - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch("AmbientOcclusionEffect::run", args->_context, [=](gpu::Batch& batch) { batch.enableStereo(false); _gpuTimer->begin(batch); @@ -518,7 +518,7 @@ void DebugAmbientOcclusion::run(const render::RenderContextPointer& renderContex auto debugPipeline = getDebugPipeline(); - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch("DebugAmbientOcclusion::run", args->_context, [=](gpu::Batch& batch) { batch.enableStereo(false); batch.setViewportTransform(sourceViewport); diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp index bdd8f19a5c..4a9b69c099 100644 --- a/libraries/render-utils/src/AntialiasingEffect.cpp +++ b/libraries/render-utils/src/AntialiasingEffect.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include "AntialiasingEffect.h" #include "StencilMaskPass.h" @@ -22,7 +23,11 @@ #include "DependencyManager.h" #include "ViewFrustum.h" #include "GeometryCache.h" +#include "FramebufferCache.h" +#define ANTIALIASING_USE_TAA 1 + +#if !ANTIALIASING_USE_TAA #include "fxaa_vert.h" #include "fxaa_frag.h" #include "fxaa_blend_frag.h" @@ -108,7 +113,7 @@ void Antialiasing::run(const render::RenderContextPointer& renderContext, const RenderArgs* args = renderContext->args; - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("Antialiasing::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); batch.setViewportTransform(args->_viewport); @@ -165,3 +170,372 @@ void Antialiasing::run(const render::RenderContextPointer& renderContext, const DependencyManager::get()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color, _geometryId); }); } +#else + +#include "taa_frag.h" +#include "fxaa_blend_frag.h" +#include "taa_blend_frag.h" + +const int AntialiasingPass_ParamsSlot = 0; +const int AntialiasingPass_FrameTransformSlot = 1; + +const int AntialiasingPass_HistoryMapSlot = 0; +const int AntialiasingPass_SourceMapSlot = 1; +const int AntialiasingPass_VelocityMapSlot = 2; +const int AntialiasingPass_DepthMapSlot = 3; + +const int AntialiasingPass_NextMapSlot = 4; + + +Antialiasing::Antialiasing() { + _antialiasingBuffers = std::make_shared(2U); +} + +Antialiasing::~Antialiasing() { + _antialiasingBuffers.reset(); + _antialiasingTextures[0].reset(); + _antialiasingTextures[1].reset(); +} + +const gpu::PipelinePointer& Antialiasing::getAntialiasingPipeline() { + + if (!_antialiasingPipeline) { + + auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); + auto ps = taa_frag::getShader(); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("taaParamsBuffer"), AntialiasingPass_ParamsSlot)); + + slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), AntialiasingPass_FrameTransformSlot)); + + slotBindings.insert(gpu::Shader::Binding(std::string("historyMap"), AntialiasingPass_HistoryMapSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("sourceMap"), AntialiasingPass_SourceMapSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("velocityMap"), AntialiasingPass_VelocityMapSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("depthMap"), AntialiasingPass_DepthMapSlot)); + + + gpu::Shader::makeProgram(*program, slotBindings); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + + PrepareStencil::testNoAA(*state); + + // Good to go add the brand new pipeline + _antialiasingPipeline = gpu::Pipeline::create(program, state); + } + + return _antialiasingPipeline; +} + +const gpu::PipelinePointer& Antialiasing::getBlendPipeline() { + if (!_blendPipeline) { + auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); + auto ps = fxaa_blend_frag::getShader(); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("colorTexture"), AntialiasingPass_NextMapSlot)); + + gpu::Shader::makeProgram(*program, slotBindings); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + PrepareStencil::testNoAA(*state); + + + // Good to go add the brand new pipeline + _blendPipeline = gpu::Pipeline::create(program, state); + _sharpenLoc = program->getUniforms().findLocation("sharpenIntensity"); + + } + return _blendPipeline; +} + +const gpu::PipelinePointer& Antialiasing::getDebugBlendPipeline() { + if (!_debugBlendPipeline) { + auto vs = gpu::StandardShaderLib::getDrawUnitQuadTexcoordVS(); + auto ps = taa_blend_frag::getShader(); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("taaParamsBuffer"), AntialiasingPass_ParamsSlot)); + + slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), AntialiasingPass_FrameTransformSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("nextMap"), AntialiasingPass_NextMapSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("historyMap"), AntialiasingPass_HistoryMapSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("sourceMap"), AntialiasingPass_SourceMapSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("velocityMap"), AntialiasingPass_VelocityMapSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("depthMap"), AntialiasingPass_DepthMapSlot)); + + + gpu::Shader::makeProgram(*program, slotBindings); + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + PrepareStencil::testNoAA(*state); + + + // Good to go add the brand new pipeline + _debugBlendPipeline = gpu::Pipeline::create(program, state); + } + return _debugBlendPipeline; +} + +void Antialiasing::configure(const Config& config) { + _sharpen = config.sharpen; + _params.edit().blend = config.blend; + _params.edit().covarianceGamma = config.covarianceGamma; + + _params.edit().setConstrainColor(config.constrainColor); + _params.edit().setFeedbackColor(config.feedbackColor); + + _params.edit().debugShowVelocityThreshold = config.debugShowVelocityThreshold; + + _params.edit().regionInfo.x = config.debugX; + _params.edit().regionInfo.z = config.debugFXAAX; + + _params.edit().setDebug(config.debug); + _params.edit().setShowDebugCursor(config.showCursorPixel); + _params.edit().setDebugCursor(config.debugCursorTexcoord); + _params.edit().setDebugOrbZoom(config.debugOrbZoom); + + _params.edit().setShowClosestFragment(config.showClosestFragment); +} + + +void Antialiasing::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { + assert(renderContext->args); + assert(renderContext->args->hasViewFrustum()); + + RenderArgs* args = renderContext->args; + + auto& deferredFrameTransform = inputs.get0(); + auto& sourceBuffer = inputs.get1(); + auto& linearDepthBuffer = inputs.get2(); + auto& velocityBuffer = inputs.get3(); + + int width = sourceBuffer->getWidth(); + int height = sourceBuffer->getHeight(); + + if (_antialiasingBuffers->get(0)) { + if (_antialiasingBuffers->get(0)->getSize() != uvec2(width, height)) {// || (sourceBuffer && (_antialiasingBuffer->getRenderBuffer(1) != sourceBuffer->getRenderBuffer(0)))) { + _antialiasingBuffers->edit(0).reset(); + _antialiasingBuffers->edit(1).reset(); + _antialiasingTextures[0].reset(); + _antialiasingTextures[1].reset(); + } + } + + if (!_antialiasingBuffers->get(0)) { + // Link the antialiasing FBO to texture + for (int i = 0; i < 2; i++) { + auto& antiAliasingBuffer = _antialiasingBuffers->edit(i); + antiAliasingBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("antialiasing")); + auto format = gpu::Element::COLOR_SRGBA_32; // DependencyManager::get()->getLightingTexture()->getTexelFormat(); + auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR); + _antialiasingTextures[i] = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, defaultSampler); + antiAliasingBuffer->setRenderBuffer(0, _antialiasingTextures[i]); + } + } + + gpu::doInBatch("Antialiasing::run", args->_context, [&](gpu::Batch& batch) { + batch.enableStereo(false); + batch.setViewportTransform(args->_viewport); + + // TAA step + getAntialiasingPipeline(); + batch.setResourceFramebufferSwapChainTexture(AntialiasingPass_HistoryMapSlot, _antialiasingBuffers, 0); + batch.setResourceTexture(AntialiasingPass_SourceMapSlot, sourceBuffer->getRenderBuffer(0)); + batch.setResourceTexture(AntialiasingPass_VelocityMapSlot, velocityBuffer->getVelocityTexture()); + // This is only used during debug + batch.setResourceTexture(AntialiasingPass_DepthMapSlot, linearDepthBuffer->getLinearDepthTexture()); + + batch.setUniformBuffer(AntialiasingPass_ParamsSlot, _params); + batch.setUniformBuffer(AntialiasingPass_FrameTransformSlot, deferredFrameTransform->getFrameTransformBuffer()); + + batch.setFramebufferSwapChain(_antialiasingBuffers, 1); + batch.setPipeline(getAntialiasingPipeline()); + batch.draw(gpu::TRIANGLE_STRIP, 4); + + // Blend step + batch.setResourceTexture(AntialiasingPass_SourceMapSlot, nullptr); + + batch.setFramebuffer(sourceBuffer); + if (_params->isDebug()) { + batch.setPipeline(getDebugBlendPipeline()); + } else { + batch.setPipeline(getBlendPipeline()); + // Disable sharpen if FXAA + batch._glUniform1f(_sharpenLoc, _sharpen * _params.get().regionInfo.z); + } + batch.setResourceFramebufferSwapChainTexture(AntialiasingPass_NextMapSlot, _antialiasingBuffers, 1); + batch.draw(gpu::TRIANGLE_STRIP, 4); + batch.advance(_antialiasingBuffers); + + batch.setUniformBuffer(AntialiasingPass_ParamsSlot, nullptr); + batch.setUniformBuffer(AntialiasingPass_FrameTransformSlot, nullptr); + + batch.setResourceTexture(AntialiasingPass_DepthMapSlot, nullptr); + batch.setResourceTexture(AntialiasingPass_HistoryMapSlot, nullptr); + batch.setResourceTexture(AntialiasingPass_VelocityMapSlot, nullptr); + batch.setResourceTexture(AntialiasingPass_NextMapSlot, nullptr); + }); +} + + +void JitterSampleConfig::setIndex(int current) { + _index = (current) % JitterSample::SEQUENCE_LENGTH; + emit dirty(); +} + +int JitterSampleConfig::cycleStopPauseRun() { + _state = (_state + 1) % 3; + switch (_state) { + case 0: { + return none(); + break; + } + case 1: { + return pause(); + break; + } + case 2: + default: { + return play(); + break; + } + } + return _state; +} + +int JitterSampleConfig::prev() { + setIndex(_index - 1); + return _index; +} + +int JitterSampleConfig::next() { + setIndex(_index + 1); + return _index; +} + +int JitterSampleConfig::none() { + _state = 0; + stop = true; + freeze = false; + setIndex(-1); + return _state; +} + +int JitterSampleConfig::pause() { + _state = 1; + stop = false; + freeze = true; + setIndex(0); + return _state; +} + + +int JitterSampleConfig::play() { + _state = 2; + stop = false; + freeze = false; + setIndex(0); + return _state; +} + +template +class Halton { +public: + + float eval(int index) const { + float f = 1.0f; + float r = 0.0f; + float invB = 1.0f / (float)B; + index++; // Indices start at 1, not 0 + + while (index > 0) { + f = f * invB; + r = r + f * (float)(index % B); + index = index / B; + + } + + return r; + } + +}; + + +JitterSample::SampleSequence::SampleSequence(){ + // Halton sequence (2,3) + Halton<2> genX; + Halton<3> genY; + + for (int i = 0; i < SEQUENCE_LENGTH; i++) { + offsets[i] = glm::vec2(genX.eval(i), genY.eval(i)); + offsets[i] -= vec2(0.5f); + } + offsets[SEQUENCE_LENGTH] = glm::vec2(0.0f); +} + +void JitterSample::configure(const Config& config) { + _freeze = config.freeze; + if (config.stop || _freeze) { + auto pausedIndex = config.getIndex(); + if (_sampleSequence.currentIndex != pausedIndex) { + _sampleSequence.currentIndex = pausedIndex; + } + } else { + if (_sampleSequence.currentIndex < 0) { + _sampleSequence.currentIndex = config.getIndex(); + } + } + _scale = config.scale; +} + +void JitterSample::run(const render::RenderContextPointer& renderContext) { + auto& current = _sampleSequence.currentIndex; + if (!_freeze) { + if (current >= 0) { + current = (current + 1) % SEQUENCE_LENGTH; + } else { + current = -1; + } + } + auto args = renderContext->args; + auto viewFrustum = args->getViewFrustum(); + + auto jit = _sampleSequence.offsets[(current < 0 ? SEQUENCE_LENGTH : current)]; + auto width = (float)args->_viewport.z; + auto height = (float)args->_viewport.w; + + auto jx = 2.0f * jit.x / width; + auto jy = 2.0f * jit.y / height; + + if (!args->isStereo()) { + auto projMat = viewFrustum.getProjection(); + + projMat[2][0] += jx; + projMat[2][1] += jy; + + viewFrustum.setProjection(projMat); + viewFrustum.calculate(); + args->setViewFrustum(viewFrustum); + } else { + mat4 projMats[2]; + args->_context->getStereoProjections(projMats); + + jx *= 2.0f; + + for (int i = 0; i < 2; i++) { + auto& projMat = projMats[i]; + projMat[2][0] += jx; + projMat[2][1] += jy; + } + + args->_context->setStereoProjections(projMats); + } +} + + +#endif \ No newline at end of file diff --git a/libraries/render-utils/src/AntialiasingEffect.h b/libraries/render-utils/src/AntialiasingEffect.h index cec2554a3b..da9a0bd384 100644 --- a/libraries/render-utils/src/AntialiasingEffect.h +++ b/libraries/render-utils/src/AntialiasingEffect.h @@ -15,7 +15,184 @@ #include #include "render/DrawTask.h" +#include "DeferredFrameTransform.h" +#include "VelocityBufferPass.h" + +class JitterSampleConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(float scale MEMBER scale NOTIFY dirty) + Q_PROPERTY(bool freeze MEMBER freeze NOTIFY dirty) + Q_PROPERTY(bool stop MEMBER stop NOTIFY dirty) + Q_PROPERTY(int index READ getIndex NOTIFY dirty) +public: + JitterSampleConfig() : render::Job::Config(true) {} + + float scale{ 0.5f }; + bool stop{ false }; + bool freeze{ false }; + + void setIndex(int current); + +public slots: + int cycleStopPauseRun(); + int prev(); + int next(); + int none(); + int pause(); + int play(); + + int getIndex() const { return _index; } + int getState() const { return _state; } +signals: + void dirty(); + +private: + int _state{ 0 }; + int _index{ 0 }; + +}; + + +class JitterSample { +public: + + enum { + SEQUENCE_LENGTH = 128 + }; + + using Config = JitterSampleConfig; + using JobModel = render::Job::Model; + + void configure(const Config& config); + void run(const render::RenderContextPointer& renderContext); + +private: + + struct SampleSequence { + SampleSequence(); + + glm::vec2 offsets[SEQUENCE_LENGTH + 1]; + int sequenceLength{ SEQUENCE_LENGTH }; + int currentIndex{ 0 }; + }; + + SampleSequence _sampleSequence; + float _scale{ 1.0 }; + bool _freeze{ false }; +}; + + +class AntialiasingConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(float blend MEMBER blend NOTIFY dirty) + Q_PROPERTY(float sharpen MEMBER sharpen NOTIFY dirty) + Q_PROPERTY(float covarianceGamma MEMBER covarianceGamma NOTIFY dirty) + + Q_PROPERTY(bool constrainColor MEMBER constrainColor NOTIFY dirty) + Q_PROPERTY(bool feedbackColor MEMBER feedbackColor NOTIFY dirty) + + Q_PROPERTY(bool debug MEMBER debug NOTIFY dirty) + Q_PROPERTY(float debugX MEMBER debugX NOTIFY dirty) + Q_PROPERTY(float debugFXAAX MEMBER debugFXAAX NOTIFY dirty) + Q_PROPERTY(float debugShowVelocityThreshold MEMBER debugShowVelocityThreshold NOTIFY dirty) + Q_PROPERTY(bool showCursorPixel MEMBER showCursorPixel NOTIFY dirty) + Q_PROPERTY(glm::vec2 debugCursorTexcoord MEMBER debugCursorTexcoord NOTIFY dirty) + Q_PROPERTY(float debugOrbZoom MEMBER debugOrbZoom NOTIFY dirty) + + Q_PROPERTY(bool showClosestFragment MEMBER showClosestFragment NOTIFY dirty) + +public: + AntialiasingConfig() : render::Job::Config(true) {} + + float blend{ 0.05f }; + float sharpen{ 0.15f }; + + bool constrainColor{ true }; + float covarianceGamma{ 0.9f }; + bool feedbackColor{ false }; + + float debugX{ 0.0f }; + float debugFXAAX{ 1.0f }; + float debugShowVelocityThreshold{ 1.0f }; + glm::vec2 debugCursorTexcoord{ 0.5f, 0.5f }; + float debugOrbZoom{ 2.0f }; + + bool debug { false }; + bool showCursorPixel { false }; + bool showClosestFragment{ false }; + +signals: + void dirty(); +}; + +#define SET_BIT(bitfield, bitIndex, value) bitfield = ((bitfield) & ~(1 << (bitIndex))) | ((value) << (bitIndex)) +#define GET_BIT(bitfield, bitIndex) ((bitfield) & (1 << (bitIndex))) + +struct TAAParams { + float nope{ 0.0f }; + float blend{ 0.05f }; + float covarianceGamma{ 1.0f }; + float debugShowVelocityThreshold{ 1.0f }; + + glm::ivec4 flags{ 0 }; + glm::vec4 pixelInfo{ 0.5f, 0.5f, 2.0f, 0.0f }; + glm::vec4 regionInfo{ 0.0f, 0.0f, 1.0f, 0.0f }; + + void setConstrainColor(bool enabled) { SET_BIT(flags.y, 1, enabled); } + bool isConstrainColor() const { return (bool)GET_BIT(flags.y, 1); } + + void setFeedbackColor(bool enabled) { SET_BIT(flags.y, 4, enabled); } + bool isFeedbackColor() const { return (bool)GET_BIT(flags.y, 4); } + + void setDebug(bool enabled) { SET_BIT(flags.x, 0, enabled); } + bool isDebug() const { return (bool) GET_BIT(flags.x, 0); } + + void setShowDebugCursor(bool enabled) { SET_BIT(flags.x, 1, enabled); } + bool showDebugCursor() const { return (bool)GET_BIT(flags.x, 1); } + + void setDebugCursor(glm::vec2 debugCursor) { pixelInfo.x = debugCursor.x; pixelInfo.y = debugCursor.y; } + glm::vec2 getDebugCursor() const { return glm::vec2(pixelInfo.x, pixelInfo.y); } + + void setDebugOrbZoom(float orbZoom) { pixelInfo.z = orbZoom; } + float getDebugOrbZoom() const { return pixelInfo.z; } + + void setShowClosestFragment(bool enabled) { SET_BIT(flags.x, 3, enabled); } + +}; +using TAAParamsBuffer = gpu::StructBuffer; + +class Antialiasing { +public: + using Inputs = render::VaryingSet4 < DeferredFrameTransformPointer, gpu::FramebufferPointer, LinearDepthFramebufferPointer, VelocityFramebufferPointer > ; + using Config = AntialiasingConfig; + using JobModel = render::Job::ModelI; + + Antialiasing(); + ~Antialiasing(); + void configure(const Config& config); + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); + + const gpu::PipelinePointer& getAntialiasingPipeline(); + const gpu::PipelinePointer& getBlendPipeline(); + const gpu::PipelinePointer& getDebugBlendPipeline(); + +private: + + gpu::FramebufferSwapChainPointer _antialiasingBuffers; + gpu::TexturePointer _antialiasingTextures[2]; + + gpu::PipelinePointer _antialiasingPipeline; + gpu::PipelinePointer _blendPipeline; + gpu::PipelinePointer _debugBlendPipeline; + + TAAParamsBuffer _params; + float _sharpen{ 0.15f }; + int _sharpenLoc{ -1 }; +}; + + +/* class AntiAliasingConfig : public render::Job::Config { Q_OBJECT Q_PROPERTY(bool enabled MEMBER enabled) @@ -27,27 +204,28 @@ class Antialiasing { public: using Config = AntiAliasingConfig; using JobModel = render::Job::ModelI; - + Antialiasing(); ~Antialiasing(); void configure(const Config& config) {} void run(const render::RenderContextPointer& renderContext, const gpu::FramebufferPointer& sourceBuffer); - - const gpu::PipelinePointer& getAntialiasingPipeline(RenderArgs* args); + + const gpu::PipelinePointer& getAntialiasingPipeline(); const gpu::PipelinePointer& getBlendPipeline(); - + private: - + // Uniforms for AA gpu::int32 _texcoordOffsetLoc; - + gpu::FramebufferPointer _antialiasingBuffer; - + gpu::TexturePointer _antialiasingTexture; - + gpu::PipelinePointer _antialiasingPipeline; gpu::PipelinePointer _blendPipeline; int _geometryId { 0 }; }; +*/ #endif // hifi_AntialiasingEffect_h diff --git a/libraries/render-utils/src/BackgroundStage.cpp b/libraries/render-utils/src/BackgroundStage.cpp index 493c28d840..886795ec79 100644 --- a/libraries/render-utils/src/BackgroundStage.cpp +++ b/libraries/render-utils/src/BackgroundStage.cpp @@ -93,7 +93,7 @@ void DrawBackgroundStage::run(const render::RenderContextPointer& renderContext, PerformanceTimer perfTimer("skybox"); auto args = renderContext->args; - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("DrawBackgroundStage::run", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; batch.enableSkybox(true); diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index 89a83a651a..ddd63f012f 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -75,7 +75,7 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons glm::ivec4 viewport{ 0, 0, bufferSize.x, bufferSize.y }; - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("BloomThreshold::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); batch.setViewportTransform(viewport); @@ -135,7 +135,7 @@ void BloomApply::run(const render::RenderContextPointer& renderContext, const In const auto blur2FB = inputs.get3(); const glm::ivec4 viewport{ 0, 0, framebufferSize.x, framebufferSize.y }; - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("BloomApply::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); batch.setFramebuffer(frameBuffer); @@ -180,7 +180,7 @@ void BloomDraw::run(const render::RenderContextPointer& renderContext, const Inp _pipeline = gpu::Pipeline::create(program, state); } - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("BloomDraw::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); batch.setFramebuffer(frameBuffer); @@ -238,7 +238,7 @@ void DebugBloom::run(const render::RenderContextPointer& renderContext, const In _pipeline = gpu::Pipeline::create(program, state); } - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("DebugBloom::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); batch.setFramebuffer(frameBuffer); diff --git a/libraries/render-utils/src/DebugDeferredBuffer.cpp b/libraries/render-utils/src/DebugDeferredBuffer.cpp index 24cffe2fb8..c17044be6d 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.cpp +++ b/libraries/render-utils/src/DebugDeferredBuffer.cpp @@ -53,7 +53,8 @@ enum TextureSlot { DiffusedCurvature, Scattering, AmbientOcclusion, - AmbientOcclusionBlurred + AmbientOcclusionBlurred, + Velocity, }; enum ParamSlot { @@ -254,6 +255,12 @@ static const std::string DEFAULT_AMBIENT_OCCLUSION_BLURRED_SHADER{ " }" }; +static const std::string DEFAULT_VELOCITY_SHADER{ + "vec4 getFragmentColor() {" + " return vec4(vec2(texture(velocityMap, uv).xy), 0.0, 1.0);" + " }" +}; + static const std::string DEFAULT_CUSTOM_SHADER { "vec4 getFragmentColor() {" " return vec4(1.0, 0.0, 0.0, 1.0);" @@ -341,6 +348,8 @@ std::string DebugDeferredBuffer::getShaderSourceCode(Mode mode, std::string cust return DEFAULT_AMBIENT_OCCLUSION_SHADER; case AmbientOcclusionBlurredMode: return DEFAULT_AMBIENT_OCCLUSION_BLURRED_SHADER; + case VelocityMode: + return DEFAULT_VELOCITY_SHADER; case CustomMode: return getFileContent(customFile, DEFAULT_CUSTOM_SHADER); default: @@ -402,6 +411,7 @@ const gpu::PipelinePointer& DebugDeferredBuffer::getPipeline(Mode mode, std::str slotBindings.insert(gpu::Shader::Binding("diffusedCurvatureMap", DiffusedCurvature)); slotBindings.insert(gpu::Shader::Binding("scatteringMap", Scattering)); slotBindings.insert(gpu::Shader::Binding("occlusionBlurredMap", AmbientOcclusionBlurred)); + slotBindings.insert(gpu::Shader::Binding("velocityMap", Velocity)); gpu::Shader::makeProgram(*program, slotBindings); auto pipeline = gpu::Pipeline::create(program, std::make_shared()); @@ -439,9 +449,10 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I auto& linearDepthTarget = inputs.get1(); auto& surfaceGeometryFramebuffer = inputs.get2(); auto& ambientOcclusionFramebuffer = inputs.get3(); - auto& frameTransform = inputs.get4(); + auto& velocityFramebuffer = inputs.get4(); + auto& frameTransform = inputs.get5(); - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("DebugDeferredBuffer::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); batch.setViewportTransform(args->_viewport); @@ -468,6 +479,9 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I batch.setResourceTexture(Depth, deferredFramebuffer->getPrimaryDepthTexture()); batch.setResourceTexture(Lighting, deferredFramebuffer->getLightingTexture()); } + if (velocityFramebuffer) { + batch.setResourceTexture(Velocity, velocityFramebuffer->getVelocityTexture()); + } auto lightStage = renderContext->_scene->getStage(); assert(lightStage); @@ -515,5 +529,7 @@ void DebugDeferredBuffer::run(const RenderContextPointer& renderContext, const I batch.setResourceTexture(AmbientOcclusion, nullptr); batch.setResourceTexture(AmbientOcclusionBlurred, nullptr); + batch.setResourceTexture(Velocity, nullptr); + }); } diff --git a/libraries/render-utils/src/DebugDeferredBuffer.h b/libraries/render-utils/src/DebugDeferredBuffer.h index 8227c4f7a3..5384a77b76 100644 --- a/libraries/render-utils/src/DebugDeferredBuffer.h +++ b/libraries/render-utils/src/DebugDeferredBuffer.h @@ -19,6 +19,7 @@ #include "DeferredFramebuffer.h" #include "SurfaceGeometryPass.h" #include "AmbientOcclusionEffect.h" +#include "VelocityBufferPass.h" class DebugDeferredBufferConfig : public render::Job::Config { Q_OBJECT @@ -38,7 +39,7 @@ signals: class DebugDeferredBuffer { public: - using Inputs = render::VaryingSet5; + using Inputs = render::VaryingSet6; using Config = DebugDeferredBufferConfig; using JobModel = render::Job::ModelI; @@ -81,6 +82,7 @@ protected: ScatteringDebugMode, AmbientOcclusionMode, AmbientOcclusionBlurredMode, + VelocityMode, CustomMode, // Needs to stay last NumModes, diff --git a/libraries/render-utils/src/DeferredFrameTransform.cpp b/libraries/render-utils/src/DeferredFrameTransform.cpp index baf523312c..d1c51bf46f 100644 --- a/libraries/render-utils/src/DeferredFrameTransform.cpp +++ b/libraries/render-utils/src/DeferredFrameTransform.cpp @@ -38,12 +38,13 @@ void DeferredFrameTransform::update(RenderArgs* args) { args->getViewFrustum().evalProjectionMatrix(frameTransformBuffer.projectionMono); - // Running in stero ? + // Running in stereo ? bool isStereo = args->isStereo(); if (!isStereo) { frameTransformBuffer.projection[0] = frameTransformBuffer.projectionMono; frameTransformBuffer.stereoInfo = glm::vec4(0.0f, (float)args->_viewport.z, 0.0f, 0.0f); frameTransformBuffer.invpixelInfo = glm::vec4(1.0f / args->_viewport.z, 1.0f / args->_viewport.w, 0.0f, 0.0f); + frameTransformBuffer.invProjection[0] = glm::inverse(frameTransformBuffer.projection[0]); } else { mat4 projMats[2]; @@ -55,6 +56,7 @@ void DeferredFrameTransform::update(RenderArgs* args) { // Compose the mono Eye space to Stereo clip space Projection Matrix auto sideViewMat = projMats[i] * eyeViews[i]; frameTransformBuffer.projection[i] = sideViewMat; + frameTransformBuffer.invProjection[i] = glm::inverse(sideViewMat); } frameTransformBuffer.stereoInfo = glm::vec4(1.0f, (float)(args->_viewport.z >> 1), 0.0f, 1.0f); diff --git a/libraries/render-utils/src/DeferredFrameTransform.h b/libraries/render-utils/src/DeferredFrameTransform.h index 93e194f052..8c2f0a7321 100644 --- a/libraries/render-utils/src/DeferredFrameTransform.h +++ b/libraries/render-utils/src/DeferredFrameTransform.h @@ -45,6 +45,8 @@ protected: glm::vec4 stereoInfo{ 0.0 }; // Mono proj matrix or Left and Right proj matrix going from Mono Eye space to side clip space glm::mat4 projection[2]; + // Inverse proj matrix or Left and Right proj matrix going from Mono Eye space to side clip space + glm::mat4 invProjection[2]; // THe mono projection for sure glm::mat4 projectionMono; // Inv View matrix from eye space (mono) to world space diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 78bc3ba195..fc35267ddc 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -431,7 +431,7 @@ void PrepareDeferred::run(const RenderContextPointer& renderContext, const Input outputs.edit0() = _deferredFramebuffer; outputs.edit1() = _deferredFramebuffer->getLightingFramebuffer(); - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("PrepareDeferred::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); diff --git a/libraries/render-utils/src/DeferredTransform.slh b/libraries/render-utils/src/DeferredTransform.slh index e7771821dc..5fccbd6a99 100644 --- a/libraries/render-utils/src/DeferredTransform.slh +++ b/libraries/render-utils/src/DeferredTransform.slh @@ -16,6 +16,9 @@ struct CameraCorrection { mat4 _correction; mat4 _correctionInverse; + + mat4 _prevView; + mat4 _prevViewInverse; }; uniform cameraCorrectionBuffer { @@ -28,6 +31,7 @@ struct DeferredFrameTransform { vec4 _depthInfo; vec4 _stereoInfo; mat4 _projection[2]; + mat4 _invProjection[2]; mat4 _projectionMono; mat4 _viewInverse; mat4 _view; @@ -37,13 +41,6 @@ uniform deferredFrameTransformBuffer { DeferredFrameTransform frameTransform; }; -DeferredFrameTransform getDeferredFrameTransform() { - DeferredFrameTransform result = frameTransform; - result._view = result._view * cameraCorrection._correctionInverse; - result._viewInverse = result._viewInverse * cameraCorrection._correction; - return result; -} - vec2 getWidthHeight(int resolutionLevel) { return vec2(ivec2(frameTransform._pixelInfo.zw) >> resolutionLevel); } @@ -79,11 +76,26 @@ float getPosLinearDepthFar() { } mat4 getViewInverse() { - return frameTransform._viewInverse * cameraCorrection._correction; + return frameTransform._viewInverse * cameraCorrection._correctionInverse; } mat4 getView() { - return frameTransform._view * cameraCorrection._correctionInverse; + return cameraCorrection._correction * frameTransform._view; +} + +mat4 getPreviousView() { + return cameraCorrection._prevView; +} + +mat4 getPreviousViewInverse() { + return cameraCorrection._prevViewInverse; +} + +DeferredFrameTransform getDeferredFrameTransform() { + DeferredFrameTransform result = frameTransform; + result._view = getView(); + result._viewInverse = getViewInverse(); + return result; } bool isStereo() { @@ -123,6 +135,14 @@ vec3 evalEyePositionFromZeye(int side, float Zeye, vec2 texcoord) { return vec3(Xe, Ye, Zeye); } +vec3 evalEyePositionFromZdb(int side, float Zdb, vec2 texcoord) { + // compute the view space position using the depth + vec3 clipPos; + clipPos.xyz = vec3(texcoord.xy, Zdb) * 2.0 - 1.0; + vec4 eyePos = frameTransform._invProjection[side] * vec4(clipPos.xyz, 1.0); + return eyePos.xyz / eyePos.w; +} + ivec2 getPixelPosTexcoordPosAndSide(in vec2 glFragCoord, out ivec2 pixelPos, out vec2 texcoordPos, out ivec4 stereoSide) { ivec2 fragPos = ivec2(glFragCoord.xy); diff --git a/libraries/render-utils/src/DrawHaze.cpp b/libraries/render-utils/src/DrawHaze.cpp index c0db1597ca..78569b2837 100644 --- a/libraries/render-utils/src/DrawHaze.cpp +++ b/libraries/render-utils/src/DrawHaze.cpp @@ -155,7 +155,7 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu auto sourceFramebufferSize = glm::ivec2(inputBuffer->getDimensions()); - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("DrawHaze::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); batch.setFramebuffer(outputBuffer); diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 7455da13b6..212cf5eae1 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -75,32 +75,6 @@ static std::array MAPPING GeometryCache::Cylinder, } }; -/**jsdoc -*

{@link Entities} and {@link Overlays} may have the following geometrical shapes:

-* -* -* -* -* -* -* -* -* -* -* -* -* -* -* -* -* -* -* -* -* -*
ValueDescription
LineA 1D line oriented in 3 dimensions.
TriangleA triangular prism.
QuadA 2D square oriented in 3 dimensions.
HexagonA hexagonal prism.
OctagonAn octagonal prism.
CircleA 2D circle oriented in 3 dimensions.
CubeA cube.
SphereA sphere.
TetrahedronA tetrahedron.
OctahedronAn octahedron.
DodecahedronA dodecahedron.
IcosahedronAn icosahedron.
TorusA torus. Not implemented.
ConeA cone.
CylinderA cylinder.
-* @typedef {string} Shape -*/ static const std::array GEOCACHE_SHAPE_STRINGS{ { "Line", "Triangle", diff --git a/libraries/render-utils/src/HighlightEffect.cpp b/libraries/render-utils/src/HighlightEffect.cpp index 0bf8e7fa71..d151da766b 100644 --- a/libraries/render-utils/src/HighlightEffect.cpp +++ b/libraries/render-utils/src/HighlightEffect.cpp @@ -161,7 +161,7 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c // Clear the framebuffer without stereo // Needs to be distinct from the other batch because using the clear call // while stereo is enabled triggers a warning - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("DrawHighlightMask::run::begin", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); batch.setFramebuffer(ressources->getDepthFramebuffer()); batch.clearDepthStencilFramebuffer(1.0f, 0); @@ -174,7 +174,7 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c render::ItemBounds itemBounds; - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("DrawHighlightMask::run", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; auto maskPipeline = _shapePlumber->pickPipeline(args, defaultKeyBuilder); @@ -212,7 +212,7 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c _boundsBuffer->setData(itemBounds.size() * sizeof(render::ItemBound), (const gpu::Byte*) itemBounds.data()); - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("DrawHighlightMask::run::end", args->_context, [&](gpu::Batch& batch) { // Setup camera, projection and viewport for all items batch.setViewportTransform(args->_viewport); batch.setProjectionTransform(projMat); @@ -284,7 +284,7 @@ void DrawHighlight::run(const render::RenderContextPointer& renderContext, const shaderParameters._size.y = size; } - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("DrawHighlight::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); batch.setFramebuffer(destinationFrameBuffer); @@ -357,7 +357,7 @@ void DebugHighlight::run(const render::RenderContextPointer& renderContext, cons assert(renderContext->args->hasViewFrustum()); RenderArgs* args = renderContext->args; - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("DebugHighlight::run", args->_context, [&](gpu::Batch& batch) { batch.setViewportTransform(args->_viewport); batch.setFramebuffer(highlightRessources->getColorFramebuffer()); diff --git a/libraries/render-utils/src/RenderCommonTask.cpp b/libraries/render-utils/src/RenderCommonTask.cpp index a824a2221b..d721664794 100644 --- a/libraries/render-utils/src/RenderCommonTask.cpp +++ b/libraries/render-utils/src/RenderCommonTask.cpp @@ -8,53 +8,23 @@ #include "RenderCommonTask.h" -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include - -#include "LightingModel.h" -#include "StencilMaskPass.h" -#include "DebugDeferredBuffer.h" -#include "DeferredFramebuffer.h" #include "DeferredLightingEffect.h" -#include "SurfaceGeometryPass.h" -#include "FramebufferCache.h" -#include "TextureCache.h" -#include "ZoneRenderer.h" -#include "FadeEffect.h" #include "RenderUtilsLogging.h" -#include "AmbientOcclusionEffect.h" -#include "AntialiasingEffect.h" -#include "ToneMappingEffect.h" -#include "SubsurfaceScattering.h" -#include "DrawHaze.h" -#include "BloomEffect.h" -#include "HighlightEffect.h" - -#include - using namespace render; extern void initOverlay3DPipelines(render::ShapePlumber& plumber, bool depthTest = false); void BeginGPURangeTimer::run(const render::RenderContextPointer& renderContext, gpu::RangeTimerPointer& timer) { timer = _gpuTimer; - gpu::doInBatch(renderContext->args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("BeginGPURangeTimer", renderContext->args->_context, [&](gpu::Batch& batch) { _gpuTimer->begin(batch); }); } void EndGPURangeTimer::run(const render::RenderContextPointer& renderContext, const gpu::RangeTimerPointer& timer) { - gpu::doInBatch(renderContext->args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("EndGPURangeTimer", renderContext->args->_context, [&](gpu::Batch& batch) { timer->end(batch); }); @@ -87,14 +57,14 @@ void DrawOverlay3D::run(const RenderContextPointer& renderContext, const Inputs& // Needs to be distinct from the other batch because using the clear call // while stereo is enabled triggers a warning if (_opaquePass) { - gpu::doInBatch(args->_context, [&](gpu::Batch& batch){ + gpu::doInBatch("DrawOverlay3D::run::clear", args->_context, [&](gpu::Batch& batch){ batch.enableStereo(false); batch.clearFramebuffer(gpu::Framebuffer::BUFFER_DEPTH, glm::vec4(), 1.f, 0, false); }); } // Render the items - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("DrawOverlay3D::main", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); @@ -127,7 +97,7 @@ void CompositeHUD::run(const RenderContextPointer& renderContext) { // Grab the HUD texture #if !defined(DISABLE_QML) - gpu::doInBatch(renderContext->args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("CompositeHUD", renderContext->args->_context, [&](gpu::Batch& batch) { if (renderContext->args->_hudOperator) { renderContext->args->_hudOperator(batch, renderContext->args->_hudTexture, renderContext->args->_renderMode == RenderArgs::RenderMode::MIRROR_RENDER_MODE); } @@ -154,7 +124,7 @@ void Blit::run(const RenderContextPointer& renderContext, const gpu::Framebuffer // Blit primary to blit FBO auto primaryFbo = srcFramebuffer; - gpu::doInBatch(renderArgs->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("Blit", renderArgs->_context, [&](gpu::Batch& batch) { batch.setFramebuffer(blitFbo); if (renderArgs->_renderMode == RenderArgs::MIRROR_RENDER_MODE) { diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 50620bfcce..2377f5131f 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -34,6 +34,7 @@ #include "DeferredFramebuffer.h" #include "DeferredLightingEffect.h" #include "SurfaceGeometryPass.h" +#include "VelocityBufferPass.h" #include "FramebufferCache.h" #include "TextureCache.h" #include "ZoneRenderer.h" @@ -94,9 +95,12 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren fadeEffect->build(task, opaques); + task.addJob("JitterCam"); + // Prepare deferred, generate the shared Deferred Frame Transform const auto deferredFrameTransform = task.addJob("DeferredFrameTransform"); const auto lightingModel = task.addJob("LightingModel"); + // GPU jobs: Start preparing the primary, deferred and lighting buffer const auto primaryFramebuffer = task.addJob("PreparePrimaryBuffer"); @@ -142,6 +146,11 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto ambientOcclusionFramebuffer = ambientOcclusionOutputs.getN(0); const auto ambientOcclusionUniforms = ambientOcclusionOutputs.getN(1); + // Velocity + const auto velocityBufferInputs = VelocityBufferPass::Inputs(deferredFrameTransform, deferredFramebuffer).asVarying(); + const auto velocityBufferOutputs = task.addJob("VelocityBuffer", velocityBufferInputs); + const auto velocityBuffer = velocityBufferOutputs.getN(0); + // Clear Light, Haze and Skybox Stages and render zones from the general metas bucket const auto zones = task.addJob("ZoneRenderer", metas); @@ -162,6 +171,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("RenderDeferred", deferredLightingInputs); + // Similar to light stage, background stage has been filled by several potential render items and resolved for the frame in this job task.addJob("DrawBackgroundDeferred", lightingModel); @@ -220,10 +230,30 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("DrawSelectionBounds", selectedItems); } - // Debugging stages + // Layered Overlays + const auto filteredOverlaysOpaque = task.addJob("FilterOverlaysLayeredOpaque", overlayOpaques, Item::LAYER_3D_FRONT); + const auto filteredOverlaysTransparent = task.addJob("FilterOverlaysLayeredTransparent", overlayTransparents, Item::LAYER_3D_FRONT); + const auto overlaysInFrontOpaque = filteredOverlaysOpaque.getN(0); + const auto overlaysInFrontTransparent = filteredOverlaysTransparent.getN(0); + + const auto overlayInFrontOpaquesInputs = DrawOverlay3D::Inputs(overlaysInFrontOpaque, lightingModel).asVarying(); + const auto overlayInFrontTransparentsInputs = DrawOverlay3D::Inputs(overlaysInFrontTransparent, lightingModel).asVarying(); + task.addJob("DrawOverlayInFrontOpaque", overlayInFrontOpaquesInputs, true); + task.addJob("DrawOverlayInFrontTransparent", overlayInFrontTransparentsInputs, false); + + { // Debug the bounds of the rendered Overlay items that are marked drawInFront, still look at the zbuffer + task.addJob("DrawOverlayInFrontOpaqueBounds", overlaysInFrontOpaque); + task.addJob("DrawOverlayInFrontTransparentBounds", overlaysInFrontTransparent); + } + + // AA job + const auto antialiasingInputs = Antialiasing::Inputs(deferredFrameTransform, primaryFramebuffer, linearDepthTarget, velocityBuffer).asVarying(); + task.addJob("Antialiasing", antialiasingInputs); + + // Debugging stages { // Debugging Deferred buffer job - const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, deferredFrameTransform)); + const auto debugFramebuffers = render::Varying(DebugDeferredBuffer::Inputs(deferredFramebuffer, linearDepthTarget, surfaceGeometryFramebuffer, ambientOcclusionFramebuffer, velocityBuffer, deferredFrameTransform)); task.addJob("DebugDeferredBuffer", debugFramebuffers); const auto debugSubsurfaceScatteringInputs = DebugSubsurfaceScattering::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel, @@ -250,25 +280,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("DrawZoneStack", deferredFrameTransform); } - // Layered Overlays - const auto filteredOverlaysOpaque = task.addJob("FilterOverlaysLayeredOpaque", overlayOpaques, Item::LAYER_3D_FRONT); - const auto filteredOverlaysTransparent = task.addJob("FilterOverlaysLayeredTransparent", overlayTransparents, Item::LAYER_3D_FRONT); - const auto overlaysInFrontOpaque = filteredOverlaysOpaque.getN(0); - const auto overlaysInFrontTransparent = filteredOverlaysTransparent.getN(0); - - const auto overlayInFrontOpaquesInputs = DrawOverlay3D::Inputs(overlaysInFrontOpaque, lightingModel).asVarying(); - const auto overlayInFrontTransparentsInputs = DrawOverlay3D::Inputs(overlaysInFrontTransparent, lightingModel).asVarying(); - task.addJob("DrawOverlayInFrontOpaque", overlayInFrontOpaquesInputs, true); - task.addJob("DrawOverlayInFrontTransparent", overlayInFrontTransparentsInputs, false); - - { // Debug the bounds of the rendered Overlay items that are marked drawInFront, still look at the zbuffer - task.addJob("DrawOverlayInFrontOpaqueBounds", overlaysInFrontOpaque); - task.addJob("DrawOverlayInFrontTransparentBounds", overlaysInFrontTransparent); - } - - // AA job to be revisited - task.addJob("Antialiasing", primaryFramebuffer); - // Composite the HUD and HUD overlays task.addJob("HUD"); @@ -304,7 +315,7 @@ void DrawDeferred::run(const RenderContextPointer& renderContext, const Inputs& RenderArgs* args = renderContext->args; - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("DrawDeferred::run", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; // Setup camera, projection and viewport for all items @@ -371,7 +382,7 @@ void DrawStateSortDeferred::run(const RenderContextPointer& renderContext, const RenderArgs* args = renderContext->args; - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("DrawStateSortDeferred::run", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; // Setup camera, projection and viewport for all items @@ -409,4 +420,3 @@ void DrawStateSortDeferred::run(const RenderContextPointer& renderContext, const config->setNumDrawn((int)inItems.size()); } - diff --git a/libraries/render-utils/src/RenderForwardTask.cpp b/libraries/render-utils/src/RenderForwardTask.cpp index 8bf060751f..63370109e0 100755 --- a/libraries/render-utils/src/RenderForwardTask.cpp +++ b/libraries/render-utils/src/RenderForwardTask.cpp @@ -136,7 +136,7 @@ void PrepareFramebuffer::run(const RenderContextPointer& renderContext, gpu::Fra } auto args = renderContext->args; - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("PrepareFramebuffer::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); @@ -152,7 +152,8 @@ void PrepareFramebuffer::run(const RenderContextPointer& renderContext, gpu::Fra void PrepareForward::run(const RenderContextPointer& renderContext, const Inputs& inputs) { RenderArgs* args = renderContext->args; - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + + gpu::doInBatch("RenderForward::Draw::run", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; graphics::LightPointer keySunLight; @@ -186,7 +187,7 @@ void DrawForward::run(const RenderContextPointer& renderContext, const Inputs& i const auto& inItems = inputs.get0(); const auto& lightingModel = inputs.get1(); - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("DrawForward::run", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp index afb6f00bae..69c5b3c689 100644 --- a/libraries/render-utils/src/RenderShadowTask.cpp +++ b/libraries/render-utils/src/RenderShadowTask.cpp @@ -140,7 +140,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con args->popViewFrustum(); args->pushViewFrustum(adjustedShadowFrustum); - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("RenderShadowMap::run", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; batch.enableStereo(false); diff --git a/libraries/render-utils/src/StencilMaskPass.cpp b/libraries/render-utils/src/StencilMaskPass.cpp index f9af157e7c..b9b8274039 100644 --- a/libraries/render-utils/src/StencilMaskPass.cpp +++ b/libraries/render-utils/src/StencilMaskPass.cpp @@ -81,7 +81,7 @@ void PrepareStencil::run(const RenderContextPointer& renderContext, const gpu::F return; } - doInBatch(args->_context, [&](gpu::Batch& batch) { + doInBatch("PrepareStencil::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); batch.setViewportTransform(args->_viewport); diff --git a/libraries/render-utils/src/SubsurfaceScattering.cpp b/libraries/render-utils/src/SubsurfaceScattering.cpp index ff415accc3..50067d003f 100644 --- a/libraries/render-utils/src/SubsurfaceScattering.cpp +++ b/libraries/render-utils/src/SubsurfaceScattering.cpp @@ -322,7 +322,7 @@ void diffuseProfileGPU(gpu::TexturePointer& profileMap, RenderArgs* args) { auto makeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("diffuseProfile")); makeFramebuffer->setRenderBuffer(0, profileMap); - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch("SubsurfaceScattering::diffuseProfileGPU", args->_context, [=](gpu::Batch& batch) { batch.enableStereo(false); batch.setViewportTransform(glm::ivec4(0, 0, width, height)); @@ -359,7 +359,7 @@ void diffuseScatterGPU(const gpu::TexturePointer& profileMap, gpu::TexturePointe auto makeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("diffuseScatter")); makeFramebuffer->setRenderBuffer(0, lut); - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch("SubsurfaceScattering::diffuseScatterGPU", args->_context, [=](gpu::Batch& batch) { batch.enableStereo(false); batch.setViewportTransform(glm::ivec4(0, 0, width, height)); @@ -396,7 +396,7 @@ void computeSpecularBeckmannGPU(gpu::TexturePointer& beckmannMap, RenderArgs* ar auto makeFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("computeSpecularBeckmann")); makeFramebuffer->setRenderBuffer(0, beckmannMap); - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch("SubsurfaceScattering::computeSpecularBeckmannGPU", args->_context, [=](gpu::Batch& batch) { batch.enableStereo(false); batch.setViewportTransform(glm::ivec4(0, 0, width, height)); @@ -537,7 +537,7 @@ void DebugSubsurfaceScattering::run(const render::RenderContextPointer& renderCo // const auto light = DependencyManager::get()->getLightStage()->getLight(0); const auto light = lightStage->getLight(0); - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch("DebugSubsurfaceScattering::run", args->_context, [=](gpu::Batch& batch) { batch.enableStereo(false); diff --git a/libraries/render-utils/src/SurfaceGeometryPass.cpp b/libraries/render-utils/src/SurfaceGeometryPass.cpp index afed9ee8fd..cfdb67ecb6 100644 --- a/libraries/render-utils/src/SurfaceGeometryPass.cpp +++ b/libraries/render-utils/src/SurfaceGeometryPass.cpp @@ -174,7 +174,7 @@ void LinearDepthPass::run(const render::RenderContextPointer& renderContext, con auto halfViewport = depthViewport >> 1; float clearLinearDepth = args->getViewFrustum().getFarClip() * 2.0f; - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch("LinearDepthPass::run", args->_context, [=](gpu::Batch& batch) { _gpuTimer->begin(batch); batch.enableStereo(false); @@ -466,7 +466,7 @@ void SurfaceGeometryPass::run(const render::RenderContextPointer& renderContext, _diffusePass.getParameters()->setLinearDepthPosFar(args->getViewFrustum().getFarClip()); - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch("SurfaceGeometryPass::run", args->_context, [=](gpu::Batch& batch) { _gpuTimer->begin(batch); batch.enableStereo(false); diff --git a/libraries/render-utils/src/ToneMappingEffect.cpp b/libraries/render-utils/src/ToneMappingEffect.cpp index 0cc5be5ff9..6857de62a7 100644 --- a/libraries/render-utils/src/ToneMappingEffect.cpp +++ b/libraries/render-utils/src/ToneMappingEffect.cpp @@ -68,7 +68,7 @@ void ToneMappingEffect::render(RenderArgs* args, const gpu::TexturePointer& ligh } auto framebufferSize = glm::ivec2(lightingBuffer->getDimensions()); - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("ToneMappingEffect::render", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); batch.setFramebuffer(destinationFramebuffer); diff --git a/libraries/render-utils/src/VelocityBufferPass.cpp b/libraries/render-utils/src/VelocityBufferPass.cpp new file mode 100644 index 0000000000..78471d48af --- /dev/null +++ b/libraries/render-utils/src/VelocityBufferPass.cpp @@ -0,0 +1,173 @@ +// +// VelocityBufferPass.cpp +// libraries/render-utils/src/ +// +// Created by Sam Gateau 8/15/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "VelocityBufferPass.h" + +#include + +#include +#include +#include "StencilMaskPass.h" + +const int VelocityBufferPass_FrameTransformSlot = 0; +const int VelocityBufferPass_DepthMapSlot = 0; + + +#include "velocityBuffer_cameraMotion_frag.h" + +VelocityFramebuffer::VelocityFramebuffer() { +} + + +void VelocityFramebuffer::updatePrimaryDepth(const gpu::TexturePointer& depthBuffer) { + //If the depth buffer or size changed, we need to delete our FBOs + bool reset = false; + if ((_primaryDepthTexture != depthBuffer)) { + _primaryDepthTexture = depthBuffer; + reset = true; + } + if (_primaryDepthTexture) { + auto newFrameSize = glm::ivec2(_primaryDepthTexture->getDimensions()); + if (_frameSize != newFrameSize) { + _frameSize = newFrameSize; + _halfFrameSize = newFrameSize >> 1; + + reset = true; + } + } + + if (reset) { + clear(); + } +} + +void VelocityFramebuffer::clear() { + _velocityFramebuffer.reset(); + _velocityTexture.reset(); +} + +void VelocityFramebuffer::allocate() { + + auto width = _frameSize.x; + auto height = _frameSize.y; + + // For Velocity Buffer: + _velocityTexture = gpu::Texture::createRenderBuffer(gpu::Element(gpu::VEC2, gpu::HALF, gpu::RGB), width, height, gpu::Texture::SINGLE_MIP, + gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR)); + _velocityFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("velocity")); + _velocityFramebuffer->setRenderBuffer(0, _velocityTexture); + _velocityFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, _primaryDepthTexture->getTexelFormat()); +} + +gpu::FramebufferPointer VelocityFramebuffer::getVelocityFramebuffer() { + if (!_velocityFramebuffer) { + allocate(); + } + return _velocityFramebuffer; +} + +gpu::TexturePointer VelocityFramebuffer::getVelocityTexture() { + if (!_velocityTexture) { + allocate(); + } + return _velocityTexture; +} + +VelocityBufferPass::VelocityBufferPass() { +} + +void VelocityBufferPass::configure(const Config& config) { +} + +void VelocityBufferPass::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { + assert(renderContext->args); + assert(renderContext->args->hasViewFrustum()); + + RenderArgs* args = renderContext->args; + + const auto& frameTransform = inputs.get0(); + const auto& deferredFramebuffer = inputs.get1(); + + if (!_gpuTimer) { + _gpuTimer = std::make_shared < gpu::RangeTimer>(__FUNCTION__); + } + + if (!_velocityFramebuffer) { + _velocityFramebuffer = std::make_shared(); + } + _velocityFramebuffer->updatePrimaryDepth(deferredFramebuffer->getPrimaryDepthTexture()); + + auto depthBuffer = deferredFramebuffer->getPrimaryDepthTexture(); + + auto velocityFBO = _velocityFramebuffer->getVelocityFramebuffer(); + auto velocityTexture = _velocityFramebuffer->getVelocityTexture(); + + outputs.edit0() = _velocityFramebuffer; + outputs.edit1() = velocityFBO; + outputs.edit2() = velocityTexture; + + auto cameraMotionPipeline = getCameraMotionPipeline(); + + auto fullViewport = args->_viewport; + + gpu::doInBatch("VelocityBufferPass::run", args->_context, [=](gpu::Batch& batch) { + _gpuTimer->begin(batch); + batch.enableStereo(false); + + batch.setViewportTransform(fullViewport); + batch.setProjectionTransform(glm::mat4()); + batch.resetViewTransform(); + batch.setModelTransform(gpu::Framebuffer::evalSubregionTexcoordTransform(_velocityFramebuffer->getDepthFrameSize(), fullViewport)); + + batch.setUniformBuffer(VelocityBufferPass_FrameTransformSlot, frameTransform->getFrameTransformBuffer()); + + // Velocity buffer camera motion + batch.setFramebuffer(velocityFBO); + batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, glm::vec4(0.0f, 0.0f, 0.0f, 0.0f)); + batch.setPipeline(cameraMotionPipeline); + batch.setResourceTexture(VelocityBufferPass_DepthMapSlot, depthBuffer); + batch.draw(gpu::TRIANGLE_STRIP, 4); + + _gpuTimer->end(batch); + }); + + auto config = std::static_pointer_cast(renderContext->jobConfig); + config->setGPUBatchRunTime(_gpuTimer->getGPUAverage(), _gpuTimer->getBatchAverage()); +} + + +const gpu::PipelinePointer& VelocityBufferPass::getCameraMotionPipeline() { + if (!_cameraMotionPipeline) { + auto vs = gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS(); + auto ps = velocityBuffer_cameraMotion_frag::getShader(); + gpu::ShaderPointer program = gpu::Shader::createProgram(vs, ps); + + gpu::Shader::BindingSet slotBindings; + slotBindings.insert(gpu::Shader::Binding(std::string("deferredFrameTransformBuffer"), VelocityBufferPass_FrameTransformSlot)); + slotBindings.insert(gpu::Shader::Binding(std::string("depthMap"), VelocityBufferPass_DepthMapSlot)); + gpu::Shader::makeProgram(*program, slotBindings); + + + gpu::StatePointer state = gpu::StatePointer(new gpu::State()); + + // Stencil test the curvature pass for objects pixels only, not the background + // PrepareStencil::testShape(*state); + + state->setColorWriteMask(true, true, false, false); + + // Good to go add the brand new pipeline + _cameraMotionPipeline = gpu::Pipeline::create(program, state); + } + + return _cameraMotionPipeline; +} + + + diff --git a/libraries/render-utils/src/VelocityBufferPass.h b/libraries/render-utils/src/VelocityBufferPass.h new file mode 100644 index 0000000000..fb2b729368 --- /dev/null +++ b/libraries/render-utils/src/VelocityBufferPass.h @@ -0,0 +1,89 @@ +// +// VelocityBufferPass.h +// libraries/render-utils/src/ +// +// Created by Sam Gateau 8/15/2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_VelocityBufferPass_h +#define hifi_VelocityBufferPass_h + +#include "SurfaceGeometryPass.h" + + +// VelocityFramebuffer is a helper class gathering in one place theframebuffers and targets describing the surface geometry linear depth +// from a z buffer +class VelocityFramebuffer { +public: + VelocityFramebuffer(); + + gpu::FramebufferPointer getVelocityFramebuffer(); + gpu::TexturePointer getVelocityTexture(); + + // Update the depth buffer which will drive the allocation of all the other resources according to its size. + void updatePrimaryDepth(const gpu::TexturePointer& depthBuffer); + + gpu::TexturePointer getPrimaryDepthTexture(); + const glm::ivec2& getDepthFrameSize() const { return _frameSize; } + + void setResolutionLevel(int level); + int getResolutionLevel() const { return _resolutionLevel; } + +protected: + void clear(); + void allocate(); + + gpu::TexturePointer _primaryDepthTexture; + + gpu::FramebufferPointer _velocityFramebuffer; + gpu::TexturePointer _velocityTexture; + + glm::ivec2 _frameSize; + glm::ivec2 _halfFrameSize; + int _resolutionLevel{ 0 }; +}; + +using VelocityFramebufferPointer = std::shared_ptr; + +class VelocityBufferPassConfig : public render::GPUJobConfig { + Q_OBJECT + Q_PROPERTY(float depthThreshold MEMBER depthThreshold NOTIFY dirty) + +public: + VelocityBufferPassConfig() : render::GPUJobConfig(true) {} + + float depthThreshold{ 5.0f }; + +signals: + void dirty(); +}; + +class VelocityBufferPass { +public: + using Inputs = render::VaryingSet2; + using Outputs = render::VaryingSet3; + using Config = VelocityBufferPassConfig; + using JobModel = render::Job::ModelIO; + + VelocityBufferPass(); + + void configure(const Config& config); + void run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs); + +private: + typedef gpu::BufferView UniformBufferView; + + VelocityFramebufferPointer _velocityFramebuffer; + + const gpu::PipelinePointer& getCameraMotionPipeline(); + gpu::PipelinePointer _cameraMotionPipeline; + + gpu::RangeTimerPointer _gpuTimer; +}; + + +#endif // hifi_VelocityBufferPass_h diff --git a/libraries/render-utils/src/ZoneRenderer.cpp b/libraries/render-utils/src/ZoneRenderer.cpp index b6d0e61577..51939efd4f 100644 --- a/libraries/render-utils/src/ZoneRenderer.cpp +++ b/libraries/render-utils/src/ZoneRenderer.cpp @@ -171,7 +171,7 @@ void DebugZoneLighting::run(const render::RenderContextPointer& context, const I } - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch("DebugZoneLighting::run", args->_context, [=](gpu::Batch& batch) { batch.setViewportTransform(args->_viewport); auto viewFrustum = args->getViewFrustum(); diff --git a/libraries/render-utils/src/animdebugdraw.slv b/libraries/render-utils/src/animdebugdraw.slv index ffa44b6cee..3255c6783c 100644 --- a/libraries/render-utils/src/animdebugdraw.slv +++ b/libraries/render-utils/src/animdebugdraw.slv @@ -17,7 +17,7 @@ out vec4 _color; void main(void) { // pass along the color - _color = colorToLinearRGBA(inColor.rgba); + _color = color_sRGBAToLinear(inColor.rgba); TransformCamera cam = getTransformCamera(); TransformObject obj = getTransformObject(); diff --git a/libraries/render-utils/src/debug_deferred_buffer.slf b/libraries/render-utils/src/debug_deferred_buffer.slf index 426de623a1..fded04ca87 100644 --- a/libraries/render-utils/src/debug_deferred_buffer.slf +++ b/libraries/render-utils/src/debug_deferred_buffer.slf @@ -22,6 +22,7 @@ uniform sampler2D halfNormalMap; uniform sampler2D occlusionMap; uniform sampler2D occlusionBlurredMap; uniform sampler2D scatteringMap; +uniform sampler2D velocityMap; <@include ShadowCore.slh@> diff --git a/libraries/render-utils/src/fxaa.slf b/libraries/render-utils/src/fxaa.slf index 7b241a3ebf..94fa75c47f 100644 --- a/libraries/render-utils/src/fxaa.slf +++ b/libraries/render-utils/src/fxaa.slf @@ -23,72 +23,112 @@ precision mediump int; #endif uniform sampler2D colorTexture; +//uniform sampler2D historyTexture; uniform vec2 texcoordOffset; -in vec2 varTexcoord; -out vec4 outFragColor; +in vec2 varTexCoord0; +layout(location = 0) out vec4 outFragColor; +//layout(location = 0) out vec4 outFragHistory; void main() { - // filter width limit for dependent "two-tap" texture samples - float FXAA_SPAN_MAX = 8.0; + outFragColor = vec4(texture(colorTexture, varTexCoord0).xyz, 1.0/8.0); - // local contrast multiplier for performing AA - // higher = sharper, but setting this value too high will cause near-vertical and near-horizontal edges to fail - // see "fxaaQualityEdgeThreshold" - float FXAA_REDUCE_MUL = 1.0 / 8.0; + // v2 + /* float ModulationFactor = 1.0 / 8.0; - // luminance threshold for processing dark colors - // see "fxaaQualityEdgeThresholdMin" - float FXAA_REDUCE_MIN = 1.0 / 128.0; + vec3 History = textureLod(historyTexture, varTexCoord0, 0.0).rgb; + vec3 CurrentSubpixel = textureLod(colorTexture, varTexCoord0, 0.0).rgb; + /* + vec3 NearColor0 = textureLodOffset(colorTexture, varTexCoord0, 0.0, ivec2(1, 0)).xyz; + vec3 NearColor1 = textureLodOffset(colorTexture, varTexCoord0, 0.0, ivec2(0, 1)).xyz; + vec3 NearColor2 = textureLodOffset(colorTexture, varTexCoord0, 0.0, ivec2(-1, 0)).xyz; + vec3 NearColor3 = textureLodOffset(colorTexture, varTexCoord0, 0.0, ivec2(0, -1)).xyz; - // fetch raw RGB values for nearby locations - // sampling pattern is "five on a die" (each diagonal direction and the center) - // computing the coordinates for these texture reads could be moved to the vertex shader for speed if needed - vec3 rgbNW = texture(colorTexture, varTexcoord + (vec2(-1.0, -1.0) * texcoordOffset)).xyz; - vec3 rgbNE = texture(colorTexture, varTexcoord + (vec2(+1.0, -1.0) * texcoordOffset)).xyz; - vec3 rgbSW = texture(colorTexture, varTexcoord + (vec2(-1.0, +1.0) * texcoordOffset)).xyz; - vec3 rgbSE = texture(colorTexture, varTexcoord + (vec2(+1.0, +1.0) * texcoordOffset)).xyz; - vec3 rgbM = texture(colorTexture, varTexcoord).xyz; - - // convert RGB values to luminance - vec3 luma = vec3(0.299, 0.587, 0.114); - float lumaNW = dot(rgbNW, luma); - float lumaNE = dot(rgbNE, luma); - float lumaSW = dot(rgbSW, luma); - float lumaSE = dot(rgbSE, luma); - float lumaM = dot( rgbM, luma); - - // luma range of local neighborhood - float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); - float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); - - // direction perpendicular to local luma gradient - vec2 dir; - dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); - dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); + vec3 BoxMin = min(CurrentSubpixel, min(NearColor0, min(NearColor1, min(NearColor2, NearColor3)))); + vec3 BoxMax = max(CurrentSubpixel, max(NearColor0, max(NearColor1, max(NearColor2, NearColor3))));; - // compute clamped direction offset for additional "two-tap" samples - // longer vector = blurry, shorter vector = sharp - float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); - float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); - dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), - max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * texcoordOffset; - - // perform additional texture sampling perpendicular to gradient - vec3 rgbA = (1.0 / 2.0) * ( - texture(colorTexture, varTexcoord + dir * (1.0 / 3.0 - 0.5)).xyz + - texture(colorTexture, varTexcoord + dir * (2.0 / 3.0 - 0.5)).xyz); - vec3 rgbB = rgbA * (1.0 / 2.0) + (1.0 / 4.0) * ( - texture(colorTexture, varTexcoord + dir * (0.0 / 3.0 - 0.5)).xyz + - texture(colorTexture, varTexcoord + dir * (3.0 / 3.0 - 0.5)).xyz); - float lumaB = dot(rgbB, luma); + if (gl_FragCoord.x > 800) { + History = clamp(History, BoxMin, BoxMax); + } - // compare luma of new samples to the luma range of the original neighborhood - // if the new samples exceed this range, just use the first two samples instead of all four - if (lumaB < lumaMin || lumaB > lumaMax) { - outFragColor.xyz=rgbA; - } else { - outFragColor.xyz=rgbB; + History = mix(CurrentSubpixel, History, ModulationFactor); + + /* outFragHistory.xyz = History; + outFragHistory.w = ModulationFactor + + outFragColor.xyz = History; + outFragColor.w = 1.0;*/ + + + + /* } else { + outFragColor.xyz = CurrentSubpixel; + outFragColor.w = 1.0; + + }*/ + if (gl_FragCoord.x > 800) { + /* // filter width limit for dependent "two-tap" texture samples + float FXAA_SPAN_MAX = 8.0; + + // local contrast multiplier for performing AA + // higher = sharper, but setting this value too high will cause near-vertical and near-horizontal edges to fail + // see "fxaaQualityEdgeThreshold" + float FXAA_REDUCE_MUL = 1.0 / 8.0; + + // luminance threshold for processing dark colors + // see "fxaaQualityEdgeThresholdMin" + float FXAA_REDUCE_MIN = 1.0 / 128.0; + + // fetch raw RGB values for nearby locations + // sampling pattern is "five on a die" (each diagonal direction and the center) + // computing the coordinates for these texture reads could be moved to the vertex shader for speed if needed + vec3 rgbNW = texture(colorTexture, varTexCoord0 + (vec2(-1.0, -1.0) * texcoordOffset)).xyz; + vec3 rgbNE = texture(colorTexture, varTexCoord0 + (vec2(+1.0, -1.0) * texcoordOffset)).xyz; + vec3 rgbSW = texture(colorTexture, varTexCoord0 + (vec2(-1.0, +1.0) * texcoordOffset)).xyz; + vec3 rgbSE = texture(colorTexture, varTexCoord0 + (vec2(+1.0, +1.0) * texcoordOffset)).xyz; + vec3 rgbM = texture(colorTexture, varTexCoord0).xyz; + + // convert RGB values to luminance + vec3 luma = vec3(0.299, 0.587, 0.114); + float lumaNW = dot(rgbNW, luma); + float lumaNE = dot(rgbNE, luma); + float lumaSW = dot(rgbSW, luma); + float lumaSE = dot(rgbSE, luma); + float lumaM = dot(rgbM, luma); + + // luma range of local neighborhood + float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); + float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); + + // direction perpendicular to local luma gradient + vec2 dir; + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); + + // compute clamped direction offset for additional "two-tap" samples + // longer vector = blurry, shorter vector = sharp + float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); + float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); + dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), + max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * texcoordOffset; + + // perform additional texture sampling perpendicular to gradient + vec3 rgbA = (1.0 / 2.0) * ( + texture(colorTexture, varTexCoord0 + dir * (1.0 / 3.0 - 0.5)).xyz + + texture(colorTexture, varTexCoord0 + dir * (2.0 / 3.0 - 0.5)).xyz); + vec3 rgbB = rgbA * (1.0 / 2.0) + (1.0 / 4.0) * ( + texture(colorTexture, varTexCoord0 + dir * (0.0 / 3.0 - 0.5)).xyz + + texture(colorTexture, varTexCoord0 + dir * (3.0 / 3.0 - 0.5)).xyz); + float lumaB = dot(rgbB, luma); + + // compare luma of new samples to the luma range of the original neighborhood + // if the new samples exceed this range, just use the first two samples instead of all four + if (lumaB < lumaMin || lumaB > lumaMax) { + outFragColor.xyz = rgbA; + } + else { + outFragColor.xyz = rgbB; + }*/ + outFragColor.a = 1.0; } - outFragColor.a = 1.0; } diff --git a/libraries/render-utils/src/fxaa_blend.slf b/libraries/render-utils/src/fxaa_blend.slf index d5819cc9a6..7a10cecb94 100644 --- a/libraries/render-utils/src/fxaa_blend.slf +++ b/libraries/render-utils/src/fxaa_blend.slf @@ -14,11 +14,27 @@ <@include DeferredBufferWrite.slh@> -in vec2 varTexcoord; +in vec2 varTexCoord0; out vec4 outFragColor; uniform sampler2D colorTexture; +uniform float sharpenIntensity; void main(void) { - outFragColor = texture(colorTexture, varTexcoord); + vec4 pixels[9]; + vec4 sharpenedPixel; + pixels[0] = texelFetch(colorTexture, ivec2(gl_FragCoord.xy)+ivec2(-1,-1), 0); + pixels[1] = texelFetch(colorTexture, ivec2(gl_FragCoord.xy)+ivec2(0,-1), 0); + pixels[2] = texelFetch(colorTexture, ivec2(gl_FragCoord.xy)+ivec2(1,-1), 0); + + pixels[3] = texelFetch(colorTexture, ivec2(gl_FragCoord.xy)+ivec2(-1,0), 0); + pixels[4] = texelFetch(colorTexture, ivec2(gl_FragCoord.xy), 0); + pixels[5] = texelFetch(colorTexture, ivec2(gl_FragCoord.xy)+ivec2(1,0), 0); + + pixels[6] = texelFetch(colorTexture, ivec2(gl_FragCoord.xy)+ivec2(-1,1), 0); + pixels[7] = texelFetch(colorTexture, ivec2(gl_FragCoord.xy)+ivec2(0,1), 0); + pixels[8] = texelFetch(colorTexture, ivec2(gl_FragCoord.xy)+ivec2(1,1), 0); + + sharpenedPixel = pixels[4]*7.8 - (pixels[1]+pixels[3]+pixels[5]+pixels[7]) - (pixels[0]+pixels[2]+pixels[6]+pixels[8])*0.7; + outFragColor = mix(pixels[4], sharpenedPixel, sharpenIntensity); } diff --git a/libraries/render-utils/src/model.slv b/libraries/render-utils/src/model.slv index 06f6030e77..ccedff9b61 100644 --- a/libraries/render-utils/src/model.slv +++ b/libraries/render-utils/src/model.slv @@ -27,7 +27,7 @@ out vec4 _position; out vec3 _normal; void main(void) { - _color = colorToLinearRGB(inColor.xyz); + _color = color_sRGBToLinear(inColor.xyz); _alpha = inColor.w; TexMapArray texMapArray = getTexMapArray(); diff --git a/libraries/render-utils/src/model_fade.slv b/libraries/render-utils/src/model_fade.slv index 4c6bc534a9..61b8e9e1b6 100644 --- a/libraries/render-utils/src/model_fade.slv +++ b/libraries/render-utils/src/model_fade.slv @@ -28,7 +28,7 @@ out vec3 _normal; out vec3 _color; void main(void) { - _color = colorToLinearRGB(inColor.xyz); + _color = color_sRGBToLinear(inColor.xyz); _alpha = inColor.w; TexMapArray texMapArray = getTexMapArray(); diff --git a/libraries/render-utils/src/model_lightmap.slv b/libraries/render-utils/src/model_lightmap.slv index 161ceed14c..e00fcb708e 100644 --- a/libraries/render-utils/src/model_lightmap.slv +++ b/libraries/render-utils/src/model_lightmap.slv @@ -28,7 +28,7 @@ out vec3 _color; void main(void) { // pass along the color in linear space - _color = colorToLinearRGB(inColor.xyz); + _color = color_sRGBToLinear(inColor.xyz); // and the texture coordinates TexMapArray texMapArray = getTexMapArray(); diff --git a/libraries/render-utils/src/model_lightmap_fade.slv b/libraries/render-utils/src/model_lightmap_fade.slv index 561049d614..d1a1194de1 100644 --- a/libraries/render-utils/src/model_lightmap_fade.slv +++ b/libraries/render-utils/src/model_lightmap_fade.slv @@ -29,7 +29,7 @@ out vec4 _worldPosition; void main(void) { // pass along the color in linear space - _color = colorToLinearRGB(inColor.xyz); + _color = color_sRGBToLinear(inColor.xyz); // and the texture coordinates TexMapArray texMapArray = getTexMapArray(); diff --git a/libraries/render-utils/src/model_lightmap_normal_map.slv b/libraries/render-utils/src/model_lightmap_normal_map.slv index 5fb60d9227..3b1ecaab0c 100644 --- a/libraries/render-utils/src/model_lightmap_normal_map.slv +++ b/libraries/render-utils/src/model_lightmap_normal_map.slv @@ -29,7 +29,7 @@ out vec3 _color; void main(void) { // pass along the color in linear space - _color = colorToLinearRGB(inColor.xyz); + _color = color_sRGBToLinear(inColor.xyz); TexMapArray texMapArray = getTexMapArray(); <$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _texCoord0)$> diff --git a/libraries/render-utils/src/model_lightmap_normal_map_fade.slv b/libraries/render-utils/src/model_lightmap_normal_map_fade.slv index 4049fb0077..5c1212b1b7 100644 --- a/libraries/render-utils/src/model_lightmap_normal_map_fade.slv +++ b/libraries/render-utils/src/model_lightmap_normal_map_fade.slv @@ -30,7 +30,7 @@ out vec4 _worldPosition; void main(void) { // pass along the color in linear space - _color = colorToLinearRGB(inColor.xyz); + _color = color_sRGBToLinear(inColor.xyz); TexMapArray texMapArray = getTexMapArray(); <$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _texCoord0)$> diff --git a/libraries/render-utils/src/model_normal_map.slv b/libraries/render-utils/src/model_normal_map.slv index 9e674d93fc..a84f8c5e2a 100644 --- a/libraries/render-utils/src/model_normal_map.slv +++ b/libraries/render-utils/src/model_normal_map.slv @@ -30,7 +30,7 @@ out float _alpha; void main(void) { // pass along the color - _color = colorToLinearRGB(inColor.rgb); + _color = color_sRGBToLinear(inColor.rgb); _alpha = inColor.a; TexMapArray texMapArray = getTexMapArray(); diff --git a/libraries/render-utils/src/model_normal_map_fade.slv b/libraries/render-utils/src/model_normal_map_fade.slv index a71900d5c3..6a6142d317 100644 --- a/libraries/render-utils/src/model_normal_map_fade.slv +++ b/libraries/render-utils/src/model_normal_map_fade.slv @@ -31,7 +31,7 @@ out float _alpha; void main(void) { // pass along the color - _color = colorToLinearRGB(inColor.rgb); + _color = color_sRGBToLinear(inColor.rgb); _alpha = inColor.a; TexMapArray texMapArray = getTexMapArray(); diff --git a/libraries/render-utils/src/model_translucent.slv b/libraries/render-utils/src/model_translucent.slv index 305aba06c3..7a57a4baec 100644 --- a/libraries/render-utils/src/model_translucent.slv +++ b/libraries/render-utils/src/model_translucent.slv @@ -28,7 +28,7 @@ out vec3 _normal; out vec3 _color; void main(void) { - _color = colorToLinearRGB(inColor.xyz); + _color = color_sRGBToLinear(inColor.xyz); _alpha = inColor.w; TexMapArray texMapArray = getTexMapArray(); diff --git a/libraries/render-utils/src/model_translucent_normal_map.slv b/libraries/render-utils/src/model_translucent_normal_map.slv index db824a3709..981a03627b 100644 --- a/libraries/render-utils/src/model_translucent_normal_map.slv +++ b/libraries/render-utils/src/model_translucent_normal_map.slv @@ -29,7 +29,7 @@ out vec3 _tangent; out vec3 _color; void main(void) { - _color = colorToLinearRGB(inColor.xyz); + _color = color_sRGBToLinear(inColor.xyz); _alpha = inColor.w; TexMapArray texMapArray = getTexMapArray(); diff --git a/libraries/render-utils/src/overlay3D.slv b/libraries/render-utils/src/overlay3D.slv index ee28367413..7184f923e4 100644 --- a/libraries/render-utils/src/overlay3D.slv +++ b/libraries/render-utils/src/overlay3D.slv @@ -23,7 +23,7 @@ out vec4 _position; out vec3 _normal; void main(void) { - _color = colorToLinearRGB(inColor.xyz); + _color = color_sRGBToLinear(inColor.xyz); _alpha = inColor.w; _texCoord0 = inTexCoord0.st; diff --git a/libraries/render-utils/src/simple.slv b/libraries/render-utils/src/simple.slv index 64d3e24192..0ce6505a65 100644 --- a/libraries/render-utils/src/simple.slv +++ b/libraries/render-utils/src/simple.slv @@ -25,7 +25,7 @@ out vec2 _texCoord0; out vec4 _position; void main(void) { - _color = colorToLinearRGBA(inColor); + _color = color_sRGBAToLinear(inColor); _texCoord0 = inTexCoord0.st; _position = inPosition; _modelNormal = inNormal.xyz; diff --git a/libraries/render-utils/src/simple_fade.slv b/libraries/render-utils/src/simple_fade.slv index 3d9eb2c812..85946045ac 100644 --- a/libraries/render-utils/src/simple_fade.slv +++ b/libraries/render-utils/src/simple_fade.slv @@ -29,7 +29,7 @@ out vec4 _position; out vec4 _worldPosition; void main(void) { - _color = colorToLinearRGBA(inColor); + _color = color_sRGBAToLinear(inColor); _texCoord0 = inTexCoord0.st; _position = inPosition; _modelNormal = inNormal.xyz; diff --git a/libraries/render-utils/src/simple_opaque_web_browser.slf b/libraries/render-utils/src/simple_opaque_web_browser.slf index 3acf104b55..af7ef78682 100644 --- a/libraries/render-utils/src/simple_opaque_web_browser.slf +++ b/libraries/render-utils/src/simple_opaque_web_browser.slf @@ -25,6 +25,6 @@ in vec2 _texCoord0; void main(void) { vec4 texel = texture(originalTexture, _texCoord0.st); - texel = colorToLinearRGBA(texel); + texel = color_sRGBAToLinear(texel); packDeferredFragmentUnlit(normalize(_normal), 1.0, _color.rgb * texel.rgb); } diff --git a/libraries/render-utils/src/simple_textured.slf b/libraries/render-utils/src/simple_textured.slf index 34fcbc77dc..4fd734aad5 100644 --- a/libraries/render-utils/src/simple_textured.slf +++ b/libraries/render-utils/src/simple_textured.slf @@ -28,7 +28,7 @@ void main(void) { vec4 texel = texture(originalTexture, _texCoord0); float colorAlpha = _color.a; if (_color.a <= 0.0) { - texel = colorToLinearRGBA(texel); + texel = color_sRGBAToLinear(texel); colorAlpha = -_color.a; } diff --git a/libraries/render-utils/src/simple_textured_fade.slf b/libraries/render-utils/src/simple_textured_fade.slf index 2061cabdfc..d378e7a5c1 100644 --- a/libraries/render-utils/src/simple_textured_fade.slf +++ b/libraries/render-utils/src/simple_textured_fade.slf @@ -40,7 +40,7 @@ void main(void) { vec4 texel = texture(originalTexture, _texCoord0); float colorAlpha = _color.a; if (_color.a <= 0.0) { - texel = colorToLinearRGBA(texel); + texel = color_sRGBAToLinear(texel); colorAlpha = -_color.a; } diff --git a/libraries/render-utils/src/simple_textured_unlit.slf b/libraries/render-utils/src/simple_textured_unlit.slf index d261fb343a..1daea2f5c7 100644 --- a/libraries/render-utils/src/simple_textured_unlit.slf +++ b/libraries/render-utils/src/simple_textured_unlit.slf @@ -27,7 +27,7 @@ void main(void) { vec4 texel = texture(originalTexture, _texCoord0.st); float colorAlpha = _color.a; if (_color.a <= 0.0) { - texel = colorToLinearRGBA(texel); + texel = color_sRGBAToLinear(texel); colorAlpha = -_color.a; } diff --git a/libraries/render-utils/src/simple_textured_unlit_fade.slf b/libraries/render-utils/src/simple_textured_unlit_fade.slf index 6f03c6746f..b3c5a914b2 100644 --- a/libraries/render-utils/src/simple_textured_unlit_fade.slf +++ b/libraries/render-utils/src/simple_textured_unlit_fade.slf @@ -39,7 +39,7 @@ void main(void) { vec4 texel = texture(originalTexture, _texCoord0.st); float colorAlpha = _color.a; if (_color.a <= 0.0) { - texel = colorToLinearRGBA(texel); + texel = color_sRGBAToLinear(texel); colorAlpha = -_color.a; } diff --git a/libraries/render-utils/src/simple_transparent_textured.slf b/libraries/render-utils/src/simple_transparent_textured.slf index 30c420233f..c6b0d83914 100644 --- a/libraries/render-utils/src/simple_transparent_textured.slf +++ b/libraries/render-utils/src/simple_transparent_textured.slf @@ -34,7 +34,7 @@ void main(void) { vec4 texel = texture(originalTexture, _texCoord0.st); float opacity = _color.a; if (_color.a <= 0.0) { - texel = colorToLinearRGBA(texel); + texel = color_sRGBAToLinear(texel); opacity = -_color.a; } opacity *= texel.a; diff --git a/libraries/render-utils/src/simple_transparent_textured_fade.slf b/libraries/render-utils/src/simple_transparent_textured_fade.slf index a8a5875a4b..a5103660b9 100644 --- a/libraries/render-utils/src/simple_transparent_textured_fade.slf +++ b/libraries/render-utils/src/simple_transparent_textured_fade.slf @@ -46,7 +46,7 @@ void main(void) { vec4 texel = texture(originalTexture, _texCoord0.st); float opacity = _color.a; if (_color.a <= 0.0) { - texel = colorToLinearRGBA(texel); + texel = color_sRGBAToLinear(texel); opacity = -_color.a; } opacity *= texel.a; diff --git a/libraries/render-utils/src/simple_transparent_textured_unlit.slf b/libraries/render-utils/src/simple_transparent_textured_unlit.slf index 693d7be2db..e9c1104cf0 100644 --- a/libraries/render-utils/src/simple_transparent_textured_unlit.slf +++ b/libraries/render-utils/src/simple_transparent_textured_unlit.slf @@ -29,7 +29,7 @@ void main(void) { vec4 texel = texture(originalTexture, _texCoord0.st); float colorAlpha = _color.a; if (_color.a <= 0.0) { - texel = colorToLinearRGBA(texel); + texel = color_sRGBAToLinear(texel); colorAlpha = -_color.a; } _fragColor0 = vec4(_color.rgb * texel.rgb, colorAlpha * texel.a); diff --git a/libraries/render-utils/src/simple_transparent_textured_unlit_fade.slf b/libraries/render-utils/src/simple_transparent_textured_unlit_fade.slf index 1c42a1f724..093b70755f 100644 --- a/libraries/render-utils/src/simple_transparent_textured_unlit_fade.slf +++ b/libraries/render-utils/src/simple_transparent_textured_unlit_fade.slf @@ -40,7 +40,7 @@ void main(void) { vec4 texel = texture(originalTexture, _texCoord0.st); float colorAlpha = _color.a; if (_color.a <= 0.0) { - texel = colorToLinearRGBA(texel); + texel = color_sRGBAToLinear(texel); colorAlpha = -_color.a; } _fragColor0 = vec4(_color.rgb * texel.rgb+fadeEmissive, colorAlpha * texel.a); diff --git a/libraries/render-utils/src/simple_transparent_web_browser.slf b/libraries/render-utils/src/simple_transparent_web_browser.slf index 19079f5d92..414f3f683f 100644 --- a/libraries/render-utils/src/simple_transparent_web_browser.slf +++ b/libraries/render-utils/src/simple_transparent_web_browser.slf @@ -25,7 +25,7 @@ in vec2 _texCoord0; void main(void) { vec4 texel = texture(originalTexture, _texCoord0.st); - texel = colorToLinearRGBA(texel); + texel = color_sRGBAToLinear(texel); packDeferredFragmentTranslucent( normalize(_normal), _color.a, diff --git a/libraries/render-utils/src/skin_model.slv b/libraries/render-utils/src/skin_model.slv index bd1655fc40..480e48a5d4 100644 --- a/libraries/render-utils/src/skin_model.slv +++ b/libraries/render-utils/src/skin_model.slv @@ -37,7 +37,7 @@ void main(void) { skinPositionNormal(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, position, interpolatedNormal); // pass along the color - _color = colorToLinearRGB(inColor.rgb); + _color = color_sRGBToLinear(inColor.rgb); _alpha = inColor.a; TexMapArray texMapArray = getTexMapArray(); diff --git a/libraries/render-utils/src/skin_model_dq.slv b/libraries/render-utils/src/skin_model_dq.slv index 96f9b4a713..6e369c7c82 100644 --- a/libraries/render-utils/src/skin_model_dq.slv +++ b/libraries/render-utils/src/skin_model_dq.slv @@ -37,7 +37,7 @@ void main(void) { skinPositionNormal(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, position, interpolatedNormal); // pass along the color - _color = colorToLinearRGB(inColor.rgb); + _color = color_sRGBToLinear(inColor.rgb); _alpha = inColor.a; TexMapArray texMapArray = getTexMapArray(); diff --git a/libraries/render-utils/src/skin_model_fade.slv b/libraries/render-utils/src/skin_model_fade.slv index b14bf1532e..4f459d75f3 100644 --- a/libraries/render-utils/src/skin_model_fade.slv +++ b/libraries/render-utils/src/skin_model_fade.slv @@ -38,7 +38,7 @@ void main(void) { skinPositionNormal(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, position, interpolatedNormal); // pass along the color - _color = colorToLinearRGB(inColor.rgb); + _color = color_sRGBToLinear(inColor.rgb); _alpha = inColor.a; TexMapArray texMapArray = getTexMapArray(); diff --git a/libraries/render-utils/src/skin_model_fade_dq.slv b/libraries/render-utils/src/skin_model_fade_dq.slv index 4f8a923a03..232170d714 100644 --- a/libraries/render-utils/src/skin_model_fade_dq.slv +++ b/libraries/render-utils/src/skin_model_fade_dq.slv @@ -2,7 +2,7 @@ <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> // -// skin_model_fade.vert +// skin_model_fade_dq.vert // vertex shader // // Created by Olivier Prat on 06/045/17. @@ -38,7 +38,7 @@ void main(void) { skinPositionNormal(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, position, interpolatedNormal); // pass along the color - _color = colorToLinearRGB(inColor.rgb); + _color = color_sRGBToLinear(inColor.rgb); _alpha = inColor.a; TexMapArray texMapArray = getTexMapArray(); diff --git a/libraries/render-utils/src/skin_model_normal_map.slv b/libraries/render-utils/src/skin_model_normal_map.slv index 666bdf865f..b54c84e5b3 100644 --- a/libraries/render-utils/src/skin_model_normal_map.slv +++ b/libraries/render-utils/src/skin_model_normal_map.slv @@ -39,7 +39,7 @@ void main(void) { skinPositionNormalTangent(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, inTangent.xyz, position, interpolatedNormal.xyz, interpolatedTangent.xyz); // pass along the color - _color = colorToLinearRGB(inColor.rgb); + _color = color_sRGBToLinear(inColor.rgb); _alpha = inColor.a; TexMapArray texMapArray = getTexMapArray(); diff --git a/libraries/render-utils/src/skin_model_normal_map_dq.slv b/libraries/render-utils/src/skin_model_normal_map_dq.slv index 02b3742f6f..b34f68d291 100644 --- a/libraries/render-utils/src/skin_model_normal_map_dq.slv +++ b/libraries/render-utils/src/skin_model_normal_map_dq.slv @@ -39,7 +39,7 @@ void main(void) { skinPositionNormalTangent(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, inTangent.xyz, position, interpolatedNormal.xyz, interpolatedTangent.xyz); // pass along the color - _color = colorToLinearRGB(inColor.rgb); + _color = color_sRGBToLinear(inColor.rgb); _alpha = inColor.a; TexMapArray texMapArray = getTexMapArray(); diff --git a/libraries/render-utils/src/skin_model_normal_map_fade.slv b/libraries/render-utils/src/skin_model_normal_map_fade.slv index d72e47702d..0e788b81b5 100644 --- a/libraries/render-utils/src/skin_model_normal_map_fade.slv +++ b/libraries/render-utils/src/skin_model_normal_map_fade.slv @@ -40,7 +40,7 @@ void main(void) { skinPositionNormalTangent(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, inTangent.xyz, position, interpolatedNormal.xyz, interpolatedTangent.xyz); // pass along the color - _color = colorToLinearRGB(inColor.rgb); + _color = color_sRGBToLinear(inColor.rgb); _alpha = inColor.a; TexMapArray texMapArray = getTexMapArray(); diff --git a/libraries/render-utils/src/skin_model_normal_map_fade_dq.slv b/libraries/render-utils/src/skin_model_normal_map_fade_dq.slv index d6e07575b1..230077ba3b 100644 --- a/libraries/render-utils/src/skin_model_normal_map_fade_dq.slv +++ b/libraries/render-utils/src/skin_model_normal_map_fade_dq.slv @@ -2,7 +2,7 @@ <$VERSION_HEADER$> // Generated on <$_SCRIBE_DATE$> // -// skin_model_normal_map.vert +// skin_model_normal_map_fade_dq.vert // vertex shader // // Created by Andrzej Kapolka on 10/29/13. @@ -40,7 +40,7 @@ void main(void) { skinPositionNormalTangent(inSkinClusterIndex, inSkinClusterWeight, inPosition, inNormal.xyz, inTangent.xyz, position, interpolatedNormal.xyz, interpolatedTangent.xyz); // pass along the color - _color = colorToLinearRGB(inColor.rgb); + _color = color_sRGBToLinear(inColor.rgb); _alpha = inColor.a; TexMapArray texMapArray = getTexMapArray(); diff --git a/libraries/render-utils/src/standardTransformPNTC.slv b/libraries/render-utils/src/standardTransformPNTC.slv index 0ced5ba6e2..8ec685cea0 100644 --- a/libraries/render-utils/src/standardTransformPNTC.slv +++ b/libraries/render-utils/src/standardTransformPNTC.slv @@ -24,7 +24,7 @@ out vec4 varColor; void main(void) { varTexCoord0 = inTexCoord0.st; - varColor = colorToLinearRGBA(inColor); + varColor = color_sRGBAToLinear(inColor); // standard transform TransformCamera cam = getTransformCamera(); diff --git a/libraries/render-utils/src/taa.slf b/libraries/render-utils/src/taa.slf new file mode 100644 index 0000000000..8d172871d4 --- /dev/null +++ b/libraries/render-utils/src/taa.slf @@ -0,0 +1,51 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// taa.frag +// fragment shader +// +// Created by Sam Gateau on 8/14/2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + + +<@include taa.slh@> + +in vec2 varTexCoord0; +layout(location = 0) out vec4 outFragColor; + +void main() { + vec2 fragUV = varTexCoord0; + + // Debug region before debug or fxaa region X + float distToRegionFXAA = fragUV.x - taa_getRegionFXAA().x; + if (distToRegionFXAA > 0.0) { + outFragColor = vec4(taa_evalFXAA(fragUV), 1.0); + return; + } + + vec2 fragVel = taa_fetchVelocityMapBest(fragUV).xy; + + vec3 sourceColor; + vec3 historyColor; + vec2 prevFragUV = taa_fetchSourceAndHistory(fragUV, fragVel, sourceColor, historyColor); + + vec3 nextColor = sourceColor; + + if (taa_constrainColor()) { + // clamp history to neighbourhood of current sample + historyColor = taa_evalConstrainColor(sourceColor, fragUV, fragVel, historyColor); + } + + if (taa_feedbackColor()) { + nextColor = taa_evalFeedbackColor(sourceColor, historyColor, params.blend); + } else { + nextColor = mix(historyColor, sourceColor, params.blend); + } + + outFragColor = vec4(taa_resolveColor(nextColor), 1.0); +} diff --git a/libraries/render-utils/src/taa.slh b/libraries/render-utils/src/taa.slh new file mode 100644 index 0000000000..583e2978c4 --- /dev/null +++ b/libraries/render-utils/src/taa.slh @@ -0,0 +1,529 @@ +// Generated on <$_SCRIBE_DATE$> +// +// TAA.slh +// Common component needed by TemporalAntialiasing fragment shader +// +// Created by Sam Gateau on 8/17/2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include DeferredTransform.slh@> +<$declareDeferredFrameTransform()$> + +<@include gpu/Color.slh@> + +uniform sampler2D depthMap; +uniform sampler2D sourceMap; +uniform sampler2D historyMap; +uniform sampler2D velocityMap; +uniform sampler2D nextMap; + +struct TAAParams +{ + float none; + float blend; + float covarianceGamma; + float debugShowVelocityThreshold; + ivec4 flags; + vec4 pixelInfo_orbZoom; + vec4 regionInfo; +}; + +layout(std140) uniform taaParamsBuffer { + TAAParams params; +}; + +#define GET_BIT(bitfield, bitIndex) bool((bitfield) & (1 << (bitIndex))) + +bool taa_isDebugEnabled() { + return GET_BIT(params.flags.x, 0); +} + +bool taa_showDebugCursor() { + return GET_BIT(params.flags.x, 1); +} + +bool taa_showClosestFragment() { + return GET_BIT(params.flags.x, 3); +} + +bool taa_constrainColor() { + return GET_BIT(params.flags.y, 1); +} + +bool taa_feedbackColor() { + return GET_BIT(params.flags.y, 4); +} + +vec2 taa_getDebugCursorTexcoord() { + return params.pixelInfo_orbZoom.xy; +} + +float taa_getDebugOrbZoom() { + return params.pixelInfo_orbZoom.z; +} + +vec2 taa_getRegionDebug() { + return params.regionInfo.xy; +} + +vec2 taa_getRegionFXAA() { + return params.regionInfo.zw; +} +#define USE_YCOCG 1 + +vec4 taa_fetchColor(sampler2D map, vec2 uv) { +#if USE_YCOCG + vec4 c = texture(map, uv); + return vec4(color_LinearToYCoCg(c.rgb), c.a); +#else + return texture(map, uv); +#endif +} + +vec3 taa_resolveColor(vec3 color) { +#if USE_YCOCG + return color_YCoCgToLinear(color); +#else + return color; +#endif +} + +vec4 taa_fetchSourceMap(vec2 uv) { +#if USE_YCOCG + vec4 c = texture(sourceMap, uv); + return vec4(color_LinearToYCoCg(c.rgb), c.a); +#else + return texture(sourceMap, uv); +#endif +} + +vec4 taa_fetchHistoryMap(vec2 uv) { +#if USE_YCOCG + vec4 c = texture(historyMap, uv); + return vec4(color_LinearToYCoCg(c.rgb), c.a); +#else + return texture(historyMap, uv); +#endif +} + +vec4 taa_fetchNextMap(vec2 uv) { +#if USE_YCOCG + vec4 c = texture(nextMap, uv); + return vec4(color_LinearToYCoCg(c.rgb), c.a); +#else + return texture(nextMap, uv); +#endif +} + +vec2 taa_fetchVelocityMap(vec2 uv) { + return texture(velocityMap, uv).xy; +} + +float taa_fetchDepth(vec2 uv) { + return -texture(depthMap, vec2(uv), 0).x; +} + + +#define ZCMP_GT(a, b) (a > b) + +vec2 taa_getImageSize() { + vec2 imageSize = getWidthHeight(0); + if (isStereo()) { + imageSize.x *= 2.0; + } + return imageSize; +} + +vec2 taa_getTexelSize() { + vec2 texelSize = getInvWidthHeight(); + if (isStereo()) { + texelSize.x *= 0.5; + } + return texelSize; +} + +vec3 taa_findClosestFragment3x3(vec2 uv) +{ + vec2 dd = abs(taa_getTexelSize()); + vec2 du = vec2(dd.x, 0.0); + vec2 dv = vec2(0.0, dd.y); + + vec3 dtl = vec3(-1, -1, taa_fetchDepth(uv - dv - du)); + vec3 dtc = vec3( 0, -1, taa_fetchDepth(uv - dv)); + vec3 dtr = vec3( 1, -1, taa_fetchDepth(uv - dv + du)); + + vec3 dml = vec3(-1, 0, taa_fetchDepth(uv - du)); + vec3 dmc = vec3( 0, 0, taa_fetchDepth(uv)); + vec3 dmr = vec3( 1, 0, taa_fetchDepth(uv + du)); + + vec3 dbl = vec3(-1, 1, taa_fetchDepth(uv + dv - du)); + vec3 dbc = vec3( 0, 1, taa_fetchDepth(uv + dv)); + vec3 dbr = vec3( 1, 1, taa_fetchDepth(uv + dv + du)); + + vec3 dmin = dtl; + if (ZCMP_GT(dmin.z, dtc.z)) dmin = dtc; + if (ZCMP_GT(dmin.z, dtr.z)) dmin = dtr; + + if (ZCMP_GT(dmin.z, dml.z)) dmin = dml; + if (ZCMP_GT(dmin.z, dmc.z)) dmin = dmc; + if (ZCMP_GT(dmin.z, dmr.z)) dmin = dmr; + + if (ZCMP_GT(dmin.z, dbl.z)) dmin = dbl; + if (ZCMP_GT(dmin.z, dbc.z)) dmin = dbc; + if (ZCMP_GT(dmin.z, dbr.z)) dmin = dbr; + + return vec3(uv + dd.xy * dmin.xy, dmin.z); +} + +vec2 taa_fetchVelocityMapBest(vec2 uv) { + vec2 dd = abs(taa_getTexelSize()); + vec2 du = vec2(dd.x, 0.0); + vec2 dv = vec2(0.0, dd.y); + + vec2 dtl = taa_fetchVelocityMap(uv - dv - du); + vec2 dtc = taa_fetchVelocityMap(uv - dv); + vec2 dtr = taa_fetchVelocityMap(uv - dv + du); + + vec2 dml = taa_fetchVelocityMap(uv - du); + vec2 dmc = taa_fetchVelocityMap(uv); + vec2 dmr = taa_fetchVelocityMap(uv + du); + + vec2 dbl = taa_fetchVelocityMap(uv + dv - du); + vec2 dbc = taa_fetchVelocityMap(uv + dv); + vec2 dbr = taa_fetchVelocityMap(uv + dv + du); + + vec3 best = vec3(dtl, dot(dtl,dtl)); + + float testSpeed = dot(dtc,dtc); + if (testSpeed > best.z) { best = vec3(dtc, testSpeed); } + testSpeed = dot(dtr,dtr); + if (testSpeed > best.z) { best = vec3(dtr, testSpeed); } + + testSpeed = dot(dml,dml); + if (testSpeed > best.z) { best = vec3(dml, testSpeed); } + testSpeed = dot(dmc,dmc); + if (testSpeed > best.z) { best = vec3(dmc, testSpeed); } + testSpeed = dot(dmr,dmr); + if (testSpeed > best.z) { best = vec3(dmr, testSpeed); } + + testSpeed = dot(dbl,dbl); + if (testSpeed > best.z) { best = vec3(dbl, testSpeed); } + testSpeed = dot(dbc,dbc); + if (testSpeed > best.z) { best = vec3(dbc, testSpeed); } + testSpeed = dot(dbr,dbr); + if (testSpeed > best.z) { best = vec3(dbr, testSpeed); } + + return best.xy; +} + +vec2 taa_fromFragUVToEyeUVAndSide(vec2 fragUV, out int stereoSide) { + vec2 eyeUV = fragUV; + stereoSide = 0; + if (isStereo()) { + if (eyeUV.x > 0.5) { + eyeUV.x -= 0.5; + stereoSide = 1; + } + eyeUV.x *= 2.0; + } + return eyeUV; +} + +vec2 taa_fromEyeUVToFragUV(vec2 eyeUV, int stereoSide) { + vec2 fragUV = eyeUV; + if (isStereo()) { + fragUV.x *= 0.5; + fragUV.x += stereoSide*0.5; + } + return fragUV; +} + +vec2 taa_computePrevFragAndEyeUV(vec2 fragUV, vec2 fragVelocity, out vec2 prevEyeUV) { + int stereoSide = 0; + vec2 eyeUV = taa_fromFragUVToEyeUVAndSide(fragUV, stereoSide); + prevEyeUV = eyeUV - fragVelocity; + return taa_fromEyeUVToFragUV(prevEyeUV, stereoSide); +} + +vec2 taa_fetchSourceAndHistory(vec2 fragUV, vec2 fragVelocity, out vec3 sourceColor, out vec3 historyColor) { + vec2 prevEyeUV; + vec2 prevFragUV = taa_computePrevFragAndEyeUV(fragUV, fragVelocity, prevEyeUV); + sourceColor = taa_fetchSourceMap(fragUV).xyz; + + historyColor = sourceColor; + if (!(any(lessThan(prevEyeUV, vec2(0.0))) || any(greaterThan(prevEyeUV, vec2(1.0))))) { + historyColor = taa_fetchHistoryMap(prevFragUV).xyz; + } + return prevFragUV; +} + +float Luminance(vec3 rgb) { + return rgb.x/4.0 + rgb.y/2.0 + rgb.z/4.0; +} + +#define MINMAX_3X3_ROUNDED 1 + +mat3 taa_evalNeighbourColorVariance(vec3 sourceColor, vec2 fragUV, vec2 fragVelocity) { + vec2 texelSize = taa_getTexelSize(); + + + vec2 du = vec2(texelSize.x, 0.0); + vec2 dv = vec2(0.0, texelSize.y); + + vec3 sampleColor = taa_fetchSourceMap(fragUV - dv - du).rgb; + vec3 sumSamples = sampleColor; + vec3 sumSamples2 = sampleColor * sampleColor; + + sampleColor = taa_fetchSourceMap(fragUV - dv).rgb; + sumSamples += sampleColor; + sumSamples2 += sampleColor * sampleColor; + + sampleColor = taa_fetchSourceMap(fragUV - dv + du).rgb; + sumSamples += sampleColor; + sumSamples2 += sampleColor * sampleColor; + + sampleColor = taa_fetchSourceMap(fragUV - du).rgb; + sumSamples += sampleColor; + sumSamples2 += sampleColor * sampleColor; + + sampleColor = sourceColor; //taa_fetchSourceMap(fragUV).rgb; // could resuse the same osurce sampleColor isn't it ? + sumSamples += sampleColor; + sumSamples2 += sampleColor * sampleColor; + + sampleColor = taa_fetchSourceMap(fragUV + du).rgb; + sumSamples += sampleColor; + sumSamples2 += sampleColor * sampleColor; + + sampleColor = taa_fetchSourceMap(fragUV + dv - du).rgb; + sumSamples += sampleColor; + sumSamples2 += sampleColor * sampleColor; + + sampleColor = taa_fetchSourceMap(fragUV + dv).rgb; + sumSamples += sampleColor; + sumSamples2 += sampleColor * sampleColor; + + sampleColor = taa_fetchSourceMap(fragUV + dv + du).rgb; + sumSamples += sampleColor; + sumSamples2 += sampleColor * sampleColor; + + + vec3 mu = sumSamples / vec3(9.0); + vec3 sigma = sqrt(max(sumSamples2 / vec3(9.0) - mu * mu, vec3(0.0))); + + float gamma = params.covarianceGamma; + vec3 cmin = mu - gamma * sigma; + vec3 cmax = mu + gamma * sigma; + + return mat3(cmin, cmax, mu); +} + +mat3 taa_evalNeighbourColorRegion(vec3 sourceColor, vec2 fragUV, vec2 fragVelocity, float fragZe) { + vec2 imageSize = taa_getImageSize(); + vec2 texelSize = taa_getTexelSize(); + vec3 cmin, cmax, cavg; + + #if MINMAX_3X3_ROUNDED + vec2 du = vec2(texelSize.x, 0.0); + vec2 dv = vec2(0.0, texelSize.y); + + vec3 ctl = taa_fetchSourceMap(fragUV - dv - du).rgb; + vec3 ctc = taa_fetchSourceMap(fragUV - dv).rgb; + vec3 ctr = taa_fetchSourceMap(fragUV - dv + du).rgb; + vec3 cml = taa_fetchSourceMap(fragUV - du).rgb; + vec3 cmc = sourceColor; //taa_fetchSourceMap(fragUV).rgb; // could resuse the same osurce sample isn't it ? + vec3 cmr = taa_fetchSourceMap(fragUV + du).rgb; + vec3 cbl = taa_fetchSourceMap(fragUV + dv - du).rgb; + vec3 cbc = taa_fetchSourceMap(fragUV + dv).rgb; + vec3 cbr = taa_fetchSourceMap(fragUV + dv + du).rgb; + + cmin = min(ctl, min(ctc, min(ctr, min(cml, min(cmc, min(cmr, min(cbl, min(cbc, cbr)))))))); + cmax = max(ctl, max(ctc, max(ctr, max(cml, max(cmc, max(cmr, max(cbl, max(cbc, cbr)))))))); + + #if MINMAX_3X3_ROUNDED || USE_YCOCG || USE_CLIPPING + cavg = (ctl + ctc + ctr + cml + cmc + cmr + cbl + cbc + cbr) / 9.0; + #elif + cavg = (cmin + cmax ) * 0.5; + #endif + + #if MINMAX_3X3_ROUNDED + vec3 cmin5 = min(ctc, min(cml, min(cmc, min(cmr, cbc)))); + vec3 cmax5 = max(ctc, max(cml, max(cmc, max(cmr, cbc)))); + vec3 cavg5 = (ctc + cml + cmc + cmr + cbc) / 5.0; + cmin = 0.5 * (cmin + cmin5); + cmax = 0.5 * (cmax + cmax5); + cavg = 0.5 * (cavg + cavg5); + #endif + #else + const float _SubpixelThreshold = 0.5; + const float _GatherBase = 0.5; + const float _GatherSubpixelMotion = 0.1666; + + vec2 texel_vel = fragVelocity * imageSize; + float texel_vel_mag = length(texel_vel) * -fragZe; + float k_subpixel_motion = clamp(_SubpixelThreshold / (0.0001 + texel_vel_mag), 0.0, 1.0); + float k_min_max_support = _GatherBase + _GatherSubpixelMotion * k_subpixel_motion; + + vec2 ss_offset01 = k_min_max_support * vec2(-texelSize.x, texelSize.y); + vec2 ss_offset11 = k_min_max_support * vec2(texelSize.x, texelSize.y); + vec3 c00 = taa_fetchSourceMap(fragUV - ss_offset11).rgb; + vec3 c10 = taa_fetchSourceMap(fragUV - ss_offset01).rgb; + vec3 c01 = taa_fetchSourceMap(fragUV + ss_offset01).rgb; + vec3 c11 = taa_fetchSourceMap(fragUV + ss_offset11).rgb; + + cmin = min(c00, min(c10, min(c01, c11))); + cmax = max(c00, max(c10, max(c01, c11))); + cavg = (cmin + cmax ) * 0.5; + + #if USE_YCOCG || USE_CLIPPING + cavg = (c00 + c10 + c01 + c11) / 4.0; + #elif + cavg = (cmin + cmax ) * 0.5; + #endif + #endif + + // shrink chroma min-max + #if USE_YCOCG + vec2 chroma_extent = vec2(0.25 * 0.5 * (cmax.r - cmin.r)); + vec2 chroma_center = sourceColor.gb; + cmin.yz = chroma_center - chroma_extent; + cmax.yz = chroma_center + chroma_extent; + cavg.yz = chroma_center; + #endif + + return mat3(cmin, cmax, cavg); +} + +//#define USE_OPTIMIZATIONS 0 + +vec3 taa_clampColor(vec3 colorMin, vec3 colorMax, vec3 colorSource, vec3 color) { + const float eps = 0.00001; + vec3 p = colorSource; + vec3 q = color; + // note: only clips towards aabb center (but fast!) + vec3 p_clip = 0.5 * (colorMax + colorMin); + vec3 e_clip = 0.5 * (colorMax - colorMin) + vec3(eps); + + vec3 v_clip = q - p_clip; + vec3 v_unit = v_clip.xyz / e_clip; + vec3 a_unit = abs(v_unit); + float ma_unit = max(a_unit.x, max(a_unit.y, a_unit.z)); + + if (ma_unit > 1.0) + return p_clip + v_clip / ma_unit; + else + return q;// point inside aabb +} + +vec3 taa_evalConstrainColor(vec3 sourceColor, vec2 sourceUV, vec2 sourceVel, vec3 candidateColor) { + mat3 colorMinMaxAvg; + + colorMinMaxAvg = taa_evalNeighbourColorVariance(sourceColor, sourceUV, sourceVel); + + // clamp history to neighbourhood of current sample + return taa_clampColor(colorMinMaxAvg[0], colorMinMaxAvg[1], sourceColor, candidateColor); +} + +vec3 taa_evalFeedbackColor(vec3 sourceColor, vec3 historyColor, float blendFactor) { + const float _FeedbackMin = 0.1; + const float _FeedbackMax = 0.9; + // feedback weight from unbiased luminance diff (t.lottes) + #if USE_YCOCG + float lum0 = sourceColor.r; + float lum1 = historyColor.r; + #else + float lum0 = Luminance(sourceColor.rgb); + float lum1 = Luminance(historyColor.rgb); + #endif + float unbiased_diff = abs(lum0 - lum1) / max(lum0, max(lum1, 0.2)); + float unbiased_weight = 1.0 - unbiased_diff; + float unbiased_weight_sqr = unbiased_weight * unbiased_weight; + float k_feedback = mix(_FeedbackMin, _FeedbackMax, unbiased_weight_sqr); + + + vec3 nextColor = mix(historyColor, sourceColor, k_feedback * blendFactor).xyz; + return nextColor; +} + + +<$declareColorWheel()$> + +vec3 taa_getVelocityColorRelative(float velocityPixLength) { + return colorRamp(velocityPixLength/params.debugShowVelocityThreshold); +} + +vec3 taa_getVelocityColorAboveThreshold(float velocityPixLength) { + return colorRamp((velocityPixLength - params.debugShowVelocityThreshold)/params.debugShowVelocityThreshold); +} + + +vec3 taa_evalFXAA(vec2 fragUV) { + + // vec2 texelSize = getInvWidthHeight(); + vec2 texelSize = taa_getTexelSize(); + + // filter width limit for dependent "two-tap" texture samples + float FXAA_SPAN_MAX = 8.0; + + // local contrast multiplier for performing AA + // higher = sharper, but setting this value too high will cause near-vertical and near-horizontal edges to fail + // see "fxaaQualityEdgeThreshold" + float FXAA_REDUCE_MUL = 1.0 / 8.0; + + // luminance threshold for processing dark colors + // see "fxaaQualityEdgeThresholdMin" + float FXAA_REDUCE_MIN = 1.0 / 128.0; + + // fetch raw RGB values for nearby locations + // sampling pattern is "five on a die" (each diagonal direction and the center) + // computing the coordinates for these texture reads could be moved to the vertex shader for speed if needed + vec3 rgbNW = texture(sourceMap, fragUV + (vec2(-1.0, -1.0) * texelSize)).xyz; + vec3 rgbNE = texture(sourceMap, fragUV + (vec2(+1.0, -1.0) * texelSize)).xyz; + vec3 rgbSW = texture(sourceMap, fragUV + (vec2(-1.0, +1.0) * texelSize)).xyz; + vec3 rgbSE = texture(sourceMap, fragUV + (vec2(+1.0, +1.0) * texelSize)).xyz; + vec3 rgbM = texture(sourceMap, fragUV).xyz; + + // convert RGB values to luminance + vec3 luma = vec3(0.299, 0.587, 0.114); + float lumaNW = dot(rgbNW, luma); + float lumaNE = dot(rgbNE, luma); + float lumaSW = dot(rgbSW, luma); + float lumaSE = dot(rgbSE, luma); + float lumaM = dot( rgbM, luma); + + // luma range of local neighborhood + float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); + float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); + + // direction perpendicular to local luma gradient + vec2 dir; + dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); + dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); + + // compute clamped direction offset for additional "two-tap" samples + // longer vector = blurry, shorter vector = sharp + float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); + float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); + dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), + max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * texelSize; + + // perform additional texture sampling perpendicular to gradient + vec3 rgbA = (1.0 / 2.0) * ( + texture(sourceMap, fragUV + dir * (1.0 / 3.0 - 0.5)).xyz + + texture(sourceMap, fragUV + dir * (2.0 / 3.0 - 0.5)).xyz); + vec3 rgbB = rgbA * (1.0 / 2.0) + (1.0 / 4.0) * ( + texture(sourceMap, fragUV + dir * (0.0 / 3.0 - 0.5)).xyz + + texture(sourceMap, fragUV + dir * (3.0 / 3.0 - 0.5)).xyz); + float lumaB = dot(rgbB, luma); + + // compare luma of new samples to the luma range of the original neighborhood + // if the new samples exceed this range, just use the first two samples instead of all four + if (lumaB < lumaMin || lumaB > lumaMax) { + return rgbA; + } else { + return rgbB; + } +} \ No newline at end of file diff --git a/libraries/render-utils/src/taa_blend.slf b/libraries/render-utils/src/taa_blend.slf new file mode 100644 index 0000000000..aca934ca71 --- /dev/null +++ b/libraries/render-utils/src/taa_blend.slf @@ -0,0 +1,156 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// taa_blend.frag +// fragment shader +// +// Created by Sam Gateau on 8/17/2017 +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include taa.slh@> + +in vec2 varTexCoord0; +layout(location = 0) out vec4 outFragColor; + +void main(void) { + vec3 nextColor = texture(nextMap, varTexCoord0).xyz; + outFragColor = vec4(nextColor, 1.0); + + + // Pixel being shaded + vec3 sourceColor = texture(sourceMap, varTexCoord0).xyz; + + vec2 imageSize = getWidthHeight(0); + vec2 texelSize = getInvWidthHeight(); + + vec2 pixPos = varTexCoord0 * imageSize; + vec2 pixVelocity = imageSize * texture(velocityMap, varTexCoord0).xy; + float pixVelocityLength = length(pixVelocity); + vec2 velocity = pixVelocity * texelSize; + int stereoSide = 0; + vec2 prevTexCoord = taa_fromFragUVToEyeUVAndSide(varTexCoord0, stereoSide) - velocity; + prevTexCoord = taa_fromEyeUVToFragUV(prevTexCoord, stereoSide); + vec2 prevPix = prevTexCoord * imageSize; + + // Pixel Debugged + if (taa_showDebugCursor()) { + vec2 cursorUVRaw = taa_getDebugCursorTexcoord(); + vec2 cursorPosRaw = floor(cursorUVRaw * imageSize) + vec2(0.5); + vec3 cursorFrag = taa_findClosestFragment3x3(cursorUVRaw); + vec2 cursorUV = cursorUVRaw; + vec2 cursorPos = cursorUV * imageSize; + vec2 cursorVelocity = texture(velocityMap, cursorUV).xy; + vec2 cursorPrevUV = taa_fromFragUVToEyeUVAndSide(cursorUV, stereoSide) - cursorVelocity; + cursorVelocity *= imageSize; + float cursorVelocityLength = length(cursorVelocity); + vec2 cursorVelocityDir = cursorVelocity / cursorVelocityLength; + + vec2 cursorToFragVec = pixPos - cursorPos; + float cursorToFragLength = length(cursorToFragVec); + + if ((cursorToFragLength <= cursorVelocityLength)) { + vec2 cursorVelocityDir = cursorVelocity / cursorVelocityLength; + vec2 cursorVelocityNor = vec2(cursorVelocityDir.y, -cursorVelocityDir.x); + + if ((dot(cursorVelocityDir, cursorToFragVec) < 0) && abs(dot(cursorVelocityNor, cursorToFragVec)) < 1.0) { + + vec3 speedColor = taa_getVelocityColorRelative(cursorToFragLength); + + outFragColor = vec4(speedColor, 1.0); + return; + } + } + + float tenPercentHeight = 0.1 * imageSize.y; + float centerWidth = imageSize.x * 0.5; + + //vec2 nextOrbPos = vec2(centerWidth, imageSize.y - 3 * tenPercentHeight); + vec2 nextOrbPos = cursorPos; + vec2 nextOrbPosToPix = pixPos - nextOrbPos; + float nextOrbPosToPixLength = length(nextOrbPosToPix); + + vec2 prevOrbPos = nextOrbPos - cursorVelocityDir * 2.0 * tenPercentHeight; + vec2 prevOrbPosToPix = pixPos - prevOrbPos; + float prevOrbPosToPixLength = length(prevOrbPosToPix); + + float orbPixThreshold = 2.0 / taa_getDebugOrbZoom(); + + if ((prevOrbPosToPixLength < tenPercentHeight) && (cursorVelocityLength > 0.5)) { + vec2 prevOrbPosToPix_uv = cursorPrevUV + prevOrbPosToPix * texelSize / taa_getDebugOrbZoom(); + vec3 preOrbColor = vec3(0.0); + if (!(any(lessThan(prevOrbPosToPix_uv, vec2(0.0))) || any(greaterThan(prevOrbPosToPix_uv, vec2(1.0))))) { + preOrbColor = texture(historyMap, prevOrbPosToPix_uv).xyz; + } + if (prevOrbPosToPixLength < orbPixThreshold) { + preOrbColor = vec3(1.0, 0.0, 1.0); + } + float distanceToNext = length(imageSize * (cursorUV - prevOrbPosToPix_uv)); + if (distanceToNext < orbPixThreshold) { + preOrbColor = vec3(1.0, 0.5, 0.0); + } + outFragColor = vec4(preOrbColor, 1.0); + return; + } + if (nextOrbPosToPixLength < tenPercentHeight) { + vec2 nextOrbPosToPix_uv = cursorUV + nextOrbPosToPix * texelSize / taa_getDebugOrbZoom(); + vec3 nextOrbColor = vec3(0.0); + if (!(any(lessThan(nextOrbPosToPix_uv, vec2(0.0))) || any(greaterThan(nextOrbPosToPix_uv, vec2(1.0))))) { + nextOrbColor = texture(nextMap, nextOrbPosToPix_uv).xyz; + } + float distanceToPrev = length(imageSize * (cursorPrevUV - nextOrbPosToPix_uv)); + if (distanceToPrev < orbPixThreshold) { + nextOrbColor = vec3(1.0, 0.0, 1.0); + } + if (nextOrbPosToPixLength < orbPixThreshold) { + nextOrbColor = vec3(1.0, 0.5, 0.0); + } + + outFragColor = vec4(nextOrbColor, 1.0); + return; + } + } + + // Debug region before debug or fxaa region X + float distToRegionDebug = varTexCoord0.x - taa_getRegionDebug().x; + float distToRegionFXAA = varTexCoord0.x - taa_getRegionFXAA().x; + if ((distToRegionFXAA < 0.0) && (distToRegionDebug > 0.0)) { + return; + } + + // draw region splitter + if ((abs(distToRegionDebug) < getInvWidthHeight().x) || (abs(distToRegionFXAA) < getInvWidthHeight().x)) { + outFragColor.rgb = vec3(1.0, 1.0, 0.0); + return; + } + + if (distToRegionFXAA > 0.0) { + return; + } + + if (taa_showClosestFragment()) { + vec3 fragUV = taa_findClosestFragment3x3(varTexCoord0); + outFragColor = vec4((fragUV.xy - varTexCoord0) * imageSize * 0.5 + vec2(0.5), 0.0, 1.0); + return; + } + + outFragColor = vec4(nextColor, 1.0); + + vec3 prevColor = nextColor; + + if (!(any(lessThan(prevTexCoord, vec2(0.0))) || any(greaterThan(prevTexCoord, vec2(1.0))))) { + prevColor = texture(historyMap, prevTexCoord).xyz; + } + + outFragColor.xyz = mix(prevColor, vec3(1,0,1), clamp(distance(prevColor, nextColor) - 0.01, 0, 1)); + + if (pixVelocityLength > params.debugShowVelocityThreshold) { + vec3 speedColor = taa_getVelocityColorAboveThreshold(pixVelocityLength); + + outFragColor = vec4(0.0, 1.0, 1.0, 1.0); + } +} diff --git a/libraries/render-utils/src/velocityBuffer_cameraMotion.slf b/libraries/render-utils/src/velocityBuffer_cameraMotion.slf new file mode 100644 index 0000000000..22a95b55d1 --- /dev/null +++ b/libraries/render-utils/src/velocityBuffer_cameraMotion.slf @@ -0,0 +1,41 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// Created by Sam Gateau on 6/3/16. +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +<@include DeferredTransform.slh@> +<$declareDeferredFrameTransform()$> + +in vec2 varTexCoord0; +out vec4 outFragColor; + +uniform sampler2D depthMap; + + +void main(void) { + // Pixel being shaded + ivec2 pixelPos; + vec2 texcoordPos; + ivec4 stereoSide; + ivec2 framePixelPos = getPixelPosTexcoordPosAndSide(gl_FragCoord.xy, pixelPos, texcoordPos, stereoSide); + + float Zdb = texelFetch(depthMap, ivec2(gl_FragCoord.xy), 0).x; + + // The position of the pixel fragment in Eye space then in world space + vec3 eyePos = evalEyePositionFromZdb(stereoSide.x, Zdb, texcoordPos); + vec3 worldPos = (getViewInverse() * vec4(eyePos, 1.0)).xyz; + + vec3 prevEyePos = (getPreviousView() * vec4(worldPos, 1.0)).xyz; + vec4 prevClipPos = (frameTransform._projection[stereoSide.x] * vec4(prevEyePos, 1.0)); + vec2 prevUV = 0.5 * (prevClipPos.xy / prevClipPos.w) + vec2(0.5); + + //vec2 imageSize = getWidthHeight(0); + vec2 imageSize = vec2(1.0, 1.0); + outFragColor = vec4( ((texcoordPos - prevUV) * imageSize), 0.0, 0.0); +} diff --git a/libraries/render/src/render/BlurTask.cpp b/libraries/render/src/render/BlurTask.cpp index 0625179a6d..4c3c52e07b 100644 --- a/libraries/render/src/render/BlurTask.cpp +++ b/libraries/render/src/render/BlurTask.cpp @@ -286,7 +286,7 @@ void BlurGaussian::run(const RenderContextPointer& renderContext, const gpu::Fra _parameters->setWidthHeight(blurredFramebuffer->getWidth(), blurredFramebuffer->getHeight(), args->isStereo()); _parameters->setTexcoordTransform(gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(textureSize, viewport)); - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch("BlurGaussian::run", args->_context, [=](gpu::Batch& batch) { batch.enableStereo(false); batch.setViewportTransform(viewport); @@ -401,7 +401,7 @@ void BlurGaussianDepthAware::run(const RenderContextPointer& renderContext, cons _parameters->setDepthPerspective(args->getViewFrustum().getProjection()[1][1]); _parameters->setLinearDepthPosFar(args->getViewFrustum().getFarClip()); - gpu::doInBatch(args->_context, [=](gpu::Batch& batch) { + gpu::doInBatch("BlurGaussianDepthAware::run", args->_context, [=](gpu::Batch& batch) { batch.enableStereo(false); batch.setViewportTransform(sourceViewport); diff --git a/libraries/render/src/render/DrawSceneOctree.cpp b/libraries/render/src/render/DrawSceneOctree.cpp index 6a35623fa0..433851eec2 100644 --- a/libraries/render/src/render/DrawSceneOctree.cpp +++ b/libraries/render/src/render/DrawSceneOctree.cpp @@ -94,7 +94,7 @@ void DrawSceneOctree::run(const RenderContextPointer& renderContext, const ItemS std::static_pointer_cast(renderContext->jobConfig)->numFreeCells = (int)scene->getSpatialTree().getNumFreeCells(); - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("DrawSceneOctree::run", args->_context, [&](gpu::Batch& batch) { glm::mat4 projMat; Transform viewMat; args->getViewFrustum().evalProjectionMatrix(projMat); @@ -201,7 +201,7 @@ void DrawItemSelection::run(const RenderContextPointer& renderContext, const Ite RenderArgs* args = renderContext->args; auto& scene = renderContext->_scene; - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("DrawItemSelection::run", args->_context, [&](gpu::Batch& batch) { glm::mat4 projMat; Transform viewMat; args->getViewFrustum().evalProjectionMatrix(projMat); diff --git a/libraries/render/src/render/DrawStatus.cpp b/libraries/render/src/render/DrawStatus.cpp index a11e9b1a88..56802a3239 100644 --- a/libraries/render/src/render/DrawStatus.cpp +++ b/libraries/render/src/render/DrawStatus.cpp @@ -163,7 +163,7 @@ void DrawStatus::run(const RenderContextPointer& renderContext, const ItemBounds } // Allright, something to render let's do it - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("DrawStatus::run", args->_context, [&](gpu::Batch& batch) { glm::mat4 projMat; Transform viewMat; args->getViewFrustum().evalProjectionMatrix(projMat); diff --git a/libraries/render/src/render/DrawTask.cpp b/libraries/render/src/render/DrawTask.cpp index 88d38d1c66..bdff97c1c1 100755 --- a/libraries/render/src/render/DrawTask.cpp +++ b/libraries/render/src/render/DrawTask.cpp @@ -143,7 +143,7 @@ void DrawLight::run(const RenderContextPointer& renderContext, const ItemBounds& RenderArgs* args = renderContext->args; // render lights - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("DrawLight::run", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; renderItems(renderContext, inLights, _maxDrawn); args->_batch = nullptr; @@ -191,7 +191,7 @@ void DrawBounds::run(const RenderContextPointer& renderContext, _drawBuffer->setData(numItems * sizeOfItemBound, (const gpu::Byte*) items.data()); - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("DrawBounds::run", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; // Setup projection @@ -235,7 +235,7 @@ void DrawQuadVolume::run(const render::RenderContextPointer& renderContext, cons } RenderArgs* args = renderContext->args; - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("DrawQuadVolume::run", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; batch.setViewportTransform(args->_viewport); batch.setStateScissorRect(args->_viewport); diff --git a/libraries/render/src/render/ResampleTask.cpp b/libraries/render/src/render/ResampleTask.cpp index 65c0ff45b9..07f7367582 100644 --- a/libraries/render/src/render/ResampleTask.cpp +++ b/libraries/render/src/render/ResampleTask.cpp @@ -66,7 +66,7 @@ void HalfDownsample::run(const RenderContextPointer& renderContext, const gpu::F const auto bufferSize = resampledFrameBuffer->getSize(); glm::ivec4 viewport{ 0, 0, bufferSize.x, bufferSize.y }; - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("HalfDownsample::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); batch.setFramebuffer(resampledFrameBuffer); diff --git a/libraries/render/src/render/Scene.cpp b/libraries/render/src/render/Scene.cpp index aaf1d2f1c8..0b4ee2f11a 100644 --- a/libraries/render/src/render/Scene.cpp +++ b/libraries/render/src/render/Scene.cpp @@ -435,44 +435,47 @@ void Scene::queryTransitionItems(const Transaction::TransitionQueries& transacti void Scene::resetHighlights(const Transaction::HighlightResets& transactions) { auto outlineStage = getStage(HighlightStage::getName()); + if (outlineStage) { + for (auto& transaction : transactions) { + const auto& selectionName = std::get<0>(transaction); + const auto& newStyle = std::get<1>(transaction); + auto outlineId = outlineStage->getHighlightIdBySelection(selectionName); - for (auto& transaction : transactions) { - const auto& selectionName = std::get<0>(transaction); - const auto& newStyle = std::get<1>(transaction); - auto outlineId = outlineStage->getHighlightIdBySelection(selectionName); - - if (HighlightStage::isIndexInvalid(outlineId)) { - outlineStage->addHighlight(selectionName, newStyle); - } else { - outlineStage->editHighlight(outlineId)._style = newStyle; + if (HighlightStage::isIndexInvalid(outlineId)) { + outlineStage->addHighlight(selectionName, newStyle); + } else { + outlineStage->editHighlight(outlineId)._style = newStyle; + } } } } void Scene::removeHighlights(const Transaction::HighlightRemoves& transactions) { auto outlineStage = getStage(HighlightStage::getName()); + if (outlineStage) { + for (auto& selectionName : transactions) { + auto outlineId = outlineStage->getHighlightIdBySelection(selectionName); - for (auto& selectionName : transactions) { - auto outlineId = outlineStage->getHighlightIdBySelection(selectionName); - - if (!HighlightStage::isIndexInvalid(outlineId)) { - outlineStage->removeHighlight(outlineId); + if (!HighlightStage::isIndexInvalid(outlineId)) { + outlineStage->removeHighlight(outlineId); + } } } } void Scene::queryHighlights(const Transaction::HighlightQueries& transactions) { auto outlineStage = getStage(HighlightStage::getName()); + if (outlineStage) { + for (auto& transaction : transactions) { + const auto& selectionName = std::get<0>(transaction); + const auto& func = std::get<1>(transaction); + auto outlineId = outlineStage->getHighlightIdBySelection(selectionName); - for (auto& transaction : transactions) { - const auto& selectionName = std::get<0>(transaction); - const auto& func = std::get<1>(transaction); - auto outlineId = outlineStage->getHighlightIdBySelection(selectionName); - - if (!HighlightStage::isIndexInvalid(outlineId)) { - func(&outlineStage->editHighlight(outlineId)._style); - } else { - func(nullptr); + if (!HighlightStage::isIndexInvalid(outlineId)) { + func(&outlineStage->editHighlight(outlineId)._style); + } else { + func(nullptr); + } } } } diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h index 23e8e3b896..68b4dcd408 100644 --- a/libraries/script-engine/src/Quat.h +++ b/libraries/script-engine/src/Quat.h @@ -35,7 +35,7 @@ * of gimbal lock. * @namespace Quat * @variation 0 - * @property IDENTITY {Quat} The identity rotation, i.e., no rotation. + * @property IDENTITY {Quat} The identity rotation, i.e., no rotation. Its value is { x: 0, y: 0, z: 0, w: 1 }. * @example Print the IDENTITY value. * print(JSON.stringify(Quat.IDENTITY)); // { x: 0, y: 0, z: 0, w: 1 } * print(JSON.stringify(Quat.safeEulerAngles(Quat.IDENTITY))); // { x: 0, y: 0, z: 0 } diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h index 1e1cd917a3..7ed0fd9e8b 100644 --- a/libraries/script-engine/src/Vec3.h +++ b/libraries/script-engine/src/Vec3.h @@ -25,27 +25,36 @@ * A 2-dimensional vector. * * @typedef {object} Vec2 - * @property {float} x X-coordinate of the vector. - * @property {float} y Y-coordinate of the vector. + * @property {number} x - X-coordinate of the vector. + * @property {number} y - Y-coordinate of the vector. */ /**jsdoc * A 3-dimensional vector. * * @typedef {object} Vec3 - * @property {float} x X-coordinate of the vector. - * @property {float} y Y-coordinate of the vector. - * @property {float} z Z-coordinate of the vector. + * @property {number} x - X-coordinate of the vector. + * @property {number} y - Y-coordinate of the vector. + * @property {number} z - Z-coordinate of the vector. */ /**jsdoc * A 4-dimensional vector. * * @typedef {object} Vec4 - * @property {float} x X-coordinate of the vector. - * @property {float} y Y-coordinate of the vector. - * @property {float} z Z-coordinate of the vector. - * @property {float} w W-coordinate of the vector. + * @property {number} x - X-coordinate of the vector. + * @property {number} y - Y-coordinate of the vector. + * @property {number} z - Z-coordinate of the vector. + * @property {number} w - W-coordinate of the vector. + */ + +/**jsdoc + * A color vector. + * + * @typedef {object} Vec3Color + * @property {number} x - Red component value. Integer in the range 0 - 255. + * @property {number} y - Green component value. Integer in the range 0 - 255. + * @property {number} z - Blue component value. Integer in the range 0 - 255. */ /// Scriptable interface a Vec3ernion helper class object. Used exclusively in the JavaScript API diff --git a/libraries/shared/src/PhysicsCollisionGroups.h b/libraries/shared/src/PhysicsCollisionGroups.h index 794f338dc5..edec61dc67 100644 --- a/libraries/shared/src/PhysicsCollisionGroups.h +++ b/libraries/shared/src/PhysicsCollisionGroups.h @@ -64,6 +64,25 @@ const int16_t BULLET_COLLISION_MASK_OTHER_AVATAR = BULLET_COLLISION_MASK_DEFAULT // COLLISIONLESS gets an empty mask. const int16_t BULLET_COLLISION_MASK_COLLISIONLESS = 0; +/**jsdoc + *

An entity may collide with the following types of items:

+ * + * + * + * + * + * + * + * + * + * + * + *
ValueDescription
1Static entities — non-dynamic entities with no velocity.
2Dynamic entities — entities that have their dynamic property set to + * true.
4Kinematic entities — non-dynamic entities with velocity.
8My avatar.
16Other avatars.
+ *

The values for the collision types that are enabled are added together to give the CollisionMask value. For example, a + * value of 31 means that an entity will collide with all item types.

+ * @typedef {number} Entities.CollisionMask + */ // The USER collision groups are exposed to script and can be used to generate per-object collision masks. // They are not necessarily the same as the BULLET_COLLISION_GROUPS, but we start them off with matching numbers. diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 7b455beae5..a3d312b9c1 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -689,6 +689,15 @@ QScriptValue qColorToScriptValue(QScriptEngine* engine, const QColor& color) { return object; } +/**jsdoc + * An axis-aligned cube, defined as the bottom right near (minimum axes values) corner of the cube plus the dimension of its + * sides. + * @typedef {object} AACube + * @property {number} x - X coordinate of the brn corner of the cube. + * @property {number} y - Y coordinate of the brn corner of the cube. + * @property {number} z - Z coordinate of the brn corner of the cube. + * @property {number} scale - The dimensions of each side of the cube. + */ QScriptValue aaCubeToScriptValue(QScriptEngine* engine, const AACube& aaCube) { QScriptValue obj = engine->newObject(); const glm::vec3& corner = aaCube.getCorner(); @@ -765,6 +774,15 @@ void pickRayFromScriptValue(const QScriptValue& object, PickRay& pickRay) { } } +/**jsdoc + * @typedef {object} Collision + * @property {ContactEventType} type - The contact type of the collision event. + * @property {Uuid} idA - The ID of one of the entities in the collision. + * @property {Uuid} idB - The ID of the other of the entities in the collision. + * @property {Vec3} penetration - The amount of penetration between the two entities. + * @property {Vec3} contactPoint - The point of contact. + * @property {Vec3} velocityChange - The change in relative velocity of the two entities, in m/s. + */ QScriptValue collisionToScriptValue(QScriptEngine* engine, const Collision& collision) { QScriptValue obj = engine->newObject(); obj.setProperty("type", collision.type); diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 4dbbd190ff..4fee78a0db 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -140,7 +140,7 @@ public: * * @typedef {object} PickRay * @property {Vec3} origin - The starting position of the PickRay. - * @property {Quat} direction - The direction that the PickRay travels. + * @property {Vec3} direction - The direction that the PickRay travels. */ class PickRay : public MathPick { public: @@ -265,6 +265,20 @@ namespace std { }; } +/**jsdoc + *

The type of a collision contact event. + * + * + * + * + * + * + * + * + * + *
ValueDescription
0Start of the collision.
1Continuation of the collision.
2End of the collision.
+ * @typedef {number} ContactEventType + */ enum ContactEventType { CONTACT_EVENT_TYPE_START, CONTACT_EVENT_TYPE_CONTINUE, @@ -328,13 +342,32 @@ namespace graphics { using MeshPointer = std::shared_ptr; - +/**jsdoc + * A handle for a mesh in an entity, such as returned by {@link Entities.getMeshes}. + * @class MeshProxy + * @deprecated Use the {@link Graphics} API instead. + */ class MeshProxy : public QObject { Q_OBJECT public: virtual MeshPointer getMeshPointer() const = 0; + + /**jsdoc + * Get the number of vertices in the mesh. + * @function MeshProxy#getNumVertices + * @returns {number} Integer number of vertices in the mesh. + * @deprecated Use the {@link Graphics} API instead. + */ Q_INVOKABLE virtual int getNumVertices() const = 0; + + /**jsdoc + * Get the position of a vertex in the mesh. + * @function MeshProxy#getPos3 + * @param {number} index - Integer index of the mesh vertex. + * @returns {Vec3} Local position of the vertex relative to the mesh. + * @deprecated Use the {@link Graphics} API instead. + */ Q_INVOKABLE virtual glm::vec3 getPos3(int index) const = 0; }; diff --git a/libraries/shared/src/ShapeInfo.cpp b/libraries/shared/src/ShapeInfo.cpp index 8cdc4bcf14..968292da87 100644 --- a/libraries/shared/src/ShapeInfo.cpp +++ b/libraries/shared/src/ShapeInfo.cpp @@ -1,6 +1,6 @@ // // ShapeInfo.cpp -// libraries/physics/src +// libraries/shared/src // // Created by Andrew Meadows 2014.10.29 // Copyright 2014 High Fidelity, Inc. @@ -15,6 +15,33 @@ #include "NumericalConstants.h" // for MILLIMETERS_PER_METER +/**jsdoc + *

A ShapeType defines the shape used for collisions or zones.

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ValueDescription
"none"No shape.
"box"A cube.
"sphere"A sphere.
"capsule-x"A capsule (cylinder with spherical ends) oriented on the x-axis.
"capsule-y"A capsule (cylinder with spherical ends) oriented on the y-axis.
"capsule-z"A capsule (cylinder with spherical ends) oriented on the z-axis.
"cylinder-x"A cylinder oriented on the x-axis.
"cylinder-y"A cylinder oriented on the y-axis.
"cylinder-z"A cylinder oriented on the z-axis.
"hull"Not used.
"compound"A compound convex hull specified in an OBJ file.
"simple-hull"A convex hull automatically generated from the model.
"simple-compound"A compound convex hull automatically generated from the model, using + * sub-meshes.
"static-mesh"The exact shape of the model.
"plane"A plane.
+ * @typedef {string} ShapeType + */ // Originally within EntityItemProperties.cpp const char* shapeTypeNames[] = { "none", diff --git a/libraries/shared/src/shared/Storage.cpp b/libraries/shared/src/shared/Storage.cpp index 683903a4e8..8fe1454242 100644 --- a/libraries/shared/src/shared/Storage.cpp +++ b/libraries/shared/src/shared/Storage.cpp @@ -85,7 +85,7 @@ FileStorage::FileStorage(const QString& filename) : _file(filename) { qCDebug(storagelogging) << "Failed to map file, falling back to memory storage " << filename; _fallback = _file.readAll(); _mapped = (uint8_t*)_fallback.data(); - } + } _valid = true; } else { qCWarning(storagelogging) << "Failed to open file " << filename; diff --git a/libraries/ui/src/MainWindow.cpp b/libraries/ui/src/MainWindow.cpp index 20f994e63d..091fd850af 100644 --- a/libraries/ui/src/MainWindow.cpp +++ b/libraries/ui/src/MainWindow.cpp @@ -107,12 +107,12 @@ void MainWindow::changeEvent(QEvent* event) { QWindowStateChangeEvent* stateChangeEvent = static_cast(event); if ((stateChangeEvent->oldState() == Qt::WindowNoState || stateChangeEvent->oldState() == Qt::WindowMaximized) && - windowState() == Qt::WindowMinimized) { + (windowState() & Qt::WindowMinimized) == Qt::WindowMinimized) { emit windowShown(false); emit windowMinimizedChanged(true); } else { emit windowShown(true); - if (stateChangeEvent->oldState() == Qt::WindowMinimized) { + if ((stateChangeEvent->oldState() & Qt::WindowMinimized) == Qt::WindowMinimized) { emit windowMinimizedChanged(false); } } diff --git a/libraries/ui/src/VirtualPadManager.cpp b/libraries/ui/src/VirtualPadManager.cpp index 486a6c2c71..cb3ef20e67 100644 --- a/libraries/ui/src/VirtualPadManager.cpp +++ b/libraries/ui/src/VirtualPadManager.cpp @@ -34,6 +34,11 @@ namespace VirtualPad { return _currentTouch; } + const float Manager::DPI = 534.0f; + const float Manager::PIXEL_SIZE = 512.0f; + const float Manager::STICK_RADIUS = 105.0f; + const float Manager::BASE_MARGIN = 59.0f; + Manager::Manager() { } diff --git a/libraries/ui/src/VirtualPadManager.h b/libraries/ui/src/VirtualPadManager.h index 3563d333f8..6b68af3acd 100644 --- a/libraries/ui/src/VirtualPadManager.h +++ b/libraries/ui/src/VirtualPadManager.h @@ -44,6 +44,12 @@ namespace VirtualPad { void hide(bool hide); int extraBottomMargin(); void setExtraBottomMargin(int margin); + + static const float DPI; + static const float PIXEL_SIZE; + static const float STICK_RADIUS; + static const float BASE_MARGIN; + private: Instance _leftVPadInstance; bool _enabled; diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h index 34827117f0..a8c8ddd9c8 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.h +++ b/libraries/ui/src/ui/TabletScriptingInterface.h @@ -78,7 +78,7 @@ public: QObject* getFlags(); signals: - /** jsdoc + /**jsdoc * Signaled when a tablet message or dialog is created * @function TabletProxy#tabletNotification * @returns {Signal} @@ -205,7 +205,7 @@ public: - /** jsdoc + /**jsdoc * Check if the tablet has a message dialog open * @function TabletProxy#isMessageDialogOpen */ @@ -291,7 +291,7 @@ signals: */ void screenChanged(QVariant type, QVariant url); - /** jsdoc + /**jsdoc * Signaled when the tablet becomes visible or becomes invisible * @function TabletProxy#isTabletShownChanged * @returns {Signal} diff --git a/scripts/developer/tests/dynamics/dynamicsTests.js b/scripts/developer/tests/dynamics/dynamicsTests.js index e9262c9308..db089f09ee 100644 --- a/scripts/developer/tests/dynamics/dynamicsTests.js +++ b/scripts/developer/tests/dynamics/dynamicsTests.js @@ -186,7 +186,7 @@ var prevEntityID = null; for (var i = 0; i < 7; i++) { var newID = Entities.addEntity({ - name: "hinge test " + i, + name: "slider test " + i, type: "Box", color: { blue: 128, green: 40 * i, red: 20 }, dimensions: { x: 0.2, y: 0.1, z: 0.2 }, diff --git a/scripts/developer/utilities/render/antialiasing.js b/scripts/developer/utilities/render/antialiasing.js new file mode 100644 index 0000000000..e915d75e93 --- /dev/null +++ b/scripts/developer/utilities/render/antialiasing.js @@ -0,0 +1,99 @@ +"use strict"; + +// +// gemstoneMagicMaker.js +// tablet-sample-app +// +// Created by Faye Li on Feb 6 2017. +// Copyright 2017 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +(function() { + var TABLET_BUTTON_NAME = "TAA"; + var QMLAPP_URL = Script.resolvePath("./antialiasing.qml"); + + + var onLuciScreen = false; + + function onClicked() { + if (onLuciScreen) { + tablet.gotoHomeScreen(); + } else { + tablet.loadQMLSource(QMLAPP_URL); + } + } + + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + var button = tablet.addButton({ + text: TABLET_BUTTON_NAME, + sortOrder: 1 + }); + + var hasEventBridge = false; + + function wireEventBridge(on) { + if (!tablet) { + print("Warning in wireEventBridge(): 'tablet' undefined!"); + return; + } + if (on) { + if (!hasEventBridge) { + tablet.fromQml.connect(fromQml); + hasEventBridge = true; + } + } else { + if (hasEventBridge) { + tablet.fromQml.disconnect(fromQml); + hasEventBridge = false; + } + } + } + + function onScreenChanged(type, url) { + if (url === QMLAPP_URL) { + onLuciScreen = true; + } else { + onLuciScreen = false; + } + + button.editProperties({isActive: onLuciScreen}); + wireEventBridge(onLuciScreen); + } + + function fromQml(message) { + } + + button.clicked.connect(onClicked); + tablet.screenChanged.connect(onScreenChanged); + + var moveDebugCursor = false; + Controller.mousePressEvent.connect(function (e) { + if (e.isMiddleButton) { + moveDebugCursor = true; + setDebugCursor(e.x, e.y); + } + }); + Controller.mouseReleaseEvent.connect(function() { moveDebugCursor = false; }); + Controller.mouseMoveEvent.connect(function (e) { if (moveDebugCursor) setDebugCursor(e.x, e.y); }); + + + Script.scriptEnding.connect(function () { + if (onLuciScreen) { + tablet.gotoHomeScreen(); + } + button.clicked.disconnect(onClicked); + tablet.screenChanged.disconnect(onScreenChanged); + tablet.removeButton(button); + }); + + function setDebugCursor(x, y) { + nx = ((x + 0.5) / Window.innerWidth); + ny = 1.0 - ((y + 0.5) / (Window.innerHeight)); + + Render.getConfig("RenderMainView").getConfig("Antialiasing").debugCursorTexcoord = { x: nx, y: ny }; + } + +}()); \ No newline at end of file diff --git a/scripts/developer/utilities/render/antialiasing.qml b/scripts/developer/utilities/render/antialiasing.qml new file mode 100644 index 0000000000..e8034c48bd --- /dev/null +++ b/scripts/developer/utilities/render/antialiasing.qml @@ -0,0 +1,177 @@ +// +// Antialiasing.qml +// +// Created by Sam Gateau on 8/14/2017 +// Copyright 2016 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html +// + +import QtQuick 2.7 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.3 + +import "qrc:///qml/styles-uit" +import "qrc:///qml/controls-uit" as HifiControls + +import "configSlider" +import "../lib/plotperf" + +Item { +Rectangle { + id: root; + + HifiConstants { id: hifi; } + color: hifi.colors.baseGray; + + Column { + id: antialiasing + spacing: 20 + padding: 10 + + Column{ + spacing: 10 + + Row { + spacing: 10 + id: fxaaOnOff + property bool debugFXAA: false + HifiControls.Button { + text: { + if (fxaaOnOff.debugFXAA) { + return "FXAA" + } else { + return "TAA" + } + } + onClicked: { + fxaaOnOff.debugFXAA = !fxaaOnOff.debugFXAA + if (fxaaOnOff.debugFXAA) { + Render.getConfig("RenderMainView.JitterCam").none(); + Render.getConfig("RenderMainView.Antialiasing").debugFXAAX = 0; + } else { + Render.getConfig("RenderMainView.JitterCam").play(); + Render.getConfig("RenderMainView.Antialiasing").debugFXAAX = 1.0; + } + + } + } + } + Separator {} + Row { + spacing: 10 + + HifiControls.Button { + text: { + var state = 2 - (Render.getConfig("RenderMainView.JitterCam").freeze * 1 - Render.getConfig("RenderMainView.JitterCam").stop * 2); + if (state === 2) { + return "Jitter" + } else if (state === 1) { + return "Paused at " + Render.getConfig("RenderMainView.JitterCam").index + "" + } else { + return "No Jitter" + } + } + onClicked: { Render.getConfig("RenderMainView.JitterCam").cycleStopPauseRun(); } + } + HifiControls.Button { + text: "<" + onClicked: { Render.getConfig("RenderMainView.JitterCam").prev(); } + } + HifiControls.Button { + text: ">" + onClicked: { Render.getConfig("RenderMainView.JitterCam").next(); } + } + } + Separator {} + HifiControls.CheckBox { + boxSize: 20 + text: "Constrain color" + checked: Render.getConfig("RenderMainView.Antialiasing")["constrainColor"] + onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["constrainColor"] = checked } + } + ConfigSlider { + label: qsTr("Covariance gamma") + integral: false + config: Render.getConfig("RenderMainView.Antialiasing") + property: "covarianceGamma" + max: 1.5 + min: 0.5 + } + Separator {} + HifiControls.CheckBox { + boxSize: 20 + text: "Feedback history color" + checked: Render.getConfig("RenderMainView.Antialiasing")["feedbackColor"] + onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["feedbackColor"] = checked } + } + + ConfigSlider { + label: qsTr("Source blend") + integral: false + config: Render.getConfig("RenderMainView.Antialiasing") + property: "blend" + max: 1.0 + min: 0.0 + } + + ConfigSlider { + label: qsTr("Post sharpen") + integral: false + config: Render.getConfig("RenderMainView.Antialiasing") + property: "sharpen" + max: 1.0 + min: 0.0 + } + Separator {} + Row { + + spacing: 10 + HifiControls.CheckBox { + boxSize: 20 + text: "Debug" + checked: Render.getConfig("RenderMainView.Antialiasing")["debug"] + onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["debug"] = checked } + } + HifiControls.CheckBox { + boxSize: 20 + text: "Show Debug Cursor" + checked: Render.getConfig("RenderMainView.Antialiasing")["showCursorPixel"] + onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["showCursorPixel"] = checked } + } + } + ConfigSlider { + label: qsTr("Debug Region <") + integral: false + config: Render.getConfig("RenderMainView.Antialiasing") + property: "debugX" + max: 1.0 + min: 0.0 + } + HifiControls.CheckBox { + boxSize: 20 + text: "Closest Fragment" + checked: Render.getConfig("RenderMainView.Antialiasing")["showClosestFragment"] + onCheckedChanged: { Render.getConfig("RenderMainView.Antialiasing")["showClosestFragment"] = checked } + } + ConfigSlider { + label: qsTr("Debug Velocity Threshold [pix]") + integral: false + config: Render.getConfig("RenderMainView.Antialiasing") + property: "debugShowVelocityThreshold" + max: 50 + min: 0.0 + } + ConfigSlider { + label: qsTr("Debug Orb Zoom") + integral: false + config: Render.getConfig("RenderMainView.Antialiasing") + property: "debugOrbZoom" + max: 32.0 + min: 1.0 + } + } + } +} +} diff --git a/scripts/developer/utilities/render/deferredLighting.qml b/scripts/developer/utilities/render/deferredLighting.qml index 1da7871172..189d23c44f 100644 --- a/scripts/developer/utilities/render/deferredLighting.qml +++ b/scripts/developer/utilities/render/deferredLighting.qml @@ -200,6 +200,7 @@ Rectangle { ListElement { text: "Debug Scattering"; color: "White" } ListElement { text: "Ambient Occlusion"; color: "White" } ListElement { text: "Ambient Occlusion Blurred"; color: "White" } + ListElement { text: "Velocity"; color: "White" } ListElement { text: "Custom"; color: "White" } } width: 200 diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 7389442649..82a450bedd 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -313,7 +313,7 @@ -
+
diff --git a/tests/gpu-test/src/TestWindow.cpp b/tests/gpu-test/src/TestWindow.cpp index 62aef37af6..1fa4862e53 100644 --- a/tests/gpu-test/src/TestWindow.cpp +++ b/tests/gpu-test/src/TestWindow.cpp @@ -114,14 +114,14 @@ void TestWindow::beginFrame() { // the rest of the renderDeferred inputs can be omitted #else - gpu::doInBatch(_renderArgs->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("TestWindow::beginFrame", _renderArgs->_context, [&](gpu::Batch& batch) { batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLORS, { 0.0f, 0.1f, 0.2f, 1.0f }); batch.clearDepthFramebuffer(1e4); batch.setViewportTransform({ 0, 0, _size.width() * devicePixelRatio(), _size.height() * devicePixelRatio() }); }); #endif - gpu::doInBatch(_renderArgs->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("TestWindow::beginFrame", _renderArgs->_context, [&](gpu::Batch& batch) { batch.setViewportTransform(_renderArgs->_viewport); batch.setStateScissorRect(_renderArgs->_viewport); batch.setProjectionTransform(_projectionMatrix); @@ -131,7 +131,7 @@ void TestWindow::beginFrame() { void TestWindow::endFrame() { #ifdef DEFERRED_LIGHTING RenderArgs* args = _renderContext->args; - gpu::doInBatch(args->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("TestWindow::endFrame::begin", args->_context, [&](gpu::Batch& batch) { args->_batch = &batch; auto deferredFboColorDepthStencil = _prepareDeferredOutputs.get0()->getDeferredFramebufferDepthColor(); batch.setViewportTransform(args->_viewport); @@ -144,7 +144,7 @@ void TestWindow::endFrame() { _renderDeferred.run(_renderContext, _renderDeferredInputs); - gpu::doInBatch(_renderArgs->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("TestWindow::endFrame::blit", _renderArgs->_context, [&](gpu::Batch& batch) { PROFILE_RANGE_BATCH(batch, "blit"); // Blit to screen auto framebufferCache = DependencyManager::get(); @@ -154,7 +154,7 @@ void TestWindow::endFrame() { }); #endif - gpu::doInBatch(_renderArgs->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("TestWindow::endFrame::finish", _renderArgs->_context, [&](gpu::Batch& batch) { batch.resetStages(); }); _glContext.swapBuffers(this); diff --git a/tests/gpu-test/src/main.cpp b/tests/gpu-test/src/main.cpp index 77ce015e3f..41d84ca026 100644 --- a/tests/gpu-test/src/main.cpp +++ b/tests/gpu-test/src/main.cpp @@ -147,7 +147,7 @@ class MyTestWindow : public TestWindow { return; } - gpu::doInBatch(_renderArgs->_context, [&](gpu::Batch& batch) { + gpu::doInBatch("main::renderFrame", _renderArgs->_context, [&](gpu::Batch& batch) { batch.setViewTransform(_camera); _renderArgs->_batch = &batch; _currentTest->renderTest(_currentTestId, _renderArgs); diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp index ec56555f68..93672cc5a2 100644 --- a/tests/render-perf/src/main.cpp +++ b/tests/render-perf/src/main.cpp @@ -919,7 +919,7 @@ private: void render(RenderArgs* renderArgs) { auto& gpuContext = renderArgs->_context; gpuContext->beginFrame(); - gpu::doInBatch(gpuContext, [&](gpu::Batch& batch) { + gpu::doInBatch("QTestWindow::render", gpuContext, [&](gpu::Batch& batch) { batch.resetStages(); }); PROFILE_RANGE(render, __FUNCTION__); diff --git a/tests/render-texture-load/src/main.cpp b/tests/render-texture-load/src/main.cpp index 62c970cab5..ce666065e3 100644 --- a/tests/render-texture-load/src/main.cpp +++ b/tests/render-texture-load/src/main.cpp @@ -563,12 +563,12 @@ private: void render() { auto& gpuContext = _renderThread._gpuContext; gpuContext->beginFrame(); - gpu::doInBatch(gpuContext, [&](gpu::Batch& batch) { + gpu::doInBatch("RenderThread::render::begin", gpuContext, [&](gpu::Batch& batch) { batch.resetStages(); }); auto framebuffer = DependencyManager::get()->getFramebuffer(); - gpu::doInBatch(gpuContext, [&](gpu::Batch& batch) { + gpu::doInBatch("RenderThread::render", gpuContext, [&](gpu::Batch& batch) { batch.enableStereo(false); batch.setFramebuffer(framebuffer); batch.clearColorFramebuffer(gpu::Framebuffer::BUFFER_COLOR0, vec4(1, 0, 0, 1)); diff --git a/tools/jsdoc/plugins/hifi.js b/tools/jsdoc/plugins/hifi.js index bb556814e8..1f73f14b2b 100644 --- a/tools/jsdoc/plugins/hifi.js +++ b/tools/jsdoc/plugins/hifi.js @@ -24,13 +24,16 @@ exports.handlers = { '../../libraries/animation/src', '../../libraries/avatars/src', '../../libraries/controllers/src/controllers/', + '../../libraries/graphics-scripting/src/graphics-scripting/', '../../libraries/entities/src', + '../../libraries/model-networking/src/model-networking/', + '../../libraries/octree/src', '../../libraries/networking/src', + '../../libraries/physics/src', '../../libraries/pointers/src', - '../../libraries/render-utils/src', + '../../libraries/script-engine/src', '../../libraries/shared/src', '../../libraries/shared/src/shared', - '../../libraries/script-engine/src', ]; var exts = ['.h', '.cpp'];