Merge remote-tracking branch 'overte/master' into application

This commit is contained in:
HifiExperiments 2025-01-04 21:39:29 -08:00
commit 22bd86f122
100 changed files with 1225 additions and 1250 deletions

View file

@ -82,16 +82,6 @@ jobs:
arch: aarch64 arch: aarch64
runner: [self_hosted, type-cax41, image-arm-app-docker-ce] runner: [self_hosted, type-cax41, image-arm-app-docker-ce]
- os: fedora-39
image: docker.io/overte/overte-server-build:0.1.4-fedora-39-amd64
arch: amd64
runner: [self_hosted, type-cx52, image-x86-app-docker-ce]
- os: fedora-39
image: docker.io/overte/overte-server-build:0.1.4-fedora-39-aarch64
arch: aarch64
runner: [self_hosted, type-cax41, image-arm-app-docker-ce]
- os: fedora-40 - os: fedora-40
image: docker.io/overte/overte-server-build:0.1.4-fedora-40-amd64 image: docker.io/overte/overte-server-build:0.1.4-fedora-40-amd64
arch: amd64 arch: amd64
@ -224,10 +214,6 @@ jobs:
else # RPM else # RPM
if [ "${{ matrix.os }}" == "rockylinux-9" ]; then if [ "${{ matrix.os }}" == "rockylinux-9" ]; then
echo "ARTIFACT_PATTERN=overte-server-$RPMVERSION-1.el9.$INSTALLER_EXT" >> $GITHUB_ENV echo "ARTIFACT_PATTERN=overte-server-$RPMVERSION-1.el9.$INSTALLER_EXT" >> $GITHUB_ENV
elif [ "${{ matrix.os }}" == "fedora-38" ]; then
echo "ARTIFACT_PATTERN=overte-server-$RPMVERSION-1.fc38.$INSTALLER_EXT" >> $GITHUB_ENV
elif [ "${{ matrix.os }}" == "fedora-39" ]; then
echo "ARTIFACT_PATTERN=overte-server-$RPMVERSION-1.fc39.$INSTALLER_EXT" >> $GITHUB_ENV
elif [ "${{ matrix.os }}" == "fedora-40" ]; then elif [ "${{ matrix.os }}" == "fedora-40" ]; then
echo "ARTIFACT_PATTERN=overte-server-$RPMVERSION-1.fc40.$INSTALLER_EXT" >> $GITHUB_ENV echo "ARTIFACT_PATTERN=overte-server-$RPMVERSION-1.fc40.$INSTALLER_EXT" >> $GITHUB_ENV
else else
@ -266,7 +252,7 @@ jobs:
- name: Archive cmake logs - name: Archive cmake logs
if: always() if: always()
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: cmake-logs-${{ matrix.os }}-${{ matrix.arch }}-${{ github.event.number }}.tar.xz name: cmake-logs-${{ matrix.os }}-${{ matrix.arch }}-${{ github.event.number }}.tar.xz
path: cmake-logs-${{ matrix.os }}-${{ matrix.arch }}-${{ github.event.number }}.tar.xz path: cmake-logs-${{ matrix.os }}-${{ matrix.arch }}-${{ github.event.number }}.tar.xz

View file

@ -65,7 +65,7 @@ jobs:
run: | run: |
echo "UPLOAD_PREFIX=build/overte/master" >> $GITHUB_ENV echo "UPLOAD_PREFIX=build/overte/master" >> $GITHUB_ENV
echo ::set-output name=github_sha_short::`echo $GIT_COMMIT | cut -c1-7` echo "{github_sha_short}={`echo $GIT_COMMIT | cut -c1-7`}" >> $GITHUB_OUTPUT
echo "JOB_NAME=build (${{matrix.os}}, ${{matrix.build_type}})" >> $GITHUB_ENV echo "JOB_NAME=build (${{matrix.os}}, ${{matrix.build_type}})" >> $GITHUB_ENV
echo "APP_TARGET_NAME=$APP_NAME" >> $GITHUB_ENV echo "APP_TARGET_NAME=$APP_NAME" >> $GITHUB_ENV
# Linux build variables # Linux build variables
@ -82,7 +82,7 @@ jobs:
echo "ZIP_ARGS=-r" >> $GITHUB_ENV echo "ZIP_ARGS=-r" >> $GITHUB_ENV
echo "INSTALLER_EXT=dmg" >> $GITHUB_ENV echo "INSTALLER_EXT=dmg" >> $GITHUB_ENV
echo "CMAKE_EXTRA=-DOVERTE_CPU_ARCHITECTURE= -DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOPENSSL_LIBRARIES=/usr/local/opt/openssl/lib -G Xcode" >> $GITHUB_ENV echo "CMAKE_EXTRA=-DOVERTE_CPU_ARCHITECTURE= -DCMAKE_OSX_SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED=OFF -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOPENSSL_LIBRARIES=/usr/local/opt/openssl/lib -G Xcode" >> $GITHUB_ENV
echo "::set-output name=symbols_archive::${BUILD_NUMBER}-${{ matrix.build_type }}-mac-symbols.zip" echo "symbols_archive=${BUILD_NUMBER}-${{ matrix.build_type }}-mac-symbols.zip" >> $GITHUB_ENV
echo "APP_TARGET_NAME=Overte" >> $GITHUB_ENV echo "APP_TARGET_NAME=Overte" >> $GITHUB_ENV
fi fi
# Windows build variables # Windows build variables
@ -91,7 +91,7 @@ jobs:
echo "ZIP_COMMAND=7z" >> $GITHUB_ENV echo "ZIP_COMMAND=7z" >> $GITHUB_ENV
echo "ZIP_ARGS=a" >> $GITHUB_ENV echo "ZIP_ARGS=a" >> $GITHUB_ENV
echo "INSTALLER_EXT=exe" >> $GITHUB_ENV echo "INSTALLER_EXT=exe" >> $GITHUB_ENV
echo "CMAKE_EXTRA=-A x64" >> $GITHUB_ENV echo "CMAKE_EXTRA=-A x64 -DJSDOC_ENABLED:BOOL=TRUE" >> $GITHUB_ENV
echo "SYMBOL_REGEX=\(exe\|dll\|pdb\)" >> $GITHUB_ENV echo "SYMBOL_REGEX=\(exe\|dll\|pdb\)" >> $GITHUB_ENV
echo "symbols_archive=${BUILD_NUMBER}-${{ matrix.build_type }}-win-symbols.zip" >> $GITHUB_ENV echo "symbols_archive=${BUILD_NUMBER}-${{ matrix.build_type }}-win-symbols.zip" >> $GITHUB_ENV
# echo "HF_PFX_PASSPHRASE=${{secrets.pfx_key}}" >> $GITHUB_ENV # echo "HF_PFX_PASSPHRASE=${{secrets.pfx_key}}" >> $GITHUB_ENV

View file

@ -31,7 +31,7 @@ jobs:
jsdoc root.js -r api-mainpage.md -c config.json -d output jsdoc root.js -r api-mainpage.md -c config.json -d output
- name: Deploy API-docs - name: Deploy API-docs
uses: SamKirkland/FTP-Deploy-Action@v4.3.4 uses: SamKirkland/FTP-Deploy-Action@v4.3.5
with: with:
server: www531.your-server.de server: www531.your-server.de
protocol: ftps protocol: ftps

View file

@ -29,7 +29,7 @@ jobs:
doxygen Doxyfile doxygen Doxyfile
- name: Deploy Doxygen - name: Deploy Doxygen
uses: SamKirkland/FTP-Deploy-Action@v4.3.4 uses: SamKirkland/FTP-Deploy-Action@v4.3.5
with: with:
server: www531.your-server.de server: www531.your-server.de
protocol: ftps protocol: ftps

View file

@ -64,8 +64,8 @@ jobs:
runner: [self_hosted, type-cx52, image-x86-app-docker-ce] runner: [self_hosted, type-cx52, image-x86-app-docker-ce]
arch: amd64 arch: amd64
build_type: full build_type: full
apt-dependencies: pkg-config libxext-dev libdouble-conversion-dev libpcre2-16-0 libpulse0 libharfbuzz-dev libnss3 libnspr4 libxdamage1 libasound2 # add missing dependencies to docker image when convenient # apt-dependencies: # add missing dependencies to docker image when convenient
image: docker.io/overte/overte-full-build:0.1.1-ubuntu-20.04-amd64 image: docker.io/overte/overte-full-build:0.1.2-ubuntu-20.04-amd64
# Android builds are currently failing # Android builds are currently failing
#- os: ubuntu-18.04 #- os: ubuntu-18.04
# build_type: android # build_type: android
@ -75,7 +75,7 @@ jobs:
runner: [self_hosted, type-cax41, image-arm-app-docker-ce] runner: [self_hosted, type-cax41, image-arm-app-docker-ce]
arch: aarch64 arch: aarch64
build_type: full build_type: full
image: docker.io/overte/overte-full-build:0.1.1-ubuntu-22.04-aarch64 image: docker.io/overte/overte-full-build:0.1.2-ubuntu-22.04-aarch64
fail-fast: false fail-fast: false
runs-on: ${{matrix.runner}} runs-on: ${{matrix.runner}}
container: ${{matrix.image}} container: ${{matrix.image}}
@ -137,9 +137,9 @@ jobs:
echo "PYTHON_EXEC=python" >> $GITHUB_ENV echo "PYTHON_EXEC=python" >> $GITHUB_ENV
echo "INSTALLER_EXT=exe" >> $GITHUB_ENV echo "INSTALLER_EXT=exe" >> $GITHUB_ENV
if [ "${{ matrix.build_type }}" = "full" ]; then if [ "${{ matrix.build_type }}" = "full" ]; then
echo "CMAKE_EXTRA=-A x64" >> $GITHUB_ENV echo "CMAKE_EXTRA=-A x64 -DJSDOC_ENABLED:BOOL=TRUE" >> $GITHUB_ENV
else else
echo "CMAKE_EXTRA=-A x64 -DCLIENT_ONLY=1" >> $GITHUB_ENV echo "CMAKE_EXTRA=-A x64 -DJSDOC_ENABLED:BOOL=TRUE -DCLIENT_ONLY=1" >> $GITHUB_ENV
fi fi
fi fi
# Android + Quest build variables # Android + Quest build variables
@ -244,7 +244,7 @@ jobs:
- name: Archive cmake logs - name: Archive cmake logs
if: always() if: always()
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: cmake-logs-${{ matrix.os }}-${{ github.event.number }}.tar.xz name: cmake-logs-${{ matrix.os }}-${{ github.event.number }}.tar.xz
path: ./cmake-logs-${{ matrix.os }}-${{ github.event.number }}.tar.xz path: ./cmake-logs-${{ matrix.os }}-${{ github.event.number }}.tar.xz

View file

@ -56,7 +56,6 @@ jobs:
echo "UPLOAD_PREFIX=build/overte/release/" >> $GITHUB_ENV echo "UPLOAD_PREFIX=build/overte/release/" >> $GITHUB_ENV
fi fi
echo ::set-output name=github_sha_short::`echo $GIT_COMMIT | cut -c1-7`
echo "JOB_NAME=${{matrix.os}}, ${{matrix.build_type}}" >> $GITHUB_ENV echo "JOB_NAME=${{matrix.os}}, ${{matrix.build_type}}" >> $GITHUB_ENV
echo "APP_TARGET_NAME=$APP_NAME" >> $GITHUB_ENV echo "APP_TARGET_NAME=$APP_NAME" >> $GITHUB_ENV
@ -101,7 +100,7 @@ jobs:
- name: Configure CMake - name: Configure CMake
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
shell: bash shell: bash
run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DVCPKG_BUILD_TYPE=release -DCLIENT_ONLY:BOOLEAN=$CLIENT_ONLY -DBYPASS_SIGNING:BOOLEAN=TRUE $CMAKE_EXTRA run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DVCPKG_BUILD_TYPE=release -DJSDOC_ENABLED:BOOL=TRUE -DCLIENT_ONLY:BOOLEAN=$CLIENT_ONLY -DBYPASS_SIGNING:BOOLEAN=TRUE $CMAKE_EXTRA
- name: Build application - name: Build application
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build

1
.gitignore vendored
View file

@ -51,6 +51,7 @@ local.properties
!.vscode/tasks.json !.vscode/tasks.json
!.vscode/launch.json !.vscode/launch.json
!.vscode/extensions.json !.vscode/extensions.json
.cache
# Workspace # Workspace
*.code-workspace *.code-workspace

View file

@ -12,9 +12,84 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
This project does **not** adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). This project does **not** adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
<!-- ## [2024.09.1] Unreleased --> ## [2024.11.1] 2024.11.23
## [2024.07.1] 2023.07.12 ### Fixes
- Hard code link colors in Armored Chat (PR1083)
- ArmoredChat: Alleviate scrolling issue (PR1106)
- Armored Chat: Change the 'open in new window' character (PR1084)
- Fix mouselook ignoring setting. (PR1081)
- Fix controllerScripts uncaught exception. (PR1086)
- Fix wireshark dissector (PR1088)
- Create App: Material Assistant: Add Mtoon, Shader_simple, missing PBR properties and bug fixes. (PR1091)
- Fix login failure handling and improve logging. (PR1093)
- add a setting to workaround the GLES colorspace conversion issue (PR1105)
- Improve model load priority (PR1085)
- fix accidentally clearing url fields when you don't have view permission (PR1138)
- Avatar App: Fixed lingering references to now deleted QML element (PR1155)
- Fix selfie mode movement (PR1127)
- Fix Create App not honoring menu bar actions (PR1123)
- Fix Uuid.NULL behavior (PR1168)
- Rebuild fonts with full charset (NOT -allglyphs) (PR1172)
- fix web entities not accepting keyboard focus (PR1187)
- Fix stutter when an object is fading (PR1185)
- fix density max typo (PR1195)
- Fix ArmoredChat quick_message qml dialog colors on light theme systems (PR1196)
- Fix missing properties in Script API (PR1215)
- Fix ArmoredChat scrolling (PR1210)
- Force enable JSDoc to get scripting console autocomplete working on Windows (PR1219)
- Fix lack of entityHostType property (PR1224)
- Fix access-after-delete on leaving domain with entity scripts (PR1230)
- fix fade out not working in forward rendering (PR1234)
- Fix access-after-delete during entity script engine cleanup (PR1236)
- Fix script-related crashes on exiting a domain (PR1251)
- Update privacy policy link (PR1237)
### Changes
- Replace Floofchat with ArmoredChat (PR961)
- MouseLook.js refactor (PR1004)
- Custom shader fallbacks (PR1058)
- Update outdated language (PR1102)
- Automated entity property serialization (PR1098)
- Create app: highlight avatar entities (PR1152)
- Update Avatar App icons (PR1141)
- Place App: Weekly promoted place (PR1153)
- Change minimum angular velocity to a lower one (PR1171)
- Places App: Persisted Maturity Filter and Default value for Newbies. (PR1164)
### Additions
- Mirrors + Portals (PR721)
- Entity tags (PR748)
- Web Entity wantsKeyboardFocus (PR814)
- Audio Zone Properties (PR847)
- Ability to smooth model animations (PR889)
- GPU Particles (PR884)
- Unlit Shapes (PR1041)
- Ambient Light Color (PR1043)
- Dump protocol data (PR1087)
- Sound Entities (PR894)
- Zone properties for tonemapping and ambient occlusion (PR1050)
- Add bloom, haze, AO, and procedural shaders to Graphics settings (PR1053)
- Create App: Revolutionary "Paste" Url buttons for the "Create Model", "Create Material" and "Create Voxels" UI (PR1094)
- Text verticalAlignment, send entity property enums as uint8_t, fix text recalculating too often, fix textSize (PR1111)
- Create app: Grab and Equip (PR1160)
- Create App: Add "Paste" button for NewSoundDialog QML (PR1202)
- Added sounds to all incoming chat messages (PR1250)
### Removals
- Remove (deprecated) attachments (PR1069)
- Remove unused onFirstRun.js (PR1089)
- Remove Google Poly (PR1137)
- Remove hifi screenshare (PR1165)
### Build System
- Add CLion-style build directories to .gitignore (PR1135)
### Security
- Sanitize notificationCore text to prevent XSS (PR1078)
## [2024.07.1] 2024.07.12
### Fixes ### Fixes
- Fix more warnings (PR1007) - Fix more warnings (PR1007)
@ -57,7 +132,7 @@ This project does **not** adhere to [Semantic Versioning](https://semver.org/spe
- Remove remnants of RELEASE_NAME. (PR1077) - Remove remnants of RELEASE_NAME. (PR1077)
## [2024.06.1] 2023.06.24 ## [2024.06.1] 2024.06.24
### Fixes ### Fixes
- Fix QNetworkRequest::FollowRedirectsAttribute deprecated warning (PR711) - Fix QNetworkRequest::FollowRedirectsAttribute deprecated warning (PR711)
@ -306,9 +381,6 @@ This project does **not** adhere to [Semantic Versioning](https://semver.org/spe
- Added a setting to disable snapshot notifications (PR189) - Added a setting to disable snapshot notifications (PR189)
- Added a setting to switch between screenshot formats (PR134) - Added a setting to switch between screenshot formats (PR134)
### Removals
-
### Build system ### Build system
- Fixed "may be used uninitialized" warning for blendtime (PR269) - Fixed "may be used uninitialized" warning for blendtime (PR269)
- Updated SPIRV-Cross to sdk-1.3.231.1 (PR271) - Updated SPIRV-Cross to sdk-1.3.231.1 (PR271)

View file

@ -567,6 +567,7 @@ void EntityScriptServer::addingEntity(const EntityItemID& entityID) {
void EntityScriptServer::deletingEntity(const EntityItemID& entityID) { void EntityScriptServer::deletingEntity(const EntityItemID& entityID) {
if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptManager) { if (_entityViewer.getTree() && !_shuttingDown && _entitiesScriptManager) {
// TODO: Check if this is running on script engine thread, otherwise lambda capturing script engine pointer is needed
_entitiesScriptManager->unloadEntityScript(entityID, true); _entitiesScriptManager->unloadEntityScript(entityID, true);
} }
} }
@ -584,6 +585,7 @@ void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, bool
EntityScriptDetails details; EntityScriptDetails details;
bool isRunning = _entitiesScriptManager->getEntityScriptDetails(entityID, details); bool isRunning = _entitiesScriptManager->getEntityScriptDetails(entityID, details);
if (entity && (forceRedownload || !isRunning || details.scriptText != entity->getServerScripts())) { if (entity && (forceRedownload || !isRunning || details.scriptText != entity->getServerScripts())) {
// TODO: Check if this is running on script engine thread, otherwise lambda capturing script engine pointer is needed
if (isRunning) { if (isRunning) {
_entitiesScriptManager->unloadEntityScript(entityID, true); _entitiesScriptManager->unloadEntityScript(entityID, true);
} }

View file

@ -157,7 +157,7 @@ endif()
u_major = int( distro.major_version() or '0' ) u_major = int( distro.major_version() or '0' )
if distro.id() == 'ubuntu' or distro.id() == 'linuxmint': if distro.id() == 'ubuntu' or distro.id() == 'linuxmint':
if (distro.id() == 'ubuntu' and u_major == 20) or distro.id() == 'linuxmint' and u_major == 20: if (distro.id() == 'ubuntu' and u_major == 20) or distro.id() == 'linuxmint' and u_major == 20:
self.qtUrl = self.assets_url + '/dependencies/qt5/qt5-install-5.15.14-2024.06.17-kde_570f5b2105df1ea052bec0d6dbf8a00137274371-ubuntu-20.04-amd64.tar.xz' self.qtUrl = self.assets_url + '/dependencies/qt5/qt5-install-5.15.16-2024.12.14-kde_32be154325bfba3ad2ba8bf75dad702f3588e8d3-ubuntu-20.04-amd64.tar.xz'
elif (distro.id() == 'ubuntu' and u_major > 20) or (distro.id() == 'linuxmint' and u_major > 20): elif (distro.id() == 'ubuntu' and u_major > 20) or (distro.id() == 'linuxmint' and u_major > 20):
self.__no_qt_package_error() self.__no_qt_package_error()
else: else:

View file

@ -3,6 +3,7 @@
// //
// Created by Stephen Birarda on 7 Dec 2016 // Created by Stephen Birarda on 7 Dec 2016
// Copyright 2016 High Fidelity, Inc. // Copyright 2016 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -395,7 +396,7 @@ Item {
text: signUpBody.termsContainerText text: signUpBody.termsContainerText
Component.onCompleted: { Component.onCompleted: {
// with the link. // with the link.
termsText.text = qsTr("By signing up, you agree to <a href='https://overte.org/legal/privacy/'>Overte Terms of Service</a>") termsText.text = qsTr("By signing up, you agree to <a href='https://overte.org/privacy_policy.html'>Overte Terms of Service</a>")
} }
} }

View file

@ -1039,6 +1039,14 @@ Rectangle {
anchors.top: pal.top; anchors.top: pal.top;
anchors.topMargin: 10; anchors.topMargin: 10;
anchors.left: pal.left; anchors.left: pal.left;
RalewayRegular {
text: " Display Name";
size: hifi.fontSizes.tabularData;
anchors.left: parent.left;
color: hifi.colors.baseGrayHighlight;
verticalAlignment: Text.AlignTop;
}
// This NameCard refers to the current user's NameCard (the one above the nearbyTable) // This NameCard refers to the current user's NameCard (the one above the nearbyTable)
NameCard { NameCard {
id: myCard; id: myCard;
@ -1054,7 +1062,7 @@ Rectangle {
width: myCardWidth; width: myCardWidth;
height: parent.height; height: parent.height;
// Anchors // Anchors
anchors.top: parent.top anchors.top: parent.children[0].bottom;
anchors.left: parent.left; anchors.left: parent.left;
} }
Item { Item {
@ -1066,7 +1074,7 @@ Rectangle {
RalewayRegular { RalewayRegular {
id: availabilityText; id: availabilityText;
text: "set availability"; text: "Availability";
// Text size // Text size
size: hifi.fontSizes.tabularData; size: hifi.fontSizes.tabularData;
// Anchors // Anchors

View file

@ -2,6 +2,8 @@
// Created by Dante Ruiz on 6/5/17. // Created by Dante Ruiz on 6/5/17.
// Copyright 2017 High Fidelity, Inc. // Copyright 2017 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors. // Copyright 2020 Vircadia contributors.
// Copyright 2024 Overte e.V.
//
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -928,7 +930,7 @@ Flickable {
hoverEnabled: true hoverEnabled: true
onEntered: privacyPolicyUnderline.visible = true; onEntered: privacyPolicyUnderline.visible = true;
onExited: privacyPolicyUnderline.visible = false; onExited: privacyPolicyUnderline.visible = false;
onClicked: About.openUrl("https://overte.org/privacy-policy"); onClicked: About.openUrl("https://overte.org/privacy_policy.html");
} }
} }

View file

@ -159,6 +159,7 @@ const bool DEFAULT_DESKTOP_TABLET_BECOMES_TOOLBAR = true;
const bool DEFAULT_HMD_TABLET_BECOMES_TOOLBAR = false; const bool DEFAULT_HMD_TABLET_BECOMES_TOOLBAR = false;
const bool DEFAULT_PREFER_STYLUS_OVER_LASER = false; const bool DEFAULT_PREFER_STYLUS_OVER_LASER = false;
const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = false; const bool DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS = false;
const bool DEFAULT_SHOW_GRAPHICS_ICON = true;
const bool DEFAULT_MINI_TABLET_ENABLED = false; const bool DEFAULT_MINI_TABLET_ENABLED = false;
const bool DEFAULT_AWAY_STATE_WHEN_FOCUS_LOST_IN_VR_ENABLED = true; const bool DEFAULT_AWAY_STATE_WHEN_FOCUS_LOST_IN_VR_ENABLED = true;
@ -224,6 +225,7 @@ Application::Application(
_hmdTabletBecomesToolbarSetting("hmdTabletBecomesToolbar", DEFAULT_HMD_TABLET_BECOMES_TOOLBAR), _hmdTabletBecomesToolbarSetting("hmdTabletBecomesToolbar", DEFAULT_HMD_TABLET_BECOMES_TOOLBAR),
_preferStylusOverLaserSetting("preferStylusOverLaser", DEFAULT_PREFER_STYLUS_OVER_LASER), _preferStylusOverLaserSetting("preferStylusOverLaser", DEFAULT_PREFER_STYLUS_OVER_LASER),
_preferAvatarFingerOverStylusSetting("preferAvatarFingerOverStylus", DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS), _preferAvatarFingerOverStylusSetting("preferAvatarFingerOverStylus", DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS),
_showGraphicsIconSetting("showGraphicsIcon", DEFAULT_SHOW_GRAPHICS_ICON),
_constrainToolbarPosition("toolbar/constrainToolbarToCenterX", true), _constrainToolbarPosition("toolbar/constrainToolbarToCenterX", true),
_awayStateWhenFocusLostInVREnabled("awayStateWhenFocusLostInVREnabled", DEFAULT_AWAY_STATE_WHEN_FOCUS_LOST_IN_VR_ENABLED), _awayStateWhenFocusLostInVREnabled("awayStateWhenFocusLostInVREnabled", DEFAULT_AWAY_STATE_WHEN_FOCUS_LOST_IN_VR_ENABLED),
_preferredCursor("preferredCursor", DEFAULT_CURSOR_NAME), _preferredCursor("preferredCursor", DEFAULT_CURSOR_NAME),

View file

@ -189,6 +189,9 @@ public:
bool getPreferAvatarFingerOverStylus() { return _preferAvatarFingerOverStylusSetting.get(); } bool getPreferAvatarFingerOverStylus() { return _preferAvatarFingerOverStylusSetting.get(); }
void setPreferAvatarFingerOverStylus(bool value) { _preferAvatarFingerOverStylusSetting.set(value); } void setPreferAvatarFingerOverStylus(bool value) { _preferAvatarFingerOverStylusSetting.set(value); }
bool getShowGraphicsIcon() { return _showGraphicsIconSetting.get(); }
void setShowGraphicsIcon(bool value);
bool getMiniTabletEnabled() { return _miniTabletEnabledSetting.get(); } bool getMiniTabletEnabled() { return _miniTabletEnabledSetting.get(); }
void setMiniTabletEnabled(bool enabled); void setMiniTabletEnabled(bool enabled);
@ -785,6 +788,7 @@ private:
Setting::Handle<bool> _hmdTabletBecomesToolbarSetting; Setting::Handle<bool> _hmdTabletBecomesToolbarSetting;
Setting::Handle<bool> _preferStylusOverLaserSetting; Setting::Handle<bool> _preferStylusOverLaserSetting;
Setting::Handle<bool> _preferAvatarFingerOverStylusSetting; Setting::Handle<bool> _preferAvatarFingerOverStylusSetting;
Setting::Handle<bool> _showGraphicsIconSetting;
Setting::Handle<bool> _constrainToolbarPosition; Setting::Handle<bool> _constrainToolbarPosition;
Setting::Handle<bool> _awayStateWhenFocusLostInVREnabled; Setting::Handle<bool> _awayStateWhenFocusLostInVREnabled;
Setting::Handle<QString> _preferredCursor; Setting::Handle<QString> _preferredCursor;

View file

@ -269,6 +269,11 @@ void Application::setHmdTabletBecomesToolbarSetting(bool value) {
updateSystemTabletMode(); updateSystemTabletMode();
} }
void Application::setShowGraphicsIcon(bool value) {
_showGraphicsIconSetting.set(value);
DependencyManager::get<MessagesClient>()->sendLocalMessage("Overte-ShowGraphicsIconChanged", "");
}
void Application::setMiniTabletEnabled(bool enabled) { void Application::setMiniTabletEnabled(bool enabled) {
_miniTabletEnabledSetting.set(enabled); _miniTabletEnabledSetting.set(enabled);
emit miniTabletEnabledChanged(enabled); emit miniTabletEnabledChanged(enabled);

View file

@ -76,6 +76,8 @@
#include "WarningsSuppression.h" #include "WarningsSuppression.h"
#include "ScriptPermissions.h" #include "ScriptPermissions.h"
#include "Application.h"
using namespace std; using namespace std;
const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.0f; const float DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES = 30.0f;
@ -227,6 +229,7 @@ MyAvatar::MyAvatar(QThread* thread) :
_scaleSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "scale", _targetScale), _scaleSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "scale", _targetScale),
_yawSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "yawSpeed", _yawSpeed), _yawSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "yawSpeed", _yawSpeed),
_hmdYawSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "hmdYawSpeed", _hmdYawSpeed), _hmdYawSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "hmdYawSpeed", _hmdYawSpeed),
_cameraSensitivitySetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "cameraSensitivity", qApp->getCamera().getSensitivity()),
_pitchSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "pitchSpeed", _pitchSpeed), _pitchSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "pitchSpeed", _pitchSpeed),
_fullAvatarURLSetting(QStringList() << SETTINGS_FULL_PRIVATE_GROUP_NAME << AVATAR_SETTINGS_GROUP_NAME << "fullAvatarURL", _fullAvatarURLSetting(QStringList() << SETTINGS_FULL_PRIVATE_GROUP_NAME << AVATAR_SETTINGS_GROUP_NAME << "fullAvatarURL",
AvatarData::defaultFullAvatarModelUrl()), AvatarData::defaultFullAvatarModelUrl()),
@ -1325,6 +1328,7 @@ void MyAvatar::saveData() {
_scaleSetting.set(_targetScale); _scaleSetting.set(_targetScale);
_yawSpeedSetting.set(_yawSpeed); _yawSpeedSetting.set(_yawSpeed);
_hmdYawSpeedSetting.set(_hmdYawSpeed); _hmdYawSpeedSetting.set(_hmdYawSpeed);
_cameraSensitivitySetting.set(getCameraSensitivity());
_pitchSpeedSetting.set(_pitchSpeed); _pitchSpeedSetting.set(_pitchSpeed);
// only save the fullAvatarURL if it has not been overwritten on command line // only save the fullAvatarURL if it has not been overwritten on command line
@ -2085,6 +2089,7 @@ void MyAvatar::loadData() {
_yawSpeed = _yawSpeedSetting.get(_yawSpeed); _yawSpeed = _yawSpeedSetting.get(_yawSpeed);
_hmdYawSpeed = _hmdYawSpeedSetting.get(_hmdYawSpeed); _hmdYawSpeed = _hmdYawSpeedSetting.get(_hmdYawSpeed);
setCameraSensitivity(_cameraSensitivitySetting.get(getCameraSensitivity()));
_pitchSpeed = _pitchSpeedSetting.get(_pitchSpeed); _pitchSpeed = _pitchSpeedSetting.get(_pitchSpeed);
_prefOverrideAnimGraphUrl.set(_animGraphURLSetting.get().toString()); _prefOverrideAnimGraphUrl.set(_animGraphURLSetting.get().toString());
@ -7003,3 +7008,11 @@ void MyAvatar::resetPointAt() {
POINT_BLEND_LINEAR_ALPHA_NAME, POINT_ALPHA_BLENDING); POINT_BLEND_LINEAR_ALPHA_NAME, POINT_ALPHA_BLENDING);
} }
} }
float MyAvatar::getCameraSensitivity() const {
return qApp->getCamera().getSensitivity();
}
void MyAvatar::setCameraSensitivity(float cameraSensitivity) {
qApp->getCamera().setSensitivity(cameraSensitivity);
}

