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
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
image: docker.io/overte/overte-server-build:0.1.4-fedora-40-amd64
arch: amd64
@ -224,10 +214,6 @@ jobs:
else # RPM
if [ "${{ matrix.os }}" == "rockylinux-9" ]; then
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
echo "ARTIFACT_PATTERN=overte-server-$RPMVERSION-1.fc40.$INSTALLER_EXT" >> $GITHUB_ENV
else
@ -266,7 +252,7 @@ jobs:
- name: Archive cmake logs
if: always()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: 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: |
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 "APP_TARGET_NAME=$APP_NAME" >> $GITHUB_ENV
# Linux build variables
@ -82,7 +82,7 @@ jobs:
echo "ZIP_ARGS=-r" >> $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 "::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
fi
# Windows build variables
@ -91,7 +91,7 @@ jobs:
echo "ZIP_COMMAND=7z" >> $GITHUB_ENV
echo "ZIP_ARGS=a" >> $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 "symbols_archive=${BUILD_NUMBER}-${{ matrix.build_type }}-win-symbols.zip" >> $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
- name: Deploy API-docs
uses: SamKirkland/FTP-Deploy-Action@v4.3.4
uses: SamKirkland/FTP-Deploy-Action@v4.3.5
with:
server: www531.your-server.de
protocol: ftps

View file

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

View file

@ -64,8 +64,8 @@ jobs:
runner: [self_hosted, type-cx52, image-x86-app-docker-ce]
arch: amd64
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
image: docker.io/overte/overte-full-build:0.1.1-ubuntu-20.04-amd64
# apt-dependencies: # add missing dependencies to docker image when convenient
image: docker.io/overte/overte-full-build:0.1.2-ubuntu-20.04-amd64
# Android builds are currently failing
#- os: ubuntu-18.04
# build_type: android
@ -75,7 +75,7 @@ jobs:
runner: [self_hosted, type-cax41, image-arm-app-docker-ce]
arch: aarch64
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
runs-on: ${{matrix.runner}}
container: ${{matrix.image}}
@ -137,9 +137,9 @@ jobs:
echo "PYTHON_EXEC=python" >> $GITHUB_ENV
echo "INSTALLER_EXT=exe" >> $GITHUB_ENV
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
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
# Android + Quest build variables
@ -244,7 +244,7 @@ jobs:
- name: Archive cmake logs
if: always()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: 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
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 "APP_TARGET_NAME=$APP_NAME" >> $GITHUB_ENV
@ -101,7 +100,7 @@ jobs:
- name: Configure CMake
working-directory: ${{runner.workspace}}/build
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
working-directory: ${{runner.workspace}}/build

1
.gitignore vendored
View file

@ -51,6 +51,7 @@ local.properties
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.cache
# 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).
<!-- ## [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
- 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)
## [2024.06.1] 2023.06.24
## [2024.06.1] 2024.06.24
### Fixes
- 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 switch between screenshot formats (PR134)
### Removals
-
### Build system
- Fixed "may be used uninitialized" warning for blendtime (PR269)
- 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) {
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);
}
}
@ -584,6 +585,7 @@ void EntityScriptServer::checkAndCallPreload(const EntityItemID& entityID, bool
EntityScriptDetails details;
bool isRunning = _entitiesScriptManager->getEntityScriptDetails(entityID, details);
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) {
_entitiesScriptManager->unloadEntityScript(entityID, true);
}

View file

@ -157,7 +157,7 @@ endif()
u_major = int( distro.major_version() or '0' )
if distro.id() == 'ubuntu' or distro.id() == 'linuxmint':
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):
self.__no_qt_package_error()
else:

View file

@ -3,6 +3,7 @@
//
// Created by Stephen Birarda on 7 Dec 2016
// Copyright 2016 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
@ -395,7 +396,7 @@ Item {
text: signUpBody.termsContainerText
Component.onCompleted: {
// 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.topMargin: 10;
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)
NameCard {
id: myCard;
@ -1054,7 +1062,7 @@ Rectangle {
width: myCardWidth;
height: parent.height;
// Anchors
anchors.top: parent.top
anchors.top: parent.children[0].bottom;
anchors.left: parent.left;
}
Item {
@ -1066,7 +1074,7 @@ Rectangle {
RalewayRegular {
id: availabilityText;
text: "set availability";
text: "Availability";
// Text size
size: hifi.fontSizes.tabularData;
// Anchors

View file

@ -2,6 +2,8 @@
// Created by Dante Ruiz on 6/5/17.
// Copyright 2017 High Fidelity, Inc.
// Copyright 2020 Vircadia contributors.
// 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
@ -928,7 +930,7 @@ Flickable {
hoverEnabled: true
onEntered: privacyPolicyUnderline.visible = true;
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_PREFER_STYLUS_OVER_LASER = 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_AWAY_STATE_WHEN_FOCUS_LOST_IN_VR_ENABLED = true;
@ -224,6 +225,7 @@ Application::Application(
_hmdTabletBecomesToolbarSetting("hmdTabletBecomesToolbar", DEFAULT_HMD_TABLET_BECOMES_TOOLBAR),
_preferStylusOverLaserSetting("preferStylusOverLaser", DEFAULT_PREFER_STYLUS_OVER_LASER),
_preferAvatarFingerOverStylusSetting("preferAvatarFingerOverStylus", DEFAULT_PREFER_AVATAR_FINGER_OVER_STYLUS),
_showGraphicsIconSetting("showGraphicsIcon", DEFAULT_SHOW_GRAPHICS_ICON),
_constrainToolbarPosition("toolbar/constrainToolbarToCenterX", true),
_awayStateWhenFocusLostInVREnabled("awayStateWhenFocusLostInVREnabled", DEFAULT_AWAY_STATE_WHEN_FOCUS_LOST_IN_VR_ENABLED),
_preferredCursor("preferredCursor", DEFAULT_CURSOR_NAME),

View file

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

View file

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

View file

@ -76,6 +76,8 @@
#include "WarningsSuppression.h"
#include "ScriptPermissions.h"
#include "Application.h"
using namespace std;
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),
_yawSpeedSetting(QStringList() << AVATAR_SETTINGS_GROUP_NAME << "yawSpeed", _yawSpeed),
_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),
_fullAvatarURLSetting(QStringList() << SETTINGS_FULL_PRIVATE_GROUP_NAME << AVATAR_SETTINGS_GROUP_NAME << "fullAvatarURL",
AvatarData::defaultFullAvatarModelUrl()),
@ -1325,6 +1328,7 @@ void MyAvatar::saveData() {
_scaleSetting.set(_targetScale);
_yawSpeedSetting.set(_yawSpeed);
_hmdYawSpeedSetting.set(_hmdYawSpeed);
_cameraSensitivitySetting.set(getCameraSensitivity());
_pitchSpeedSetting.set(_pitchSpeed);
// only save the fullAvatarURL if it has not been overwritten on command line
@ -2085,6 +2089,7 @@ void MyAvatar::loadData() {
_yawSpeed = _yawSpeedSetting.get(_yawSpeed);
_hmdYawSpeed = _hmdYawSpeedSetting.get(_hmdYawSpeed);
setCameraSensitivity(_cameraSensitivitySetting.get(getCameraSensitivity()));
_pitchSpeed = _pitchSpeedSetting.get(_pitchSpeed);
_prefOverrideAnimGraphUrl.set(_animGraphURLSetting.get().toString());
@ -7003,3 +7008,11 @@ void MyAvatar::resetPointAt() {
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; }
void setHMDYawSpeed(float speed) { _hmdYawSpeed = speed; }
float getCameraSensitivity() const;
void setCameraSensitivity(float cameraSensitivity);
static const float ZOOM_MIN;
static const float ZOOM_MAX;
static const float ZOOM_DEFAULT;
@ -3007,6 +3010,7 @@ private:
Setting::Handle<float> _scaleSetting;
Setting::Handle<float> _yawSpeedSetting;
Setting::Handle<float> _hmdYawSpeedSetting;
Setting::Handle<float> _cameraSensitivitySetting;
Setting::Handle<float> _pitchSpeedSetting;
Setting::Handle<QUrl> _fullAvatarURLSetting;
Setting::Handle<QUrl> _fullAvatarModelNameSetting;

View file

@ -225,6 +225,12 @@ void setupPreferences() {
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" };
{
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)");
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
@ -1010,18 +1010,18 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const
// Compute background position/size
static const float SLIGHTLY_IN_FRONT = 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 left = text_x - border;
const int bottom = text_y - border;
const int width = nameDynamicRect.width() + 2.0f * border;
// FIXME: Beveled box is broken
// const int left = text_x - 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 bevelDistance = BEVEL_FACTOR * height;
// const int bevelDistance = BEVEL_FACTOR * height;
// Display name and background colors
glm::vec4 textColor(0.93f, 0.93f, 0.93f, _displayNameAlpha);
glm::vec4 backgroundColor(0.2f, 0.2f, 0.2f,
(_displayNameAlpha / DISPLAYNAME_ALPHA) * DISPLAYNAME_BACKGROUND_ALPHA);
glm::vec4 backgroundColor(0.2f, 0.2f, 0.2f,(_displayNameAlpha / DISPLAYNAME_ALPHA) * DISPLAYNAME_BACKGROUND_ALPHA);
// Compute display name transform
auto textTransform = calculateDisplayNameTransform(view, textPosition);
@ -1029,12 +1029,11 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const
textTransform.postScale(1.0f / height);
batch.setModelTransform(textTransform);
{
PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderBevelCornersRect");
DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, false, true, true, true, forward);
DependencyManager::get<GeometryCache>()->renderBevelCornersRect(batch, left, bottom, width, height,
bevelDistance, backgroundColor, _nameRectGeometryID);
}
// {
// PROFILE_RANGE_BATCH(batch, __FUNCTION__":renderBevelCornersRect");
// DependencyManager::get<GeometryCache>()->bindSimpleProgram(batch, false, false, true, true, true, forward);
// DependencyManager::get<GeometryCache>()->renderBevelCornersRect(batch, left, bottom, width, height, bevelDistance, backgroundColor, _nameRectGeometryID);
// }
// Render actual name
QByteArray nameUTF8 = renderedDisplayName.toLocal8Bit();
@ -1044,7 +1043,7 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& view, const
batch.setModelTransform(textTransform);
{
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() {
// This runs script engine shutdown procedure in a separate thread, avoiding a deadlock when script engine is doing
// a blocking call to main thread
if (_persistentEntitiesScriptManager) {
QtConcurrent::run([manager = _persistentEntitiesScriptManager] {
ScriptManagerPointer scriptManager = _persistentEntitiesScriptManager;
// Clear the pointer before lambda is run on another thread.
_persistentEntitiesScriptManager.reset();
if (scriptManager) {
QtConcurrent::run([manager = scriptManager] {
manager->unloadAllEntityScripts(true);
manager->stop();
manager->waitTillDoneRunning();
@ -251,8 +254,11 @@ void EntityTreeRenderer::resetPersistentEntitiesScriptEngine() {
void EntityTreeRenderer::resetNonPersistentEntitiesScriptEngine() {
// This runs script engine shutdown procedure in a separate thread, avoiding a deadlock when script engine is doing
// a blocking call to main thread
if (_nonPersistentEntitiesScriptManager) {
QtConcurrent::run([manager = _nonPersistentEntitiesScriptManager] {
ScriptManagerPointer scriptManager = _nonPersistentEntitiesScriptManager;
// Release the pointer as soon as possible.
_nonPersistentEntitiesScriptManager.reset();
if (scriptManager) {
QtConcurrent::run([manager = scriptManager] {
manager->unloadAllEntityScripts(true);
manager->stop();
manager->waitTillDoneRunning();
@ -288,7 +294,10 @@ void EntityTreeRenderer::stopDomainAndNonOwnedEntities() {
EntityItemPointer entityItem = getTree()->findEntityByEntityItemID(entityID);
if (entityItem && !entityItem->getScript().isEmpty()) {
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)) {
scriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
}
scriptEngine->unloadEntityScript(entityID, true);
QMetaObject::invokeMethod(scriptEngine.get(), [scriptEngine, entityID]{
scriptEngine->unloadEntityScript(entityID, true);
});
}
auto scene = _viewState->getMain3DScene();
@ -1163,7 +1174,9 @@ void EntityTreeRenderer::checkAndCallPreload(const EntityItemID& entityID, bool
if (_currentEntitiesInside.contains(entityID)) {
scriptEngine->callEntityScriptMethod(entityID, "leaveEntity");
}
scriptEngine->unloadEntityScript(entityID);
QMetaObject::invokeMethod(scriptEngine.get(), [scriptEngine, entityID]{
scriptEngine->unloadEntityScript(entityID);
});
}
entity->scriptHasUnloaded();
}

View file

@ -4,6 +4,7 @@
//
// Created by Clement on 4/22/15.
// Copyright 2015 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
@ -39,46 +40,46 @@ ZoneEntityRenderer::ZoneEntityRenderer(const EntityItemPointer& entity)
void ZoneEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity) {
if (_stage) {
if (!LightStage::isIndexInvalid(_sunIndex)) {
_stage->removeLight(_sunIndex);
_stage->removeElement(_sunIndex);
_sunIndex = INVALID_INDEX;
}
if (!LightStage::isIndexInvalid(_ambientIndex)) {
_stage->removeLight(_ambientIndex);
_stage->removeElement(_ambientIndex);
_ambientIndex = INVALID_INDEX;
}
}
if (_backgroundStage) {
if (!BackgroundStage::isIndexInvalid(_backgroundIndex)) {
_backgroundStage->removeBackground(_backgroundIndex);
_backgroundStage->removeElement(_backgroundIndex);
_backgroundIndex = INVALID_INDEX;
}
}
if (_hazeStage) {
if (!HazeStage::isIndexInvalid(_hazeIndex)) {
_hazeStage->removeHaze(_hazeIndex);
_hazeStage->removeElement(_hazeIndex);
_hazeIndex = INVALID_INDEX;
}
}
if (_bloomStage) {
if (!BloomStage::isIndexInvalid(_bloomIndex)) {
_bloomStage->removeBloom(_bloomIndex);
_bloomStage->removeElement(_bloomIndex);
_bloomIndex = INVALID_INDEX;
}
}
if (_tonemappingStage) {
if (!TonemappingStage::isIndexInvalid(_tonemappingIndex)) {
_tonemappingStage->removeTonemapping(_tonemappingIndex);
_tonemappingStage->removeElement(_tonemappingIndex);
_tonemappingIndex = INVALID_INDEX;
}
}
if (_ambientOcclusionStage) {
if (!AmbientOcclusionStage::isIndexInvalid(_ambientOcclusionIndex)) {
_ambientOcclusionStage->removeAmbientOcclusion(_ambientOcclusionIndex);
_ambientOcclusionStage->removeElement(_ambientOcclusionIndex);
_ambientOcclusionIndex = INVALID_INDEX;
}
}
@ -123,7 +124,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
{ // Sun
if (_needSunUpdate) {
if (LightStage::isIndexInvalid(_sunIndex)) {
_sunIndex = _stage->addLight(_sunLight);
_sunIndex = _stage->addElement(_sunLight);
} else {
_stage->updateLightArrayBuffer(_sunIndex);
}
@ -136,7 +137,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
if (_needAmbientUpdate) {
if (LightStage::isIndexInvalid(_ambientIndex)) {
_ambientIndex = _stage->addLight(_ambientLight);
_ambientIndex = _stage->addElement(_ambientLight);
} else {
_stage->updateLightArrayBuffer(_ambientIndex);
}
@ -149,7 +150,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
if (_needBackgroundUpdate) {
if (BackgroundStage::isIndexInvalid(_backgroundIndex)) {
_backgroundIndex = _backgroundStage->addBackground(_background);
_backgroundIndex = _backgroundStage->addElement(_background);
}
_needBackgroundUpdate = false;
}
@ -158,7 +159,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
{
if (_needHazeUpdate) {
if (HazeStage::isIndexInvalid(_hazeIndex)) {
_hazeIndex = _hazeStage->addHaze(_haze);
_hazeIndex = _hazeStage->addElement(_haze);
}
_needHazeUpdate = false;
}
@ -167,7 +168,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
{
if (_needBloomUpdate) {
if (BloomStage::isIndexInvalid(_bloomIndex)) {
_bloomIndex = _bloomStage->addBloom(_bloom);
_bloomIndex = _bloomStage->addElement(_bloom);
}
_needBloomUpdate = false;
}
@ -176,7 +177,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
{
if (_needTonemappingUpdate) {
if (TonemappingStage::isIndexInvalid(_tonemappingIndex)) {
_tonemappingIndex = _tonemappingStage->addTonemapping(_tonemapping);
_tonemappingIndex = _tonemappingStage->addElement(_tonemapping);
}
_needTonemappingUpdate = false;
}
@ -185,7 +186,7 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
{
if (_needAmbientOcclusionUpdate) {
if (AmbientOcclusionStage::isIndexInvalid(_ambientOcclusionIndex)) {
_ambientOcclusionIndex = _ambientOcclusionStage->addAmbientOcclusion(_ambientOcclusion);
_ambientOcclusionIndex = _ambientOcclusionStage->addElement(_ambientOcclusion);
}
_needAmbientOcclusionUpdate = false;
}
@ -205,9 +206,9 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) {
}
if (_skyboxMode == COMPONENT_MODE_DISABLED) {
_backgroundStage->_currentFrame.pushBackground(INVALID_INDEX);
_backgroundStage->_currentFrame.pushElement(INVALID_INDEX);
} else if (_skyboxMode == COMPONENT_MODE_ENABLED) {
_backgroundStage->_currentFrame.pushBackground(_backgroundIndex);
_backgroundStage->_currentFrame.pushElement(_backgroundIndex);
}
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
if (_hazeMode != COMPONENT_MODE_INHERIT) {
_hazeStage->_currentFrame.pushHaze(_hazeIndex);
_hazeStage->_currentFrame.pushElement(_hazeIndex);
}
if (_bloomMode == COMPONENT_MODE_DISABLED) {
_bloomStage->_currentFrame.pushBloom(INVALID_INDEX);
_bloomStage->_currentFrame.pushElement(INVALID_INDEX);
} else if (_bloomMode == COMPONENT_MODE_ENABLED) {
_bloomStage->_currentFrame.pushBloom(_bloomIndex);
_bloomStage->_currentFrame.pushElement(_bloomIndex);
}
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) {
_tonemappingStage->_currentFrame.pushTonemapping(_tonemappingIndex);
_tonemappingStage->_currentFrame.pushElement(_tonemappingIndex);
}
if (_ambientOcclusionMode == COMPONENT_MODE_DISABLED) {
_ambientOcclusionStage->_currentFrame.pushAmbientOcclusion(INVALID_INDEX);
_ambientOcclusionStage->_currentFrame.pushElement(INVALID_INDEX);
} 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@
// 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;
}

View file

@ -30,7 +30,7 @@ group:grab,
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,
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: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",

View file

@ -935,7 +935,9 @@
*
* @typedef {object} Entities.EntityProperties-Image
* @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
* shouldn't.
* @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));
}
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
// Well porting to gles i need it...
void runLambda(std::function<void()> f);

View file

@ -4,6 +4,7 @@
//
// Created by Nissim Hadar on 9/13/2017.
// Copyright 2014 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
@ -182,3 +183,7 @@ void Haze::setHazeBackgroundBlend(const float hazeBackgroundBlend) {
_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.
// Copyright 2014 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
@ -95,6 +96,8 @@ namespace graphics {
using UniformBufferView = gpu::BufferView;
UniformBufferView getHazeParametersBuffer() const { return _hazeParametersBuffer; }
bool isActive() const;
protected:
class Parameters {
public:

View file

@ -254,6 +254,18 @@ NetworkTexturePointer TextureCache::getTexture(const QUrl& url, image::TextureUs
if (url.scheme() == RESOURCE_SCHEME) {
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());
if (decodedURL.startsWith("{")) {
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());
}
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);
}
}

View file

@ -356,16 +356,53 @@ void Procedural::prepare(gpu::Batch& batch,
}
}
// 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()) {
std::string uniformName = key.toLocal8Bit().data();
for (auto reflection : allFragmentReflections) {
reflection->uniforms[uniformName] = customSlot;
bool isArrayUniform = false;
size_t numSlots = 0;
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() {
_uniforms.clear();
// Set any userdata specified uniforms
int slot = procedural::slot::uniform::Custom;
for (const auto& key : _data.uniforms.keys()) {
std::string uniformName = key.toLocal8Bit().data();
QJsonValue value = _data.uniforms[key];
const std::string uniformName = key.toLocal8Bit().data();
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()) {
float v = value.toDouble();
const float v = value.toDouble();
_uniforms.push_back([slot, v](gpu::Batch& batch) { batch._glUniform1f(slot, v); });
} else if (value.isArray()) {
auto valueArray = value.toArray();
switch (valueArray.size()) {
case 0:
break;
case 1: {
float v = valueArray[0].toDouble();
_uniforms.push_back([slot, v](gpu::Batch& batch) { batch._glUniform1f(slot, v); });
break;
}
case 2: {
glm::vec2 v{ valueArray[0].toDouble(), valueArray[1].toDouble() };
_uniforms.push_back([slot, v](gpu::Batch& batch) { batch._glUniform2f(slot, v.x, v.y); });
break;
}
case 3: {
glm::vec3 v{
valueArray[0].toDouble(),
valueArray[1].toDouble(),
valueArray[2].toDouble(),
};
_uniforms.push_back([slot, v](gpu::Batch& batch) { batch._glUniform3f(slot, v.x, v.y, v.z); });
break;
}
default:
case 4: {
glm::vec4 v{
valueArray[0].toDouble(),
valueArray[1].toDouble(),
valueArray[2].toDouble(),
valueArray[3].toDouble(),
};
_uniforms.push_back([slot, v](gpu::Batch& batch) { batch._glUniform4f(slot, v.x, v.y, v.z, v.w); });
break;
}
const QJsonArray valueArray = value.toArray();
if (valueArray.size() > 0) {
if (valueArray[0].isArray()) {
const size_t valueLength = valueArray[0].toArray().size();
std::vector<float> vs;
vs.reserve(valueLength * valueArray.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) {
for (size_t i = 0; i < valueLength; i++) {
vs.push_back(innerValueArray[i].toDouble());
}
count++;
}
}
}
}
if (count > 0) {
switch (valueLength) {
case 3: {
_uniforms.push_back([slot, vs, count](gpu::Batch& batch) { batch._glUniform3fv(slot, count, vs.data()); });
break;
}
case 4: {
_uniforms.push_back([slot, vs, count](gpu::Batch& batch) { batch._glUniform4fv(slot, count, vs.data()); });
break;
}
case 9: {
_uniforms.push_back([slot, vs, count](gpu::Batch& batch) { batch._glUniformMatrix3fv(slot, count, false, vs.data()); });
break;
}
case 16: {
_uniforms.push_back([slot, vs, count](gpu::Batch& batch) { batch._glUniformMatrix4fv(slot, count, false, vs.data()); });
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) {
@ -578,4 +694,4 @@ void graphics::ProceduralMaterial::initializeProcedural() {
_procedural._transparentFragmentSource = gpu::Shader::getFragmentShaderSource(shader::render_utils::fragment::simple_procedural_translucent);
_procedural._errorFallbackFragmentPath = ":" + QUrl("qrc:///shaders/errorShader.frag").path();
}
}

View file

@ -190,6 +190,7 @@ protected:
NetworkTexturePointer _channels[MAX_PROCEDURAL_TEXTURE_CHANNELS];
std::unordered_map<std::string, std::string> _vertexReplacements;
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> _errorPipelines;

View file

@ -4,6 +4,7 @@
//
// Created by Niraj Venkat on 7/15/15.
// Copyright 2015 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
@ -606,8 +607,8 @@ void AmbientOcclusionEffect::run(const render::RenderContextPointer& renderConte
graphics::AmbientOcclusionPointer ambientOcclusion;
if (_debug) {
ambientOcclusion = _debugAmbientOcclusion;
} else if (ambientOcclusionStage && ambientOcclusionFrame->_ambientOcclusions.size()) {
ambientOcclusion = ambientOcclusionStage->getAmbientOcclusion(ambientOcclusionFrame->_ambientOcclusions.front());
} else if (ambientOcclusionStage && ambientOcclusionFrame->_elements.size()) {
ambientOcclusion = ambientOcclusionStage->getElement(ambientOcclusionFrame->_elements.front());
}
if (!ambientOcclusion || !lightingModel->isAmbientOcclusionEnabled()) {

View file

@ -7,50 +7,8 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AmbientOcclusionStage.h"
#include <gpu/Context.h>
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>());
}
}
template <>
std::string render::PointerStage<graphics::AmbientOcclusion, graphics::AmbientOcclusionPointer>::_name { "AMBIENT_OCCLUSION_STAGE" };

View file

@ -11,74 +11,17 @@
#ifndef 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 <render/Stage.h>
#include <render/StageSetup.h>
// AmbientOcclusion stage to set up ambientOcclusion-related rendering tasks
class AmbientOcclusionStage : public render::Stage {
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;
};
class AmbientOcclusionStage : public render::PointerStage<graphics::AmbientOcclusion, graphics::AmbientOcclusionPointer> {};
using AmbientOcclusionStagePointer = std::shared_ptr<AmbientOcclusionStage>;
class AmbientOcclusionStageSetup {
class AmbientOcclusionStageSetup : public render::StageSetup<AmbientOcclusionStage> {
public:
using JobModel = render::Job::Model<AmbientOcclusionStageSetup>;
AmbientOcclusionStageSetup();
void run(const render::RenderContextPointer& renderContext);
protected:
};
#endif

View file

@ -1,6 +1,7 @@
//
// Created by Samuel Gateau on 2018/12/06
// Copyright 2013-2018 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

View file

@ -3,58 +3,20 @@
//
// Created by Sam Gateau on 5/9/2017.
// Copyright 2015 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 "BackgroundStage.h"
#include "DeferredLightingEffect.h"
#include <gpu/Context.h>
#include <graphics/ShaderConstants.h>
std::string BackgroundStage::_stageName { "BACKGROUND_STAGE" };
const BackgroundStage::Index BackgroundStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
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;
}
template <>
std::string render::PointerStage<graphics::SunSkyStage, graphics::SunSkyStagePointer>::_name { "BACKGROUND_STAGE" };
void DrawBackgroundStage::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) {
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& backgroundFrame = inputs.get1();
graphics::SkyboxPointer skybox;
if (backgroundStage && backgroundFrame->_backgrounds.size()) {
const auto& background = backgroundStage->getBackground(backgroundFrame->_backgrounds.front());
if (backgroundStage && backgroundFrame->_elements.size()) {
const auto& background = backgroundStage->getElement(backgroundFrame->_elements.front());
if (background) {
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 (args->_renderMethod == render::Args::RenderMethod::FORWARD) {
const auto& hazeStage = args->_scene->getStage<HazeStage>();
if (hazeStage && hazeFrame->_hazes.size() > 0) {
const auto& hazePointer = hazeStage->getHaze(hazeFrame->_hazes.front());
if (hazeStage && hazeFrame->_elements.size() > 0) {
const auto& hazePointer = hazeStage->getElement(hazeFrame->_elements.front());
if (hazePointer) {
batch.setUniformBuffer(graphics::slot::buffer::Buffer::HazeParams, hazePointer->getHazeParametersBuffer());
}
@ -111,14 +73,3 @@ void DrawBackgroundStage::run(const render::RenderContextPointer& renderContext,
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
//
// Created by Sam Gateau on 5/9/2017.
// Copyright 2015 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
@ -12,72 +13,19 @@
#define hifi_render_utils_BackgroundStage_h
#include <graphics/Stage.h>
#include <set>
#include <unordered_map>
#include <render/IndexedContainer.h>
#include <render/Stage.h>
#include "HazeStage.h"
#include <render/StageSetup.h>
#include "HazeStage.h"
#include "LightingModel.h"
// Background stage to set up background-related rendering tasks
class BackgroundStage : public render::Stage {
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;
};
class BackgroundStage : public render::PointerStage<graphics::SunSkyStage, graphics::SunSkyStagePointer> {};
using BackgroundStagePointer = std::shared_ptr<BackgroundStage>;
class BackgroundStageSetup {
class BackgroundStageSetup : public render::StageSetup<BackgroundStage> {
public:
using JobModel = render::Job::Model<BackgroundStageSetup>;
BackgroundStageSetup();
void run(const render::RenderContextPointer& renderContext);
};
class DrawBackgroundStage {

View file

@ -4,6 +4,7 @@
//
// Created by Olivier Prat on 09/25/17.
// 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
@ -43,8 +44,8 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons
const auto lightingModel = inputs.get3();
const auto& bloomStage = renderContext->_scene->getStage<BloomStage>();
graphics::BloomPointer bloom;
if (bloomStage && bloomFrame->_blooms.size()) {
bloom = bloomStage->getBloom(bloomFrame->_blooms.front());
if (bloomStage && bloomFrame->_elements.size()) {
bloom = bloomStage->getElement(bloomFrame->_elements.front());
}
if (!bloom || (lightingModel && !lightingModel->isBloomEnabled())) {
renderContext->taskFlow.abortTask();

View file

@ -3,56 +3,13 @@
//
// Created by Sam Gondelman on 8/7/2018
// Copyright 2018 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 "BloomStage.h"
#include "DeferredLightingEffect.h"
#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>());
}
}
template <>
std::string render::PointerStage<graphics::Bloom, graphics::BloomPointer>::_name { "BLOOM_STAGE" };

View file

@ -3,6 +3,7 @@
// Created by Sam Gondelman on 8/7/2018
// Copyright 2018 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
@ -11,74 +12,17 @@
#ifndef 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 <render/Stage.h>
#include <render/StageSetup.h>
// Bloom stage to set up bloom-related rendering tasks
class BloomStage : public render::Stage {
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;
};
class BloomStage : public render::PointerStage<graphics::Bloom, graphics::BloomPointer> {};
using BloomStagePointer = std::shared_ptr<BloomStage>;
class BloomStageSetup {
class BloomStageSetup : public render::StageSetup<BloomStage> {
public:
using JobModel = render::Job::Model<BloomStageSetup>;
BloomStageSetup();
void run(const render::RenderContextPointer& renderContext);
protected:
};
#endif

View file

@ -4,6 +4,7 @@
//
// Created by Andrzej Kapolka on 9/11/14.
// Copyright 2014 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
@ -375,7 +376,7 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext,
// Global directional light, maybe shadow and ambient pass
auto lightStage = renderContext->_scene->getStage<LightStage>();
assert(lightStage);
assert(lightStage->getNumLights() > 0);
assert(lightStage->getNumElements() > 0);
auto keyLight = lightStage->getCurrentKeyLight(*lightFrame);
// Check if keylight casts shadows
@ -391,7 +392,7 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext,
// Global Ambient light
graphics::LightPointer ambientLight;
if (lightStage && lightFrame->_ambientLights.size()) {
ambientLight = lightStage->getLight(lightFrame->_ambientLights.front());
ambientLight = lightStage->getElement(lightFrame->_ambientLights.front());
}
bool hasAmbientMap = (ambientLight != nullptr);
@ -430,8 +431,8 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext,
// Haze
const auto& hazeStage = args->_scene->getStage<HazeStage>();
if (hazeStage && hazeFrame->_hazes.size() > 0) {
const auto& hazePointer = hazeStage->getHaze(hazeFrame->_hazes.front());
if (hazeStage && hazeFrame->_elements.size() > 0) {
const auto& hazePointer = hazeStage->getElement(hazeFrame->_elements.front());
if (hazePointer) {
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)
// Set this light to be the default
_defaultLightID = lightStage->addLight(lp, true);
_defaultLightID = lightStage->addElement(lp, true);
}
auto backgroundStage = renderContext->_scene->getStage<BackgroundStage>();
@ -649,7 +650,7 @@ void DefaultLightingSetup::run(const RenderContextPointer& renderContext) {
_defaultBackground = background;
// 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>();
_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>();
_defaultTonemapping = tonemapping;
_defaultTonemappingID = tonemappingStage->addTonemapping(_defaultTonemapping);
_defaultTonemappingID = tonemappingStage->addElement(_defaultTonemapping);
}
}
}

View file

@ -4,6 +4,7 @@
//
// Created by Nissim Hadar on 9/1/2017.
// Copyright 2015 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
@ -39,8 +40,8 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu
const auto hazeFrame = inputs.get0();
const auto& hazeStage = renderContext->args->_scene->getStage<HazeStage>();
graphics::HazePointer haze;
if (hazeStage && hazeFrame->_hazes.size() > 0) {
haze = hazeStage->getHaze(hazeFrame->_hazes.front());
if (hazeStage && hazeFrame->_elements.size() > 0) {
haze = hazeStage->getElement(hazeFrame->_elements.front());
}
if (!haze) {
@ -55,6 +56,10 @@ void DrawHaze::run(const render::RenderContextPointer& renderContext, const Inpu
auto depthBuffer = framebuffer->getLinearDepthTexture();
if (!lightingModel->isHazeEnabled() || !haze->isActive()) {
return;
}
RenderArgs* args = renderContext->args;
if (!_hazePipeline) {

View file

@ -3,6 +3,7 @@
// Created by Olivier Prat on 17/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
@ -28,10 +29,8 @@ FadeEffect::FadeEffect() {
}
void FadeEffect::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) {
auto editedFadeCategory = task.addJob<FadeJob>("Fade");
const auto fadeEditInput = FadeEditJob::Input(inputs, editedFadeCategory).asVarying();
task.addJob<FadeEditJob>("FadeEdit", fadeEditInput);
const auto editedFadeCategory = task.addJob<FadeJob>("Fade");
task.addJob<FadeEditJob>("FadeEdit", editedFadeCategory);
}
render::ShapePipeline::BatchSetter FadeEffect::getBatchSetter() {
@ -47,7 +46,7 @@ render::ShapePipeline::ItemSetter FadeEffect::getItemUniformSetter() {
const auto& scene = args->_scene;
const auto& batch = args->_batch;
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>)) {
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 {
public:
using Input = render::ItemBounds;
using JobModel = render::Task::ModelI<FadeEffect, render::ItemBounds>;
using JobModel = render::Task::Model<FadeEffect>;
FadeEffect();

View file

@ -3,6 +3,7 @@
// 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
@ -35,7 +36,7 @@ void FadeEditJob::configure(const Config& config) {
_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;
if (_isEditEnabled) {
@ -63,7 +64,7 @@ void FadeEditJob::run(const render::RenderContextPointer& renderContext, const F
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) {
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
for (auto transitionId : *transitionStage) {
auto& state = transitionStage->editTransition(transitionId);
auto& state = transitionStage->editElement(transitionId);
#ifdef DEBUG
auto& item = scene->getItem(state.itemId);
assert(item.getTransitionId() == transitionId);

View file

@ -177,13 +177,13 @@ class FadeEditJob {
public:
using Config = FadeEditConfig;
using Input = render::VaryingSet2<render::ItemBounds, FadeCategory>;
using Input = FadeCategory;
using JobModel = render::Job::ModelI<FadeEditJob, Input, Config>;
FadeEditJob() {}
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:

View file

@ -6,6 +6,7 @@
//
// Created by Nissim Hadar on 9/5/2107.
// Copyright 2016 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
@ -45,10 +46,6 @@ layout(location=0) in vec2 varTexCoord0;
layout(location=0) out vec4 outFragColor;
void main(void) {
if ((isHazeEnabled() == 0.0) || (hazeParams.hazeMode & HAZE_MODE_IS_ACTIVE) != HAZE_MODE_IS_ACTIVE) {
discard;
}
vec4 fragPositionES = unpackPositionFromZeye(varTexCoord0);
mat4 viewInverse = getViewInverse();

View file

@ -3,59 +3,13 @@
//
// Created by Nissim Hadar on 9/26/2017.
// Copyright 2015 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 "HazeStage.h"
#include "DeferredLightingEffect.h"
#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>());
}
}
template <>
std::string render::PointerStage<graphics::Haze, graphics::HazePointer>::_name { "HAZE_STAGE" };

View file

@ -3,6 +3,7 @@
// Created by Nissim Hadar on 9/26/2017.
// Copyright 2015 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
@ -11,74 +12,17 @@
#ifndef 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 <render/Stage.h>
#include <render/StageSetup.h>
// Haze stage to set up haze-related rendering tasks
class HazeStage : public render::Stage {
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;
};
class HazeStage : public render::PointerStage<graphics::Haze, graphics::HazePointer> {};
using HazeStagePointer = std::shared_ptr<HazeStage>;
class HazeStageSetup {
class HazeStageSetup : public render::StageSetup<HazeStage> {
public:
using JobModel = render::Job::Model<HazeStageSetup>;
HazeStageSetup();
void run(const render::RenderContextPointer& renderContext);
protected:
};
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)) {
auto resources = inputs.get1();
auto& highlight = highlightStage->getHighlight(highlightId);
auto& highlight = highlightStage->getElement(highlightId);
RenderArgs* args = renderContext->args;
@ -247,7 +247,7 @@ void DrawHighlight::run(const render::RenderContextPointer& renderContext, const
auto highlightStage = renderContext->_scene->getStage<render::HighlightStage>();
auto highlightId = _sharedParameters->_highlightIds[_highlightPassIndex];
if (!render::HighlightStage::isIndexInvalid(highlightId)) {
auto& highlight = highlightStage->getHighlight(highlightId);
auto& highlight = highlightStage->getElement(highlightId);
auto pipeline = getPipeline(highlight._style);
{
auto& shaderParameters = _configuration.edit();
@ -432,7 +432,7 @@ void SelectionToHighlight::run(const render::RenderContextPointer& renderContext
auto highlightList = highlightStage->getActiveHighlightIds();
for (auto styleId : highlightList) {
auto highlight = highlightStage->getHighlight(styleId);
auto highlight = highlightStage->getElement(styleId);
if (!scene->isSelectionEmpty(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>();
for (auto index : inputs.get0()) {
std::string selectionName = highlightStage->getHighlight(index)._selectionName;
highlightStage->removeHighlight(index);
std::string selectionName = highlightStage->getElement(index)._selectionName;
highlightStage->removeElement(index);
scene->removeSelection(selectionName);
}

View file

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

View file

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

View file

@ -52,7 +52,7 @@ LightPayload::LightPayload() :
LightPayload::~LightPayload() {
if (!LightStage::isIndexInvalid(_index)) {
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 ?
if (LightStage::isIndexInvalid(_index)) {
_index = _stage->addLight(_light);
_index = _stage->addElement(_light);
_needUpdate = false;
}
// Need an update ?
@ -122,7 +122,7 @@ _light(std::make_shared<graphics::Light>())
KeyLightPayload::~KeyLightPayload() {
if (!LightStage::isIndexInvalid(_index)) {
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 ?
if (LightStage::isIndexInvalid(_index)) {
_index = _stage->addLight(_light);
_index = _stage->addElement(_light);
_needUpdate = false;
}
// Need an update ?

View file

@ -4,6 +4,7 @@
//
// Created by Zach Pomerantz on 1/14/2015.
// Copyright 2015 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
@ -15,7 +16,9 @@
#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)
const glm::mat4 LightStage::Shadow::_biasMatrix {
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 };
const int LightStage::Shadow::MAP_SIZE = 1024;
const LightStage::Index LightStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };
LightStage::LightStage() {
// Add off lights
const LightPointer ambientOffLight { std::make_shared<graphics::Light>() };
@ -33,28 +34,28 @@ LightStage::LightStage() {
ambientOffLight->setColor(graphics::Vec3(0.0));
ambientOffLight->setIntensity(0.0f);
ambientOffLight->setType(graphics::Light::Type::AMBIENT);
_ambientOffLightId = addLight(ambientOffLight);
_ambientOffLightId = addElement(ambientOffLight);
const LightPointer pointOffLight { std::make_shared<graphics::Light>() };
pointOffLight->setAmbientIntensity(0.0f);
pointOffLight->setColor(graphics::Vec3(0.0));
pointOffLight->setIntensity(0.0f);
pointOffLight->setType(graphics::Light::Type::POINT);
_pointOffLightId = addLight(pointOffLight);
_pointOffLightId = addElement(pointOffLight);
const LightPointer spotOffLight { std::make_shared<graphics::Light>() };
spotOffLight->setAmbientIntensity(0.0f);
spotOffLight->setColor(graphics::Vec3(0.0));
spotOffLight->setIntensity(0.0f);
spotOffLight->setType(graphics::Light::Type::SPOT);
_spotOffLightId = addLight(spotOffLight);
_spotOffLightId = addElement(spotOffLight);
const LightPointer sunOffLight { std::make_shared<graphics::Light>() };
sunOffLight->setAmbientIntensity(0.0f);
sunOffLight->setColor(graphics::Vec3(0.0));
sunOffLight->setIntensity(0.0f);
sunOffLight->setType(graphics::Light::Type::SUN);
_sunOffLightId = addLight(sunOffLight);
_sunOffLightId = addElement(sunOffLight);
// Set default light to the off ambient light (until changed)
_defaultLightId = _ambientOffLightId;
@ -72,7 +73,7 @@ LightStage::Shadow::Schema::Schema() {
maxDistance = 20.0f;
}
LightStage::Shadow::Cascade::Cascade() :
LightStage::Shadow::Cascade::Cascade() :
_frustum{ std::make_shared<ViewFrustum>() },
_minDistance{ 0.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 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
// 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
@ -121,7 +122,7 @@ float LightStage::Shadow::Cascade::computeFarDistance(const ViewFrustum& viewFru
return far;
}
LightStage::Shadow::Shadow(graphics::LightPointer light, unsigned int cascadeCount) :
LightStage::Shadow::Shadow(graphics::LightPointer light, unsigned int cascadeCount) :
_light{ light } {
cascadeCount = std::min(cascadeCount, (unsigned int)SHADOW_CASCADE_MAX_COUNT);
Schema schema;
@ -323,21 +324,12 @@ void LightStage::Shadow::setCascadeFrustum(unsigned int cascadeIndex, const View
schemaCascade.reprojection = _biasMatrix * shadowFrustum.getProjection() * viewInverse.getMatrix();
}
LightStage::Index LightStage::findLight(const LightPointer& light) const {
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) {
LightStage::Index LightStage::addElement(const LightPointer& light, const bool shouldSetAsDefault) {
Index lightId;
auto found = _lightMap.find(light);
if (found == _lightMap.end()) {
lightId = _lights.newElement(light);
auto found = _elementMap.find(light);
if (found == _elementMap.end()) {
lightId = _elements.newElement(light);
// Avoid failing to allocate a light, just pass
if (lightId != INVALID_INDEX) {
@ -349,8 +341,8 @@ LightStage::Index LightStage::addLight(const LightPointer& light, const bool sho
_descs[lightId] = Desc();
}
// INsert the light and its index in the reverese map
_lightMap.insert(LightMap::value_type(light, lightId));
// Insert the light and its index in the reverese map
_elementMap[light] = lightId;
updateLightArrayBuffer(lightId);
}
@ -365,30 +357,30 @@ LightStage::Index LightStage::addLight(const LightPointer& light, const bool sho
return lightId;
}
LightStage::LightPointer LightStage::removeLight(Index index) {
LightPointer removedLight = _lights.freeElement(index);
LightStage::LightPointer LightStage::removeElement(Index index) {
LightPointer removedLight = _elements.freeElement(index);
if (removedLight) {
_lightMap.erase(removedLight);
_elementMap.erase(removedLight);
_descs[index] = Desc();
}
assert(_descs.size() <= (size_t)index || _descs[index].shadowId == INVALID_INDEX);
return removedLight;
}
LightStage::LightPointer LightStage::getCurrentKeyLight(const LightStage::Frame& frame) const {
LightStage::LightPointer LightStage::getCurrentKeyLight(const LightFrame& frame) const {
Index keyLightId { _defaultLightId };
if (!frame._sunLights.empty()) {
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 };
if (!frame._ambientLights.empty()) {
keyLightId = frame._ambientLights.front();
}
return _lights.get(keyLightId);
return _elements.get(keyLightId);
}
void LightStage::updateLightArrayBuffer(Index lightId) {
@ -397,14 +389,12 @@ void LightStage::updateLightArrayBuffer(Index lightId) {
_lightArrayBuffer = std::make_shared<gpu::Buffer>(lightSize);
}
assert(checkLightId(lightId));
if (lightId > (Index)_lightArrayBuffer->getNumTypedElements<graphics::Light::LightSchema>()) {
_lightArrayBuffer->resize(lightSize * (lightId + 10));
}
// lightArray is big enough so we can remap
auto light = _lights._elements[lightId];
auto light = _elements._elements[lightId];
if (light) {
const auto& lightSchema = light->getLightSchemaBuffer().get();
_lightArrayBuffer->setSubData<graphics::Light::LightSchema>(lightId, lightSchema);
@ -412,17 +402,3 @@ void LightStage::updateLightArrayBuffer(Index lightId) {
// 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.
// Copyright 2015 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
@ -12,34 +13,44 @@
#ifndef hifi_render_utils_LightStage_h
#define hifi_render_utils_LightStage_h
#include <set>
#include <unordered_map>
#include <gpu/Framebuffer.h>
#include <graphics/Light.h>
#include <render/IndexedContainer.h>
#include <render/Stage.h>
#include <render/Engine.h>
#include <render/StageSetup.h>
class ViewFrustum;
// Light stage to set up light-related rendering tasks
class LightStage : public render::Stage {
class LightFrame {
public:
static std::string _stageName;
static const std::string& getName() { return _stageName; }
LightFrame() {}
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 {
public:
@ -74,9 +85,9 @@ public:
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,
float nearDepth = 1.0f, float farDepth = 1000.0f);
@ -93,83 +104,46 @@ public:
float getMaxDistance() const { return _maxDistance; }
void setMaxDistance(float value);
const graphics::LightPointer& getLight() const { return _light; }
const LightPointer& getLight() const { return _light; }
gpu::TexturePointer map;
#include "Shadows_shared.slh"
class Schema : public ShadowParameters {
public:
Schema();
};
protected:
using Cascades = std::vector<Cascade>;
static const glm::mat4 _biasMatrix;
graphics::LightPointer _light;
LightPointer _light;
float _maxDistance{ 0.0f };
Cascades _cascades;
UniformBufferView _schemaBuffer = nullptr;
};
using ShadowPointer = std::shared_ptr<Shadow>;
Index findLight(const LightPointer& light) const;
Index addLight(const LightPointer& light, const bool shouldSetAsDefault = false);
Index addElement(const LightPointer& light) override { return addElement(light, false); }
Index addElement(const LightPointer& light, const bool shouldSetAsDefault);
LightPointer removeElement(Index index) override;
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();
gpu::BufferPointer getLightArrayBuffer() const { return _lightArrayBuffer; }
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 {
public:
ShadowFrame() {}
void clear() {}
using Object = ShadowPointer;
using Objects = std::vector<Object>;
@ -177,20 +151,17 @@ public:
_objects.emplace_back(shadow);
}
Objects _objects;
};
using ShadowFramePointer = std::shared_ptr<ShadowFrame>;
Frame _currentFrame;
Index getAmbientOffLight() { return _ambientOffLightId; }
Index getPointOffLight() { return _pointOffLightId; }
Index getSpotOffLight() { return _spotOffLightId; }
Index getSunOffLight() { return _sunOffLightId; }
LightPointer getCurrentKeyLight(const LightStage::Frame& frame) const;
LightPointer getCurrentAmbientLight(const LightStage::Frame& frame) const;
LightPointer getCurrentKeyLight(const LightFrame& frame) const;
LightPointer getCurrentAmbientLight(const LightFrame& frame) const;
protected:
@ -201,9 +172,7 @@ protected:
gpu::BufferPointer _lightArrayBuffer;
Lights _lights;
Descs _descs;
LightMap _lightMap;
// define off lights
Index _ambientOffLightId;
@ -216,16 +185,9 @@ protected:
};
using LightStagePointer = std::shared_ptr<LightStage>;
class LightStageSetup {
class LightStageSetup : public render::StageSetup<LightStage> {
public:
using JobModel = render::Job::Model<LightStageSetup>;
LightStageSetup();
void run(const render::RenderContextPointer& renderContext);
protected:
};
#endif

View file

@ -1,6 +1,9 @@
//
// RenderCommonTask.cpp
//
// Created by Bradley Austin Davis on 2018/01/09
// Copyright 2013-2018 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
@ -78,9 +81,9 @@ void DrawLayered3D::run(const RenderContextPointer& renderContext, const Inputs&
graphics::HazePointer haze;
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.
haze = hazeStage->getHaze(hazeFrame->_hazes.back());
haze = hazeStage->getElement(hazeFrame->_elements.back());
}
// Clear the framebuffer without stereo

View file

@ -1,6 +1,9 @@
//
// RenderCommonTask.h
//
// Created by Bradley Austin Davis on 2018/01/09
// Copyright 2013-2018 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

View file

@ -5,6 +5,7 @@
//
// Created by Sam Gateau on 5/29/15.
// Copyright 2016 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
@ -145,10 +146,6 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren
// Shadow Stage Frame
const auto shadowFrame = shadowTaskOutputs[1];
if (depth == 0) {
task.addJob<FadeEffect>("FadeEffect", opaques);
}
const auto jitter = task.addJob<JitterSample>("JitterCam");
// 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
const auto& hazeStage = args->_scene->getStage<HazeStage>();
if (hazeStage && hazeFrame->_hazes.size() > 0) {
const auto& hazePointer = hazeStage->getHaze(hazeFrame->_hazes.front());
if (hazeStage && hazeFrame->_elements.size() > 0) {
const auto& hazePointer = hazeStage->getElement(hazeFrame->_elements.front());
if (hazePointer) {
batch.setUniformBuffer(graphics::slot::buffer::Buffer::HazeParams, hazePointer->getHazeParametersBuffer());
}

View file

@ -5,6 +5,7 @@
//
// Created by Zach Pomerantz on 12/13/2016.
// Copyright 2016 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
@ -271,8 +272,8 @@ void DrawForward::run(const RenderContextPointer& renderContext, const Inputs& i
graphics::HazePointer haze;
const auto& hazeStage = renderContext->args->_scene->getStage<HazeStage>();
if (hazeStage && hazeFrame->_hazes.size() > 0) {
haze = hazeStage->getHaze(hazeFrame->_hazes.front());
if (hazeStage && hazeFrame->_elements.size() > 0) {
haze = hazeStage->getElement(hazeFrame->_elements.front());
}
gpu::doInBatch("DrawForward::run", args->_context, [&](gpu::Batch& batch) {

View file

@ -10,6 +10,7 @@
//
#include "RenderViewTask.h"
#include "FadeEffect.h"
#include "RenderShadowTask.h"
#include "RenderCommonTask.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) {
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
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>();
assert(lightStage);
// const auto light = DependencyManager::get<DeferredLightingEffect>()->getLightStage()->getLight(0);
const auto light = lightStage->getLight(0);
// const auto light = DependencyManager::get<DeferredLightingEffect>()->getLightStage()->getElement(0);
const auto light = lightStage->getElement(0);
if (!_debugParams) {
_debugParams = std::make_shared<gpu::Buffer>(sizeof(glm::vec4), nullptr);
_debugParams->setSubData(0, _debugCursorTexcoord);

View file

@ -4,6 +4,7 @@
//
// Created by Anna Brewer on 7/3/19.
// Copyright 2019 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
@ -74,8 +75,8 @@ void ToneMapAndResample::run(const RenderContextPointer& renderContext, const In
const auto& tonemappingStage = renderContext->_scene->getStage<TonemappingStage>();
graphics::TonemappingPointer tonemapping;
if (tonemappingStage && tonemappingFrame->_tonemappings.size()) {
tonemapping = tonemappingStage->getTonemapping(tonemappingFrame->_tonemappings.front());
if (tonemappingStage && tonemappingFrame->_elements.size()) {
tonemapping = tonemappingStage->getElement(tonemappingFrame->_elements.front());
}
if (_debug) {

View file

@ -7,50 +7,8 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "TonemappingStage.h"
#include <gpu/Context.h>
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>());
}
}
template <>
std::string render::PointerStage<graphics::Tonemapping, graphics::TonemappingPointer>::_name { "TONEMAPPING_STAGE" };

View file

@ -11,74 +11,17 @@
#ifndef 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 <render/Stage.h>
#include <render/StageSetup.h>
// Tonemapping stage to set up tonemapping-related rendering tasks
class TonemappingStage : public render::Stage {
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;
};
class TonemappingStage : public render::PointerStage<graphics::Tonemapping, graphics::TonemappingPointer> {};
using TonemappingStagePointer = std::shared_ptr<TonemappingStage>;
class TonemappingStageSetup {
class TonemappingStageSetup : public render::StageSetup<TonemappingStage> {
public:
using JobModel = render::Job::Model<TonemappingStageSetup>;
TonemappingStageSetup();
void run(const render::RenderContextPointer& renderContext);
protected:
};
#endif

View file

@ -4,6 +4,7 @@
//
// Created by Sam Gateau on 4/4/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
@ -86,11 +87,11 @@ void SetupZones::run(const RenderContextPointer& context, const Input& input) {
// Finally add the default lights and background:
lightStage->_currentFrame.pushSunLight(lightStage->getDefaultLight());
lightStage->_currentFrame.pushAmbientLight(lightStage->getDefaultLight());
backgroundStage->_currentFrame.pushBackground(0);
hazeStage->_currentFrame.pushHaze(0);
bloomStage->_currentFrame.pushBloom(INVALID_INDEX);
tonemappingStage->_currentFrame.pushTonemapping(0);
ambientOcclusionStage->_currentFrame.pushAmbientOcclusion(INVALID_INDEX);
backgroundStage->_currentFrame.pushElement(0);
hazeStage->_currentFrame.pushElement(0);
bloomStage->_currentFrame.pushElement(INVALID_INDEX);
tonemappingStage->_currentFrame.pushElement(0);
ambientOcclusionStage->_currentFrame.pushElement(INVALID_INDEX);
}
gpu::PipelinePointer DebugZoneLighting::_keyLightPipeline;
@ -144,22 +145,22 @@ void DebugZoneLighting::run(const render::RenderContextPointer& context, const I
std::vector<graphics::LightPointer> keyLightStack;
if (lightStage && lightFrame->_sunLights.size()) {
for (auto index : lightFrame->_sunLights) {
keyLightStack.push_back(lightStage->getLight(index));
keyLightStack.push_back(lightStage->getElement(index));
}
}
std::vector<graphics::LightPointer> ambientLightStack;
if (lightStage && lightFrame->_ambientLights.size()) {
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());
std::vector<graphics::SkyboxPointer> skyboxStack;
if (backgroundStage && backgroundFrame->_backgrounds.size()) {
for (auto index : backgroundFrame->_backgrounds) {
auto background = backgroundStage->getBackground(index);
if (backgroundStage && backgroundFrame->_elements.size()) {
for (auto index : backgroundFrame->_elements) {
auto background = backgroundStage->getElement(index);
if (background) {
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) {
// 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) ||
positionMS.y < params.bounds.y - params.bounds.w) {
if ((params.bounds.z > 0.0 && (positionMS.x < params.bounds.x || positionMS.x > (params.bounds.x + params.bounds.z))) ||
(params.bounds.w > 0.0 && (positionMS.y < params.bounds.y - params.bounds.w))) {
return vec4(0.0);
}

View file

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

View file

@ -71,8 +71,8 @@ public:
bool forward, bool mirror) :
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) {}
DrawProps(const QString& str, const glm::vec4& color, const glm::vec2& origin, const glm::vec2& bounds, bool forward) :
str(str), color(color), origin(origin), bounds(bounds), forward(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), alignment(alignment), forward(forward) {}
const QString& str;
const glm::vec4& color;

View file

@ -4,6 +4,7 @@
//
// Created by Niraj Venkat on 6/29/15.
// Copyright 2015 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
@ -163,7 +164,7 @@ void DrawStatus::run(const RenderContextPointer& renderContext, const Input& inp
status.setColor(Item::Status::Value::RED);
}
// Set icon based on transition type
auto& transition = transitionStage->getTransition(transitionID);
auto& transition = transitionStage->getElement(transitionID);
switch (transition.eventType) {
case Transition::Type::USER_ENTER_DOMAIN:
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 "Engine.h"
using namespace render;
std::string HighlightStage::_name("Highlight");
const HighlightStage::Index HighlightStage::INVALID_INDEX{ render::indexed_container::INVALID_INDEX };
template <>
std::string TypedStage<Highlight>::_name { "HIGHLIGHT_STAGE" };
HighlightStage::Index HighlightStage::addHighlight(const std::string& selectionName, const HighlightStyle& style) {
Highlight outline{ selectionName, style };
Index id;
id = _highlights.newElement(outline);
_activeHighlightIds.push_back(id);
return id;
Highlight outline { selectionName, style };
return addElement(outline);
}
void HighlightStage::removeHighlight(Index index) {
HighlightIdList::iterator idIterator = std::find(_activeHighlightIds.begin(), _activeHighlightIds.end(), index);
if (idIterator != _activeHighlightIds.end()) {
_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);
HighlightStage::Index HighlightStage::getHighlightIdBySelection(const std::string& selectionName) const {
for (auto outlineId : _activeElementIDs) {
const auto& outline = _elements.get(outlineId);
if (outline._selectionName == selectionName) {
return outlineId;
}
@ -100,9 +98,6 @@ void HighlightStageConfig::setOccludedFillOpacity(float value) {
emit dirty();
}
HighlightStageSetup::HighlightStageSetup() {
}
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
// thread safe...
@ -112,8 +107,7 @@ void HighlightStageSetup::configure(const Config& config) {
void HighlightStageSetup::run(const render::RenderContextPointer& renderContext) {
auto stage = renderContext->_scene->getStage<HighlightStage>(HighlightStage::getName());
if (!stage) {
stage = std::make_shared<HighlightStage>();
renderContext->_scene->resetStage(HighlightStage::getName(), stage);
renderContext->_scene->resetStage(HighlightStage::getName(), std::make_shared<HighlightStage>());
}
if (!_styles.empty()) {
@ -127,4 +121,3 @@ void HighlightStageSetup::run(const render::RenderContextPointer& renderContext)
_styles.clear();
}
}

View file

@ -3,6 +3,7 @@
// 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
@ -11,56 +12,27 @@
#ifndef hifi_render_utils_HighlightStage_h
#define hifi_render_utils_HighlightStage_h
#include "Stage.h"
#include "Engine.h"
#include "IndexedContainer.h"
#include "HighlightStyle.h"
#include "Stage.h"
namespace render {
// Highlight stage to set up HighlightStyle-related effects
class HighlightStage : public Stage {
class Highlight {
public:
Highlight(const std::string& selectionName, const HighlightStyle& style) : _selectionName{ selectionName }, _style{ style } { }
class Highlight {
public:
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); }
std::string _selectionName;
HighlightStyle _style;
};
// 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 getHighlightIdBySelection(const std::string& selectionName) const;
void removeHighlight(Index index);
HighlightIdList::iterator begin() { return _activeHighlightIds.begin(); }
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;
const IDList& getActiveHighlightIds() const { return _activeElementIDs; }
};
using HighlightStagePointer = std::shared_ptr<HighlightStage>;
@ -122,7 +94,7 @@ namespace render {
using Config = HighlightStageConfig;
using JobModel = render::Job::Model<HighlightStageSetup, Config>;
HighlightStageSetup();
HighlightStageSetup() {}
void configure(const Config& config);
void run(const RenderContextPointer& renderContext);

View file

@ -17,6 +17,8 @@
#include <string>
#include <ViewFrustum.h>
namespace render {
// 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();
if (!TransitionStage::isIndexInvalid(transitionId)) {
auto& transition = transitionStage->getTransition(transitionId);
auto& transition = transitionStage->getElement(transitionId);
func(itemId, &transition);
} else {
func(itemId, nullptr);
@ -477,7 +477,7 @@ void Scene::resetHighlights(const Transaction::HighlightResets& transactions) {
if (HighlightStage::isIndexInvalid(outlineId)) {
outlineStage->addHighlight(selectionName, newStyle);
} 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);
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);
if (!HighlightStage::isIndexInvalid(outlineId)) {
func(&outlineStage->editHighlight(outlineId)._style);
func(&outlineStage->editElement(outlineId)._style);
} else {
func(nullptr);
}
@ -559,7 +559,7 @@ void Scene::removeItemTransition(ItemID itemId) {
auto& item = _items[itemId];
TransitionStage::Index transitionId = item.getTransitionId();
if (!render::TransitionStage::isIndexInvalid(transitionId)) {
const auto& transition = transitionStage->getTransition(transitionId);
const auto& transition = transitionStage->getElement(transitionId);
const auto transitionOwner = transition.itemId;
if (transitionOwner == itemId) {
// No more items will be using this transition. Clean it up.
@ -570,7 +570,7 @@ void Scene::removeItemTransition(ItemID itemId) {
}
}
_transitionFinishedOperatorMap.erase(transitionId);
transitionStage->removeTransition(transitionId);
transitionStage->removeElement(transitionId);
}
setItemTransition(itemId, render::TransitionStage::INVALID_INDEX);

View file

@ -4,23 +4,13 @@
//
// Created by Sam Gateau on 6/14/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 "Stage.h"
using namespace render;
Stage::~Stage() {
}
Stage::Stage() :
_name()
{
}
const Stage::Index Stage::INVALID_INDEX { render::indexed_container::INVALID_INDEX };

View file

@ -4,6 +4,7 @@
//
// Created by Sam Gateau on 6/14/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
@ -12,27 +13,141 @@
#ifndef hifi_render_Stage_h
#define hifi_render_Stage_h
#include <memory>
#include <map>
#include <memory>
#include <unordered_map>
#include <string>
#include "IndexedContainer.h"
namespace render {
using ElementIndices = std::vector<indexed_container::Index>;
class Stage {
public:
Stage() {}
virtual ~Stage() {}
using Name = std::string;
using Index = indexed_container::Index;
static const Index INVALID_INDEX;
using IDList = indexed_container::Indices;
Stage();
virtual ~Stage();
protected:
Name _name;
static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; }
};
using StagePointer = std::shared_ptr<Stage>;
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

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;
std::string TransitionStage::_name("Transition");
const TransitionStage::Index TransitionStage::INVALID_INDEX{ indexed_container::INVALID_INDEX };
template <>
std::string TypedStage<Transition>::_name { "TRANSITION_STAGE" };
TransitionStage::Index TransitionStage::addTransition(ItemID itemId, Transition::Type type, ItemID boundId) {
Transition transition;
Index id;
transition.eventType = type;
transition.itemId = itemId;
transition.boundItemId = boundId;
id = _transitions.newElement(transition);
_activeTransitionIds.push_back(id);
return id;
return addElement(transition);
}
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
//
// 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
@ -12,55 +13,22 @@
#define hifi_render_TransitionStage_h
#include "Stage.h"
#include "IndexedContainer.h"
#include "Engine.h"
#include "StageSetup.h"
#include "Transition.h"
namespace render {
// Transition stage to set up Transition-related effects
class TransitionStage : public render::Stage {
class TransitionStage : public TypedStage<Transition> {
public:
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); }
bool isTransitionUsed(Index index) const { return _elements.checkIndex(index) && !_elements.isElementFreed(index); }
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>;
class TransitionStageSetup {
class TransitionStageSetup : public StageSetup<TransitionStage> {
public:
using JobModel = render::Job::Model<TransitionStageSetup>;
TransitionStageSetup();
void run(const RenderContextPointer& renderContext);
protected:
using JobModel = Job::Model<TransitionStageSetup>;
};
}

View file

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

View file

@ -280,9 +280,6 @@ public:
*/
class ScriptManager : public QObject, public EntitiesScriptEngineProvider, public std::enable_shared_from_this<ScriptManager> {
Q_OBJECT
Q_PROPERTY(QString context READ getContext)
Q_PROPERTY(QString type READ getTypeAsString)
Q_PROPERTY(QString fileName MEMBER _fileNameString CONSTANT)
public:
static const QString SCRIPT_EXCEPTION_FORMAT;
static const QString SCRIPT_BACKTRACE_SEP;
@ -381,8 +378,11 @@ public:
Q_ENUM(Type);
static int processLevelMaxRetries;
ScriptManager(Context context, const QString& scriptContents = NO_SCRIPT, const QString& fileNameString = QString("about:ScriptEngine"));
~ScriptManager();
private:
// 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
typedef void (*ScriptManagerInitializer)(ScriptManager*);
@ -390,7 +390,7 @@ public:
public:
ScriptManagerInitializer init;
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);
@ -398,7 +398,7 @@ public:
public:
ScriptManagerInitializer init;
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);
@ -1668,6 +1668,7 @@ protected:
friend ScriptManagerPointer newScriptManager(Context context, const QString& scriptContents, const QString& fileNameString);
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 {
Q_OBJECT
Q_PROPERTY(QString context READ getContext)
Q_PROPERTY(QString type READ getTypeAsString)
Q_PROPERTY(QString fileName READ getAbsoluteFilename CONSTANT)
public:
ScriptManagerScriptingInterface(ScriptManager *parent);
@ -96,6 +99,26 @@ public:
*/
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
* 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() {
// 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();
#ifdef OVERTE_SCRIPT_USE_AFTER_DELETE_GUARD
_wasDestroyed = true;
@ -272,14 +279,14 @@ void ScriptEngineV8::perManagerLoopIterationCleanup() {
void ScriptEngineV8::disconnectSignalProxies() {
_signalProxySetLock.lockForRead();
while (!_signalProxySet.empty()) {
auto proxy = *_signalProxySet.begin();
_signalProxySetLock.unlock();
delete *_signalProxySet.begin();
delete proxy;
_signalProxySetLock.lockForRead();
}
_signalProxySetLock.unlock();
}
void ScriptEngineV8::deleteUnusedValueWrappers() {
while (!_scriptValueWrappersToDelete.empty()) {
auto wrapper = _scriptValueWrappersToDelete.dequeue();

View file

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

View file

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

View file

@ -23,6 +23,7 @@
#include <QtCore/QPointer>
#include <QtCore/QString>
#include "ScriptEngineDebugFlags.h"
#include "../ScriptEngine.h"
#include "../Scriptable.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.
int _callCounter{0};
float _totalCallTime_s{ 0.0 };
#ifdef OVERTE_SCRIPT_USE_AFTER_DELETE_GUARD
std::atomic<bool> _wasDeleted{false};
#endif
Q_DISABLE_COPY(ScriptSignalV8Proxy)
};

View file

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

View file

@ -1,5 +1,5 @@
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.3
import controlsUit 1.0 as HifiControlsUit
@ -30,7 +30,7 @@ Rectangle {
running: false
repeat: false
onTriggered: {
scrollToBottom();
scrollToBottom(true);
}
}
@ -145,49 +145,60 @@ Rectangle {
anchors.top: navigation_bar.bottom
visible: ["local", "domain"].includes(pageVal) ? true : false
// Chat Message History
ListView {
Flickable {
width: parent.width
height: parent.height - 40
contentWidth: parent.width
contentHeight: listview.height
clip: true
interactive: true
spacing: 5
id: listview
id: messageViewFlickable
delegate: Loader {
property int delegateIndex: index
property string delegateText: model.text
property string delegateUsername: model.username
property string delegateDate: model.date
width: listview.width
ColumnLayout {
id: listview
Layout.fillWidth: true
sourceComponent: {
if (model.type === "chat") {
return template_chat_message;
} else if (model.type === "notification") {
return template_notification;
Repeater {
model: getChannel(pageVal)
delegate: Loader {
property int delegateIndex: model.index
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 {
id: chat_scrollbar
height: 100
size: 0.05
ScrollBar.vertical: ScrollBar {
size: 100
minimumSize: 0.1
}
model: getChannel(pageVal)
rebound: Transition {
NumberAnimation {
properties: "x,y"
duration: 1
}
}
}
ListModel {
id: local
}
ListModel {
id: domain
}
}
// Chat Entry
Rectangle {
@ -205,6 +216,8 @@ Rectangle {
height: parent.height
placeholderText: pageVal.charAt(0).toUpperCase() + pageVal.slice(1) + " chat message..."
clip: false
font.italic: text == ""
Keys.onPressed: {
if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && !(event.modifiers & Qt.ShiftModifier)) {
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 {
id: template_chat_message
Rectangle{
Rectangle {
property int index: delegateIndex
property string texttest: delegateText
property string username: delegateUsername
@ -352,6 +390,8 @@ Rectangle {
height: Math.max(65, children[1].height + 30)
color: index % 2 === 0 ? "transparent" : Qt.rgba(0.15,0.15,0.15,1)
width: listview.parent.parent.width
Layout.fillWidth: true
Item {
width: parent.width - 10
@ -370,7 +410,7 @@ Rectangle {
}
}
TextEdit{
TextEdit {
anchors.top: parent.children[0].bottom
x: 5
text: texttest
@ -451,16 +491,25 @@ Rectangle {
}
property var channels: {
"local": local,
"domain": domain,
}
function scrollToBottom() {
if (listview.count == 0) return;
listview.positionViewAtEnd();
function scrollToBottom(bypassDistanceCheck = false, extraMoveDistance = 0) {
const totalHeight = listview.height; // Total height of the content
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
message = formatContent(message);
message = embedImages(message);
if (type === "notification"){
channel.append({ text: message, date: date, type: "notification" });
last_message_user = "";
scrollToBottom();
scrollToBottom(null, 30);
last_message_time = new Date();
return;
}
@ -487,22 +536,18 @@ Rectangle {
var last_item_index = channel.count - 1;
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
// to the bottom of the last message. This current implimentation causes issues with scrollToBottom()
// Specifically, scrolling to the bottom does not like image embeds.
// This needs to be reworked entirely before it can be reimplimented
// if (last_message_user === username && elapsed_minutes < 1 && last_item){
// message = "<br>" + message
// last_item.text = last_item.text += "\n" + message;
// scrollToBottom()
// last_message_time = new Date();
// return;
// }
if (last_message_user === username && elapsed_minutes < 1 && last_item){
message = "<br>" + message
last_item.text = last_item.text += "\n" + message;
load_scroll_timer.running = true;
last_message_time = new Date();
return;
}
last_message_user = username;
last_message_time = new Date();
channel.append({ text: message, username: username, date: date, type: type });
scrollToBottom();
load_scroll_timer.running = true;
}
function getChannel(id) {
@ -542,15 +587,13 @@ Rectangle {
// Messages from script
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){
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;
case "notification":
addMessage("SYSTEM", message.message, `[ ${time} - ${date} ]`, "domain", "notification");
addMessage("SYSTEM", message.message, `[ ${message.timeString} - ${message.dateString} ]`, "domain", "notification");
break;
case "clear_messages":
local.clear();
@ -559,6 +602,7 @@ Rectangle {
case "initial_settings":
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.join_notification) s_join_notification.checked = true;
break;
}
}

View file

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

View file

@ -22,7 +22,12 @@ var DEFAULT_OFFSET = 10;
var FLOOF_NOTIFICATION_CHANNEL = "Floof-Notif";
var MAIN_CHAT_APP_CHANNEL = "Chat";
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;
@ -48,6 +53,7 @@ function messageReceived(channel, message, sender, local) {
}
} else {
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) {
var noti = {

View file

@ -2,7 +2,7 @@
// NewSoundDialog.qml
// qml/hifi
//
// Created by HifiExperiments on 4/7/24
// Created by HifiExperiments on April 7th, 2024
// Copyright 2024 Overte e.V.
//
// Distributed under the Apache License, Version 2.0.
@ -53,17 +53,32 @@ Rectangle {
Text {
id: text1
text: qsTr("Sound URL")
text: qsTr("Sound URL <i></i>&nbsp;&nbsp;&nbsp;")
color: "#ffffff"
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 {
id: soundURL
height: 20
text: qsTr("")
color: "white"
anchors.top: text1.bottom
anchors.top: pasteBtn.bottom
anchors.topMargin: 5
anchors.left: parent.left
anchors.leftMargin: 0

View file

@ -1,15 +1,20 @@
//
// graphicsSettings.js
//
// Created by Kalila L. on 8/5/2020
// Created by Kalila L. on August 5th, 2020
// Copyright 2020 Vircadia contributors.
// 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
//
(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');
// cellphone-cog MDI
@ -49,22 +54,49 @@
}
function startup() {
ui = new AppUi({
buttonName: BUTTON_NAME,
sortOrder: 8,
normalButton: getIcon(),
activeButton: getIcon().replace('white', 'black'),
home: GRAPHICS_QML_SOURCE
});
if (!appStatus) {
ui = new AppUi({
buttonName: BUTTON_NAME,
sortOrder: 8,
normalButton: getIcon(),
activeButton: getIcon().replace('white', 'black'),
home: GRAPHICS_QML_SOURCE
});
}
appStatus = true;
}
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.
//
startup();
Script.scriptEnding.connect(shutdown);
if (Settings.getValue(GRAPHICS_HIDE_AND_SHOW_SETTING_KEY, GRAPHICS_HIDE_AND_SHOW_DEFAULT_VALUE)) {
startup();
}
Messages.subscribe(channelComm);
Messages.messageReceived.connect(onMessageReceived);
Script.scriptEnding.connect(cleanup);
}()); // 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 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();

View file

@ -1,8 +1,8 @@
# Copyright 2022-2023 Overte e.V.
# Copyright 2022-2024 Overte e.V.
# SPDX-License-Identifier: Apache-2.0
# 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
LABEL maintainer="Julian Groß (julian.gro@overte.org)"
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
# Install server-console build dependencies
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
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
# 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
LABEL maintainer="Julian Groß (julian.gro@overte.org)"
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
RUN apt-get -y install libqt5webchannel5-dev qtwebengine5-dev libqt5xmlpatterns5-dev
# 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
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.
var exts = ['.h', '.cpp'];
var exts = ['.h', '.h.in', '.cpp', '.cpp.in'];
var fs = require('fs');
dirList.forEach(function (dir) {
@ -123,9 +123,9 @@ exports.handlers = {
// Append an Available In: sentence at the beginning of the namespace description.
if (rows.length > 0) {
var availableIn = "<p class='availableIn'><b>Supported Script Types:</b> " + rows.join(" &bull; ") + "</p>";
e.doclet.description = availableIn + (e.doclet.description ? e.doclet.description : "");
}
}
}
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
# - 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.
# - 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.
# - 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 cp extract:qt5-install-5.15.14-2024.06.17-kde_570f5b2105df1ea052bec0d6dbf8a00137274371-ubuntu-20.04-amd64.tar.xz /path/on/host`
# `docker create --name extract overte-qt5:5.15.16-2024.12.14-kde_32be154325bfba3ad2ba8bf75dad702f3588e8d3`
# `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`
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
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
@ -58,12 +58,12 @@ WORKDIR ../../qt5-install
RUN find . -name \*.prl -exec sed -i -e '/^QMAKE_PRL_BUILD_DIR/d' {} \;
# 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 cp lib/libQt5WebEngine.so.5.15.17 lib/libQt5WebEngine.so.5.15.14
RUN cp lib/libQt5WebEngineCore.so.5.15.17 lib/libQt5WebEngineCore.so.5.15.14
RUN cp lib/libQt5WebEngineWidgets.so.5.15.17 lib/libQt5WebEngineWidgets.so.5.15.14
RUN cp lib/libQt5Pdf.so.5.15.17 lib/libQt5Pdf.so.5.15.14
RUN cp lib/libQt5PdfWidgets.so.5.15.17 lib/libQt5PdfWidgets.so.5.15.14
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.19 lib/libQt5WebEngine.so.5.15.16
RUN cp lib/libQt5WebEngineCore.so.5.15.19 lib/libQt5WebEngineCore.so.5.15.16
RUN cp lib/libQt5WebEngineWidgets.so.5.15.19 lib/libQt5WebEngineWidgets.so.5.15.16
RUN cp lib/libQt5Pdf.so.5.15.19 lib/libQt5Pdf.so.5.15.16
RUN cp lib/libQt5PdfWidgets.so.5.15.19 lib/libQt5PdfWidgets.so.5.15.16
COPY ./qt.conf ./bin/
@ -71,4 +71,4 @@ COPY ./qt.conf ./bin/
RUN cp ../qt5-build/config.summary ./
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