View file

@ -1407,6 +1407,9 @@ public:
float getHMDYawSpeed() const { return _hmdYawSpeed; } float getHMDYawSpeed() const { return _hmdYawSpeed; }
void setHMDYawSpeed(float speed) { _hmdYawSpeed = speed; } void setHMDYawSpeed(float speed) { _hmdYawSpeed = speed; }
float getCameraSensitivity() const;
void setCameraSensitivity(float cameraSensitivity);
static const float ZOOM_MIN; static const float ZOOM_MIN;
static const float ZOOM_MAX; static const float ZOOM_MAX;
static const float ZOOM_DEFAULT; static const float ZOOM_DEFAULT;
@ -3007,6 +3010,7 @@ private:
Setting::Handle<float> _scaleSetting; Setting::Handle<float> _scaleSetting;
Setting::Handle<float> _yawSpeedSetting; Setting::Handle<float> _yawSpeedSetting;
Setting::Handle<float> _hmdYawSpeedSetting; Setting::Handle<float> _hmdYawSpeedSetting;
Setting::Handle<float> _cameraSensitivitySetting;
Setting::Handle<float> _pitchSpeedSetting; Setting::Handle<float> _pitchSpeedSetting;
Setting::Handle<QUrl> _fullAvatarURLSetting; Setting::Handle<QUrl> _fullAvatarURLSetting;
Setting::Handle<QUrl> _fullAvatarModelNameSetting; Setting::Handle<QUrl> _fullAvatarModelNameSetting;

View file

@ -225,6 +225,12 @@ void setupPreferences() {
preferences->addPreference(delaySlider); preferences->addPreference(delaySlider);
} }
{
auto getter = []() -> bool { return qApp->getShowGraphicsIcon(); };
auto setter = [](bool value) { qApp->setShowGraphicsIcon(value); };
preferences->addPreference(new CheckPreference(UI_CATEGORY, "Show Graphics icon on tablet and toolbar", getter, setter));
}
static const QString VIEW_CATEGORY{ "View" }; static const QString VIEW_CATEGORY{ "View" };
{ {
auto getter = [myAvatar]()->float { return myAvatar->getRealWorldFieldOfView(); }; auto getter = [myAvatar]()->float { return myAvatar->getRealWorldFieldOfView(); };

View file

@ -994,9 +994,9 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const
QString statsFormat = QString("(%1 Kbps, %2 Hz)"); QString statsFormat = QString("(%1 Kbps, %2 Hz)");
if (!renderedDisplayName.isEmpty()) { if (!renderedDisplayName.isEmpty()) {
statsFormat.prepend(" - "); statsFormat.append("\n");
} }
renderedDisplayName += statsFormat.arg(QString::number(kilobitsPerSecond, 'f', 2)).arg(getReceiveRate()); renderedDisplayName = statsFormat.arg(QString::number(kilobitsPerSecond, 'f', 2)).arg(getReceiveRate()) + renderedDisplayName;
} }
// Compute display name extent/position offset // Compute display name extent/position offset
@ -1010,18 +1010,18 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const
// Compute background position/size // Compute background position/size
static const float SLIGHTLY_IN_FRONT = 0.1f; static const float SLIGHTLY_IN_FRONT = 0.1f;
static const float BORDER_RELATIVE_SIZE = 0.1f; static const float BORDER_RELATIVE_SIZE = 0.1f;
static const float BEVEL_FACTOR = 0.1f; // static const float BEVEL_FACTOR = 0.1f;
const int border = BORDER_RELATIVE_SIZE * nameDynamicRect.height(); const int border = BORDER_RELATIVE_SIZE * nameDynamicRect.height();
const int left = text_x - border; // FIXME: Beveled box is broken
const int bottom = text_y - border; // const int left = text_x - border;
const int width = nameDynamicRect.width() + 2.0f * border; // const int bottom = text_y - border;
// const int width = nameDynamicRect.width() + 2.0f * border;
const int height = nameDynamicRect.height() + 2.0f * border; const int height = nameDynamicRect.height() + 2.0f * border;
const int bevelDistance = BEVEL_FACTOR * height; // const int bevelDistance = BEVEL_FACTOR * height;
// Display name and background colors // Display name and background colors
glm::vec4 textColor(0.93f, 0.93f, 0.93f, _displayNameAlpha); glm::vec4 textColor(0.93f, 0.93f, 0.93f, _displayNameAlpha);
glm::vec4 backgroundColor(0.2f, 0.2f, 0.2f, glm::vec4 backgroundColor(0.2f, 0.2f, 0.2f,(_displayNameAlpha / DISPLAYNAME_ALPHA) * DISPLAYNAME_BACKGROUND_ALPHA);
(_displayNameAlpha / DISPLAYNAME_ALPHA) * DISPLAYNAME_BACKGROUND_ALPHA);
// Compute display name transform // Compute display name transform
auto textTransform = calculateDisplayNameTransform(view, textPosition); auto textTransform = calculateDisplayNameTransform(view, textPosition);
@ -1029,12 +1029,11 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const
textTransform.postScale(1.0f / height); textTransform.postScale(1.0f / height);
batch.setModelTransform(textTransform); batch.setModelTransform(textTransform);
{ // {
PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderBevelCornersRect"); // PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderBevelCornersRect");
DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, false, true, true, true, forward); // DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, false, true, true, true, forward);
DependencyManager::get<GeometryCache>()->renderBevelCornersRect(batch, left, bottom, width, height, // DependencyManager::get<GeometryCache>()->renderBevelCornersRect(batch, left, bottom, width, height, bevelDistance, backgroundColor, _nameRectGeometryID);
bevelDistance, backgroundColor, _nameRectGeometryID); // }
}
// Render actual name // Render actual name
QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit(); QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit();
@ -1044,7 +1043,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const
batch.setModelTransform(textTransform); batch.setModelTransform(textTransform);
{ {
PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderText"); PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderText");
displayNameRenderer->draw(batch, { nameUTF8.data(), textColor, { text_x, -text_y }, glm::vec2(-1.0f), forward }); displayNameRenderer->draw(batch, { nameUTF8.data(), textColor, { text_x, -text_y }, glm::vec2(-1.0f), TextAlignment::LEFT, forward });
} }
} }
} }

View file

@ -220,8 +220,11 @@ void EntityTreeRenderer::setupEntityScriptEngineSignals(const ScriptManagerPoint
void EntityTreeRenderer::resetPersistentEntitiesScriptEngine() { void EntityTreeRenderer::resetPersistentEntitiesScriptEngine() {
// This runs script engine shutdown procedure in a separate thread, avoiding a deadlock when script engine is doing // This runs script engine shutdown procedure in a separate thread, avoiding a deadlock when script engine is doing
// a blocking call to main thread // a blocking call to main thread
if (_persistentEntitiesScriptManager) { ScriptManagerPointer scriptManager = _persistentEntitiesScriptManager;
QtConcurrent::run([manager = _persistentEntitiesScriptManager] { // Clear the pointer before lambda is run on another thread.
_persistentEntitiesScriptManager.reset();
if (scriptManager) {
QtConcurrent::run([manager = scriptManager] {
manager->unloadAllEntityScripts(true); manager->unloadAllEntityScripts(true);
manager->stop(); manager->stop();
manager->waitTillDoneRunning(); manager->waitTillDoneRunning();
@ -251,8 +254,11 @@ void EntityTreeRenderer::resetPersistentEntitiesScriptEngine() {
void EntityTreeRenderer::resetNonPersistentEntitiesScriptEngine() { void EntityTreeRenderer::resetNonPersistentEntitiesScriptEngine() {
// This runs script engine shutdown procedure in a separate thread, avoiding a deadlock when script engine is doing // This runs script engine shutdown procedure in a separate thread, avoiding a deadlock when script engine is doing
// a blocking call to main thread // a blocking call to main thread
if (_nonPersistentEntitiesScriptManager) { ScriptManagerPointer scriptManager = _nonPersistentEntitiesScriptManager;
QtConcurrent::run([manager = _nonPersistentEntitiesScriptManager] { // Release the pointer as soon as possible.
_nonPersistentEntitiesScriptManager.reset();
if (scriptManager) {
QtConcurrent::run([manager = scriptManager] {
manager->unloadAllEntityScripts(true); manager->unloadAllEntityScripts(true);
manager->stop(); manager->stop();
manager->waitTillDoneRunning(); manager->waitTillDoneRunning();
@ -288,7 +294,10 @@ void EntityTreeRenderer::stopDomainAndNonOwnedEntities() {
EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID); EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID);
if (entityItem && !entityItem->getScript().isEmpty()) { if (entityItem && !entityItem->getScript().isEmpty()) {
if (!(entityItem->isLocalEntity() || entityItem->isMyAvatarEntity())) { if (!(entityItem->isLocalEntity() || entityItem->isMyAvatarEntity())) {
_nonPersistentEntitiesScriptManager->unloadEntityScript(entityID, true); auto scriptEnginePtr = _nonPersistentEntitiesScriptManager;
QMetaObject::invokeMethod(scriptEnginePtr.get(), [scriptEnginePtr, entityID]{
scriptEnginePtr->unloadEntityScript(entityID, true);
});
} }
} }
} }
@ -1110,7 +1119,9 @@ void EntityTreeRenderer::deletingEntity(const EntityItemID& entityID) {
if (_currentEntitiesInside.contains(entityID)) { if (_currentEntitiesInside.contains(entityID)) {
scriptEngine->callEntityScriptMethod(entityID, "leaveEntity"); scriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
} }
scriptEngine->unloadEntityScript(entityID, true); QMetaObject::invokeMethod(scriptEngine.get(), [scriptEngine, entityID]{
scriptEngine->unloadEntityScript(entityID, true);
});
} }
auto scene = _viewState->getMain3DScene(); auto scene = _viewState->getMain3DScene();
@ -1163,7 +1174,9 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, bool
if (_currentEntitiesInside.contains(entityID)) { if (_currentEntitiesInside.contains(entityID)) {
scriptEngine->callEntityScriptMethod(entityID, "leaveEntity"); scriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
} }
scriptEngine->unloadEntityScript(entityID); QMetaObject::invokeMethod(scriptEngine.get(), [scriptEngine, entityID]{
scriptEngine->unloadEntityScript(entityID);
});
} }
entity->scriptHasUnloaded(); entity->scriptHasUnloaded();
} }

View file

@ -4,6 +4,7 @@
// //
// Created by Clement on 4/22/15. // Created by Clement on 4/22/15.
// Copyright 2015 High Fidelity, Inc. // Copyright 2015 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -39,46 +40,46 @@ ZoneEntityRenderer::ZoneEntityRenderer(const EntityItemPointer& entity)
void ZoneEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity) { void ZoneEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity) {
if (_stage) { if (_stage) {
if (!LightStage::isIndexInvalid(_sunIndex)) { if (!LightStage::isIndexInvalid(_sunIndex)) {
_stage->removeLight(_sunIndex); _stage->removeElement(_sunIndex);
_sunIndex = INVALID_INDEX; _sunIndex = INVALID_INDEX;
} }
if (!LightStage::isIndexInvalid(_ambientIndex)) { if (!LightStage::isIndexInvalid(_ambientIndex)) {
_stage->removeLight(_ambientIndex); _stage->removeElement(_ambientIndex);
_ambientIndex = INVALID_INDEX; _ambientIndex = INVALID_INDEX;
} }
} }
if (_backgroundStage) { if (_backgroundStage) {
if (!BackgroundStage::isIndexInvalid(_backgroundIndex)) { if (!BackgroundStage::isIndexInvalid(_backgroundIndex)) {
_backgroundStage->removeBackground(_backgroundIndex); _backgroundStage->removeElement(_backgroundIndex);
_backgroundIndex = INVALID_INDEX; _backgroundIndex = INVALID_INDEX;
} }
} }
if (_hazeStage) { if (_hazeStage) {
if (!HazeStage::isIndexInvalid(_hazeIndex)) { if (!HazeStage::isIndexInvalid(_hazeIndex)) {
_hazeStage->removeHaze(_hazeIndex); _hazeStage->removeElement(_hazeIndex);
_hazeIndex = INVALID_INDEX; _hazeIndex = INVALID_INDEX;
} }
} }
if (_bloomStage) { if (_bloomStage) {
if (!BloomStage::isIndexInvalid(_bloomIndex)) { if (!BloomStage::isIndexInvalid(_bloomIndex)) {
_bloomStage->removeBloom(_bloomIndex); _bloomStage->removeElement(_bloomIndex);
_bloomIndex = INVALID_INDEX; _bloomIndex = INVALID_INDEX;
} }
} }
if (_tonemappingStage) { if (_tonemappingStage) {
if (!TonemappingStage::isIndexInvalid(_tonemappingIndex)) { if (!TonemappingStage::isIndexInvalid(_tonemappingIndex)) {
_tonemappingStage->removeTonemapping(_tonemappingIndex); _tonemappingStage->removeElement(_tonemappingIndex);
_tonemappingIndex = INVALID_INDEX; _tonemappingIndex = INVALID_INDEX;
} }
} }
if (_ambientOcclusionStage) { if (_ambientOcclusionStage) {
if (!AmbientOcclusionStage::isIndexInvalid(_ambientOcclusionIndex)) { if (!AmbientOcclusionStage::isIndexInvalid(_ambientOcclusionIndex)) {
_ambientOcclusionStage->removeAmbientOcclusion(_ambientOcclusionIndex); _ambientOcclusionStage->removeElement(_ambientOcclusionIndex);
_ambientOcclusionIndex = INVALID_INDEX; _ambientOcclusionIndex = INVALID_INDEX;
} }
} }
@ -123,7 +124,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
{ // Sun { // Sun
if (_needSunUpdate) { if (_needSunUpdate) {
if (LightStage::isIndexInvalid(_sunIndex)) { if (LightStage::isIndexInvalid(_sunIndex)) {
_sunIndex = _stage->addLight(_sunLight); _sunIndex = _stage->addElement(_sunLight);
} else { } else {
_stage->updateLightArrayBuffer(_sunIndex); _stage->updateLightArrayBuffer(_sunIndex);
} }
@ -136,7 +137,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
if (_needAmbientUpdate) { if (_needAmbientUpdate) {
if (LightStage::isIndexInvalid(_ambientIndex)) { if (LightStage::isIndexInvalid(_ambientIndex)) {
_ambientIndex = _stage->addLight(_ambientLight); _ambientIndex = _stage->addElement(_ambientLight);
} else { } else {
_stage->updateLightArrayBuffer(_ambientIndex); _stage->updateLightArrayBuffer(_ambientIndex);
} }
@ -149,7 +150,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
if (_needBackgroundUpdate) { if (_needBackgroundUpdate) {
if (BackgroundStage::isIndexInvalid(_backgroundIndex)) { if (BackgroundStage::isIndexInvalid(_backgroundIndex)) {
_backgroundIndex = _backgroundStage->addBackground(_background); _backgroundIndex = _backgroundStage->addElement(_background);
} }
_needBackgroundUpdate = false; _needBackgroundUpdate = false;
} }
@ -158,7 +159,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
{ {
if (_needHazeUpdate) { if (_needHazeUpdate) {
if (HazeStage::isIndexInvalid(_hazeIndex)) { if (HazeStage::isIndexInvalid(_hazeIndex)) {
_hazeIndex = _hazeStage->addHaze(_haze); _hazeIndex = _hazeStage->addElement(_haze);
} }
_needHazeUpdate = false; _needHazeUpdate = false;
} }
@ -167,7 +168,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
{ {
if (_needBloomUpdate) { if (_needBloomUpdate) {
if (BloomStage::isIndexInvalid(_bloomIndex)) { if (BloomStage::isIndexInvalid(_bloomIndex)) {
_bloomIndex = _bloomStage->addBloom(_bloom); _bloomIndex = _bloomStage->addElement(_bloom);
} }
_needBloomUpdate = false; _needBloomUpdate = false;
} }
@ -176,7 +177,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
{ {
if (_needTonemappingUpdate) { if (_needTonemappingUpdate) {
if (TonemappingStage::isIndexInvalid(_tonemappingIndex)) { if (TonemappingStage::isIndexInvalid(_tonemappingIndex)) {
_tonemappingIndex = _tonemappingStage->addTonemapping(_tonemapping); _tonemappingIndex = _tonemappingStage->addElement(_tonemapping);
} }
_needTonemappingUpdate = false; _needTonemappingUpdate = false;
} }
@ -185,7 +186,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
{ {
if (_needAmbientOcclusionUpdate) { if (_needAmbientOcclusionUpdate) {
if (AmbientOcclusionStage::isIndexInvalid(_ambientOcclusionIndex)) { if (AmbientOcclusionStage::isIndexInvalid(_ambientOcclusionIndex)) {
_ambientOcclusionIndex = _ambientOcclusionStage->addAmbientOcclusion(_ambientOcclusion); _ambientOcclusionIndex = _ambientOcclusionStage->addElement(_ambientOcclusion);
} }
_needAmbientOcclusionUpdate = false; _needAmbientOcclusionUpdate = false;
} }
@ -205,9 +206,9 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
} }
if (_skyboxMode == COMPONENT_MODE_DISABLED) { if (_skyboxMode == COMPONENT_MODE_DISABLED) {
_backgroundStage->_currentFrame.pushBackground(INVALID_INDEX); _backgroundStage->_currentFrame.pushElement(INVALID_INDEX);
} else if (_skyboxMode == COMPONENT_MODE_ENABLED) { } else if (_skyboxMode == COMPONENT_MODE_ENABLED) {
_backgroundStage->_currentFrame.pushBackground(_backgroundIndex); _backgroundStage->_currentFrame.pushElement(_backgroundIndex);
} }
if (_ambientLightMode == COMPONENT_MODE_DISABLED) { if (_ambientLightMode == COMPONENT_MODE_DISABLED) {
@ -218,25 +219,25 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
// Haze only if the mode is not inherit, as the model deals with on/off // Haze only if the mode is not inherit, as the model deals with on/off
if (_hazeMode != COMPONENT_MODE_INHERIT) { if (_hazeMode != COMPONENT_MODE_INHERIT) {
_hazeStage->_currentFrame.pushHaze(_hazeIndex); _hazeStage->_currentFrame.pushElement(_hazeIndex);
} }
if (_bloomMode == COMPONENT_MODE_DISABLED) { if (_bloomMode == COMPONENT_MODE_DISABLED) {
_bloomStage->_currentFrame.pushBloom(INVALID_INDEX); _bloomStage->_currentFrame.pushElement(INVALID_INDEX);
} else if (_bloomMode == COMPONENT_MODE_ENABLED) { } else if (_bloomMode == COMPONENT_MODE_ENABLED) {
_bloomStage->_currentFrame.pushBloom(_bloomIndex); _bloomStage->_currentFrame.pushElement(_bloomIndex);
} }
if (_tonemappingMode == COMPONENT_MODE_DISABLED) { if (_tonemappingMode == COMPONENT_MODE_DISABLED) {
_tonemappingStage->_currentFrame.pushTonemapping(0); // Use the fallback tonemapping for "off" _tonemappingStage->_currentFrame.pushElement(0); // Use the fallback tonemapping for "off"
} else if (_tonemappingMode == COMPONENT_MODE_ENABLED) { } else if (_tonemappingMode == COMPONENT_MODE_ENABLED) {
_tonemappingStage->_currentFrame.pushTonemapping(_tonemappingIndex); _tonemappingStage->_currentFrame.pushElement(_tonemappingIndex);
} }
if (_ambientOcclusionMode == COMPONENT_MODE_DISABLED) { if (_ambientOcclusionMode == COMPONENT_MODE_DISABLED) {
_ambientOcclusionStage->_currentFrame.pushAmbientOcclusion(INVALID_INDEX); _ambientOcclusionStage->_currentFrame.pushElement(INVALID_INDEX);
} else if (_ambientOcclusionMode == COMPONENT_MODE_ENABLED) { } else if (_ambientOcclusionMode == COMPONENT_MODE_ENABLED) {
_ambientOcclusionStage->_currentFrame.pushAmbientOcclusion(_ambientOcclusionIndex); _ambientOcclusionStage->_currentFrame.pushElement(_ambientOcclusionIndex);
} }
} }

View file

@ -77,6 +77,11 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param
@Base_REQUESTED_PROPS@ @Base_REQUESTED_PROPS@
// Some of the properties not transmitted over network need to be added manually here:
requestedProperties += PROP_ENTITY_HOST_TYPE;
requestedProperties += PROP_OWNING_AVATAR_ID;
requestedProperties += PROP_VISIBLE_IN_SECONDARY_CAMERA;
return requestedProperties; return requestedProperties;
} }

View file

@ -30,7 +30,7 @@ group:grab,
enum:MIRROR_MODE prop:mirrorMode type:MirrorMode default:MirrorMode::NONE enum renderProp, enum:MIRROR_MODE prop:mirrorMode type:MirrorMode default:MirrorMode::NONE enum renderProp,
enum:PORTAL_EXIT_ID prop:portalExitID type:QUuid default:UNKNOWN_ENTITY_ID renderProp, enum:PORTAL_EXIT_ID prop:portalExitID type:QUuid default:UNKNOWN_ENTITY_ID renderProp,
Physics Physics
enum:DENSITY prop:density type:float default:ENTITY_ITEM_DEFAULT_DENSITY min:ENTITY_ITEM_MIN_DENSITY max:ENTITY_ITEM_MIN_DENSITY, enum:DENSITY prop:density type:float default:ENTITY_ITEM_DEFAULT_DENSITY min:ENTITY_ITEM_MIN_DENSITY max:ENTITY_ITEM_MAX_DENSITY,
enum:VELOCITY prop:velocity type:vec3 default:ENTITY_ITEM_DEFAULT_VELOCITY inherited variableCopyGetter:getLocalVelocity variableNetworkGetter:getLocalVelocity() variableNetworkSetter:customUpdateVelocityFromNetwork debugString:"m/s" debugGetter:getWorldVelocity(), enum:VELOCITY prop:velocity type:vec3 default:ENTITY_ITEM_DEFAULT_VELOCITY inherited variableCopyGetter:getLocalVelocity variableNetworkGetter:getLocalVelocity() variableNetworkSetter:customUpdateVelocityFromNetwork debugString:"m/s" debugGetter:getWorldVelocity(),
enum:ANGULAR_VELOCITY prop:angularVelocity type:vec3 default:ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY inherited variableCopyGetter:getLocalAngularVelocity variableNetworkGetter:getLocalAngularVelocity() variableNetworkSetter:customUpdateAngularVelocityFromNetwork debugGetter:getWorldAngularVelocity(), enum:ANGULAR_VELOCITY prop:angularVelocity type:vec3 default:ENTITY_ITEM_DEFAULT_ANGULAR_VELOCITY inherited variableCopyGetter:getLocalAngularVelocity variableNetworkGetter:getLocalAngularVelocity() variableNetworkSetter:customUpdateAngularVelocityFromNetwork debugGetter:getWorldAngularVelocity(),
enum:GRAVITY prop:gravity type:vec3 default:ENTITY_ITEM_DEFAULT_GRAVITY debugString:"m/s^2", enum:GRAVITY prop:gravity type:vec3 default:ENTITY_ITEM_DEFAULT_GRAVITY debugString:"m/s^2",

View file

@ -935,7 +935,9 @@
* *
* @typedef {object} Entities.EntityProperties-Image * @typedef {object} Entities.EntityProperties-Image
* @property {Vec3} dimensions=0.1,0.1,0.01 - The dimensions of the entity. * @property {Vec3} dimensions=0.1,0.1,0.01 - The dimensions of the entity.
* @property {string} imageURL="" - The URL of the image to use. * @property {string} imageURL="" - The URL of the image to use. It can also contain a base64 encoded image, in the same format as glTF.
* For network transmitted entities there's about 1000-character limit for the length of this field. For base64 image
* the property string needs to begin with `data:image/png;base64,`, `data:image/jpeg;base64,` or `data:image/webp;base64,`.
* @property {boolean} emissive=false - <code>true</code> if the image should be emissive (unlit), <code>false</code> if it * @property {boolean} emissive=false - <code>true</code> if the image should be emissive (unlit), <code>false</code> if it
* shouldn't. * shouldn't.
* @property {boolean} keepAspectRatio=true - <code>true</code> if the image should maintain its aspect ratio, * @property {boolean} keepAspectRatio=true - <code>true</code> if the image should maintain its aspect ratio,

View file

@ -288,6 +288,10 @@ public:
_glUniformMatrix3fv(location, 1, false, glm::value_ptr(v)); _glUniformMatrix3fv(location, 1, false, glm::value_ptr(v));
} }
void _glUniform(int location, const glm::mat4& v) {
_glUniformMatrix4fv(location, 1, false, glm::value_ptr(v));
}
// Maybe useful but shoudln't be public. Please convince me otherwise // Maybe useful but shoudln't be public. Please convince me otherwise
// Well porting to gles i need it... // Well porting to gles i need it...
void runLambda(std::function<void()> f); void runLambda(std::function<void()> f);

View file

@ -4,6 +4,7 @@
// //
// Created by Nissim Hadar on 9/13/2017. // Created by Nissim Hadar on 9/13/2017.
// Copyright 2014 High Fidelity, Inc. // Copyright 2014 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -182,3 +183,7 @@ void Haze::setHazeBackgroundBlend(const float hazeBackgroundBlend) {
_hazeParametersBuffer.edit<Parameters>().hazeBackgroundBlend = newBlend; _hazeParametersBuffer.edit<Parameters>().hazeBackgroundBlend = newBlend;
} }
} }
bool Haze::isActive() const {
return (_hazeParametersBuffer.get<Parameters>().hazeMode & HAZE_MODE_IS_ACTIVE) == HAZE_MODE_IS_ACTIVE;
}

View file

@ -4,6 +4,7 @@
// //
// Created by Nissim Hadar on 9/13/2017. // Created by Nissim Hadar on 9/13/2017.
// Copyright 2014 High Fidelity, Inc. // Copyright 2014 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -95,6 +96,8 @@ namespace graphics {
using UniformBufferView = gpu::BufferView; using UniformBufferView = gpu::BufferView;
UniformBufferView getHazeParametersBuffer() const { return _hazeParametersBuffer; } UniformBufferView getHazeParametersBuffer() const { return _hazeParametersBuffer; }
bool isActive() const;
protected: protected:
class Parameters { class Parameters {
public: public:

View file

@ -254,6 +254,18 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUs
if (url.scheme() == RESOURCE_SCHEME) { if (url.scheme() == RESOURCE_SCHEME) {
return getResourceTexture(url); return getResourceTexture(url);
} }
QString urlString = url.toString();
if (content.isEmpty() && (urlString.startsWith("data:image/jpeg;base64,")
|| urlString.startsWith("data:image/png;base64,")
|| urlString.startsWith("data:image/webp;base64,"))) {
QString binaryUrl = urlString.split(",")[1];
auto decodedContent = binaryUrl.isEmpty() ? QByteArray() : QByteArray::fromBase64(binaryUrl.toUtf8());
if (!decodedContent.isEmpty()) {
return getTexture(url, type, decodedContent, maxNumPixels, sourceChannel);
}
}
QString decodedURL = QUrl::fromPercentEncoding(url.toEncoded()); QString decodedURL = QUrl::fromPercentEncoding(url.toEncoded());
if (decodedURL.startsWith("{")) { if (decodedURL.startsWith("{")) {
return getTextureByUUID(decodedURL); return getTextureByUUID(decodedURL);

View file

@ -1335,7 +1335,7 @@ HFMTexture GLTFSerializer::getHFMTexture(const cgltf_texture *texture) {
hfmTex.filename = textureUrl.toEncoded().append(QString::number(imageIndex).toUtf8()); hfmTex.filename = textureUrl.toEncoded().append(QString::number(imageIndex).toUtf8());
} }
if (url.contains("data:image/jpeg;base64,") || url.contains("data:image/png;base64,") || url.contains("data:image/webp;base64,")) { if (url.startsWith("data:image/jpeg;base64,") || url.startsWith("data:image/png;base64,") || url.startsWith("data:image/webp;base64,")) {
hfmTex.content = requestEmbeddedData(url); hfmTex.content = requestEmbeddedData(url);
} }
} }

View file

@ -356,16 +356,53 @@ void Procedural::prepare(gpu::Batch& batch,
} }
} }
// Then fill in every reflections the new custom bindings // Then fill in every reflections the new custom bindings
int customSlot = procedural::slot::uniform::Custom; size_t customSlot = procedural::slot::uniform::Custom;
_slotMap.clear();
for (const auto& key : _data.uniforms.keys()) { for (const auto& key : _data.uniforms.keys()) {
std::string uniformName = key.toLocal8Bit().data(); bool isArrayUniform = false;
for (auto reflection : allFragmentReflections) { size_t numSlots = 0;
reflection->uniforms[uniformName] = customSlot; const QJsonValue& value = _data.uniforms[key];
if (value.isDouble()) {
numSlots = 1;
} else if (value.isArray()) {
const QJsonArray valueArray = value.toArray();
if (valueArray.size() > 0) {
if (valueArray[0].isArray()) {
const size_t valueLength = valueArray[0].toArray().size();
size_t count = 0;
for (const QJsonValue& value : valueArray) {
if (value.isArray()) {
const QJsonArray innerValueArray = value.toArray();
if (innerValueArray.size() == valueLength) {
if (valueLength == 3 || valueLength == 4 || valueLength == 9 || valueLength == 16) {
count++;
isArrayUniform = true;
}
}
}
}
numSlots = count;
} else if (valueArray[0].isDouble()) {
numSlots = 1;
}
}
} }
for (auto reflection : allVertexReflections) {
reflection->uniforms[uniformName] = customSlot; if (numSlots > 0) {
std::string uniformName = key.toLocal8Bit().data();
std::string trueUniformName = uniformName;
if (isArrayUniform) {
trueUniformName += "[0]";
}
for (auto reflection : allFragmentReflections) {
reflection->uniforms[trueUniformName] = customSlot;
}
for (auto reflection : allVertexReflections) {
reflection->uniforms[trueUniformName] = customSlot;
}
_slotMap[uniformName] = customSlot;
customSlot += numSlots;
} }
++customSlot;
} }
} }
@ -448,59 +485,138 @@ void Procedural::prepare(gpu::Batch& batch,
} }
} }
void Procedural::setupUniforms() { void Procedural::setupUniforms() {
_uniforms.clear(); _uniforms.clear();
// Set any userdata specified uniforms // Set any userdata specified uniforms
int slot = procedural::slot::uniform::Custom;
for (const auto& key : _data.uniforms.keys()) { for (const auto& key : _data.uniforms.keys()) {
std::string uniformName = key.toLocal8Bit().data(); const std::string uniformName = key.toLocal8Bit().data();
QJsonValue value = _data.uniforms[key]; auto slotItr = _slotMap.find(uniformName);
if (slotItr == _slotMap.end()) {
continue;
}
const size_t slot = slotItr->second;
const QJsonValue& value = _data.uniforms[key];
if (value.isDouble()) { if (value.isDouble()) {
float v = value.toDouble(); const float v = value.toDouble();
_uniforms.push_back([slot, v](gpu::Batch& batch) { batch._glUniform1f(slot, v); }); _uniforms.push_back([slot, v](gpu::Batch& batch) { batch._glUniform1f(slot, v); });
} else if (value.isArray()) { } else if (value.isArray()) {
auto valueArray = value.toArray(); const QJsonArray valueArray = value.toArray();
switch (valueArray.size()) { if (valueArray.size() > 0) {
case 0: if (valueArray[0].isArray()) {
break; const size_t valueLength = valueArray[0].toArray().size();
std::vector<float> vs;
case 1: { vs.reserve(valueLength * valueArray.size());
float v = valueArray[0].toDouble(); size_t count = 0;
_uniforms.push_back([slot, v](gpu::Batch& batch) { batch._glUniform1f(slot, v); }); for (const QJsonValue& value : valueArray) {
break; if (value.isArray()) {
} const QJsonArray innerValueArray = value.toArray();
if (innerValueArray.size() == valueLength) {
case 2: { if (valueLength == 3 || valueLength == 4 || valueLength == 9 || valueLength == 16) {
glm::vec2 v{ valueArray[0].toDouble(), valueArray[1].toDouble() }; for (size_t i = 0; i < valueLength; i++) {
_uniforms.push_back([slot, v](gpu::Batch& batch) { batch._glUniform2f(slot, v.x, v.y); }); vs.push_back(innerValueArray[i].toDouble());
break; }
} count++;
}
case 3: { }
glm::vec3 v{ }
valueArray[0].toDouble(), }
valueArray[1].toDouble(), if (count > 0) {
valueArray[2].toDouble(), switch (valueLength) {
}; case 3: {
_uniforms.push_back([slot, v](gpu::Batch& batch) { batch._glUniform3f(slot, v.x, v.y, v.z); }); _uniforms.push_back([slot, vs, count](gpu::Batch& batch) { batch._glUniform3fv(slot, count, vs.data()); });
break; break;
} }
case 4: {
default: _uniforms.push_back([slot, vs, count](gpu::Batch& batch) { batch._glUniform4fv(slot, count, vs.data()); });
case 4: { break;
glm::vec4 v{ }
valueArray[0].toDouble(), case 9: {
valueArray[1].toDouble(), _uniforms.push_back([slot, vs, count](gpu::Batch& batch) { batch._glUniformMatrix3fv(slot, count, false, vs.data()); });
valueArray[2].toDouble(), break;
valueArray[3].toDouble(), }
}; case 16: {
_uniforms.push_back([slot, v](gpu::Batch& batch) { batch._glUniform4f(slot, v.x, v.y, v.z, v.w); }); _uniforms.push_back([slot, vs, count](gpu::Batch& batch) { batch._glUniformMatrix4fv(slot, count, false, vs.data()); });
break; break;
} }
default:
break;
}
}
} else if (valueArray[0].isDouble()) {
switch (valueArray.size()) {
case 1: {
const float v = valueArray[0].toDouble();
_uniforms.push_back([slot, v](gpu::Batch& batch) { batch._glUniform(slot, v); });
break;
}
case 2: {
const glm::vec2 v{ valueArray[0].toDouble(), valueArray[1].toDouble() };
_uniforms.push_back([slot, v](gpu::Batch& batch) { batch._glUniform(slot, v); });
break;
}
case 3: {
const glm::vec3 v{
valueArray[0].toDouble(),
valueArray[1].toDouble(),
valueArray[2].toDouble(),
};
_uniforms.push_back([slot, v](gpu::Batch& batch) { batch._glUniform(slot, v); });
break;
}
case 4: {
const glm::vec4 v{
valueArray[0].toDouble(),
valueArray[1].toDouble(),
valueArray[2].toDouble(),
valueArray[3].toDouble(),
};
_uniforms.push_back([slot, v](gpu::Batch& batch) { batch._glUniform(slot, v); });
break;
}
case 9: {
const glm::mat3 m{
valueArray[0].toDouble(),
valueArray[1].toDouble(),
valueArray[2].toDouble(),
valueArray[3].toDouble(),
valueArray[4].toDouble(),
valueArray[5].toDouble(),
valueArray[6].toDouble(),
valueArray[7].toDouble(),
valueArray[8].toDouble(),
};
_uniforms.push_back([slot, m](gpu::Batch& batch) { batch._glUniform(slot, m); });
break;
}
case 16: {
const glm::mat4 m{
valueArray[0].toDouble(),
valueArray[1].toDouble(),
valueArray[2].toDouble(),
valueArray[3].toDouble(),
valueArray[4].toDouble(),
valueArray[5].toDouble(),
valueArray[6].toDouble(),
valueArray[7].toDouble(),
valueArray[8].toDouble(),
valueArray[9].toDouble(),
valueArray[10].toDouble(),
valueArray[11].toDouble(),
valueArray[12].toDouble(),
valueArray[13].toDouble(),
valueArray[14].toDouble(),
valueArray[15].toDouble(),
};
_uniforms.push_back([slot, m](gpu::Batch& batch) { batch._glUniform(slot, m); });
break;
}
default:
break;
}
}
} }
} }
slot++;
} }
_uniforms.push_back([this](gpu::Batch& batch) { _uniforms.push_back([this](gpu::Batch& batch) {
@ -578,4 +694,4 @@ void graphics::ProceduralMaterial::initializeProcedural() {
_procedural._transparentFragmentSource = gpu::Shader::getFragmentShaderSource(shader::render_utils::fragment::simple_procedural_translucent); _procedural._transparentFragmentSource = gpu::Shader::getFragmentShaderSource(shader::render_utils::fragment::simple_procedural_translucent);
_procedural._errorFallbackFragmentPath = ":" + QUrl("qrc:///shaders/errorShader.frag").path(); _procedural._errorFallbackFragmentPath = ":" + QUrl("qrc:///shaders/errorShader.frag").path();
} }

View file

@ -190,6 +190,7 @@ protected:
NetworkTexturePointer _channels[MAX_PROCEDURAL_TEXTURE_CHANNELS]; NetworkTexturePointer _channels[MAX_PROCEDURAL_TEXTURE_CHANNELS];
std::unordered_map<std::string, std::string> _vertexReplacements; std::unordered_map<std::string, std::string> _vertexReplacements;
std::unordered_map<std::string, std::string> _fragmentReplacements; std::unordered_map<std::string, std::string> _fragmentReplacements;
std::unordered_map<std::string, size_t> _slotMap;
std::unordered_map<ProceduralProgramKey, gpu::PipelinePointer> _proceduralPipelines; std::unordered_map<ProceduralProgramKey, gpu::PipelinePointer> _proceduralPipelines;
std::unordered_map<ProceduralProgramKey, gpu::PipelinePointer> _errorPipelines; std::unordered_map<ProceduralProgramKey, gpu::PipelinePointer> _errorPipelines;

View file

@ -4,6 +4,7 @@
// //
// Created by Niraj Venkat on 7/15/15. // Created by Niraj Venkat on 7/15/15.
// Copyright 2015 High Fidelity, Inc. // Copyright 2015 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -606,8 +607,8 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte
graphics::AmbientOcclusionPointer ambientOcclusion; graphics::AmbientOcclusionPointer ambientOcclusion;
if (_debug) { if (_debug) {
ambientOcclusion = _debugAmbientOcclusion; ambientOcclusion = _debugAmbientOcclusion;
} else if (ambientOcclusionStage && ambientOcclusionFrame->_ambientOcclusions.size()) { } else if (ambientOcclusionStage && ambientOcclusionFrame->_elements.size()) {
ambientOcclusion = ambientOcclusionStage->getAmbientOcclusion(ambientOcclusionFrame->_ambientOcclusions.front()); ambientOcclusion = ambientOcclusionStage->getElement(ambientOcclusionFrame->_elements.front());
} }
if (!ambientOcclusion || !lightingModel->isAmbientOcclusionEnabled()) { if (!ambientOcclusion || !lightingModel->isAmbientOcclusionEnabled()) {

View file

@ -7,50 +7,8 @@
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "AmbientOcclusionStage.h" #include "AmbientOcclusionStage.h"
#include <gpu/Context.h> template <>
std::string render::PointerStage<graphics::AmbientOcclusion, graphics::AmbientOcclusionPointer>::_name { "AMBIENT_OCCLUSION_STAGE" };
std::string AmbientOcclusionStage::_stageName { "AMBIENT_OCCLUSION_STAGE" };
const AmbientOcclusionStage::Index AmbientOcclusionStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
AmbientOcclusionStage::Index AmbientOcclusionStage::findAmbientOcclusion(const AmbientOcclusionPointer& ambientOcclusion) const {
auto found = _ambientOcclusionMap.find(ambientOcclusion);
if (found != _ambientOcclusionMap.end()) {
return INVALID_INDEX;
} else {
return (*found).second;
}
}
AmbientOcclusionStage::Index AmbientOcclusionStage::addAmbientOcclusion(const AmbientOcclusionPointer& ambientOcclusion) {
auto found = _ambientOcclusionMap.find(ambientOcclusion);
if (found == _ambientOcclusionMap.end()) {
auto ambientOcclusionId = _ambientOcclusions.newElement(ambientOcclusion);
// Avoid failing to allocate a ambientOcclusion, just pass
if (ambientOcclusionId != INVALID_INDEX) {
// Insert the ambientOcclusion and its index in the reverse map
_ambientOcclusionMap.insert(AmbientOcclusionMap::value_type(ambientOcclusion, ambientOcclusionId));
}
return ambientOcclusionId;
} else {
return (*found).second;
}
}
AmbientOcclusionStage::AmbientOcclusionPointer AmbientOcclusionStage::removeAmbientOcclusion(Index index) {
AmbientOcclusionPointer removed = _ambientOcclusions.freeElement(index);
if (removed) {
_ambientOcclusionMap.erase(removed);
}
return removed;
}
AmbientOcclusionStageSetup::AmbientOcclusionStageSetup() {}
void AmbientOcclusionStageSetup::run(const render::RenderContextPointer& renderContext) {
auto stage = renderContext->_scene->getStage(AmbientOcclusionStage::getName());
if (!stage) {
renderContext->_scene->resetStage(AmbientOcclusionStage::getName(), std::make_shared<AmbientOcclusionStage>());
}
}

View file

@ -11,74 +11,17 @@
#ifndef hifi_render_utils_AmbientOcclusionStage_h #ifndef hifi_render_utils_AmbientOcclusionStage_h
#define hifi_render_utils_AmbientOcclusionStage_h #define hifi_render_utils_AmbientOcclusionStage_h
#include <graphics/Stage.h>
#include <set>
#include <unordered_map>
#include <render/IndexedContainer.h>
#include <render/Stage.h>
#include <render/Forward.h>
#include <render/DrawTask.h>
#include <graphics/AmbientOcclusion.h> #include <graphics/AmbientOcclusion.h>
#include <render/Stage.h>
#include <render/StageSetup.h>
// AmbientOcclusion stage to set up ambientOcclusion-related rendering tasks // AmbientOcclusion stage to set up ambientOcclusion-related rendering tasks
class AmbientOcclusionStage : public render::Stage { class AmbientOcclusionStage : public render::PointerStage<graphics::AmbientOcclusion, graphics::AmbientOcclusionPointer> {};
public:
static std::string _stageName;
static const std::string& getName() { return _stageName; }
using Index = render::indexed_container::Index;
static const Index INVALID_INDEX;
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
using AmbientOcclusionPointer = graphics::AmbientOcclusionPointer;
using AmbientOcclusions = render::indexed_container::IndexedPointerVector<graphics::AmbientOcclusion>;
using AmbientOcclusionMap = std::unordered_map<AmbientOcclusionPointer, Index>;
using AmbientOcclusionIndices = std::vector<Index>;
Index findAmbientOcclusion(const AmbientOcclusionPointer& ambientOcclusion) const;
Index addAmbientOcclusion(const AmbientOcclusionPointer& ambientOcclusion);
AmbientOcclusionPointer removeAmbientOcclusion(Index index);
bool checkAmbientOcclusionId(Index index) const { return _ambientOcclusions.checkIndex(index); }
Index getNumAmbientOcclusions() const { return _ambientOcclusions.getNumElements(); }
Index getNumFreeAmbientOcclusions() const { return _ambientOcclusions.getNumFreeIndices(); }
Index getNumAllocatedAmbientOcclusions() const { return _ambientOcclusions.getNumAllocatedIndices(); }
AmbientOcclusionPointer getAmbientOcclusion(Index ambientOcclusionId) const {
return _ambientOcclusions.get(ambientOcclusionId);
}
AmbientOcclusions _ambientOcclusions;
AmbientOcclusionMap _ambientOcclusionMap;
class Frame {
public:
Frame() {}
void clear() { _ambientOcclusions.clear(); }
void pushAmbientOcclusion(AmbientOcclusionStage::Index index) { _ambientOcclusions.emplace_back(index); }
AmbientOcclusionStage::AmbientOcclusionIndices _ambientOcclusions;
};
using FramePointer = std::shared_ptr<Frame>;
Frame _currentFrame;
};
using AmbientOcclusionStagePointer = std::shared_ptr<AmbientOcclusionStage>; using AmbientOcclusionStagePointer = std::shared_ptr<AmbientOcclusionStage>;
class AmbientOcclusionStageSetup { class AmbientOcclusionStageSetup : public render::StageSetup<AmbientOcclusionStage> {
public: public:
using JobModel = render::Job::Model<AmbientOcclusionStageSetup>; using JobModel = render::Job::Model<AmbientOcclusionStageSetup>;
AmbientOcclusionStageSetup();
void run(const render::RenderContextPointer& renderContext);
protected:
}; };
#endif #endif

View file

@ -1,6 +1,7 @@
// //
// Created by Samuel Gateau on 2018/12/06 // Created by Samuel Gateau on 2018/12/06
// Copyright 2013-2018 High Fidelity, Inc. // Copyright 2013-2018 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html

View file

@ -3,58 +3,20 @@
// //
// Created by Sam Gateau on 5/9/2017. // Created by Sam Gateau on 5/9/2017.
// Copyright 2015 High Fidelity, Inc. // Copyright 2015 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "BackgroundStage.h" #include "BackgroundStage.h"
#include "DeferredLightingEffect.h" #include "DeferredLightingEffect.h"
#include <gpu/Context.h>
#include <graphics/ShaderConstants.h> #include <graphics/ShaderConstants.h>
std::string BackgroundStage::_stageName { "BACKGROUND_STAGE" }; template <>
const BackgroundStage::Index BackgroundStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; std::string render::PointerStage<graphics::SunSkyStage, graphics::SunSkyStagePointer>::_name { "BACKGROUND_STAGE" };
BackgroundStage::Index BackgroundStage::findBackground(const BackgroundPointer& background) const {
auto found = _backgroundMap.find(background);
if (found != _backgroundMap.end()) {
return INVALID_INDEX;
} else {
return (*found).second;
}
}
BackgroundStage::Index BackgroundStage::addBackground(const BackgroundPointer& background) {
auto found = _backgroundMap.find(background);
if (found == _backgroundMap.end()) {
auto backgroundId = _backgrounds.newElement(background);
// Avoid failing to allocate a background, just pass
if (backgroundId != INVALID_INDEX) {
// Insert the background and its index in the reverse map
_backgroundMap.insert(BackgroundMap::value_type(background, backgroundId));
}
return backgroundId;
} else {
return (*found).second;
}
}
BackgroundStage::BackgroundPointer BackgroundStage::removeBackground(Index index) {
BackgroundPointer removed = _backgrounds.freeElement(index);
if (removed) {
_backgroundMap.erase(removed);
}
return removed;
}
void DrawBackgroundStage::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { void DrawBackgroundStage::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
const auto& lightingModel = inputs.get0(); const auto& lightingModel = inputs.get0();
@ -66,8 +28,8 @@ void DrawBackgroundStage::run(const render::RenderContextPointer& renderContext,
const auto& backgroundStage = renderContext->_scene->getStage<BackgroundStage>(); const auto& backgroundStage = renderContext->_scene->getStage<BackgroundStage>();
const auto& backgroundFrame = inputs.get1(); const auto& backgroundFrame = inputs.get1();
graphics::SkyboxPointer skybox; graphics::SkyboxPointer skybox;
if (backgroundStage && backgroundFrame->_backgrounds.size()) { if (backgroundStage && backgroundFrame->_elements.size()) {
const auto& background = backgroundStage->getBackground(backgroundFrame->_backgrounds.front()); const auto& background = backgroundStage->getElement(backgroundFrame->_elements.front());
if (background) { if (background) {
skybox = background->getSkybox(); skybox = background->getSkybox();
} }
@ -98,8 +60,8 @@ void DrawBackgroundStage::run(const render::RenderContextPointer& renderContext,
// If we're using forward rendering, we need to calculate haze // If we're using forward rendering, we need to calculate haze
if (args->_renderMethod == render::Args::RenderMethod::FORWARD) { if (args->_renderMethod == render::Args::RenderMethod::FORWARD) {
const auto& hazeStage = args->_scene->getStage<HazeStage>(); const auto& hazeStage = args->_scene->getStage<HazeStage>();
if (hazeStage && hazeFrame->_hazes.size() > 0) { if (hazeStage && hazeFrame->_elements.size() > 0) {
const auto& hazePointer = hazeStage->getHaze(hazeFrame->_hazes.front()); const auto& hazePointer = hazeStage->getElement(hazeFrame->_elements.front());
if (hazePointer) { if (hazePointer) {
batch.setUniformBuffer(graphics::slot::buffer::Buffer::HazeParams, hazePointer->getHazeParametersBuffer()); batch.setUniformBuffer(graphics::slot::buffer::Buffer::HazeParams, hazePointer->getHazeParametersBuffer());
} }
@ -111,14 +73,3 @@ void DrawBackgroundStage::run(const render::RenderContextPointer& renderContext,
args->_batch = nullptr; args->_batch = nullptr;
} }
} }
BackgroundStageSetup::BackgroundStageSetup() {
}
void BackgroundStageSetup::run(const render::RenderContextPointer& renderContext) {
auto stage = renderContext->_scene->getStage(BackgroundStage::getName());
if (!stage) {
renderContext->_scene->resetStage(BackgroundStage::getName(), std::make_shared<BackgroundStage>());
}
}

View file

@ -1,8 +1,9 @@
// //
// BackgroundStage.h // BackgroundStage.h
//
// Created by Sam Gateau on 5/9/2017. // Created by Sam Gateau on 5/9/2017.
// Copyright 2015 High Fidelity, Inc. // Copyright 2015 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -12,72 +13,19 @@
#define hifi_render_utils_BackgroundStage_h #define hifi_render_utils_BackgroundStage_h
#include <graphics/Stage.h> #include <graphics/Stage.h>
#include <set>
#include <unordered_map>
#include <render/IndexedContainer.h>
#include <render/Stage.h> #include <render/Stage.h>
#include "HazeStage.h" #include <render/StageSetup.h>
#include "HazeStage.h"
#include "LightingModel.h" #include "LightingModel.h"
// Background stage to set up background-related rendering tasks // Background stage to set up background-related rendering tasks
class BackgroundStage : public render::Stage { class BackgroundStage : public render::PointerStage<graphics::SunSkyStage, graphics::SunSkyStagePointer> {};
public:
static std::string _stageName;
static const std::string& getName() { return _stageName; }
using Index = render::indexed_container::Index;
static const Index INVALID_INDEX;
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
using BackgroundPointer = graphics::SunSkyStagePointer;
using Backgrounds = render::indexed_container::IndexedPointerVector<graphics::SunSkyStage>;
using BackgroundMap = std::unordered_map<BackgroundPointer, Index>;
using BackgroundIndices = std::vector<Index>;
Index findBackground(const BackgroundPointer& background) const;
Index addBackground(const BackgroundPointer& background);
BackgroundPointer removeBackground(Index index);
bool checkBackgroundId(Index index) const { return _backgrounds.checkIndex(index); }
Index getNumBackgrounds() const { return _backgrounds.getNumElements(); }
Index getNumFreeBackgrounds() const { return _backgrounds.getNumFreeIndices(); }
Index getNumAllocatedBackgrounds() const { return _backgrounds.getNumAllocatedIndices(); }
BackgroundPointer getBackground(Index backgroundId) const {
return _backgrounds.get(backgroundId);
}
Backgrounds _backgrounds;
BackgroundMap _backgroundMap;
class Frame {
public:
Frame() {}
void clear() { _backgrounds.clear(); }
void pushBackground(BackgroundStage::Index index) { _backgrounds.emplace_back(index); }
BackgroundStage::BackgroundIndices _backgrounds;
};
using FramePointer = std::shared_ptr<Frame>;
Frame _currentFrame;
};
using BackgroundStagePointer = std::shared_ptr<BackgroundStage>; using BackgroundStagePointer = std::shared_ptr<BackgroundStage>;
class BackgroundStageSetup { class BackgroundStageSetup : public render::StageSetup<BackgroundStage> {
public: public:
using JobModel = render::Job::Model<BackgroundStageSetup>; using JobModel = render::Job::Model<BackgroundStageSetup>;
BackgroundStageSetup();
void run(const render::RenderContextPointer& renderContext);
}; };
class DrawBackgroundStage { class DrawBackgroundStage {

View file

@ -4,6 +4,7 @@
// //
// Created by Olivier Prat on 09/25/17. // Created by Olivier Prat on 09/25/17.
// Copyright 2017 High Fidelity, Inc. // Copyright 2017 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -43,8 +44,8 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons
const auto lightingModel = inputs.get3(); const auto lightingModel = inputs.get3();
const auto& bloomStage = renderContext->_scene->getStage<BloomStage>(); const auto& bloomStage = renderContext->_scene->getStage<BloomStage>();
graphics::BloomPointer bloom; graphics::BloomPointer bloom;
if (bloomStage && bloomFrame->_blooms.size()) { if (bloomStage && bloomFrame->_elements.size()) {
bloom = bloomStage->getBloom(bloomFrame->_blooms.front()); bloom = bloomStage->getElement(bloomFrame->_elements.front());
} }
if (!bloom || (lightingModel && !lightingModel->isBloomEnabled())) { if (!bloom || (lightingModel && !lightingModel->isBloomEnabled())) {
renderContext->taskFlow.abortTask(); renderContext->taskFlow.abortTask();

View file

@ -3,56 +3,13 @@
// //
// Created by Sam Gondelman on 8/7/2018 // Created by Sam Gondelman on 8/7/2018
// Copyright 2018 High Fidelity, Inc. // Copyright 2018 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "BloomStage.h" #include "BloomStage.h"
#include "DeferredLightingEffect.h" template <>
std::string render::PointerStage<graphics::Bloom, graphics::BloomPointer>::_name { "BLOOM_STAGE" };
#include <gpu/Context.h>
std::string BloomStage::_stageName { "BLOOM_STAGE" };
const BloomStage::Index BloomStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
BloomStage::Index BloomStage::findBloom(const BloomPointer& bloom) const {
auto found = _bloomMap.find(bloom);
if (found != _bloomMap.end()) {
return INVALID_INDEX;
} else {
return (*found).second;
}
}
BloomStage::Index BloomStage::addBloom(const BloomPointer& bloom) {
auto found = _bloomMap.find(bloom);
if (found == _bloomMap.end()) {
auto bloomId = _blooms.newElement(bloom);
// Avoid failing to allocate a bloom, just pass
if (bloomId != INVALID_INDEX) {
// Insert the bloom and its index in the reverse map
_bloomMap.insert(BloomMap::value_type(bloom, bloomId));
}
return bloomId;
} else {
return (*found).second;
}
}
BloomStage::BloomPointer BloomStage::removeBloom(Index index) {
BloomPointer removed = _blooms.freeElement(index);
if (removed) {
_bloomMap.erase(removed);
}
return removed;
}
BloomStageSetup::BloomStageSetup() {}
void BloomStageSetup::run(const render::RenderContextPointer& renderContext) {
auto stage = renderContext->_scene->getStage(BloomStage::getName());
if (!stage) {
renderContext->_scene->resetStage(BloomStage::getName(), std::make_shared<BloomStage>());
}
}

View file

@ -3,6 +3,7 @@
// Created by Sam Gondelman on 8/7/2018 // Created by Sam Gondelman on 8/7/2018
// Copyright 2018 High Fidelity, Inc. // Copyright 2018 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -11,74 +12,17 @@
#ifndef hifi_render_utils_BloomStage_h #ifndef hifi_render_utils_BloomStage_h
#define hifi_render_utils_BloomStage_h #define hifi_render_utils_BloomStage_h
#include <graphics/Stage.h>
#include <set>
#include <unordered_map>
#include <render/IndexedContainer.h>
#include <render/Stage.h>
#include <render/Forward.h>
#include <render/DrawTask.h>
#include <graphics/Bloom.h> #include <graphics/Bloom.h>
#include <render/Stage.h>
#include <render/StageSetup.h>
// Bloom stage to set up bloom-related rendering tasks // Bloom stage to set up bloom-related rendering tasks
class BloomStage : public render::Stage { class BloomStage : public render::PointerStage<graphics::Bloom, graphics::BloomPointer> {};
public:
static std::string _stageName;
static const std::string& getName() { return _stageName; }
using Index = render::indexed_container::Index;
static const Index INVALID_INDEX;
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
using BloomPointer = graphics::BloomPointer;
using Blooms = render::indexed_container::IndexedPointerVector<graphics::Bloom>;
using BloomMap = std::unordered_map<BloomPointer, Index>;
using BloomIndices = std::vector<Index>;
Index findBloom(const BloomPointer& bloom) const;
Index addBloom(const BloomPointer& bloom);
BloomPointer removeBloom(Index index);
bool checkBloomId(Index index) const { return _blooms.checkIndex(index); }
Index getNumBlooms() const { return _blooms.getNumElements(); }
Index getNumFreeBlooms() const { return _blooms.getNumFreeIndices(); }
Index getNumAllocatedBlooms() const { return _blooms.getNumAllocatedIndices(); }
BloomPointer getBloom(Index bloomId) const {
return _blooms.get(bloomId);
}
Blooms _blooms;
BloomMap _bloomMap;
class Frame {
public:
Frame() {}
void clear() { _blooms.clear(); }
void pushBloom(BloomStage::Index index) { _blooms.emplace_back(index); }
BloomStage::BloomIndices _blooms;
};
using FramePointer = std::shared_ptr<Frame>;
Frame _currentFrame;
};
using BloomStagePointer = std::shared_ptr<BloomStage>; using BloomStagePointer = std::shared_ptr<BloomStage>;
class BloomStageSetup { class BloomStageSetup : public render::StageSetup<BloomStage> {
public: public:
using JobModel = render::Job::Model<BloomStageSetup>; using JobModel = render::Job::Model<BloomStageSetup>;
BloomStageSetup();
void run(const render::RenderContextPointer& renderContext);
protected:
}; };
#endif #endif

View file

@ -4,6 +4,7 @@
// //
// Created by Andrzej Kapolka on 9/11/14. // Created by Andrzej Kapolka on 9/11/14.
// Copyright 2014 High Fidelity, Inc. // Copyright 2014 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -375,7 +376,7 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext,
// Global directional light, maybe shadow and ambient pass // Global directional light, maybe shadow and ambient pass
auto lightStage = renderContext->_scene->getStage<LightStage>(); auto lightStage = renderContext->_scene->getStage<LightStage>();
assert(lightStage); assert(lightStage);
assert(lightStage->getNumLights() > 0); assert(lightStage->getNumElements() > 0);
auto keyLight = lightStage->getCurrentKeyLight(*lightFrame); auto keyLight = lightStage->getCurrentKeyLight(*lightFrame);
// Check if keylight casts shadows // Check if keylight casts shadows
@ -391,7 +392,7 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext,
// Global Ambient light // Global Ambient light
graphics::LightPointer ambientLight; graphics::LightPointer ambientLight;
if (lightStage && lightFrame->_ambientLights.size()) { if (lightStage && lightFrame->_ambientLights.size()) {
ambientLight = lightStage->getLight(lightFrame->_ambientLights.front()); ambientLight = lightStage->getElement(lightFrame->_ambientLights.front());
} }
bool hasAmbientMap = (ambientLight != nullptr); bool hasAmbientMap = (ambientLight != nullptr);
@ -430,8 +431,8 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext,
// Haze // Haze
const auto& hazeStage = args->_scene->getStage<HazeStage>(); const auto& hazeStage = args->_scene->getStage<HazeStage>();
if (hazeStage && hazeFrame->_hazes.size() > 0) { if (hazeStage && hazeFrame->_elements.size() > 0) {
const auto& hazePointer = hazeStage->getHaze(hazeFrame->_hazes.front()); const auto& hazePointer = hazeStage->getElement(hazeFrame->_elements.front());
if (hazePointer) { if (hazePointer) {
batch.setUniformBuffer(graphics::slot::buffer::Buffer::HazeParams, hazePointer->getHazeParametersBuffer()); batch.setUniformBuffer(graphics::slot::buffer::Buffer::HazeParams, hazePointer->getHazeParametersBuffer());
} }
@ -636,7 +637,7 @@ void DefaultLightingSetup::run(const RenderContextPointer& renderContext) {
// Add the global light to the light stage (for later shadow rendering) // Add the global light to the light stage (for later shadow rendering)
// Set this light to be the default // Set this light to be the default
_defaultLightID = lightStage->addLight(lp, true); _defaultLightID = lightStage->addElement(lp, true);
} }
auto backgroundStage = renderContext->_scene->getStage<BackgroundStage>(); auto backgroundStage = renderContext->_scene->getStage<BackgroundStage>();
@ -649,7 +650,7 @@ void DefaultLightingSetup::run(const RenderContextPointer& renderContext) {
_defaultBackground = background; _defaultBackground = background;
// Add the global light to the light stage (for later shadow rendering) // Add the global light to the light stage (for later shadow rendering)
_defaultBackgroundID = backgroundStage->addBackground(_defaultBackground); _defaultBackgroundID = backgroundStage->addElement(_defaultBackground);
} }
} }
@ -659,7 +660,7 @@ void DefaultLightingSetup::run(const RenderContextPointer& renderContext) {
auto haze = std::make_shared<graphics::Haze>(); auto haze = std::make_shared<graphics::Haze>();
_defaultHaze = haze; _defaultHaze = haze;
_defaultHazeID = hazeStage->addHaze(_defaultHaze); _defaultHazeID = hazeStage->addElement(_defaultHaze);
} }
} }
@ -669,7 +670,7 @@ void DefaultLightingSetup::run(const RenderContextPointer& renderContext) {
auto tonemapping = std::make_shared<graphics::Tonemapping>(); auto tonemapping = std::make_shared<graphics::Tonemapping>();
_defaultTonemapping = tonemapping; _defaultTonemapping = tonemapping;
_defaultTonemappingID = tonemappingStage->addTonemapping(_defaultTonemapping); _defaultTonemappingID = tonemappingStage->addElement(_defaultTonemapping);
} }
} }
} }

View file

@ -4,6 +4,7 @@
// //
// Created by Nissim Hadar on 9/1/2017. // Created by Nissim Hadar on 9/1/2017.
// Copyright 2015 High Fidelity, Inc. // Copyright 2015 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -39,8 +40,8 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu
const auto hazeFrame = inputs.get0(); const auto hazeFrame = inputs.get0();
const auto& hazeStage = renderContext->args->_scene->getStage<HazeStage>(); const auto& hazeStage = renderContext->args->_scene->getStage<HazeStage>();
graphics::HazePointer haze; graphics::HazePointer haze;
if (hazeStage && hazeFrame->_hazes.size() > 0) { if (hazeStage && hazeFrame->_elements.size() > 0) {
haze = hazeStage->getHaze(hazeFrame->_hazes.front()); haze = hazeStage->getElement(hazeFrame->_elements.front());
} }
if (!haze) { if (!haze) {
@ -55,6 +56,10 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu
auto depthBuffer = framebuffer->getLinearDepthTexture(); auto depthBuffer = framebuffer->getLinearDepthTexture();
if (!lightingModel->isHazeEnabled() || !haze->isActive()) {
return;
}
RenderArgs* args = renderContext->args; RenderArgs* args = renderContext->args;
if (!_hazePipeline) { if (!_hazePipeline) {

View file

@ -3,6 +3,7 @@
// Created by Olivier Prat on 17/07/2017. // Created by Olivier Prat on 17/07/2017.
// Copyright 2017 High Fidelity, Inc. // Copyright 2017 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -28,10 +29,8 @@ FadeEffect::FadeEffect() {
} }
void FadeEffect::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { void FadeEffect::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) {
auto editedFadeCategory = task.addJob<FadeJob>("Fade"); const auto editedFadeCategory = task.addJob<FadeJob>("Fade");
task.addJob<FadeEditJob>("FadeEdit", editedFadeCategory);
const auto fadeEditInput = FadeEditJob::Input(inputs, editedFadeCategory).asVarying();
task.addJob<FadeEditJob>("FadeEdit", fadeEditInput);
} }
render::ShapePipeline::BatchSetter FadeEffect::getBatchSetter() { render::ShapePipeline::BatchSetter FadeEffect::getBatchSetter() {
@ -47,7 +46,7 @@ render::ShapePipeline::ItemSetter FadeEffect::getItemUniformSetter() {
const auto& scene = args->_scene; const auto& scene = args->_scene;
const auto& batch = args->_batch; const auto& batch = args->_batch;
auto transitionStage = scene->getStage<render::TransitionStage>(); auto transitionStage = scene->getStage<render::TransitionStage>();
auto& transitionState = transitionStage->getTransition(item.getTransitionId()); auto& transitionState = transitionStage->getElement(item.getTransitionId());
if (transitionState.paramsBuffer._size != sizeof(gpu::StructBuffer<FadeObjectParams>)) { if (transitionState.paramsBuffer._size != sizeof(gpu::StructBuffer<FadeObjectParams>)) {
static_assert(sizeof(transitionState.paramsBuffer) == sizeof(gpu::StructBuffer<FadeObjectParams>), "Assuming gpu::StructBuffer is a helper class for gpu::BufferView"); static_assert(sizeof(transitionState.paramsBuffer) == sizeof(gpu::StructBuffer<FadeObjectParams>), "Assuming gpu::StructBuffer is a helper class for gpu::BufferView");

View file

@ -15,8 +15,7 @@
class FadeEffect { class FadeEffect {
public: public:
using Input = render::ItemBounds; using JobModel = render::Task::Model<FadeEffect>;
using JobModel = render::Task::ModelI<FadeEffect, render::ItemBounds>;
FadeEffect(); FadeEffect();

View file

@ -3,6 +3,7 @@
// Created by Olivier Prat on 07/07/2017. // Created by Olivier Prat on 07/07/2017.
// Copyright 2017 High Fidelity, Inc. // Copyright 2017 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -35,7 +36,7 @@ void FadeEditJob::configure(const Config& config) {
_isEditEnabled = config.editFade; _isEditEnabled = config.editFade;
} }
void FadeEditJob::run(const render::RenderContextPointer& renderContext, const FadeEditJob::Input& inputs) { void FadeEditJob::run(const render::RenderContextPointer& renderContext, const FadeEditJob::Input& input) {
auto scene = renderContext->_scene; auto scene = renderContext->_scene;
if (_isEditEnabled) { if (_isEditEnabled) {
@ -63,7 +64,7 @@ void FadeEditJob::run(const render::RenderContextPointer& renderContext, const F
render::Transition::AVATAR_CHANGE render::Transition::AVATAR_CHANGE
}; };
auto transitionType = categoryToTransition[inputs.get1()]; auto transitionType = categoryToTransition[input];
transaction.queryTransitionOnItem(_editedItem, [transitionType, scene](render::ItemID id, const render::Transition* transition) { transaction.queryTransitionOnItem(_editedItem, [transitionType, scene](render::ItemID id, const render::Transition* transition) {
if (transition == nullptr || transition->isFinished || transition->eventType != transitionType) { if (transition == nullptr || transition->isFinished || transition->eventType != transitionType) {
@ -572,7 +573,7 @@ void FadeJob::run(const render::RenderContextPointer& renderContext, FadeJob::Ou
// And now update fade effect // And now update fade effect
for (auto transitionId : *transitionStage) { for (auto transitionId : *transitionStage) {
auto& state = transitionStage->editTransition(transitionId); auto& state = transitionStage->editElement(transitionId);
#ifdef DEBUG #ifdef DEBUG
auto& item = scene->getItem(state.itemId); auto& item = scene->getItem(state.itemId);
assert(item.getTransitionId() == transitionId); assert(item.getTransitionId() == transitionId);

View file

@ -177,13 +177,13 @@ class FadeEditJob {
public: public:
using Config = FadeEditConfig; using Config = FadeEditConfig;
using Input = render::VaryingSet2<render::ItemBounds, FadeCategory>; using Input = FadeCategory;
using JobModel = render::Job::ModelI<FadeEditJob, Input, Config>; using JobModel = render::Job::ModelI<FadeEditJob, Input, Config>;
FadeEditJob() {} FadeEditJob() {}
void configure(const Config& config); void configure(const Config& config);
void run(const render::RenderContextPointer& renderContext, const FadeEditJob::Input& inputs); void run(const render::RenderContextPointer& renderContext, const FadeEditJob::Input& input);
private: private:

View file

@ -6,6 +6,7 @@
// //
// Created by Nissim Hadar on 9/5/2107. // Created by Nissim Hadar on 9/5/2107.
// Copyright 2016 High Fidelity, Inc. // Copyright 2016 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -45,10 +46,6 @@ layout(location=0) in vec2 varTexCoord0;
layout(location=0) out vec4 outFragColor; layout(location=0) out vec4 outFragColor;
void main(void) { void main(void) {
if ((isHazeEnabled() == 0.0) || (hazeParams.hazeMode & HAZE_MODE_IS_ACTIVE) != HAZE_MODE_IS_ACTIVE) {
discard;
}
vec4 fragPositionES = unpackPositionFromZeye(varTexCoord0); vec4 fragPositionES = unpackPositionFromZeye(varTexCoord0);
mat4 viewInverse = getViewInverse(); mat4 viewInverse = getViewInverse();

View file

@ -3,59 +3,13 @@
// //
// Created by Nissim Hadar on 9/26/2017. // Created by Nissim Hadar on 9/26/2017.
// Copyright 2015 High Fidelity, Inc. // Copyright 2015 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "HazeStage.h" #include "HazeStage.h"
#include "DeferredLightingEffect.h" template <>
std::string render::PointerStage<graphics::Haze, graphics::HazePointer>::_name { "HAZE_STAGE" };
#include <gpu/Context.h>
std::string HazeStage::_stageName { "HAZE_STAGE" };
const HazeStage::Index HazeStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
HazeStage::Index HazeStage::findHaze(const HazePointer& haze) const {
auto found = _hazeMap.find(haze);
if (found != _hazeMap.end()) {
return INVALID_INDEX;
} else {
return (*found).second;
}
}
HazeStage::Index HazeStage::addHaze(const HazePointer& haze) {
auto found = _hazeMap.find(haze);
if (found == _hazeMap.end()) {
auto hazeId = _hazes.newElement(haze);
// Avoid failing to allocate a haze, just pass
if (hazeId != INVALID_INDEX) {
// Insert the haze and its index in the reverse map
_hazeMap.insert(HazeMap::value_type(haze, hazeId));
}
return hazeId;
} else {
return (*found).second;
}
}
HazeStage::HazePointer HazeStage::removeHaze(Index index) {
HazePointer removed = _hazes.freeElement(index);
if (removed) {
_hazeMap.erase(removed);
}
return removed;
}
HazeStageSetup::HazeStageSetup() {
}
void HazeStageSetup::run(const render::RenderContextPointer& renderContext) {
auto stage = renderContext->_scene->getStage(HazeStage::getName());
if (!stage) {
renderContext->_scene->resetStage(HazeStage::getName(), std::make_shared<HazeStage>());
}
}

View file

@ -3,6 +3,7 @@
// Created by Nissim Hadar on 9/26/2017. // Created by Nissim Hadar on 9/26/2017.
// Copyright 2015 High Fidelity, Inc. // Copyright 2015 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -11,74 +12,17 @@
#ifndef hifi_render_utils_HazeStage_h #ifndef hifi_render_utils_HazeStage_h
#define hifi_render_utils_HazeStage_h #define hifi_render_utils_HazeStage_h
#include <graphics/Stage.h>
#include <set>
#include <unordered_map>
#include <render/IndexedContainer.h>
#include <render/Stage.h>
#include <render/Forward.h>
#include <render/DrawTask.h>
#include <graphics/Haze.h> #include <graphics/Haze.h>
#include <render/Stage.h>
#include <render/StageSetup.h>
// Haze stage to set up haze-related rendering tasks // Haze stage to set up haze-related rendering tasks
class HazeStage : public render::Stage { class HazeStage : public render::PointerStage<graphics::Haze, graphics::HazePointer> {};
public:
static std::string _stageName;
static const std::string& getName() { return _stageName; }
using Index = render::indexed_container::Index;
static const Index INVALID_INDEX;
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
using HazePointer = graphics::HazePointer;
using Hazes = render::indexed_container::IndexedPointerVector<graphics::Haze>;
using HazeMap = std::unordered_map<HazePointer, Index>;
using HazeIndices = std::vector<Index>;
Index findHaze(const HazePointer& haze) const;
Index addHaze(const HazePointer& haze);
HazePointer removeHaze(Index index);
bool checkHazeId(Index index) const { return _hazes.checkIndex(index); }
Index getNumHazes() const { return _hazes.getNumElements(); }
Index getNumFreeHazes() const { return _hazes.getNumFreeIndices(); }
Index getNumAllocatedHazes() const { return _hazes.getNumAllocatedIndices(); }
HazePointer getHaze(Index hazeId) const {
return _hazes.get(hazeId);
}
Hazes _hazes;
HazeMap _hazeMap;
class Frame {
public:
Frame() {}
void clear() { _hazes.clear(); }
void pushHaze(HazeStage::Index index) { _hazes.emplace_back(index); }
HazeStage::HazeIndices _hazes;
};
using FramePointer = std::shared_ptr<Frame>;
Frame _currentFrame;
};
using HazeStagePointer = std::shared_ptr<HazeStage>; using HazeStagePointer = std::shared_ptr<HazeStage>;
class HazeStageSetup { class HazeStageSetup : public render::StageSetup<HazeStage> {
public: public:
using JobModel = render::Job::Model<HazeStageSetup>; using JobModel = render::Job::Model<HazeStageSetup>;
HazeStageSetup();
void run(const render::RenderContextPointer& renderContext);
protected:
}; };
class FetchHazeConfig : public render::Job::Config { class FetchHazeConfig : public render::Job::Config {

View file

@ -161,7 +161,7 @@ void DrawHighlightMask::run(const render::RenderContextPointer& renderContext, c
if (!inShapes.empty() && !render::HighlightStage::isIndexInvalid(highlightId)) { if (!inShapes.empty() && !render::HighlightStage::isIndexInvalid(highlightId)) {
auto resources = inputs.get1(); auto resources = inputs.get1();
auto& highlight = highlightStage->getHighlight(highlightId); auto& highlight = highlightStage->getElement(highlightId);
RenderArgs* args = renderContext->args; RenderArgs* args = renderContext->args;
@ -247,7 +247,7 @@ void DrawHighlight::run(const render::RenderContextPointer& renderContext, const
auto highlightStage = renderContext->_scene->getStage<render::HighlightStage>(); auto highlightStage = renderContext->_scene->getStage<render::HighlightStage>();
auto highlightId = _sharedParameters->_highlightIds[_highlightPassIndex]; auto highlightId = _sharedParameters->_highlightIds[_highlightPassIndex];
if (!render::HighlightStage::isIndexInvalid(highlightId)) { if (!render::HighlightStage::isIndexInvalid(highlightId)) {
auto& highlight = highlightStage->getHighlight(highlightId); auto& highlight = highlightStage->getElement(highlightId);
auto pipeline = getPipeline(highlight._style); auto pipeline = getPipeline(highlight._style);
{ {
auto& shaderParameters = _configuration.edit(); auto& shaderParameters = _configuration.edit();
@ -432,7 +432,7 @@ void SelectionToHighlight::run(const render::RenderContextPointer& renderContext
auto highlightList = highlightStage->getActiveHighlightIds(); auto highlightList = highlightStage->getActiveHighlightIds();
for (auto styleId : highlightList) { for (auto styleId : highlightList) {
auto highlight = highlightStage->getHighlight(styleId); auto highlight = highlightStage->getElement(styleId);
if (!scene->isSelectionEmpty(highlight._selectionName)) { if (!scene->isSelectionEmpty(highlight._selectionName)) {
auto highlightId = highlightStage->getHighlightIdBySelection(highlight._selectionName); auto highlightId = highlightStage->getHighlightIdBySelection(highlight._selectionName);
@ -572,8 +572,8 @@ void HighlightCleanup::run(const render::RenderContextPointer& renderContext, co
auto highlightStage = scene->getStage<render::HighlightStage>(); auto highlightStage = scene->getStage<render::HighlightStage>();
for (auto index : inputs.get0()) { for (auto index : inputs.get0()) {
std::string selectionName = highlightStage->getHighlight(index)._selectionName; std::string selectionName = highlightStage->getElement(index)._selectionName;
highlightStage->removeHighlight(index); highlightStage->removeElement(index);
scene->removeSelection(selectionName); scene->removeSelection(selectionName);
} }

View file

@ -348,7 +348,7 @@ glm::ivec3 LightClusters::updateClusters() {
uint32_t numClusteredLights = 0; uint32_t numClusteredLights = 0;
for (size_t lightNum = 1; lightNum < _visibleLightIndices.size(); ++lightNum) { for (size_t lightNum = 1; lightNum < _visibleLightIndices.size(); ++lightNum) {
auto lightId = _visibleLightIndices[lightNum]; auto lightId = _visibleLightIndices[lightNum];
auto light = _lightStage->getLight(lightId); auto light = _lightStage->getElement(lightId);
if (!light) { if (!light) {
continue; continue;
} }
@ -567,9 +567,9 @@ void LightClusteringPass::run(const render::RenderContextPointer& renderContext,
output = _lightClusters; output = _lightClusters;
auto config = std::static_pointer_cast<Config>(renderContext->jobConfig); auto config = std::static_pointer_cast<Config>(renderContext->jobConfig);
config->numSceneLights = lightStage->getNumLights(); config->numSceneLights = lightStage->getNumElements();
config->numFreeSceneLights = lightStage->getNumFreeLights(); config->numFreeSceneLights = lightStage->getNumFreeElements();
config->numAllocatedSceneLights = lightStage->getNumAllocatedLights(); config->numAllocatedSceneLights = lightStage->getNumAllocatedElements();
config->setNumInputLights(clusteringStats.x); config->setNumInputLights(clusteringStats.x);
config->setNumClusteredLights(clusteringStats.y); config->setNumClusteredLights(clusteringStats.y);
config->setNumClusteredLightReferences(clusteringStats.z); config->setNumClusteredLightReferences(clusteringStats.z);

View file

@ -95,7 +95,7 @@ public:
FrustumGrid::Planes _gridPlanes[3]; FrustumGrid::Planes _gridPlanes[3];
LightStage::LightIndices _visibleLightIndices; render::ElementIndices _visibleLightIndices;
gpu::BufferView _lightIndicesBuffer; gpu::BufferView _lightIndicesBuffer;
const uint32_t EMPTY_CLUSTER { 0x0000FFFF }; const uint32_t EMPTY_CLUSTER { 0x0000FFFF };

View file

@ -52,7 +52,7 @@ LightPayload::LightPayload() :
LightPayload::~LightPayload() { LightPayload::~LightPayload() {
if (!LightStage::isIndexInvalid(_index)) { if (!LightStage::isIndexInvalid(_index)) {
if (_stage) { if (_stage) {
_stage->removeLight(_index); _stage->removeElement(_index);
} }
} }
} }
@ -64,7 +64,7 @@ void LightPayload::render(RenderArgs* args) {
} }
// Do we need to allocate the light in the stage ? // Do we need to allocate the light in the stage ?
if (LightStage::isIndexInvalid(_index)) { if (LightStage::isIndexInvalid(_index)) {
_index = _stage->addLight(_light); _index = _stage->addElement(_light);
_needUpdate = false; _needUpdate = false;
} }
// Need an update ? // Need an update ?
@ -122,7 +122,7 @@ _light(std::make_shared<graphics::Light>())
KeyLightPayload::~KeyLightPayload() { KeyLightPayload::~KeyLightPayload() {
if (!LightStage::isIndexInvalid(_index)) { if (!LightStage::isIndexInvalid(_index)) {
if (_stage) { if (_stage) {
_stage->removeLight(_index); _stage->removeElement(_index);
} }
} }
} }
@ -134,7 +134,7 @@ void KeyLightPayload::render(RenderArgs* args) {
} }
// Do we need to allocate the light in the stage ? // Do we need to allocate the light in the stage ?
if (LightStage::isIndexInvalid(_index)) { if (LightStage::isIndexInvalid(_index)) {
_index = _stage->addLight(_light); _index = _stage->addElement(_light);
_needUpdate = false; _needUpdate = false;
} }
// Need an update ? // Need an update ?

View file

@ -4,6 +4,7 @@
// //
// Created by Zach Pomerantz on 1/14/2015. // Created by Zach Pomerantz on 1/14/2015.
// Copyright 2015 High Fidelity, Inc. // Copyright 2015 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -15,7 +16,9 @@
#include "ViewFrustum.h" #include "ViewFrustum.h"
std::string LightStage::_stageName { "LIGHT_STAGE" }; template <>
std::string render::PointerStage<graphics::Light, graphics::LightPointer, LightFrame>::_name { "LIGHT_STAGE" };
// The bias matrix goes from homogeneous coordinates to UV coords (see http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/#basic-shader) // The bias matrix goes from homogeneous coordinates to UV coords (see http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/#basic-shader)
const glm::mat4 LightStage::Shadow::_biasMatrix { const glm::mat4 LightStage::Shadow::_biasMatrix {
0.5, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0,
@ -24,8 +27,6 @@ const glm::mat4 LightStage::Shadow::_biasMatrix {
0.5, 0.5, 0.5, 1.0 }; 0.5, 0.5, 0.5, 1.0 };
const int LightStage::Shadow::MAP_SIZE = 1024; const int LightStage::Shadow::MAP_SIZE = 1024;
const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
LightStage::LightStage() { LightStage::LightStage() {
// Add off lights // Add off lights
const LightPointer ambientOffLight { std::make_shared<graphics::Light>() }; const LightPointer ambientOffLight { std::make_shared<graphics::Light>() };
@ -33,28 +34,28 @@ LightStage::LightStage() {
ambientOffLight->setColor(graphics::Vec3(0.0)); ambientOffLight->setColor(graphics::Vec3(0.0));
ambientOffLight->setIntensity(0.0f); ambientOffLight->setIntensity(0.0f);
ambientOffLight->setType(graphics::Light::Type::AMBIENT); ambientOffLight->setType(graphics::Light::Type::AMBIENT);
_ambientOffLightId = addLight(ambientOffLight); _ambientOffLightId = addElement(ambientOffLight);
const LightPointer pointOffLight { std::make_shared<graphics::Light>() }; const LightPointer pointOffLight { std::make_shared<graphics::Light>() };
pointOffLight->setAmbientIntensity(0.0f); pointOffLight->setAmbientIntensity(0.0f);
pointOffLight->setColor(graphics::Vec3(0.0)); pointOffLight->setColor(graphics::Vec3(0.0));
pointOffLight->setIntensity(0.0f); pointOffLight->setIntensity(0.0f);
pointOffLight->setType(graphics::Light::Type::POINT); pointOffLight->setType(graphics::Light::Type::POINT);
_pointOffLightId = addLight(pointOffLight); _pointOffLightId = addElement(pointOffLight);
const LightPointer spotOffLight { std::make_shared<graphics::Light>() }; const LightPointer spotOffLight { std::make_shared<graphics::Light>() };
spotOffLight->setAmbientIntensity(0.0f); spotOffLight->setAmbientIntensity(0.0f);
spotOffLight->setColor(graphics::Vec3(0.0)); spotOffLight->setColor(graphics::Vec3(0.0));
spotOffLight->setIntensity(0.0f); spotOffLight->setIntensity(0.0f);
spotOffLight->setType(graphics::Light::Type::SPOT); spotOffLight->setType(graphics::Light::Type::SPOT);
_spotOffLightId = addLight(spotOffLight); _spotOffLightId = addElement(spotOffLight);
const LightPointer sunOffLight { std::make_shared<graphics::Light>() }; const LightPointer sunOffLight { std::make_shared<graphics::Light>() };
sunOffLight->setAmbientIntensity(0.0f); sunOffLight->setAmbientIntensity(0.0f);
sunOffLight->setColor(graphics::Vec3(0.0)); sunOffLight->setColor(graphics::Vec3(0.0));
sunOffLight->setIntensity(0.0f); sunOffLight->setIntensity(0.0f);
sunOffLight->setType(graphics::Light::Type::SUN); sunOffLight->setType(graphics::Light::Type::SUN);
_sunOffLightId = addLight(sunOffLight); _sunOffLightId = addElement(sunOffLight);
// Set default light to the off ambient light (until changed) // Set default light to the off ambient light (until changed)
_defaultLightId = _ambientOffLightId; _defaultLightId = _ambientOffLightId;
@ -72,7 +73,7 @@ LightStage::Shadow::Schema::Schema() {
maxDistance = 20.0f; maxDistance = 20.0f;
} }
LightStage::Shadow::Cascade::Cascade() : LightStage::Shadow::Cascade::Cascade() :
_frustum{ std::make_shared<ViewFrustum>() }, _frustum{ std::make_shared<ViewFrustum>() },
_minDistance{ 0.0f }, _minDistance{ 0.0f },
_maxDistance{ 20.0f } { _maxDistance{ 20.0f } {
@ -88,7 +89,7 @@ const glm::mat4& LightStage::Shadow::Cascade::getProjection() const {
float LightStage::Shadow::Cascade::computeFarDistance(const ViewFrustum& viewFrustum, const Transform& shadowViewInverse, float LightStage::Shadow::Cascade::computeFarDistance(const ViewFrustum& viewFrustum, const Transform& shadowViewInverse,
float left, float right, float bottom, float top, float viewMaxShadowDistance) const { float left, float right, float bottom, float top, float viewMaxShadowDistance) const {
// Far distance should be extended to the intersection of the infinitely extruded shadow frustum // Far distance should be extended to the intersection of the infinitely extruded shadow frustum
// with the view frustum side planes. To do so, we generate 10 triangles in shadow space which are the result of // with the view frustum side planes. To do so, we generate 10 triangles in shadow space which are the result of
// tesselating the side and far faces of the view frustum and clip them with the 4 side planes of the // tesselating the side and far faces of the view frustum and clip them with the 4 side planes of the
// shadow frustum. The resulting clipped triangle vertices with the farthest Z gives the desired // shadow frustum. The resulting clipped triangle vertices with the farthest Z gives the desired
@ -121,7 +122,7 @@ float LightStage::Shadow::Cascade::computeFarDistance(const ViewFrustum& viewFru
return far; return far;
} }
LightStage::Shadow::Shadow(graphics::LightPointer light, unsigned int cascadeCount) : LightStage::Shadow::Shadow(graphics::LightPointer light, unsigned int cascadeCount) :
_light{ light } { _light{ light } {
cascadeCount = std::min(cascadeCount, (unsigned int)SHADOW_CASCADE_MAX_COUNT); cascadeCount = std::min(cascadeCount, (unsigned int)SHADOW_CASCADE_MAX_COUNT);
Schema schema; Schema schema;
@ -323,21 +324,12 @@ void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const View
schemaCascade.reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix(); schemaCascade.reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix();
} }
LightStage::Index LightStage::findLight(const LightPointer& light) const { LightStage::Index LightStage::addElement(const LightPointer& light, const bool shouldSetAsDefault) {
auto found = _lightMap.find(light);
if (found != _lightMap.end()) {
return INVALID_INDEX;
} else {
return (*found).second;
}
}
LightStage::Index LightStage::addLight(const LightPointer& light, const bool shouldSetAsDefault) {
Index lightId; Index lightId;
auto found = _lightMap.find(light); auto found = _elementMap.find(light);
if (found == _lightMap.end()) { if (found == _elementMap.end()) {
lightId = _lights.newElement(light); lightId = _elements.newElement(light);
// Avoid failing to allocate a light, just pass // Avoid failing to allocate a light, just pass
if (lightId != INVALID_INDEX) { if (lightId != INVALID_INDEX) {
@ -349,8 +341,8 @@ LightStage::Index LightStage::addLight(const LightPointer& light, const bool sho
_descs[lightId] = Desc(); _descs[lightId] = Desc();
} }
// INsert the light and its index in the reverese map // Insert the light and its index in the reverese map
_lightMap.insert(LightMap::value_type(light, lightId)); _elementMap[light] = lightId;
updateLightArrayBuffer(lightId); updateLightArrayBuffer(lightId);
} }
@ -365,30 +357,30 @@ LightStage::Index LightStage::addLight(const LightPointer& light, const bool sho
return lightId; return lightId;
} }
LightStage::LightPointer LightStage::removeLight(Index index) { LightStage::LightPointer LightStage::removeElement(Index index) {
LightPointer removedLight = _lights.freeElement(index); LightPointer removedLight = _elements.freeElement(index);
if (removedLight) { if (removedLight) {
_lightMap.erase(removedLight); _elementMap.erase(removedLight);
_descs[index] = Desc(); _descs[index] = Desc();
} }
assert(_descs.size() <= (size_t)index || _descs[index].shadowId == INVALID_INDEX); assert(_descs.size() <= (size_t)index || _descs[index].shadowId == INVALID_INDEX);
return removedLight; return removedLight;
} }
LightStage::LightPointer LightStage::getCurrentKeyLight(const LightStage::Frame& frame) const { LightStage::LightPointer LightStage::getCurrentKeyLight(const LightFrame& frame) const {
Index keyLightId { _defaultLightId }; Index keyLightId { _defaultLightId };
if (!frame._sunLights.empty()) { if (!frame._sunLights.empty()) {
keyLightId = frame._sunLights.front(); keyLightId = frame._sunLights.front();
} }
return _lights.get(keyLightId); return _elements.get(keyLightId);
} }
LightStage::LightPointer LightStage::getCurrentAmbientLight(const LightStage::Frame& frame) const { LightStage::LightPointer LightStage::getCurrentAmbientLight(const LightFrame& frame) const {
Index keyLightId { _defaultLightId }; Index keyLightId { _defaultLightId };
if (!frame._ambientLights.empty()) { if (!frame._ambientLights.empty()) {
keyLightId = frame._ambientLights.front(); keyLightId = frame._ambientLights.front();
} }
return _lights.get(keyLightId); return _elements.get(keyLightId);
} }
void LightStage::updateLightArrayBuffer(Index lightId) { void LightStage::updateLightArrayBuffer(Index lightId) {
@ -397,14 +389,12 @@ void LightStage::updateLightArrayBuffer(Index lightId) {
_lightArrayBuffer = std::make_shared<gpu::Buffer>(lightSize); _lightArrayBuffer = std::make_shared<gpu::Buffer>(lightSize);
} }
assert(checkLightId(lightId));
if (lightId > (Index)_lightArrayBuffer->getNumTypedElements<graphics::Light::LightSchema>()) { if (lightId > (Index)_lightArrayBuffer->getNumTypedElements<graphics::Light::LightSchema>()) {
_lightArrayBuffer->resize(lightSize * (lightId + 10)); _lightArrayBuffer->resize(lightSize * (lightId + 10));
} }
// lightArray is big enough so we can remap // lightArray is big enough so we can remap
auto light = _lights._elements[lightId]; auto light = _elements._elements[lightId];
if (light) { if (light) {
const auto& lightSchema = light->getLightSchemaBuffer().get(); const auto& lightSchema = light->getLightSchemaBuffer().get();
_lightArrayBuffer->setSubData<graphics::Light::LightSchema>(lightId, lightSchema); _lightArrayBuffer->setSubData<graphics::Light::LightSchema>(lightId, lightSchema);
@ -412,17 +402,3 @@ void LightStage::updateLightArrayBuffer(Index lightId) {
// this should not happen ? // this should not happen ?
} }
} }
LightStageSetup::LightStageSetup() {
}
void LightStageSetup::run(const render::RenderContextPointer& renderContext) {
if (renderContext->_scene) {
auto stage = renderContext->_scene->getStage(LightStage::getName());
if (!stage) {
stage = std::make_shared<LightStage>();
renderContext->_scene->resetStage(LightStage::getName(), stage);
}
}
}

View file

@ -4,6 +4,7 @@
// //
// Created by Zach Pomerantz on 1/14/2015. // Created by Zach Pomerantz on 1/14/2015.
// Copyright 2015 High Fidelity, Inc. // Copyright 2015 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -12,34 +13,44 @@
#ifndef hifi_render_utils_LightStage_h #ifndef hifi_render_utils_LightStage_h
#define hifi_render_utils_LightStage_h #define hifi_render_utils_LightStage_h
#include <set>
#include <unordered_map>
#include <gpu/Framebuffer.h> #include <gpu/Framebuffer.h>
#include <graphics/Light.h> #include <graphics/Light.h>
#include <render/IndexedContainer.h>
#include <render/Stage.h> #include <render/Stage.h>
#include <render/Engine.h> #include <render/StageSetup.h>
class ViewFrustum; class ViewFrustum;
// Light stage to set up light-related rendering tasks class LightFrame {
class LightStage : public render::Stage {
public: public:
static std::string _stageName; LightFrame() {}
static const std::string& getName() { return _stageName; }
using Index = render::indexed_container::Index; using Index = render::indexed_container::Index;
static const Index INVALID_INDEX;
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
using LightPointer = graphics::LightPointer;
using Lights = render::indexed_container::IndexedPointerVector<graphics::Light>;
using LightMap = std::unordered_map<LightPointer, Index>;
using LightIndices = std::vector<Index>; void clear() { _pointLights.clear(); _spotLights.clear(); _sunLights.clear(); _ambientLights.clear(); }
void pushLight(Index index, graphics::Light::Type type) {
switch (type) {
case graphics::Light::POINT: { pushPointLight(index); break; }
case graphics::Light::SPOT: { pushSpotLight(index); break; }
case graphics::Light::SUN: { pushSunLight(index); break; }
case graphics::Light::AMBIENT: { pushAmbientLight(index); break; }
default: { break; }
}
}
void pushPointLight(Index index) { _pointLights.emplace_back(index); }
void pushSpotLight(Index index) { _spotLights.emplace_back(index); }
void pushSunLight(Index index) { _sunLights.emplace_back(index); }
void pushAmbientLight(Index index) { _ambientLights.emplace_back(index); }
render::ElementIndices _pointLights;
render::ElementIndices _spotLights;
render::ElementIndices _sunLights;
render::ElementIndices _ambientLights;
};
// Light stage to set up light-related rendering tasks
class LightStage : public render::PointerStage<graphics::Light, graphics::LightPointer, LightFrame> {
public:
using LightPointer = graphics::LightPointer;
class Shadow { class Shadow {
public: public:
@ -74,9 +85,9 @@ public:
float left, float right, float bottom, float top, float viewMaxShadowDistance) const; float left, float right, float bottom, float top, float viewMaxShadowDistance) const;
}; };
Shadow(graphics::LightPointer light, unsigned int cascadeCount = 1); Shadow(LightPointer light, unsigned int cascadeCount = 1);
void setLight(graphics::LightPointer light); void setLight(LightPointer light);
void setKeylightFrustum(const ViewFrustum& viewFrustum, void setKeylightFrustum(const ViewFrustum& viewFrustum,
float nearDepth = 1.0f, float farDepth = 1000.0f); float nearDepth = 1.0f, float farDepth = 1000.0f);
@ -93,83 +104,46 @@ public:
float getMaxDistance() const { return _maxDistance; } float getMaxDistance() const { return _maxDistance; }
void setMaxDistance(float value); void setMaxDistance(float value);
const graphics::LightPointer& getLight() const { return _light; } const LightPointer& getLight() const { return _light; }
gpu::TexturePointer map; gpu::TexturePointer map;
#include "Shadows_shared.slh" #include "Shadows_shared.slh"
class Schema : public ShadowParameters { class Schema : public ShadowParameters {
public: public:
Schema(); Schema();
}; };
protected: protected:
using Cascades = std::vector<Cascade>; using Cascades = std::vector<Cascade>;
static const glm::mat4 _biasMatrix; static const glm::mat4 _biasMatrix;
graphics::LightPointer _light; LightPointer _light;
float _maxDistance{ 0.0f }; float _maxDistance{ 0.0f };
Cascades _cascades; Cascades _cascades;
UniformBufferView _schemaBuffer = nullptr; UniformBufferView _schemaBuffer = nullptr;
}; };
using ShadowPointer = std::shared_ptr<Shadow>; using ShadowPointer = std::shared_ptr<Shadow>;
Index findLight(const LightPointer& light) const; Index addElement(const LightPointer& light) override { return addElement(light, false); }
Index addLight(const LightPointer& light, const bool shouldSetAsDefault = false); Index addElement(const LightPointer& light, const bool shouldSetAsDefault);
LightPointer removeElement(Index index) override;
Index getDefaultLight() { return _defaultLightId; } Index getDefaultLight() { return _defaultLightId; }
LightPointer removeLight(Index index);
bool checkLightId(Index index) const { return _lights.checkIndex(index); }
Index getNumLights() const { return _lights.getNumElements(); }
Index getNumFreeLights() const { return _lights.getNumFreeIndices(); }
Index getNumAllocatedLights() const { return _lights.getNumAllocatedIndices(); }
LightPointer getLight(Index lightId) const { return _lights.get(lightId); }
LightStage(); LightStage();
gpu::BufferPointer getLightArrayBuffer() const { return _lightArrayBuffer; } gpu::BufferPointer getLightArrayBuffer() const { return _lightArrayBuffer; }
void updateLightArrayBuffer(Index lightId); void updateLightArrayBuffer(Index lightId);
class Frame {
public:
Frame() {}
void clear() { _pointLights.clear(); _spotLights.clear(); _sunLights.clear(); _ambientLights.clear(); }
void pushLight(LightStage::Index index, graphics::Light::Type type) {
switch (type) {
case graphics::Light::POINT: { pushPointLight(index); break; }
case graphics::Light::SPOT: { pushSpotLight(index); break; }
case graphics::Light::SUN: { pushSunLight(index); break; }
case graphics::Light::AMBIENT: { pushAmbientLight(index); break; }
default: { break; }
}
}
void pushPointLight(LightStage::Index index) { _pointLights.emplace_back(index); }
void pushSpotLight(LightStage::Index index) { _spotLights.emplace_back(index); }
void pushSunLight(LightStage::Index index) { _sunLights.emplace_back(index); }
void pushAmbientLight(LightStage::Index index) { _ambientLights.emplace_back(index); }
LightStage::LightIndices _pointLights;
LightStage::LightIndices _spotLights;
LightStage::LightIndices _sunLights;
LightStage::LightIndices _ambientLights;
};
using FramePointer = std::shared_ptr<Frame>;
class ShadowFrame { class ShadowFrame {
public: public:
ShadowFrame() {} ShadowFrame() {}
void clear() {} void clear() {}
using Object = ShadowPointer; using Object = ShadowPointer;
using Objects = std::vector<Object>; using Objects = std::vector<Object>;
@ -177,20 +151,17 @@ public:
_objects.emplace_back(shadow); _objects.emplace_back(shadow);
} }
Objects _objects; Objects _objects;
}; };
using ShadowFramePointer = std::shared_ptr<ShadowFrame>; using ShadowFramePointer = std::shared_ptr<ShadowFrame>;
Frame _currentFrame;
Index getAmbientOffLight() { return _ambientOffLightId; } Index getAmbientOffLight() { return _ambientOffLightId; }
Index getPointOffLight() { return _pointOffLightId; } Index getPointOffLight() { return _pointOffLightId; }
Index getSpotOffLight() { return _spotOffLightId; } Index getSpotOffLight() { return _spotOffLightId; }
Index getSunOffLight() { return _sunOffLightId; } Index getSunOffLight() { return _sunOffLightId; }
LightPointer getCurrentKeyLight(const LightStage::Frame& frame) const; LightPointer getCurrentKeyLight(const LightFrame& frame) const;
LightPointer getCurrentAmbientLight(const LightStage::Frame& frame) const; LightPointer getCurrentAmbientLight(const LightFrame& frame) const;
protected: protected:
@ -201,9 +172,7 @@ protected:
gpu::BufferPointer _lightArrayBuffer; gpu::BufferPointer _lightArrayBuffer;
Lights _lights;
Descs _descs; Descs _descs;
LightMap _lightMap;
// define off lights // define off lights
Index _ambientOffLightId; Index _ambientOffLightId;
@ -216,16 +185,9 @@ protected:
}; };
using LightStagePointer = std::shared_ptr<LightStage>; using LightStagePointer = std::shared_ptr<LightStage>;
class LightStageSetup : public render::StageSetup<LightStage> {
class LightStageSetup {
public: public:
using JobModel = render::Job::Model<LightStageSetup>; using JobModel = render::Job::Model<LightStageSetup>;
LightStageSetup();
void run(const render::RenderContextPointer& renderContext);
protected:
}; };
#endif #endif

View file

@ -1,6 +1,9 @@
// //
// RenderCommonTask.cpp
//
// Created by Bradley Austin Davis on 2018/01/09 // Created by Bradley Austin Davis on 2018/01/09
// Copyright 2013-2018 High Fidelity, Inc. // Copyright 2013-2018 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -78,9 +81,9 @@ void DrawLayered3D::run(const RenderContextPointer& renderContext, const Inputs&
graphics::HazePointer haze; graphics::HazePointer haze;
const auto& hazeStage = renderContext->args->_scene->getStage<HazeStage>(); const auto& hazeStage = renderContext->args->_scene->getStage<HazeStage>();
if (hazeStage && hazeFrame->_hazes.size() > 0) { if (hazeStage && hazeFrame->_elements.size() > 0) {
// We use _hazes.back() here because the last haze object will always have haze disabled. // We use _hazes.back() here because the last haze object will always have haze disabled.
haze = hazeStage->getHaze(hazeFrame->_hazes.back()); haze = hazeStage->getElement(hazeFrame->_elements.back());
} }
// Clear the framebuffer without stereo // Clear the framebuffer without stereo

View file

@ -1,6 +1,9 @@
// //
// RenderCommonTask.h
//
// Created by Bradley Austin Davis on 2018/01/09 // Created by Bradley Austin Davis on 2018/01/09
// Copyright 2013-2018 High Fidelity, Inc. // Copyright 2013-2018 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html

View file

@ -5,6 +5,7 @@
// //
// Created by Sam Gateau on 5/29/15. // Created by Sam Gateau on 5/29/15.
// Copyright 2016 High Fidelity, Inc. // Copyright 2016 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -145,10 +146,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
// Shadow Stage Frame // Shadow Stage Frame
const auto shadowFrame = shadowTaskOutputs[1]; const auto shadowFrame = shadowTaskOutputs[1];
if (depth == 0) {
task.addJob<FadeEffect>("FadeEffect", opaques);
}
const auto jitter = task.addJob<JitterSample>("JitterCam"); const auto jitter = task.addJob<JitterSample>("JitterCam");
// GPU jobs: Start preparing the primary, deferred and lighting buffer // GPU jobs: Start preparing the primary, deferred and lighting buffer
@ -515,8 +512,8 @@ void RenderTransparentDeferred::run(const RenderContextPointer& renderContext, c
// Setup haze if current zone has haze // Setup haze if current zone has haze
const auto& hazeStage = args->_scene->getStage<HazeStage>(); const auto& hazeStage = args->_scene->getStage<HazeStage>();
if (hazeStage && hazeFrame->_hazes.size() > 0) { if (hazeStage && hazeFrame->_elements.size() > 0) {
const auto& hazePointer = hazeStage->getHaze(hazeFrame->_hazes.front()); const auto& hazePointer = hazeStage->getElement(hazeFrame->_elements.front());
if (hazePointer) { if (hazePointer) {
batch.setUniformBuffer(graphics::slot::buffer::Buffer::HazeParams, hazePointer->getHazeParametersBuffer()); batch.setUniformBuffer(graphics::slot::buffer::Buffer::HazeParams, hazePointer->getHazeParametersBuffer());
} }

View file

@ -5,6 +5,7 @@
// //
// Created by Zach Pomerantz on 12/13/2016. // Created by Zach Pomerantz on 12/13/2016.
// Copyright 2016 High Fidelity, Inc. // Copyright 2016 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -271,8 +272,8 @@ void DrawForward::run(const RenderContextPointer& renderContext, const Inputs& i
graphics::HazePointer haze; graphics::HazePointer haze;
const auto& hazeStage = renderContext->args->_scene->getStage<HazeStage>(); const auto& hazeStage = renderContext->args->_scene->getStage<HazeStage>();
if (hazeStage && hazeFrame->_hazes.size() > 0) { if (hazeStage && hazeFrame->_elements.size() > 0) {
haze = hazeStage->getHaze(hazeFrame->_hazes.front()); haze = hazeStage->getElement(hazeFrame->_elements.front());
} }
gpu::doInBatch("DrawForward::run", args->_context, [&](gpu::Batch& batch) { gpu::doInBatch("DrawForward::run", args->_context, [&](gpu::Batch& batch) {

View file

@ -10,6 +10,7 @@
// //
#include "RenderViewTask.h" #include "RenderViewTask.h"
#include "FadeEffect.h"
#include "RenderShadowTask.h" #include "RenderShadowTask.h"
#include "RenderCommonTask.h" #include "RenderCommonTask.h"
#include "RenderDeferredTask.h" #include "RenderDeferredTask.h"
@ -40,6 +41,13 @@ void DeferredForwardSwitchJob::build(JobModel& task, const render::Varying& inpu
void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask, size_t depth) { void RenderViewTask::build(JobModel& task, const render::Varying& input, render::Varying& output, render::CullFunctor cullFunctor, uint8_t tagBits, uint8_t tagMask, size_t depth) {
const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor, tagBits, tagMask); const auto items = task.addJob<RenderFetchCullSortTask>("FetchCullSort", cullFunctor, tagBits, tagMask);
if (depth == 0 && tagBits == render::ItemKey::TAG_BITS_0) {
// TODO: This doesn't actually do any rendering, it simply processes the fade transactions. Even though forward rendering
// doesn't support fading right now, we still need to do this once for both paths, otherwise we are left with orphaned objects
// after they fade out. In the future, we should refactor this to happen elsewhere.
task.addJob<FadeEffect>("FadeEffect");
}
// Issue the lighting model, aka the big global settings for the view // Issue the lighting model, aka the big global settings for the view
const auto lightingModel = task.addJob<MakeLightingModel>("LightingModel"); const auto lightingModel = task.addJob<MakeLightingModel>("LightingModel");

View file

@ -473,8 +473,8 @@ void DebugSubsurfaceScattering::run(const render::RenderContextPointer& renderCo
auto lightStage = renderContext->_scene->getStage<LightStage>(); auto lightStage = renderContext->_scene->getStage<LightStage>();
assert(lightStage); assert(lightStage);
// const auto light = DependencyManager::get<DeferredLightingEffect>()->getLightStage()->getLight(0); // const auto light = DependencyManager::get<DeferredLightingEffect>()->getLightStage()->getElement(0);
const auto light = lightStage->getLight(0); const auto light = lightStage->getElement(0);
if (!_debugParams) { if (!_debugParams) {
_debugParams = std::make_shared<gpu::Buffer>(sizeof(glm::vec4), nullptr); _debugParams = std::make_shared<gpu::Buffer>(sizeof(glm::vec4), nullptr);
_debugParams->setSubData(0, _debugCursorTexcoord); _debugParams->setSubData(0, _debugCursorTexcoord);

View file

@ -4,6 +4,7 @@
// //
// Created by Anna Brewer on 7/3/19. // Created by Anna Brewer on 7/3/19.
// Copyright 2019 High Fidelity, Inc. // Copyright 2019 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -74,8 +75,8 @@ void ToneMapAndResample::run(const RenderContextPointer& renderContext, const In
const auto& tonemappingStage = renderContext->_scene->getStage<TonemappingStage>(); const auto& tonemappingStage = renderContext->_scene->getStage<TonemappingStage>();
graphics::TonemappingPointer tonemapping; graphics::TonemappingPointer tonemapping;
if (tonemappingStage && tonemappingFrame->_tonemappings.size()) { if (tonemappingStage && tonemappingFrame->_elements.size()) {
tonemapping = tonemappingStage->getTonemapping(tonemappingFrame->_tonemappings.front()); tonemapping = tonemappingStage->getElement(tonemappingFrame->_elements.front());
} }
if (_debug) { if (_debug) {

View file

@ -7,50 +7,8 @@
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "TonemappingStage.h" #include "TonemappingStage.h"
#include <gpu/Context.h> template <>
std::string render::PointerStage<graphics::Tonemapping, graphics::TonemappingPointer>::_name { "TONEMAPPING_STAGE" };
std::string TonemappingStage::_stageName { "TONEMAPPING_STAGE" };
const TonemappingStage::Index TonemappingStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
TonemappingStage::Index TonemappingStage::findTonemapping(const TonemappingPointer& tonemapping) const {
auto found = _tonemappingMap.find(tonemapping);
if (found != _tonemappingMap.end()) {
return INVALID_INDEX;
} else {
return (*found).second;
}
}
TonemappingStage::Index TonemappingStage::addTonemapping(const TonemappingPointer& tonemapping) {
auto found = _tonemappingMap.find(tonemapping);
if (found == _tonemappingMap.end()) {
auto tonemappingId = _tonemappings.newElement(tonemapping);
// Avoid failing to allocate a tonemapping, just pass
if (tonemappingId != INVALID_INDEX) {
// Insert the tonemapping and its index in the reverse map
_tonemappingMap.insert(TonemappingMap::value_type(tonemapping, tonemappingId));
}
return tonemappingId;
} else {
return (*found).second;
}
}
TonemappingStage::TonemappingPointer TonemappingStage::removeTonemapping(Index index) {
TonemappingPointer removed = _tonemappings.freeElement(index);
if (removed) {
_tonemappingMap.erase(removed);
}
return removed;
}
TonemappingStageSetup::TonemappingStageSetup() {}
void TonemappingStageSetup::run(const render::RenderContextPointer& renderContext) {
auto stage = renderContext->_scene->getStage(TonemappingStage::getName());
if (!stage) {
renderContext->_scene->resetStage(TonemappingStage::getName(), std::make_shared<TonemappingStage>());
}
}

View file

@ -11,74 +11,17 @@
#ifndef hifi_render_utils_TonemappingStage_h #ifndef hifi_render_utils_TonemappingStage_h
#define hifi_render_utils_TonemappingStage_h #define hifi_render_utils_TonemappingStage_h
#include <graphics/Stage.h>
#include <set>
#include <unordered_map>
#include <render/IndexedContainer.h>
#include <render/Stage.h>
#include <render/Forward.h>
#include <render/DrawTask.h>
#include <graphics/Tonemapping.h> #include <graphics/Tonemapping.h>
#include <render/Stage.h>
#include <render/StageSetup.h>
// Tonemapping stage to set up tonemapping-related rendering tasks // Tonemapping stage to set up tonemapping-related rendering tasks
class TonemappingStage : public render::Stage { class TonemappingStage : public render::PointerStage<graphics::Tonemapping, graphics::TonemappingPointer> {};
public:
static std::string _stageName;
static const std::string& getName() { return _stageName; }
using Index = render::indexed_container::Index;
static const Index INVALID_INDEX;
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
using TonemappingPointer = graphics::TonemappingPointer;
using Tonemappings = render::indexed_container::IndexedPointerVector<graphics::Tonemapping>;
using TonemappingMap = std::unordered_map<TonemappingPointer, Index>;
using TonemappingIndices = std::vector<Index>;
Index findTonemapping(const TonemappingPointer& tonemapping) const;
Index addTonemapping(const TonemappingPointer& tonemapping);
TonemappingPointer removeTonemapping(Index index);
bool checkTonemappingId(Index index) const { return _tonemappings.checkIndex(index); }
Index getNumTonemappings() const { return _tonemappings.getNumElements(); }
Index getNumFreeTonemappings() const { return _tonemappings.getNumFreeIndices(); }
Index getNumAllocatedTonemappings() const { return _tonemappings.getNumAllocatedIndices(); }
TonemappingPointer getTonemapping(Index tonemappingId) const {
return _tonemappings.get(tonemappingId);
}
Tonemappings _tonemappings;
TonemappingMap _tonemappingMap;
class Frame {
public:
Frame() {}
void clear() { _tonemappings.clear(); }
void pushTonemapping(TonemappingStage::Index index) { _tonemappings.emplace_back(index); }
TonemappingStage::TonemappingIndices _tonemappings;
};
using FramePointer = std::shared_ptr<Frame>;
Frame _currentFrame;
};
using TonemappingStagePointer = std::shared_ptr<TonemappingStage>; using TonemappingStagePointer = std::shared_ptr<TonemappingStage>;
class TonemappingStageSetup { class TonemappingStageSetup : public render::StageSetup<TonemappingStage> {
public: public:
using JobModel = render::Job::Model<TonemappingStageSetup>; using JobModel = render::Job::Model<TonemappingStageSetup>;
TonemappingStageSetup();
void run(const render::RenderContextPointer& renderContext);
protected:
}; };
#endif #endif

View file

@ -4,6 +4,7 @@
// //
// Created by Sam Gateau on 4/4/2017. // Created by Sam Gateau on 4/4/2017.
// Copyright 2017 High Fidelity, Inc. // Copyright 2017 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -86,11 +87,11 @@ void SetupZones::run(const RenderContextPointer& context, const Input& input) {
// Finally add the default lights and background: // Finally add the default lights and background:
lightStage->_currentFrame.pushSunLight(lightStage->getDefaultLight()); lightStage->_currentFrame.pushSunLight(lightStage->getDefaultLight());
lightStage->_currentFrame.pushAmbientLight(lightStage->getDefaultLight()); lightStage->_currentFrame.pushAmbientLight(lightStage->getDefaultLight());
backgroundStage->_currentFrame.pushBackground(0); backgroundStage->_currentFrame.pushElement(0);
hazeStage->_currentFrame.pushHaze(0); hazeStage->_currentFrame.pushElement(0);
bloomStage->_currentFrame.pushBloom(INVALID_INDEX); bloomStage->_currentFrame.pushElement(INVALID_INDEX);
tonemappingStage->_currentFrame.pushTonemapping(0); tonemappingStage->_currentFrame.pushElement(0);
ambientOcclusionStage->_currentFrame.pushAmbientOcclusion(INVALID_INDEX); ambientOcclusionStage->_currentFrame.pushElement(INVALID_INDEX);
} }
gpu::PipelinePointer DebugZoneLighting::_keyLightPipeline; gpu::PipelinePointer DebugZoneLighting::_keyLightPipeline;
@ -144,22 +145,22 @@ void DebugZoneLighting::run(const render::RenderContextPointer& context, const I
std::vector<graphics::LightPointer> keyLightStack; std::vector<graphics::LightPointer> keyLightStack;
if (lightStage && lightFrame->_sunLights.size()) { if (lightStage && lightFrame->_sunLights.size()) {
for (auto index : lightFrame->_sunLights) { for (auto index : lightFrame->_sunLights) {
keyLightStack.push_back(lightStage->getLight(index)); keyLightStack.push_back(lightStage->getElement(index));
} }
} }
std::vector<graphics::LightPointer> ambientLightStack; std::vector<graphics::LightPointer> ambientLightStack;
if (lightStage && lightFrame->_ambientLights.size()) { if (lightStage && lightFrame->_ambientLights.size()) {
for (auto index : lightFrame->_ambientLights) { for (auto index : lightFrame->_ambientLights) {
ambientLightStack.push_back(lightStage->getLight(index)); ambientLightStack.push_back(lightStage->getElement(index));
} }
} }
auto backgroundStage = context->_scene->getStage<BackgroundStage>(BackgroundStage::getName()); auto backgroundStage = context->_scene->getStage<BackgroundStage>(BackgroundStage::getName());
std::vector<graphics::SkyboxPointer> skyboxStack; std::vector<graphics::SkyboxPointer> skyboxStack;
if (backgroundStage && backgroundFrame->_backgrounds.size()) { if (backgroundStage && backgroundFrame->_elements.size()) {
for (auto index : backgroundFrame->_backgrounds) { for (auto index : backgroundFrame->_elements) {
auto background = backgroundStage->getBackground(index); auto background = backgroundStage->getElement(index);
if (background) { if (background) {
skyboxStack.push_back(background->getSkybox()); skyboxStack.push_back(background->getSkybox());
} }

View file

@ -95,8 +95,8 @@ vec4 evalSDFColor(vec2 texCoord, vec4 glyphBounds) {
vec4 evalSDFSuperSampled(vec2 texCoord, vec2 positionMS, vec4 glyphBounds) { vec4 evalSDFSuperSampled(vec2 texCoord, vec2 positionMS, vec4 glyphBounds) {
// Clip to edges. Note: We don't need to check the top edge. // Clip to edges. Note: We don't need to check the top edge.
if (positionMS.x < params.bounds.x || positionMS.x > (params.bounds.x + params.bounds.z) || if ((params.bounds.z > 0.0 && (positionMS.x < params.bounds.x || positionMS.x > (params.bounds.x + params.bounds.z))) ||
positionMS.y < params.bounds.y - params.bounds.w) { (params.bounds.w > 0.0 && (positionMS.y < params.bounds.y - params.bounds.w))) {
return vec4(0.0); return vec4(0.0);
} }

View file

@ -438,7 +438,7 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
// Draw the token // Draw the token
for (const QChar& c : token) { for (const QChar& c : token) {
if (advance.x > rightEdge) { if (bounds.x != -1 && advance.x > rightEdge) {
break; break;
} }
const Glyph& glyph = _glyphs[c]; const Glyph& glyph = _glyphs[c];

View file

@ -71,8 +71,8 @@ public:
bool forward, bool mirror) : bool forward, bool mirror) :
str(str), color(color), effectColor(effectColor), origin(origin), bounds(bounds), scale(scale), effectThickness(effectThickness), str(str), color(color), effectColor(effectColor), origin(origin), bounds(bounds), scale(scale), effectThickness(effectThickness),
effect(effect), alignment(alignment), verticalAlignment(verticalAlignment), unlit(unlit), forward(forward), mirror(mirror) {} effect(effect), alignment(alignment), verticalAlignment(verticalAlignment), unlit(unlit), forward(forward), mirror(mirror) {}
DrawProps(const QString& str, const glm::vec4& color, const glm::vec2& origin, const glm::vec2& bounds, bool forward) : DrawProps(const QString& str, const glm::vec4& color, const glm::vec2& origin, const glm::vec2& bounds, TextAlignment alignment, bool forward) :
str(str), color(color), origin(origin), bounds(bounds), forward(forward) {} str(str), color(color), origin(origin), bounds(bounds), alignment(alignment), forward(forward) {}
const QString& str; const QString& str;
const glm::vec4& color; const glm::vec4& color;

View file

@ -4,6 +4,7 @@
// //
// Created by Niraj Venkat on 6/29/15. // Created by Niraj Venkat on 6/29/15.
// Copyright 2015 High Fidelity, Inc. // Copyright 2015 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -163,7 +164,7 @@ void DrawStatus::run(const RenderContextPointer& renderContext, const Input& inp
status.setColor(Item::Status::Value::RED); status.setColor(Item::Status::Value::RED);
} }
// Set icon based on transition type // Set icon based on transition type
auto& transition = transitionStage->getTransition(transitionID); auto& transition = transitionStage->getElement(transitionID);
switch (transition.eventType) { switch (transition.eventType) {
case Transition::Type::USER_ENTER_DOMAIN: case Transition::Type::USER_ENTER_DOMAIN:
status.setIcon((unsigned char)Item::Status::Icon::USER_TRANSITION_IN); status.setIcon((unsigned char)Item::Status::Icon::USER_TRANSITION_IN);

View file

@ -1,33 +1,31 @@
//
// HighlightStage.cpp
//
// Created by Olivier Prat on 07/07/2017.
// Copyright 2017 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "HighlightStage.h" #include "HighlightStage.h"
#include "Engine.h"
using namespace render; using namespace render;
std::string HighlightStage::_name("Highlight"); template <>
const HighlightStage::Index HighlightStage::INVALID_INDEX{ render::indexed_container::INVALID_INDEX }; std::string TypedStage<Highlight>::_name { "HIGHLIGHT_STAGE" };
HighlightStage::Index HighlightStage::addHighlight(const std::string& selectionName, const HighlightStyle& style) { HighlightStage::Index HighlightStage::addHighlight(const std::string& selectionName, const HighlightStyle& style) {
Highlight outline{ selectionName, style }; Highlight outline { selectionName, style };
Index id; return addElement(outline);
id = _highlights.newElement(outline);
_activeHighlightIds.push_back(id);
return id;
} }
void HighlightStage::removeHighlight(Index index) { HighlightStage::Index HighlightStage::getHighlightIdBySelection(const std::string& selectionName) const {
HighlightIdList::iterator idIterator = std::find(_activeHighlightIds.begin(), _activeHighlightIds.end(), index); for (auto outlineId : _activeElementIDs) {
if (idIterator != _activeHighlightIds.end()) { const auto& outline = _elements.get(outlineId);
_activeHighlightIds.erase(idIterator);
}
if (!_highlights.isElementFreed(index)) {
_highlights.freeElement(index);
}
}
Index HighlightStage::getHighlightIdBySelection(const std::string& selectionName) const {
for (auto outlineId : _activeHighlightIds) {
const auto& outline = _highlights.get(outlineId);
if (outline._selectionName == selectionName) { if (outline._selectionName == selectionName) {
return outlineId; return outlineId;
} }
@ -100,9 +98,6 @@ void HighlightStageConfig::setOccludedFillOpacity(float value) {
emit dirty(); emit dirty();
} }
HighlightStageSetup::HighlightStageSetup() {
}
void HighlightStageSetup::configure(const Config& config) { void HighlightStageSetup::configure(const Config& config) {
// Copy the styles here but update the stage with the new styles in run to be sure everything is // Copy the styles here but update the stage with the new styles in run to be sure everything is
// thread safe... // thread safe...
@ -112,8 +107,7 @@ void HighlightStageSetup::configure(const Config& config) {
void HighlightStageSetup::run(const render::RenderContextPointer& renderContext) { void HighlightStageSetup::run(const render::RenderContextPointer& renderContext) {
auto stage = renderContext->_scene->getStage<HighlightStage>(HighlightStage::getName()); auto stage = renderContext->_scene->getStage<HighlightStage>(HighlightStage::getName());
if (!stage) { if (!stage) {
stage = std::make_shared<HighlightStage>(); renderContext->_scene->resetStage(HighlightStage::getName(), std::make_shared<HighlightStage>());
renderContext->_scene->resetStage(HighlightStage::getName(), stage);
} }
if (!_styles.empty()) { if (!_styles.empty()) {
@ -127,4 +121,3 @@ void HighlightStageSetup::run(const render::RenderContextPointer& renderContext)
_styles.clear(); _styles.clear();
} }
} }

View file

@ -3,6 +3,7 @@
// Created by Olivier Prat on 07/07/2017. // Created by Olivier Prat on 07/07/2017.
// Copyright 2017 High Fidelity, Inc. // Copyright 2017 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -11,56 +12,27 @@
#ifndef hifi_render_utils_HighlightStage_h #ifndef hifi_render_utils_HighlightStage_h
#define hifi_render_utils_HighlightStage_h #define hifi_render_utils_HighlightStage_h
#include "Stage.h"
#include "Engine.h" #include "Engine.h"
#include "IndexedContainer.h"
#include "HighlightStyle.h" #include "HighlightStyle.h"
#include "Stage.h"
namespace render { namespace render {
// Highlight stage to set up HighlightStyle-related effects class Highlight {
class HighlightStage : public Stage {
public: public:
Highlight(const std::string& selectionName, const HighlightStyle& style) : _selectionName{ selectionName }, _style{ style } { }
class Highlight { std::string _selectionName;
public: HighlightStyle _style;
};
Highlight(const std::string& selectionName, const HighlightStyle& style) : _selectionName{ selectionName }, _style{ style } { }
std::string _selectionName;
HighlightStyle _style;
};
static const std::string& getName() { return _name; }
using Index = render::indexed_container::Index;
static const Index INVALID_INDEX;
using HighlightIdList = render::indexed_container::Indices;
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
bool checkHighlightId(Index index) const { return _highlights.checkIndex(index); }
const Highlight& getHighlight(Index index) const { return _highlights.get(index); }
Highlight& editHighlight(Index index) { return _highlights.edit(index); }
// Highlight stage to set up HighlightStyle-related effects
class HighlightStage : public TypedStage<Highlight> {
public:
Index addHighlight(const std::string& selectionName, const HighlightStyle& style = HighlightStyle()); Index addHighlight(const std::string& selectionName, const HighlightStyle& style = HighlightStyle());
Index getHighlightIdBySelection(const std::string& selectionName) const; Index getHighlightIdBySelection(const std::string& selectionName) const;
void removeHighlight(Index index);
HighlightIdList::iterator begin() { return _activeHighlightIds.begin(); } const IDList& getActiveHighlightIds() const { return _activeElementIDs; }
HighlightIdList::iterator end() { return _activeHighlightIds.end(); }
const HighlightIdList& getActiveHighlightIds() const { return _activeHighlightIds; }
private:
using Highlights = render::indexed_container::IndexedVector<Highlight>;
static std::string _name;
Highlights _highlights;
HighlightIdList _activeHighlightIds;
}; };
using HighlightStagePointer = std::shared_ptr<HighlightStage>; using HighlightStagePointer = std::shared_ptr<HighlightStage>;
@ -122,7 +94,7 @@ namespace render {
using Config = HighlightStageConfig; using Config = HighlightStageConfig;
using JobModel = render::Job::Model<HighlightStageSetup, Config>; using JobModel = render::Job::Model<HighlightStageSetup, Config>;
HighlightStageSetup(); HighlightStageSetup() {}
void configure(const Config& config); void configure(const Config& config);
void run(const RenderContextPointer& renderContext); void run(const RenderContextPointer& renderContext);

View file

@ -17,6 +17,8 @@
#include <string> #include <string>
#include <ViewFrustum.h>
namespace render { namespace render {
// This holds the configuration for a particular outline style // This holds the configuration for a particular outline style

View file

@ -440,7 +440,7 @@ void Scene::queryTransitionItems(const Transaction::TransitionQueries& transacti
auto transitionId = item.getTransitionId(); auto transitionId = item.getTransitionId();
if (!TransitionStage::isIndexInvalid(transitionId)) { if (!TransitionStage::isIndexInvalid(transitionId)) {
auto& transition = transitionStage->getTransition(transitionId); auto& transition = transitionStage->getElement(transitionId);
func(itemId, &transition); func(itemId, &transition);
} else { } else {
func(itemId, nullptr); func(itemId, nullptr);
@ -477,7 +477,7 @@ void Scene::resetHighlights(const Transaction::HighlightResets& transactions) {
if (HighlightStage::isIndexInvalid(outlineId)) { if (HighlightStage::isIndexInvalid(outlineId)) {
outlineStage->addHighlight(selectionName, newStyle); outlineStage->addHighlight(selectionName, newStyle);
} else { } else {
outlineStage->editHighlight(outlineId)._style = newStyle; outlineStage->editElement(outlineId)._style = newStyle;
} }
} }
} }
@ -490,7 +490,7 @@ void Scene::removeHighlights(const Transaction::HighlightRemoves& transactions)
auto outlineId = outlineStage->getHighlightIdBySelection(selectionName); auto outlineId = outlineStage->getHighlightIdBySelection(selectionName);
if (!HighlightStage::isIndexInvalid(outlineId)) { if (!HighlightStage::isIndexInvalid(outlineId)) {
outlineStage->removeHighlight(outlineId); outlineStage->removeElement(outlineId);
} }
} }
} }
@ -505,7 +505,7 @@ void Scene::queryHighlights(const Transaction::HighlightQueries& transactions) {
auto outlineId = outlineStage->getHighlightIdBySelection(selectionName); auto outlineId = outlineStage->getHighlightIdBySelection(selectionName);
if (!HighlightStage::isIndexInvalid(outlineId)) { if (!HighlightStage::isIndexInvalid(outlineId)) {
func(&outlineStage->editHighlight(outlineId)._style); func(&outlineStage->editElement(outlineId)._style);
} else { } else {
func(nullptr); func(nullptr);
} }
@ -559,7 +559,7 @@ void Scene::removeItemTransition(ItemID itemId) {
auto& item = _items[itemId]; auto& item = _items[itemId];
TransitionStage::Index transitionId = item.getTransitionId(); TransitionStage::Index transitionId = item.getTransitionId();
if (!render::TransitionStage::isIndexInvalid(transitionId)) { if (!render::TransitionStage::isIndexInvalid(transitionId)) {
const auto& transition = transitionStage->getTransition(transitionId); const auto& transition = transitionStage->getElement(transitionId);
const auto transitionOwner = transition.itemId; const auto transitionOwner = transition.itemId;
if (transitionOwner == itemId) { if (transitionOwner == itemId) {
// No more items will be using this transition. Clean it up. // No more items will be using this transition. Clean it up.
@ -570,7 +570,7 @@ void Scene::removeItemTransition(ItemID itemId) {
} }
} }
_transitionFinishedOperatorMap.erase(transitionId); _transitionFinishedOperatorMap.erase(transitionId);
transitionStage->removeTransition(transitionId); transitionStage->removeElement(transitionId);
} }
setItemTransition(itemId, render::TransitionStage::INVALID_INDEX); setItemTransition(itemId, render::TransitionStage::INVALID_INDEX);

View file

@ -4,23 +4,13 @@
// //
// Created by Sam Gateau on 6/14/2017. // Created by Sam Gateau on 6/14/2017.
// Copyright 2017 High Fidelity, Inc. // Copyright 2017 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include "Stage.h" #include "Stage.h"
using namespace render; using namespace render;
const Stage::Index Stage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
Stage::~Stage() {
}
Stage::Stage() :
_name()
{
}

View file

@ -4,6 +4,7 @@
// //
// Created by Sam Gateau on 6/14/2017. // Created by Sam Gateau on 6/14/2017.
// Copyright 2017 High Fidelity, Inc. // Copyright 2017 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -12,27 +13,141 @@
#ifndef hifi_render_Stage_h #ifndef hifi_render_Stage_h
#define hifi_render_Stage_h #define hifi_render_Stage_h
#include <memory>
#include <map> #include <map>
#include <memory>
#include <unordered_map>
#include <string> #include <string>
#include "IndexedContainer.h"
namespace render { namespace render {
using ElementIndices = std::vector<indexed_container::Index>;
class Stage { class Stage {
public: public:
Stage() {}
virtual ~Stage() {}
using Name = std::string; using Name = std::string;
using Index = indexed_container::Index;
static const Index INVALID_INDEX;
using IDList = indexed_container::Indices;
Stage(); static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
virtual ~Stage();
protected:
Name _name;
}; };
using StagePointer = std::shared_ptr<Stage>; using StagePointer = std::shared_ptr<Stage>;
using StageMap = std::map<const Stage::Name, StagePointer>; using StageMap = std::map<const Stage::Name, StagePointer>;
template<typename T>
class TypedStage : public Stage {
public:
TypedStage() {}
virtual ~TypedStage() {}
static const Name& getName() { return _name; }
bool checkId(Index index) const { return _elements.checkIndex(index); }
const T& getElement(Index id) const { return _elements.get(id); }
T& editElement(Index id) { return _elements.edit(id); }
Index addElement(const T& element) {
Index id = _elements.newElement(element);
_activeElementIDs.push_back(id);
return id;
}
void removeElement(Index index) {
IDList::iterator idIterator = std::find(_activeElementIDs.begin(), _activeElementIDs.end(), index);
if (idIterator != _activeElementIDs.end()) {
_activeElementIDs.erase(idIterator);
}
if (!_elements.isElementFreed(index)) {
_elements.freeElement(index);
}
}
IDList::iterator begin() { return _activeElementIDs.begin(); }
IDList::iterator end() { return _activeElementIDs.end(); }
protected:
static Name _name;
indexed_container::IndexedVector<T> _elements;
IDList _activeElementIDs;
};
class Frame {
public:
Frame() {}
using Index = indexed_container::Index;
void clear() { _elements.clear(); }
void pushElement(Index index) { _elements.emplace_back(index); }
ElementIndices _elements;
};
template<typename T, typename P, typename F = Frame>
class PointerStage : public Stage {
public:
PointerStage() {}
virtual ~PointerStage() {}
static const Name& getName() { return _name; }
bool checkId(Index index) const { return _elements.checkIndex(index); }
Index getNumElements() const { return _elements.getNumElements(); }
Index getNumFreeElements() const { return _elements.getNumFreeIndices(); }
Index getNumAllocatedElements() const { return _elements.getNumAllocatedIndices(); }
P getElement(Index id) const { return _elements.get(id); }
Index findElement(const P& element) const {
auto found = _elementMap.find(element);
if (found != _elementMap.end()) {
return INVALID_INDEX;
} else {
return (*found).second;
}
}
virtual Index addElement(const P& element) {
auto found = _elementMap.find(element);
if (found == _elementMap.end()) {
auto id = _elements.newElement(element);
// Avoid failing to allocate an element, just pass
if (id != INVALID_INDEX) {
// Insert the element and its index in the reverse map
_elementMap[element] = id;
}
return id;
} else {
return (*found).second;
}
}
virtual P removeElement(Index index) {
P removed = _elements.freeElement(index);
if (removed) {
_elementMap.erase(removed);
}
return removed;
}
using Frame = F;
using FramePointer = std::shared_ptr<F>;
F _currentFrame;
protected:
static Name _name;
indexed_container::IndexedPointerVector<T> _elements;
std::unordered_map<P, Index> _elementMap;
};
} }
#endif // hifi_render_Stage_h #endif // hifi_render_Stage_h

View file

@ -0,0 +1,35 @@
//
// StageSetup.h
// render/src/render
//
// Created by HifiExperiments on 10/16/24
// Copyright 2024 Overte e.V.
//
// 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_render_StageSetup_h
#define hifi_render_StageSetup_h
#include "Engine.h"
namespace render {
template <typename T>
class StageSetup {
public:
StageSetup() {}
void run(const RenderContextPointer& renderContext) {
if (renderContext->_scene) {
auto stage = renderContext->_scene->getStage(T::getName());
if (!stage) {
renderContext->_scene->resetStage(T::getName(), std::make_shared<T>());
}
}
}
};
}
#endif // hifi_render_StageSetup_h

View file

@ -1,43 +1,25 @@
#include "TransitionStage.h" //
// TransitionStage.cpp
//
// Created by Olivier Prat on 07/07/2017.
// Copyright 2017 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <algorithm> #include "TransitionStage.h"
using namespace render; using namespace render;
std::string TransitionStage::_name("Transition"); template <>
const TransitionStage::Index TransitionStage::INVALID_INDEX{ indexed_container::INVALID_INDEX }; std::string TypedStage<Transition>::_name { "TRANSITION_STAGE" };
TransitionStage::Index TransitionStage::addTransition(ItemID itemId, Transition::Type type, ItemID boundId) { TransitionStage::Index TransitionStage::addTransition(ItemID itemId, Transition::Type type, ItemID boundId) {
Transition transition; Transition transition;
Index id;
transition.eventType = type; transition.eventType = type;
transition.itemId = itemId; transition.itemId = itemId;
transition.boundItemId = boundId; transition.boundItemId = boundId;
id = _transitions.newElement(transition); return addElement(transition);
_activeTransitionIds.push_back(id);
return id;
} }
void TransitionStage::removeTransition(Index index) {
TransitionIdList::iterator idIterator = std::find(_activeTransitionIds.begin(), _activeTransitionIds.end(), index);
if (idIterator != _activeTransitionIds.end()) {
_activeTransitionIds.erase(idIterator);
}
if (!_transitions.isElementFreed(index)) {
_transitions.freeElement(index);
}
}
TransitionStageSetup::TransitionStageSetup() {
}
void TransitionStageSetup::run(const RenderContextPointer& renderContext) {
auto stage = renderContext->_scene->getStage(TransitionStage::getName());
if (!stage) {
stage = std::make_shared<TransitionStage>();
renderContext->_scene->resetStage(TransitionStage::getName(), stage);
}
}

View file

@ -1,8 +1,9 @@
// //
// TransitionStage.h // TransitionStage.h
//
// Created by Olivier Prat on 07/07/2017. // Created by Olivier Prat on 07/07/2017.
// Copyright 2017 High Fidelity, Inc. // Copyright 2017 High Fidelity, Inc.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@ -12,55 +13,22 @@
#define hifi_render_TransitionStage_h #define hifi_render_TransitionStage_h
#include "Stage.h" #include "Stage.h"
#include "IndexedContainer.h" #include "StageSetup.h"
#include "Engine.h"
#include "Transition.h" #include "Transition.h"
namespace render { namespace render {
// Transition stage to set up Transition-related effects // Transition stage to set up Transition-related effects
class TransitionStage : public render::Stage { class TransitionStage : public TypedStage<Transition> {
public: public:
bool isTransitionUsed(Index index) const { return _elements.checkIndex(index) && !_elements.isElementFreed(index); }
static const std::string& getName() { return _name; }
using Index = indexed_container::Index;
static const Index INVALID_INDEX;
using TransitionIdList = indexed_container::Indices;
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
bool isTransitionUsed(Index index) const { return _transitions.checkIndex(index) && !_transitions.isElementFreed(index); }
const Transition& getTransition(Index TransitionId) const { return _transitions.get(TransitionId); }
Transition& editTransition(Index TransitionId) { return _transitions.edit(TransitionId); }
Index addTransition(ItemID itemId, Transition::Type type, ItemID boundId); Index addTransition(ItemID itemId, Transition::Type type, ItemID boundId);
void removeTransition(Index index);
TransitionIdList::iterator begin() { return _activeTransitionIds.begin(); }
TransitionIdList::iterator end() { return _activeTransitionIds.end(); }
private:
using Transitions = indexed_container::IndexedVector<Transition>;
static std::string _name;
Transitions _transitions;
TransitionIdList _activeTransitionIds;
}; };
using TransitionStagePointer = std::shared_ptr<TransitionStage>; using TransitionStagePointer = std::shared_ptr<TransitionStage>;
class TransitionStageSetup { class TransitionStageSetup : public StageSetup<TransitionStage> {
public: public:
using JobModel = render::Job::Model<TransitionStageSetup>; using JobModel = Job::Model<TransitionStageSetup>;
TransitionStageSetup();
void run(const RenderContextPointer& renderContext);
protected:
}; };
} }

View file

@ -381,6 +381,7 @@ ScriptManager::~ScriptManager() {
if (_type == ScriptManager::Type::ENTITY_CLIENT) { if (_type == ScriptManager::Type::ENTITY_CLIENT) {
printf("ScriptManager::~ScriptManager"); printf("ScriptManager::~ScriptManager");
} }
_isDeleted = true;
} }
void ScriptManager::disconnectNonEssentialSignals() { void ScriptManager::disconnectNonEssentialSignals() {
@ -426,7 +427,10 @@ void ScriptManager::runInThread() {
void ScriptManager::executeOnScriptThread(std::function<void()> function, const Qt::ConnectionType& type ) { void ScriptManager::executeOnScriptThread(std::function<void()> function, const Qt::ConnectionType& type ) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "executeOnScriptThread", type, Q_ARG(std::function<void()>, function)); // Lambda is necessary there to keep shared_ptr counter above zero
QMetaObject::invokeMethod(this, [=, manager = shared_from_this()]{
manager->executeOnScriptThread(function, type);
});
return; return;
} }
@ -858,10 +862,10 @@ void ScriptManager::removeEventHandler(const EntityItemID& entityID, const QStri
qCDebug(scriptengine) << "*** WARNING *** ScriptManager::removeEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " qCDebug(scriptengine) << "*** WARNING *** ScriptManager::removeEventHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
"entityID:" << entityID << " eventName:" << eventName; "entityID:" << entityID << " eventName:" << eventName;
#endif #endif
QMetaObject::invokeMethod(this, "removeEventHandler", // Lambda is necessary there to keep shared_ptr counter above zero
Q_ARG(const EntityItemID&, entityID), QMetaObject::invokeMethod(this, [=, manager = shared_from_this()]{
Q_ARG(const QString&, eventName), manager->removeEventHandler(entityID, eventName, handler);
Q_ARG(const ScriptValue&, handler)); });
return; return;
} }
#ifdef THREAD_DEBUGGING #ifdef THREAD_DEBUGGING
@ -905,10 +909,10 @@ void ScriptManager::addEventHandler(const EntityItemID& entityID, const QString&
"entityID:" << entityID << " eventName:" << eventName; "entityID:" << entityID << " eventName:" << eventName;
#endif #endif
QMetaObject::invokeMethod(this, "addEventHandler", // Lambda is necessary there to keep shared_ptr counter above zero
Q_ARG(const EntityItemID&, entityID), QMetaObject::invokeMethod(this, [=, manager = shared_from_this()]{
Q_ARG(const QString&, eventName), manager->addEventHandler(entityID, eventName, handler);
Q_ARG(const ScriptValue&, handler)); });
return; return;
} }
#ifdef THREAD_DEBUGGING #ifdef THREAD_DEBUGGING
@ -1167,7 +1171,10 @@ void ScriptManager::stop(bool marshal) {
_isStopping = true; // this can be done on any thread _isStopping = true; // this can be done on any thread
if (marshal) { if (marshal) {
QMetaObject::invokeMethod(this, "stop"); // Lambda is necessary there to keep shared_ptr counter above zero if this gets called from different thread
QMetaObject::invokeMethod(this, [=, manager = shared_from_this()]{
manager->stop(false);
});
return; return;
} }
@ -2011,11 +2018,10 @@ bool ScriptManager::hasEntityScriptDetails(const EntityItemID& entityID) const {
void ScriptManager::loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) { void ScriptManager::loadEntityScript(const EntityItemID& entityID, const QString& entityScript, bool forceRedownload) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "loadEntityScript", // Lambda is necessary there to keep shared_ptr counter above zero
Q_ARG(const EntityItemID&, entityID), QMetaObject::invokeMethod(this, [=, manager = shared_from_this()]{
Q_ARG(const QString&, entityScript), manager->loadEntityScript(entityID, entityScript, forceRedownload);
Q_ARG(bool, forceRedownload) });
);
return; return;
} }
PROFILE_RANGE(script, __FUNCTION__); PROFILE_RANGE(script, __FUNCTION__);
@ -2092,13 +2098,10 @@ void ScriptManager::entityScriptContentAvailable(const EntityItemID& entityID, c
<< contents << "isURL:" << isURL << "success:" << success; << contents << "isURL:" << isURL << "success:" << success;
#endif #endif
QMetaObject::invokeMethod(this, "entityScriptContentAvailable", // Lambda is necessary there to keep shared_ptr counter above zero
Q_ARG(const EntityItemID&, entityID), QMetaObject::invokeMethod(this, [=, manager = shared_from_this()]{
Q_ARG(const QString&, scriptOrURL), manager->entityScriptContentAvailable(entityID, scriptOrURL, contents, isURL, success, status);
Q_ARG(const QString&, contents), });
Q_ARG(bool, isURL),
Q_ARG(bool, success),
Q_ARG(const QString&, status));
return; return;
} }
@ -2442,9 +2445,10 @@ void ScriptManager::unloadEntityScript(const EntityItemID& entityID, bool should
"entityID:" << entityID; "entityID:" << entityID;
#endif #endif
QMetaObject::invokeMethod(this, "unloadEntityScript", // Lambda is necessary there to keep shared_ptr counter above zero
Q_ARG(const EntityItemID&, entityID), QMetaObject::invokeMethod(this, [=, manager = shared_from_this()]{
Q_ARG(bool, shouldRemoveFromMap)); manager->unloadEntityScript(entityID, shouldRemoveFromMap);
});
return; return;
} }
#ifdef THREAD_DEBUGGING #ifdef THREAD_DEBUGGING
@ -2494,9 +2498,10 @@ void ScriptManager::unloadAllEntityScripts(bool blockingCall) {
#ifdef THREAD_DEBUGGING #ifdef THREAD_DEBUGGING
qCDebug(scriptengine) << "*** WARNING *** ScriptManager::unloadAllEntityScripts() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]"; qCDebug(scriptengine) << "*** WARNING *** ScriptManager::unloadAllEntityScripts() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";
#endif #endif
// Lambda is necessary there to keep shared_ptr counter above zero
QMetaObject::invokeMethod(this, "unloadAllEntityScripts", QMetaObject::invokeMethod(this, [=, manager = shared_from_this()] {
blockingCall ? Qt::BlockingQueuedConnection : Qt::QueuedConnection); manager->unloadAllEntityScripts(blockingCall);
}, blockingCall ? Qt::BlockingQueuedConnection : Qt::QueuedConnection);
return; return;
} }
#ifdef THREAD_DEBUGGING #ifdef THREAD_DEBUGGING
@ -2589,12 +2594,10 @@ void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const Q
qCDebug(scriptengine) << "*** WARNING *** ScriptManager::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] " qCDebug(scriptengine) << "*** WARNING *** ScriptManager::callEntityScriptMethod() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "] "
"entityID:" << entityID << "methodName:" << methodName; "entityID:" << entityID << "methodName:" << methodName;
#endif #endif
// Lambda is necessary there to keep shared_ptr counter above zero
QMetaObject::invokeMethod(this, "callEntityScriptMethod", QMetaObject::invokeMethod(this, [=, manager = shared_from_this()]{
Q_ARG(const EntityItemID&, entityID), manager->callEntityScriptMethod(entityID, methodName, params, remoteCallerID);
Q_ARG(const QString&, methodName), });
Q_ARG(const QStringList&, params),
Q_ARG(const QUuid&, remoteCallerID));
return; return;
} }
#ifdef THREAD_DEBUGGING #ifdef THREAD_DEBUGGING
@ -2659,10 +2662,10 @@ void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const Q
"entityID:" << entityID << "methodName:" << methodName << "event: mouseEvent"; "entityID:" << entityID << "methodName:" << methodName << "event: mouseEvent";
#endif #endif
QMetaObject::invokeMethod(this, "callEntityScriptMethod", // Lambda is necessary there to keep shared_ptr counter above zero
Q_ARG(const EntityItemID&, entityID), QMetaObject::invokeMethod(this, [=, manager = shared_from_this()]{
Q_ARG(const QString&, methodName), manager->callEntityScriptMethod(entityID, methodName, event);
Q_ARG(const PointerEvent&, event)); });
return; return;
} }
#ifdef THREAD_DEBUGGING #ifdef THREAD_DEBUGGING
@ -2698,11 +2701,10 @@ void ScriptManager::callEntityScriptMethod(const EntityItemID& entityID, const Q
"entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision"; "entityID:" << entityID << "methodName:" << methodName << "otherID:" << otherID << "collision: collision";
#endif #endif
QMetaObject::invokeMethod(this, "callEntityScriptMethod", // Lambda is necessary there to keep shared_ptr counter above zero
Q_ARG(const EntityItemID&, entityID), QMetaObject::invokeMethod(this, [=, manager = shared_from_this()]{
Q_ARG(const QString&, methodName), manager->callEntityScriptMethod(entityID, methodName, otherID, collision);
Q_ARG(const EntityItemID&, otherID), });
Q_ARG(const Collision&, collision));
return; return;
} }
#ifdef THREAD_DEBUGGING #ifdef THREAD_DEBUGGING

View file

@ -280,9 +280,6 @@ public:
*/ */
class ScriptManager : public QObject, public EntitiesScriptEngineProvider, public std::enable_shared_from_this<ScriptManager> { class ScriptManager : public QObject, public EntitiesScriptEngineProvider, public std::enable_shared_from_this<ScriptManager> {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QString context READ getContext)
Q_PROPERTY(QString type READ getTypeAsString)
Q_PROPERTY(QString fileName MEMBER _fileNameString CONSTANT)
public: public:
static const QString SCRIPT_EXCEPTION_FORMAT; static const QString SCRIPT_EXCEPTION_FORMAT;
static const QString SCRIPT_BACKTRACE_SEP; static const QString SCRIPT_BACKTRACE_SEP;
@ -381,8 +378,11 @@ public:
Q_ENUM(Type); Q_ENUM(Type);
static int processLevelMaxRetries; static int processLevelMaxRetries;
ScriptManager(Context context, const QString& scriptContents = NO_SCRIPT, const QString& fileNameString = QString("about:ScriptEngine")); private:
~ScriptManager(); // Constructor is private so that only properly generated shared pointer can be used
explicit ScriptManager(Context context, const QString& scriptContents = NO_SCRIPT, const QString& fileNameString = QString("about:ScriptEngine"));
public:
~ScriptManager() override;
// static initialization support // static initialization support
typedef void (*ScriptManagerInitializer)(ScriptManager*); typedef void (*ScriptManagerInitializer)(ScriptManager*);
@ -390,7 +390,7 @@ public:
public: public:
ScriptManagerInitializer init; ScriptManagerInitializer init;
StaticInitializerNode* prev; StaticInitializerNode* prev;
inline StaticInitializerNode(ScriptManagerInitializer&& pInit) : init(std::move(pInit)),prev(nullptr) { registerNewStaticInitializer(this); } inline explicit StaticInitializerNode(ScriptManagerInitializer&& pInit) : init(std::move(pInit)),prev(nullptr) { registerNewStaticInitializer(this); }
}; };
static void registerNewStaticInitializer(StaticInitializerNode* dest); static void registerNewStaticInitializer(StaticInitializerNode* dest);
@ -398,7 +398,7 @@ public:
public: public:
ScriptManagerInitializer init; ScriptManagerInitializer init;
StaticTypesInitializerNode* prev; StaticTypesInitializerNode* prev;
inline StaticTypesInitializerNode(ScriptManagerInitializer&& pInit) : init(std::move(pInit)),prev(nullptr) { registerNewStaticTypesInitializer(this); } inline explicit StaticTypesInitializerNode(ScriptManagerInitializer&& pInit) : init(std::move(pInit)),prev(nullptr) { registerNewStaticTypesInitializer(this); }
}; };
static void registerNewStaticTypesInitializer(StaticTypesInitializerNode* dest); static void registerNewStaticTypesInitializer(StaticTypesInitializerNode* dest);
@ -1668,6 +1668,7 @@ protected:
friend ScriptManagerPointer newScriptManager(Context context, const QString& scriptContents, const QString& fileNameString); friend ScriptManagerPointer newScriptManager(Context context, const QString& scriptContents, const QString& fileNameString);
friend class ScriptManagerScriptingInterface; friend class ScriptManagerScriptingInterface;
std::atomic<bool> _isDeleted {false}; // This is used for debugging use-after-delete. It happens quite often, so I'm keeping it here for now.
}; };
/** /**

View file

@ -58,6 +58,9 @@ public:
class ScriptManagerScriptingInterface : public QObject { class ScriptManagerScriptingInterface : public QObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QString context READ getContext)
Q_PROPERTY(QString type READ getTypeAsString)
Q_PROPERTY(QString fileName READ getAbsoluteFilename CONSTANT)
public: public:
ScriptManagerScriptingInterface(ScriptManager *parent); ScriptManagerScriptingInterface(ScriptManager *parent);
@ -96,6 +99,26 @@ public:
*/ */
Q_INVOKABLE QString getContext() const { return _manager->getContext(); } Q_INVOKABLE QString getContext() const { return _manager->getContext(); }
/*@jsdoc
* Gets the type of script that is running: Interface, avatar, client entity, server entity, or assignment client.
* @function Script.getTypeAsString
* @returns {string} The type of script that is running:
* <ul>
* <li><code>"client"</code>: An Interface script.</li>
* <li><code>"entity_client"</code>: A client entity script.</li>
* <li><code>"avatar"</code>: An avatar script.</li>
* <li><code>"entity_server"</code>: A server entity script.</li>
* <li><code>"agent"</code>: An assignment client script.</li>
* </ul>
*/
Q_INVOKABLE QString getTypeAsString() const { return _manager->getTypeAsString(); }
/*@jsdoc
* Gets the filename of the script file.
* @function Script.getAbsoluteFilename
* @returns {string} The filename of the script file.
*/
Q_INVOKABLE QString getAbsoluteFilename() const { return _manager->getAbsoluteFilename(); }
/*@jsdoc /*@jsdoc
* Checks whether the script is running as an Interface or avatar script. * Checks whether the script is running as an Interface or avatar script.

View file

@ -0,0 +1,18 @@
//
// ScriptEngineDebugFlags.h
// libraries/script-engine/src/v8/ScriptEngineDebugFlags.h
//
// Created by dr Karol Suprynowicz on 2024/11/14.
// Copyright 2024 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// SPDX-License-Identifier: Apache-2.0
//
#ifndef overte_ScriptEngineDebugFlags_h
#define overte_ScriptEngineDebugFlags_h
#define OVERTE_SCRIPT_USE_AFTER_DELETE_GUARD
#endif

View file

@ -258,6 +258,13 @@ ScriptEngineV8::ScriptEngineV8(ScriptManager *manager) : ScriptEngine(manager),
} }
ScriptEngineV8::~ScriptEngineV8() { ScriptEngineV8::~ScriptEngineV8() {
// Process remaining events to avoid problems with `deleteLater` calling destructor of script proxies after script engine has been deleted:
{
QEventLoop loop;
loop.processEvents();
}
// This is necessary for script engines that don't run in ScriptManager::run(), for example entity scripts:
disconnectSignalProxies();
deleteUnusedValueWrappers(); deleteUnusedValueWrappers();
#ifdef OVERTE_SCRIPT_USE_AFTER_DELETE_GUARD #ifdef OVERTE_SCRIPT_USE_AFTER_DELETE_GUARD
_wasDestroyed = true; _wasDestroyed = true;
@ -272,14 +279,14 @@ void ScriptEngineV8::perManagerLoopIterationCleanup() {
void ScriptEngineV8::disconnectSignalProxies() { void ScriptEngineV8::disconnectSignalProxies() {
_signalProxySetLock.lockForRead(); _signalProxySetLock.lockForRead();
while (!_signalProxySet.empty()) { while (!_signalProxySet.empty()) {
auto proxy = *_signalProxySet.begin();
_signalProxySetLock.unlock(); _signalProxySetLock.unlock();
delete *_signalProxySet.begin(); delete proxy;
_signalProxySetLock.lockForRead(); _signalProxySetLock.lockForRead();
} }
_signalProxySetLock.unlock(); _signalProxySetLock.unlock();
} }
void ScriptEngineV8::deleteUnusedValueWrappers() { void ScriptEngineV8::deleteUnusedValueWrappers() {
while (!_scriptValueWrappersToDelete.empty()) { while (!_scriptValueWrappersToDelete.empty()) {
auto wrapper = _scriptValueWrappersToDelete.dequeue(); auto wrapper = _scriptValueWrappersToDelete.dequeue();

View file

@ -35,6 +35,7 @@
#include "libplatform/libplatform.h" #include "libplatform/libplatform.h"
#include "v8.h" #include "v8.h"
#include "ScriptEngineDebugFlags.h"
#include "../ScriptEngine.h" #include "../ScriptEngine.h"
#include "../ScriptManager.h" #include "../ScriptManager.h"
#include "../ScriptException.h" #include "../ScriptException.h"
@ -58,8 +59,6 @@ using ScriptContextV8Pointer = std::shared_ptr<ScriptContextV8Wrapper>;
const double GARBAGE_COLLECTION_TIME_LIMIT_S = 1.0; const double GARBAGE_COLLECTION_TIME_LIMIT_S = 1.0;
#define OVERTE_SCRIPT_USE_AFTER_DELETE_GUARD
Q_DECLARE_METATYPE(ScriptEngine::FunctionSignature) Q_DECLARE_METATYPE(ScriptEngine::FunctionSignature)
/// [V8] Implements ScriptEngine for V8 and translates calls for QScriptEngine /// [V8] Implements ScriptEngine for V8 and translates calls for QScriptEngine

View file

@ -1217,7 +1217,9 @@ ScriptSignalV8Proxy::~ScriptSignalV8Proxy() {
_objectLifetime.Reset(); _objectLifetime.Reset();
_v8Context.Reset(); _v8Context.Reset();
#ifdef OVERTE_SCRIPT_USE_AFTER_DELETE_GUARD #ifdef OVERTE_SCRIPT_USE_AFTER_DELETE_GUARD
Q_ASSERT(!_wasDeleted);
Q_ASSERT(!_engine->_wasDestroyed); Q_ASSERT(!_engine->_wasDestroyed);
_wasDeleted = true;
#endif #endif
_engine->_signalProxySetLock.lockForWrite(); _engine->_signalProxySetLock.lockForWrite();
_engine->_signalProxySet.remove(this); _engine->_signalProxySet.remove(this);

View file

@ -23,6 +23,7 @@
#include <QtCore/QPointer> #include <QtCore/QPointer>
#include <QtCore/QString> #include <QtCore/QString>
#include "ScriptEngineDebugFlags.h"
#include "../ScriptEngine.h" #include "../ScriptEngine.h"
#include "../Scriptable.h" #include "../Scriptable.h"
#include "ScriptEngineV8.h" #include "ScriptEngineV8.h"
@ -295,6 +296,9 @@ private: // storage
// Call counter for debugging purposes. It can be used to determine which signals are overwhelming script engine. // Call counter for debugging purposes. It can be used to determine which signals are overwhelming script engine.
int _callCounter{0}; int _callCounter{0};
float _totalCallTime_s{ 0.0 }; float _totalCallTime_s{ 0.0 };
#ifdef OVERTE_SCRIPT_USE_AFTER_DELETE_GUARD
std::atomic<bool> _wasDeleted{false};
#endif
Q_DISABLE_COPY(ScriptSignalV8Proxy) Q_DISABLE_COPY(ScriptSignalV8Proxy)
}; };

View file

@ -14,6 +14,7 @@
var settings = { var settings = {
external_window: false, external_window: false,
maximum_messages: 200, maximum_messages: 200,
join_notification: true
}; };
// Global vars // Global vars
@ -97,24 +98,31 @@
if (channel !== "chat") return; if (channel !== "chat") return;
message = JSON.parse(message); message = JSON.parse(message);
// Get the message data
const currentTimestamp = _getTimestamp();
const timeArray = _formatTimestamp(currentTimestamp);
if (!message.channel) message.channel = "domain"; // We don't know where to put this message. Assume it is a domain wide message. if (!message.channel) message.channel = "domain"; // We don't know where to put this message. Assume it is a domain wide message.
if (message.forApp) return; // Floofchat if (message.forApp) return; // Floofchat
// Floofchat compatibility hook // Floofchat compatibility hook
message = floofChatCompatibilityConversion(message); message = floofChatCompatibilityConversion(message);
message.channel = message.channel.toLowerCase(); // Make sure the "local", "domain", etc. is formatted consistently message.channel = message.channel.toLowerCase();
if (!channels.includes(message.channel)) return; // Check the channel // Check the channel. If the channel is not one we have, do nothing.
if ( if (!channels.includes(message.channel)) return;
message.channel == "local" &&
Vec3.distance(MyAvatar.position, message.position) > // If message is local, and if player is too far away from location, do nothing.
maxLocalDistance if (message.channel == "local" && isTooFar(message.position)) return;
)
return; // If message is local, and if player is too far away from location, don't do anything // Format the timestamp
message.timeString = timeArray[0];
message.dateString = timeArray[1];
// Update qml view of to new message // Update qml view of to new message
_emitEvent({ type: "show_message", ...message }); _emitEvent({ type: "show_message", ...message });
// Show new message on screen
Messages.sendLocalMessage( Messages.sendLocalMessage(
"Floof-Notif", "Floof-Notif",
JSON.stringify({ JSON.stringify({
@ -125,20 +133,25 @@
// Save message to history // Save message to history
let savedMessage = message; let savedMessage = message;
// Remove unnecessary data.
delete savedMessage.position; delete savedMessage.position;
savedMessage.timeString = new Date().toLocaleTimeString(undefined, { delete savedMessage.timeString;
hour12: false, delete savedMessage.dateString;
}); delete savedMessage.action;
savedMessage.dateString = new Date().toLocaleDateString(undefined, {
year: "numeric", savedMessage.timestamp = currentTimestamp;
month: "long",
day: "numeric",
});
messageHistory.push(savedMessage); messageHistory.push(savedMessage);
while (messageHistory.length > settings.maximum_messages) { while (messageHistory.length > settings.maximum_messages) {
messageHistory.shift(); messageHistory.shift();
} }
Settings.setValue("ArmoredChat-Messages", messageHistory); Settings.setValue("ArmoredChat-Messages", messageHistory);
// Check to see if the message is close enough to the user
function isTooFar(messagePosition) {
return Vec3.distance(MyAvatar.position, messagePosition) > maxLocalDistance;
}
} }
function fromQML(event) { function fromQML(event) {
switch (event.type) { switch (event.type) {
@ -146,25 +159,25 @@
_sendMessage(event.message, event.channel); _sendMessage(event.message, event.channel);
break; break;
case "setting_change": case "setting_change":
// Set the setting value, and save the config
settings[event.setting] = event.value; // Update local settings settings[event.setting] = event.value; // Update local settings
_saveSettings(); // Save local settings _saveSettings(); // Save local settings
// Extra actions to preform.
switch (event.setting) { switch (event.setting) {
case "external_window": case "external_window":
chatOverlayWindow.presentationMode = event.value chatOverlayWindow.presentationMode = event.value
? Desktop.PresentationMode.NATIVE ? Desktop.PresentationMode.NATIVE
: Desktop.PresentationMode.VIRTUAL; : Desktop.PresentationMode.VIRTUAL;
break; break;
case "maximum_messages":
// Do nothing
break;
} }
break; break;
case "action": case "action":
switch (event.action) { switch (event.action) {
case "erase_history": case "erase_history":
Settings.setValue("ArmoredChat-Messages", []); Settings.setValue("ArmoredChat-Messages", null);
messageHistory = [];
_emitEvent({ _emitEvent({
type: "clear_messages", type: "clear_messages",
}); });
@ -213,9 +226,7 @@
// Get the display name of the user // Get the display name of the user
let displayName = ""; let displayName = "";
displayName = displayName = AvatarManager.getPalData([sessionId])?.data[0]?.sessionDisplayName || null;
AvatarManager.getPalData([sessionId])?.data[0]
?.sessionDisplayName || null;
if (displayName == null) { if (displayName == null) {
for (let i = 0; i < palData.length; i++) { for (let i = 0; i < palData.length; i++) {
if (palData[i].sessionUUID == sessionId) { if (palData[i].sessionUUID == sessionId) {
@ -226,8 +237,22 @@
// Format the packet // Format the packet
let message = {}; let message = {};
const timeArray = _formatTimestamp(_getTimestamp());
message.timeString = timeArray[0];
message.dateString = timeArray[1];
message.message = `${displayName} ${type}`; message.message = `${displayName} ${type}`;
// Show new message on screen
if (settings.join_notification){
Messages.sendLocalMessage(
"Floof-Notif",
JSON.stringify({
sender: displayName,
text: type,
})
);
}
_emitEvent({ type: "notification", ...message }); _emitEvent({ type: "notification", ...message });
}, 1500); }, 1500);
} }
@ -237,7 +262,9 @@
if (messageHistory) { if (messageHistory) {
// Load message history // Load message history
messageHistory.forEach((message) => { messageHistory.forEach((message) => {
delete message.action; const timeArray = _formatTimestamp(_getTimestamp());
message.timeString = timeArray[0];
message.dateString = timeArray[1];
_emitEvent({ type: "show_message", ...message }); _emitEvent({ type: "show_message", ...message });
}); });
} }
@ -249,6 +276,24 @@
console.log("Saving config"); console.log("Saving config");
Settings.setValue("ArmoredChat-Config", settings); Settings.setValue("ArmoredChat-Config", settings);
} }
function _getTimestamp(){
return Date.now();
}
function _formatTimestamp(timestamp){
let timeArray = [];
timeArray.push(new Date().toLocaleTimeString(undefined, {
hour12: false,
}));
timeArray.push(new Date(timestamp).toLocaleDateString(undefined, {
year: "numeric",
month: "long",
day: "numeric",
}));
return timeArray;
}
/** /**
* Emit a packet to the HTML front end. Easy communication! * Emit a packet to the HTML front end. Easy communication!

View file

@ -1,5 +1,5 @@
import QtQuick 2.7 import QtQuick 2.7
import QtQuick.Controls 2.0 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import controlsUit 1.0 as HifiControlsUit import controlsUit 1.0 as HifiControlsUit
@ -30,7 +30,7 @@ Rectangle {
running: false running: false
repeat: false repeat: false
onTriggered: { onTriggered: {
scrollToBottom(); scrollToBottom(true);
} }
} }
@ -145,49 +145,60 @@ Rectangle {
anchors.top: navigation_bar.bottom anchors.top: navigation_bar.bottom
visible: ["local", "domain"].includes(pageVal) ? true : false visible: ["local", "domain"].includes(pageVal) ? true : false
// Chat Message History // Chat Message History
ListView { Flickable {
width: parent.width width: parent.width
height: parent.height - 40 height: parent.height - 40
contentWidth: parent.width
contentHeight: listview.height
clip: true clip: true
interactive: true id: messageViewFlickable
spacing: 5
id: listview
delegate: Loader { ColumnLayout {
property int delegateIndex: index id: listview
property string delegateText: model.text Layout.fillWidth: true
property string delegateUsername: model.username
property string delegateDate: model.date
width: listview.width
sourceComponent: { Repeater {
if (model.type === "chat") { model: getChannel(pageVal)
return template_chat_message; delegate: Loader {
} else if (model.type === "notification") { property int delegateIndex: model.index
return template_notification; property string delegateText: model.text
property string delegateUsername: model.username
property string delegateDate: model.date
sourceComponent: {
if (model.type === "chat") {
return template_chat_message;
} else if (model.type === "notification") {
return template_notification;
}
}
} }
} }
} }
ScrollBar.vertical: ScrollBar { ScrollBar.vertical: ScrollBar {
id: chat_scrollbar size: 100
height: 100 minimumSize: 0.1
size: 0.05
} }
model: getChannel(pageVal) rebound: Transition {
NumberAnimation {
properties: "x,y"
duration: 1
}
}
} }
ListModel { ListModel {
id: local id: local
} }
ListModel { ListModel {
id: domain id: domain
} }
// Chat Entry // Chat Entry
Rectangle { Rectangle {
@ -205,6 +216,8 @@ Rectangle {
height: parent.height height: parent.height
placeholderText: pageVal.charAt(0).toUpperCase() + pageVal.slice(1) + " chat message..." placeholderText: pageVal.charAt(0).toUpperCase() + pageVal.slice(1) + " chat message..."
clip: false clip: false
font.italic: text == ""
Keys.onPressed: { Keys.onPressed: {
if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && !(event.modifiers & Qt.ShiftModifier)) { if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && !(event.modifiers & Qt.ShiftModifier)) {
event.accepted = true; event.accepted = true;
@ -335,6 +348,31 @@ Rectangle {
} }
} }
} }
// Join notification
Rectangle {
width: parent.width
height: 40
color: "transparent"
Text{
text: "Join notification"
color: "white"
font.pointSize: 12
anchors.verticalCenter: parent.verticalCenter
}
CheckBox{
id: s_join_notification
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
onCheckedChanged: {
toScript({type: 'setting_change', setting: 'join_notification', value: checked})
}
}
}
} }
} }
@ -344,7 +382,7 @@ Rectangle {
Component { Component {
id: template_chat_message id: template_chat_message
Rectangle{ Rectangle {
property int index: delegateIndex property int index: delegateIndex
property string texttest: delegateText property string texttest: delegateText
property string username: delegateUsername property string username: delegateUsername
@ -352,6 +390,8 @@ Rectangle {
height: Math.max(65, children[1].height + 30) height: Math.max(65, children[1].height + 30)
color: index % 2 === 0 ? "transparent" : Qt.rgba(0.15,0.15,0.15,1) color: index % 2 === 0 ? "transparent" : Qt.rgba(0.15,0.15,0.15,1)
width: listview.parent.parent.width
Layout.fillWidth: true
Item { Item {
width: parent.width - 10 width: parent.width - 10
@ -370,7 +410,7 @@ Rectangle {
} }
} }
TextEdit{ TextEdit {
anchors.top: parent.children[0].bottom anchors.top: parent.children[0].bottom
x: 5 x: 5
text: texttest text: texttest
@ -451,16 +491,25 @@ Rectangle {
} }
property var channels: { property var channels: {
"local": local, "local": local,
"domain": domain, "domain": domain,
} }
function scrollToBottom() { function scrollToBottom(bypassDistanceCheck = false, extraMoveDistance = 0) {
if (listview.count == 0) return; const totalHeight = listview.height; // Total height of the content
listview.positionViewAtEnd(); const currentPosition = messageViewFlickable.contentY; // Current position of the view
const windowHeight = listview.parent.parent.height; // Total height of the window
const bottomPosition = currentPosition + windowHeight;
// Check if the view is within 300 units from the bottom
const closeEnoughToBottom = totalHeight - bottomPosition <= 300;
if (!bypassDistanceCheck && !closeEnoughToBottom) return;
if (totalHeight < windowHeight) return; // No reason to scroll, we don't have an overflow.
if (bottomPosition == totalHeight) return; // At the bottom, do nothing.
messageViewFlickable.contentY = listview.height - listview.parent.parent.height;
messageViewFlickable.returnToBounds();
} }
@ -469,13 +518,13 @@ Rectangle {
// Format content // Format content
message = formatContent(message); message = formatContent(message);
message = embedImages(message); message = embedImages(message);
if (type === "notification"){ if (type === "notification"){
channel.append({ text: message, date: date, type: "notification" }); channel.append({ text: message, date: date, type: "notification" });
last_message_user = ""; last_message_user = "";
scrollToBottom(); scrollToBottom(null, 30);
last_message_time = new Date(); last_message_time = new Date();
return; return;
} }
@ -487,22 +536,18 @@ Rectangle {
var last_item_index = channel.count - 1; var last_item_index = channel.count - 1;
var last_item = channel.get(last_item_index); var last_item = channel.get(last_item_index);
// FIXME: When adding a new message this would check to see if we could append the incoming message if (last_message_user === username && elapsed_minutes < 1 && last_item){
// to the bottom of the last message. This current implimentation causes issues with scrollToBottom() message = "<br>" + message
// Specifically, scrolling to the bottom does not like image embeds. last_item.text = last_item.text += "\n" + message;
// This needs to be reworked entirely before it can be reimplimented load_scroll_timer.running = true;
// if (last_message_user === username && elapsed_minutes < 1 && last_item){ last_message_time = new Date();
// message = "<br>" + message return;
// last_item.text = last_item.text += "\n" + message; }
// scrollToBottom()
// last_message_time = new Date();
// return;
// }
last_message_user = username; last_message_user = username;
last_message_time = new Date(); last_message_time = new Date();
channel.append({ text: message, username: username, date: date, type: type }); channel.append({ text: message, username: username, date: date, type: type });
scrollToBottom(); load_scroll_timer.running = true;
} }
function getChannel(id) { function getChannel(id) {
@ -542,15 +587,13 @@ Rectangle {
// Messages from script // Messages from script
function fromScript(message) { function fromScript(message) {
let time = new Date().toLocaleTimeString(undefined, { hour12: false });
let date = new Date().toLocaleDateString(undefined, { year: "numeric", month: "long", day: "numeric", });
switch (message.type){ switch (message.type){
case "show_message": case "show_message":
addMessage(message.displayName, message.message, `[ ${message.timeString || time} - ${message.dateString || date} ]`, message.channel, "chat"); addMessage(message.displayName, message.message, `[ ${message.timeString} - ${message.dateString} ]`, message.channel, "chat");
break; break;
case "notification": case "notification":
addMessage("SYSTEM", message.message, `[ ${time} - ${date} ]`, "domain", "notification"); addMessage("SYSTEM", message.message, `[ ${message.timeString} - ${message.dateString} ]`, "domain", "notification");
break; break;
case "clear_messages": case "clear_messages":
local.clear(); local.clear();
@ -559,6 +602,7 @@ Rectangle {
case "initial_settings": case "initial_settings":
if (message.settings.external_window) s_external_window.checked = true; if (message.settings.external_window) s_external_window.checked = true;
if (message.settings.maximum_messages) s_maximum_messages.value = message.settings.maximum_messages; if (message.settings.maximum_messages) s_maximum_messages.value = message.settings.maximum_messages;
if (message.settings.join_notification) s_join_notification.checked = true;
break; break;
} }
} }

View file

@ -1,7 +1,7 @@
import QtQuick 2.5 import QtQuick 2.5
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
Rectangle { Item {
id: root id: root
property var window property var window
@ -21,15 +21,22 @@ Rectangle {
z: 99 z: 99
visible: false visible: false
TextArea { Rectangle {
id: textArea
x: 0
width: parent.width width: parent.width
height: parent.height height: parent.height
text:"" color: Qt.rgba(0.95,0.95,0.95,1)
textColor: "#ffffff" }
TextInput {
id: textArea
x: 5
width: parent.width
height: parent.height
text: ""
color: "#000"
clip: false clip: false
font.pointSize: 18 font.pointSize: 18
verticalAlignment: Text.AlignVCenter
Keys.onReturnPressed: { _onEnterPressed(); } Keys.onReturnPressed: { _onEnterPressed(); }
Keys.onEnterPressed: { _onEnterPressed(); } Keys.onEnterPressed: { _onEnterPressed(); }
@ -52,33 +59,36 @@ Rectangle {
text: "Local message..." text: "Local message..."
font.pointSize: 16 font.pointSize: 16
color: "gray" color: "gray"
x: 0 x: 5
width: parent.width width: parent.width
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: textArea.text == "" visible: textArea.text == ""
font.italic: true
} }
Button { Rectangle {
id: button id: button
x: parent.width - width x: parent.width - width
y: 0 y: 0
width: 64 width: 64
height: parent.height height: parent.height
clip: false clip: false
visible: true color: "#262626"
Image { Image {
id: image id: image
width: 30 width: 30
height: 30 height: 30
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
visible: true
anchors.centerIn: parent anchors.centerIn: parent
source: "./img/ui/send_white.png" source: "./img/ui/send_white.png"
} }
onClicked: { MouseArea {
_onEnterPressed(); anchors.fill: parent
onClicked: {
_onEnterPressed();
}
} }
} }

View file

@ -22,7 +22,12 @@ var DEFAULT_OFFSET = 10;
var FLOOF_NOTIFICATION_CHANNEL = "Floof-Notif"; var FLOOF_NOTIFICATION_CHANNEL = "Floof-Notif";
var MAIN_CHAT_APP_CHANNEL = "Chat"; var MAIN_CHAT_APP_CHANNEL = "Chat";
var ARROW_REGEX = /\</gi; var ARROW_REGEX = /\</gi;
var notificationSound = SoundCache.getSound(Script.resolvePath("resources/click.wav"));
var soundInjectorOptions = {
localOnly: true,
position: MyAvatar.position,
volume: 0.04
};
var offset = DEFAULT_OFFSET; var offset = DEFAULT_OFFSET;
@ -48,6 +53,7 @@ function messageReceived(channel, message, sender, local) {
} }
} else { } else {
notificationCore.add(cmd.text, cmd.sender, cmd.colour); notificationCore.add(cmd.text, cmd.sender, cmd.colour);
playSound();
} }
} }
} }
@ -113,6 +119,10 @@ function cleanUp() {
}); });
} }
function playSound() {
Audio.playSound(notificationSound, soundInjectorOptions);
}
function notif(text, colour) { function notif(text, colour) {
var noti = { var noti = {

View file

@ -2,7 +2,7 @@
// NewSoundDialog.qml // NewSoundDialog.qml
// qml/hifi // qml/hifi
// //
// Created by HifiExperiments on 4/7/24 // Created by HifiExperiments on April 7th, 2024
// Copyright 2024 Overte e.V. // Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
@ -53,17 +53,32 @@ Rectangle {
Text { Text {
id: text1 id: text1
text: qsTr("Sound URL") text: qsTr("Sound URL <i></i>&nbsp;&nbsp;&nbsp;")
color: "#ffffff" color: "#ffffff"
font.pixelSize: 12 font.pixelSize: 12
} }
Button {
id: pasteBtn
text: "Paste"
font.pixelSize: 11
height: 16
width: 40
radius: 4
anchors.top: text1.top
anchors.left: text1.right
anchors.bottom: text1.bottom
onClicked: {
soundURL.paste()
}
}
TextInput { TextInput {
id: soundURL id: soundURL
height: 20 height: 20
text: qsTr("") text: qsTr("")
color: "white" color: "white"
anchors.top: text1.bottom anchors.top: pasteBtn.bottom
anchors.topMargin: 5 anchors.topMargin: 5
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: 0 anchors.leftMargin: 0

View file

@ -1,15 +1,20 @@
// //
// graphicsSettings.js // graphicsSettings.js
// //
// Created by Kalila L. on 8/5/2020 // Created by Kalila L. on August 5th, 2020
// Copyright 2020 Vircadia contributors. // Copyright 2020 Vircadia contributors.
// Copyright 2024 Overte e.V.
// //
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
(function() { // BEGIN LOCAL_SCOPE (function() { // BEGIN LOCAL_SCOPE
var channelComm = "Overte-ShowGraphicsIconChanged";
var appStatus = false;
var GRAPHICS_HIDE_AND_SHOW_SETTING_KEY = "showGraphicsIcon";
var GRAPHICS_HIDE_AND_SHOW_DEFAULT_VALUE = true;
var AppUi = Script.require('appUi'); var AppUi = Script.require('appUi');
// cellphone-cog MDI // cellphone-cog MDI
@ -49,22 +54,49 @@
} }
function startup() { function startup() {
ui = new AppUi({ if (!appStatus) {
buttonName: BUTTON_NAME, ui = new AppUi({
sortOrder: 8, buttonName: BUTTON_NAME,
normalButton: getIcon(), sortOrder: 8,
activeButton: getIcon().replace('white', 'black'), normalButton: getIcon(),
home: GRAPHICS_QML_SOURCE activeButton: getIcon().replace('white', 'black'),
}); home: GRAPHICS_QML_SOURCE
});
}
appStatus = true;
} }
function shutdown() { function shutdown() {
if (appStatus) {
ui.onScriptEnding();
appStatus = false;
}
}
function cleanup() {
Messages.messageReceived.disconnect(onMessageReceived);
Messages.unsubscribe(channelComm);
}
function onMessageReceived(channel, message, sender, localOnly) {
if (channel === channelComm && localOnly) {
if (Settings.getValue(GRAPHICS_HIDE_AND_SHOW_SETTING_KEY, GRAPHICS_HIDE_AND_SHOW_DEFAULT_VALUE)) {
startup();
} else {
shutdown();
}
}
} }
// //
// Run the functions. // Run the functions.
// //
startup(); if (Settings.getValue(GRAPHICS_HIDE_AND_SHOW_SETTING_KEY, GRAPHICS_HIDE_AND_SHOW_DEFAULT_VALUE)) {
Script.scriptEnding.connect(shutdown); startup();
}
Messages.subscribe(channelComm);
Messages.messageReceived.connect(onMessageReceived);
Script.scriptEnding.connect(cleanup);
}()); // END LOCAL_SCOPE }()); // END LOCAL_SCOPE

View file

@ -56,7 +56,7 @@ const menuNotificationIcon = path.join(__dirname, '../resources/tray-menu-notifi
const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Days const DELETE_LOG_FILES_OLDER_THAN_X_SECONDS = 60 * 60 * 24 * 7; // 7 Days
const LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/; const LOG_FILE_REGEX = /(domain-server|ac-monitor|ac)-.*-std(out|err).txt/;
const HOME_CONTENT_URL = "http://cdn-1.vircadia.com/us-e-1/DomainContent/Sandbox/Rearranged_Basic_Sandbox.tar.gz"; const HOME_CONTENT_URL = "https://content.overte.org/DomainContent/Sandbox/Rearranged_Basic_Sandbox.tar.gz";
const buildInfo = GetBuildInfo(); const buildInfo = GetBuildInfo();

View file

@ -1,8 +1,8 @@
# Copyright 2022-2023 Overte e.V. # Copyright 2022-2024 Overte e.V.
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
# Docker file for building Overte # Docker file for building Overte
# Example build: docker build -t overte/overte-full-build:0.1.1-ubuntu-20.04 -f Dockerfile_build_ubuntu-20.04 . # Example build: docker build -t overte/overte-full-build:0.1.2-ubuntu-20.04 -f Dockerfile_build_ubuntu-20.04 .
FROM ubuntu:20.04 FROM ubuntu:20.04
LABEL maintainer="Julian Groß (julian.gro@overte.org)" LABEL maintainer="Julian Groß (julian.gro@overte.org)"
LABEL description="Development image for full Overte builds" LABEL description="Development image for full Overte builds"
@ -18,6 +18,8 @@ RUN apt-get update && apt-get -y install tzdata
RUN apt-get -y install curl ninja-build git cmake g++ libssl-dev python3-distutils python3-distro mesa-common-dev libgl1-mesa-dev libsystemd-dev RUN apt-get -y install curl ninja-build git cmake g++ libssl-dev python3-distutils python3-distro mesa-common-dev libgl1-mesa-dev libsystemd-dev
# Install server-console build dependencies # Install server-console build dependencies
RUN apt-get -y install npm RUN apt-get -y install npm
# Install Interface dependencies
RUN apt-get -y install pkg-config libxext-dev libdouble-conversion-dev libpcre2-16-0 libpulse0 libharfbuzz-dev libnss3 libnspr4 libxdamage1 libasound2 vulkan-validationlayers libvulkan-dev libvulkan1
# Install tools for package creation # Install tools for package creation
RUN apt-get -y install sudo chrpath binutils dh-make RUN apt-get -y install sudo chrpath binutils dh-make

View file

@ -1,8 +1,8 @@
# Copyright 2022-2023 Overte e.V. # Copyright 2022-2024 Overte e.V.
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
# Docker file for building Overte # Docker file for building Overte
# Example build: docker build -t overte/overte-full-build:0.1.1-ubuntu-22.04 -f Dockerfile_build_ubuntu-22.04 . # Example build: docker build -t overte/overte-full-build:0.1.2-ubuntu-22.04 -f Dockerfile_build_ubuntu-22.04 .
FROM ubuntu:22.04 FROM ubuntu:22.04
LABEL maintainer="Julian Groß (julian.gro@overte.org)" LABEL maintainer="Julian Groß (julian.gro@overte.org)"
LABEL description="Development image for full Overte builds" LABEL description="Development image for full Overte builds"
@ -19,7 +19,7 @@ RUN apt-get -y install curl ninja-build git cmake g++ libssl-dev libqt5websocket
# Install Overte tools build dependencies # Install Overte tools build dependencies
RUN apt-get -y install libqt5webchannel5-dev qtwebengine5-dev libqt5xmlpatterns5-dev RUN apt-get -y install libqt5webchannel5-dev qtwebengine5-dev libqt5xmlpatterns5-dev
# Install Overte Interface build dependencies # Install Overte Interface build dependencies
RUN apt-get -y install libqt5svg5-dev qttools5-dev RUN apt-get -y install libqt5svg5-dev qttools5-dev vulkan-validationlayers libvulkan-dev libvulkan1 libqt5x11extras5-dev qtbase5-private-dev
# Install server-console build dependencies # Install server-console build dependencies
RUN apt-get -y install npm RUN apt-get -y install npm

View file

@ -1,20 +0,0 @@
# Copyright 2022-2024 Overte e.V.
# SPDX-License-Identifier: Apache-2.0
# Docker file for building Overte Server
# Example build: docker build -t overte/overte-server-build:0.1.4-fedora-39 -f Dockerfile_build_fedora-39 .
FROM fedora:39
LABEL maintainer="Julian Groß (julian.gro@overte.org)"
LABEL description="Development image for Overte Domain server and assignment clients."
# Install Overte domain-server and assignment-client build dependencies
RUN dnf -y install curl ninja-build git cmake gcc gcc-c++ openssl-devel qt5-qtwebsockets-devel qt5-qtmultimedia-devel unzip libXext-devel qt5-qtwebchannel-devel qt5-qtwebengine-devel qt5-qtxmlpatterns-devel systemd-devel python3.11
# Install additional build tools
RUN dnf -y install zip unzip
# Install tools for package creation
RUN dnf -y install chrpath rpmdevtools
# Install tools needed for our Github Actions Workflow
Run dnf -y install python3-boto3 python3-pygithub

View file

@ -71,7 +71,7 @@ exports.handlers = {
]; ];
// only files with this extension will be searched for jsdoc comments. // only files with this extension will be searched for jsdoc comments.
var exts = ['.h', '.cpp']; var exts = ['.h', '.h.in', '.cpp', '.cpp.in'];
var fs = require('fs'); var fs = require('fs');
dirList.forEach(function (dir) { dirList.forEach(function (dir) {
@ -123,9 +123,9 @@ exports.handlers = {
// Append an Available In: sentence at the beginning of the namespace description. // Append an Available In: sentence at the beginning of the namespace description.
if (rows.length > 0) { if (rows.length > 0) {
var availableIn = "<p class='availableIn'><b>Supported Script Types:</b> " + rows.join(" &bull; ") + "</p>"; var availableIn = "<p class='availableIn'><b>Supported Script Types:</b> " + rows.join(" &bull; ") + "</p>";
e.doclet.description = availableIn + (e.doclet.description ? e.doclet.description : ""); e.doclet.description = availableIn + (e.doclet.description ? e.doclet.description : "");
} }
} }
if (e.doclet.kind === "function" && e.doclet.returns && e.doclet.returns[0].type if (e.doclet.kind === "function" && e.doclet.returns && e.doclet.returns[0].type

View file

@ -4,11 +4,11 @@
# - Check which commit you are building https://invent.kde.org/qt/qt/qt5/-/tree/kde/5.15 # - Check which commit you are building https://invent.kde.org/qt/qt/qt5/-/tree/kde/5.15
# - Adjust this file to include the commit hash you are building, the date, the number of threads you want to use (-j10), the platform, and the Qt and QtWebEngine versions. # - Adjust this file to include the commit hash you are building, the date, the number of threads you want to use (-j10), the platform, and the Qt and QtWebEngine versions.
# Keep in mind that building Qt requires a lot of memory. You should have over 1.2GiB of system memory available per thread. # Keep in mind that building Qt requires a lot of memory. You should have over 1.2GiB of system memory available per thread.
# - Run the build process with something like `PROGRESS_NO_TRUNC=1 DOCKER_BUILDKIT=1 BUILDKIT_STEP_LOG_MAX_SIZE=-1 docker build --progress plain -t overte-qt5:5.15.14-2024.06.17-kde_570f5b2105df1ea052bec0d6dbf8a00137274371 -f Dockerfile_Ubuntu_20.04_Qt5 .` # - Run the build process with something like `PROGRESS_NO_TRUNC=1 DOCKER_BUILDKIT=1 BUILDKIT_STEP_LOG_MAX_SIZE=-1 docker build --progress plain -t overte-qt5:5.15.16-2024.12.14-kde_32be154325bfba3ad2ba8bf75dad702f3588e8d3 -f Dockerfile_Ubuntu_20.04_Qt5 .`
# Buildkit is used to cache intermittent steps in case you need to modify something afterwards. # Buildkit is used to cache intermittent steps in case you need to modify something afterwards.
# - Once the build has completed, create a container from the image and export the created Qt package. # - Once the build has completed, create a container from the image and export the created Qt package.
# `docker create --name extract overte-qt5:5.15.14-2024.06.17-kde_570f5b2105df1ea052bec0d6dbf8a00137274371` # `docker create --name extract overte-qt5:5.15.16-2024.12.14-kde_32be154325bfba3ad2ba8bf75dad702f3588e8d3`
# `docker cp extract:qt5-install-5.15.14-2024.06.17-kde_570f5b2105df1ea052bec0d6dbf8a00137274371-ubuntu-20.04-amd64.tar.xz /path/on/host` # `docker cp extract:qt5-install-5.15.16-2024.12.14-kde_32be154325bfba3ad2ba8bf75dad702f3588e8d3-ubuntu-20.04-amd64.tar.xz /path/on/host`
# `docker rm extract` # `docker rm extract`
FROM ubuntu:20.04 FROM ubuntu:20.04
@ -47,7 +47,7 @@ RUN apt-get -y install git python gperf flex bison pkg-config mesa-utils libgl1-
RUN mkdir qt5-install && mkdir qt5-build RUN mkdir qt5-install && mkdir qt5-build
WORKDIR qt5-build WORKDIR qt5-build
RUN ../qt5/configure -force-debug-info -release -opensource -confirm-license -platform linux-g++ -recheck-all -nomake tests -nomake examples -skip qttranslations -skip qtserialport -skip qt3d -skip qtlocation -skip qtwayland -skip qtsensors -skip qtgamepad -skip qtcharts -skip qtx11extras -skip qtmacextras -skip qtvirtualkeyboard -skip qtpurchasing -skip qtdatavis3d -skip qtlottie -skip qtquick3d -skip qtpim -skip qtdocgallery -no-warnings-are-errors -no-pch -no-icu -prefix ../qt5-install RUN ../qt5/configure -force-debug-info -release -opensource -confirm-license -platform linux-g++ -recheck-all -nomake tests -nomake examples -skip qttranslations -skip qtserialport -skip qt3d -skip qtlocation -skip qtwayland -skip qtsensors -skip qtgamepad -skip qtcharts -skip qtmacextras -skip qtvirtualkeyboard -skip qtpurchasing -skip qtdatavis3d -skip qtlottie -skip qtquick3d -skip qtpim -skip qtdocgallery -no-warnings-are-errors -no-pch -no-icu -prefix ../qt5-install
RUN NINJAFLAGS='-j6' make -j6 RUN NINJAFLAGS='-j6' make -j6
@ -58,12 +58,12 @@ WORKDIR ../../qt5-install
RUN find . -name \*.prl -exec sed -i -e '/^QMAKE_PRL_BUILD_DIR/d' {} \; RUN find . -name \*.prl -exec sed -i -e '/^QMAKE_PRL_BUILD_DIR/d' {} \;
# Overwrite QtWebengine version to work around version conflicts # Overwrite QtWebengine version to work around version conflicts
RUN find . -name \Qt5WebEngine*Config.cmake -exec sed -i '' -e 's/5\.15\.17/5\.15\.14/g' {} \; RUN find . -name \Qt5WebEngine*Config.cmake -exec sed -i '' -e 's/5\.15\.19/5\.15\.16/g' {} \;
RUN cp lib/libQt5WebEngine.so.5.15.17 lib/libQt5WebEngine.so.5.15.14 RUN cp lib/libQt5WebEngine.so.5.15.19 lib/libQt5WebEngine.so.5.15.16
RUN cp lib/libQt5WebEngineCore.so.5.15.17 lib/libQt5WebEngineCore.so.5.15.14 RUN cp lib/libQt5WebEngineCore.so.5.15.19 lib/libQt5WebEngineCore.so.5.15.16
RUN cp lib/libQt5WebEngineWidgets.so.5.15.17 lib/libQt5WebEngineWidgets.so.5.15.14 RUN cp lib/libQt5WebEngineWidgets.so.5.15.19 lib/libQt5WebEngineWidgets.so.5.15.16
RUN cp lib/libQt5Pdf.so.5.15.17 lib/libQt5Pdf.so.5.15.14 RUN cp lib/libQt5Pdf.so.5.15.19 lib/libQt5Pdf.so.5.15.16
RUN cp lib/libQt5PdfWidgets.so.5.15.17 lib/libQt5PdfWidgets.so.5.15.14 RUN cp lib/libQt5PdfWidgets.so.5.15.19 lib/libQt5PdfWidgets.so.5.15.16
COPY ./qt.conf ./bin/ COPY ./qt.conf ./bin/
@ -71,4 +71,4 @@ COPY ./qt.conf ./bin/
RUN cp ../qt5-build/config.summary ./ RUN cp ../qt5-build/config.summary ./
WORKDIR .. WORKDIR ..
RUN XZ_OPT='-T0' tar -Jcvf qt5-install-5.15.14-2024.06.17-kde_570f5b2105df1ea052bec0d6dbf8a00137274371-ubuntu-20.04-amd64.tar.xz qt5-install RUN XZ_OPT='-T0' tar -Jcvf qt5-install-5.15.16-2024.12.14-kde_32be154325bfba3ad2ba8bf75dad702f3588e8d3-ubuntu-20.04-amd64.tar.xz qt5-install