diff --git a/src/gui/plugins/CMakeLists.txt b/src/gui/plugins/CMakeLists.txt index 956158f39c..ec0644a287 100644 --- a/src/gui/plugins/CMakeLists.txt +++ b/src/gui/plugins/CMakeLists.txt @@ -137,6 +137,8 @@ add_subdirectory(entity_context_menu) add_subdirectory(entity_tree) add_subdirectory(environment_loader) add_subdirectory(environment_visualization) +add_subdirectory(global_illumination_civct) +add_subdirectory(global_illumination_vct) add_subdirectory(joint_position_controller) add_subdirectory(lights) add_subdirectory(playback_scrubber) diff --git a/src/gui/plugins/global_illumination_civct/CMakeLists.txt b/src/gui/plugins/global_illumination_civct/CMakeLists.txt new file mode 100644 index 0000000000..3ebf36c93d --- /dev/null +++ b/src/gui/plugins/global_illumination_civct/CMakeLists.txt @@ -0,0 +1,6 @@ +gz_add_gui_plugin(GlobalIlluminationCiVct + SOURCES CiVctCascadePrivate.cc CiVctCascadePrivate.qml GlobalIlluminationCiVct.cc GlobalIlluminationCiVct.qml GlobalIlluminationCiVct.qrc + QT_HEADERS CiVctCascadePrivate.hh GlobalIlluminationCiVct.hh + PRIVATE_LINK_LIBS + ${GZ-RENDERING_LIBRARIES} +) diff --git a/src/gui/plugins/global_illumination_civct/CiVctCascadePrivate.cc b/src/gui/plugins/global_illumination_civct/CiVctCascadePrivate.cc new file mode 100644 index 0000000000..de9149b5b1 --- /dev/null +++ b/src/gui/plugins/global_illumination_civct/CiVctCascadePrivate.cc @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#define _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS + +#include "CiVctCascadePrivate.hh" + +#include "GlobalIlluminationCiVct.hh" + +#include "gz/rendering/GlobalIlluminationCiVct.hh" + +using namespace gz; +using namespace sim; + +CiVctCascadePrivate::CiVctCascadePrivate(std::mutex &_serviceMutex, + GlobalIlluminationCiVct &_creator, + rendering::CiVctCascadePtr _cascade) : + cascade(_cascade), + creator(_creator), + serviceMutex(_serviceMutex) +{ +} + +///////////////////////////////////////////////// +CiVctCascadePrivate::~CiVctCascadePrivate() +{ +} + +void CiVctCascadePrivate::UpdateResolution(int _axis, uint32_t _res) +{ + if (!this->creator.CascadesEditable()) + return; + + std::lock_guard lock(this->serviceMutex); + uint32_t resolution[3]; + memcpy(resolution, this->cascade->Resolution(), sizeof(resolution)); + resolution[_axis] = _res; + this->cascade->SetResolution(resolution); +} + +///////////////////////////////////////////////// +void CiVctCascadePrivate::UpdateOctantCount(int _axis, uint32_t _count) +{ + std::lock_guard lock(this->serviceMutex); + uint32_t octantCount[3]; + memcpy(octantCount, this->cascade->OctantCount(), sizeof(octantCount)); + octantCount[_axis] = _count; + this->cascade->SetOctantCount(octantCount); +} + +///////////////////////////////////////////////// +void CiVctCascadePrivate::UpdateAreaHalfSize(int _axis, float _halfSize) +{ + if (!this->creator.CascadesEditable()) + return; + + std::lock_guard lock(this->serviceMutex); + math::Vector3d areaHalfSize = this->cascade->AreaHalfSize(); + areaHalfSize[(size_t)_axis] = static_cast(_halfSize); + this->cascade->SetAreaHalfSize(areaHalfSize); +} + +///////////////////////////////////////////////// +void CiVctCascadePrivate::SetAreaHalfSizeX(const float _v) +{ + this->UpdateAreaHalfSize(0, _v); +} + +///////////////////////////////////////////////// +float CiVctCascadePrivate::AreaHalfSizeX() const +{ + std::lock_guard lock(this->serviceMutex); + return static_cast(this->cascade->AreaHalfSize().X()); +} + +///////////////////////////////////////////////// +void CiVctCascadePrivate::SetAreaHalfSizeY(const float _v) +{ + this->UpdateAreaHalfSize(1, _v); +} + +///////////////////////////////////////////////// +float CiVctCascadePrivate::AreaHalfSizeY() const +{ + std::lock_guard lock(this->serviceMutex); + return static_cast(this->cascade->AreaHalfSize().Y()); +} + +///////////////////////////////////////////////// +void CiVctCascadePrivate::SetAreaHalfSizeZ(const float _v) +{ + this->UpdateAreaHalfSize(2, _v); +} + +///////////////////////////////////////////////// +float CiVctCascadePrivate::AreaHalfSizeZ() const +{ + std::lock_guard lock(this->serviceMutex); + return static_cast(this->cascade->AreaHalfSize().Z()); +} + +///////////////////////////////////////////////// +void CiVctCascadePrivate::SetResolutionX(const uint32_t _res) +{ + this->UpdateResolution(0, _res); +} + +///////////////////////////////////////////////// +uint32_t CiVctCascadePrivate::ResolutionX() const +{ + std::lock_guard lock(this->serviceMutex); + return this->cascade->Resolution()[0]; +} + +///////////////////////////////////////////////// +void CiVctCascadePrivate::SetResolutionY(const uint32_t _res) +{ + this->UpdateResolution(1, _res); +} + +///////////////////////////////////////////////// +uint32_t CiVctCascadePrivate::ResolutionY() const +{ + std::lock_guard lock(this->serviceMutex); + return this->cascade->Resolution()[1]; +} + +///////////////////////////////////////////////// +void CiVctCascadePrivate::SetResolutionZ(const uint32_t _res) +{ + this->UpdateResolution(2, _res); +} + +///////////////////////////////////////////////// +uint32_t CiVctCascadePrivate::ResolutionZ() const +{ + std::lock_guard lock(this->serviceMutex); + return this->cascade->Resolution()[2]; +} + +///////////////////////////////////////////////// +void CiVctCascadePrivate::SetOctantCountX(const uint32_t _octantCount) +{ + this->UpdateOctantCount(0, _octantCount); +} + +///////////////////////////////////////////////// +uint32_t CiVctCascadePrivate::OctantCountX() const +{ + std::lock_guard lock(this->serviceMutex); + return this->cascade->OctantCount()[0]; +} + +///////////////////////////////////////////////// +void CiVctCascadePrivate::SetOctantCountY(const uint32_t _octantCount) +{ + this->UpdateOctantCount(1, _octantCount); +} + +///////////////////////////////////////////////// +uint32_t CiVctCascadePrivate::OctantCountY() const +{ + std::lock_guard lock(this->serviceMutex); + return this->cascade->OctantCount()[1]; +} + +///////////////////////////////////////////////// +void CiVctCascadePrivate::SetOctantCountZ(const uint32_t _octantCount) +{ + this->UpdateOctantCount(2, _octantCount); +} + +///////////////////////////////////////////////// +uint32_t CiVctCascadePrivate::OctantCountZ() const +{ + std::lock_guard lock(this->serviceMutex); + return this->cascade->OctantCount()[2]; +} + +///////////////////////////////////////////////// +void CiVctCascadePrivate::SetThinWallCounter(const float _thinWallCounter) +{ + std::lock_guard lock(this->serviceMutex); + this->cascade->SetThinWallCounter(_thinWallCounter); +} + +///////////////////////////////////////////////// +float CiVctCascadePrivate::ThinWallCounter() const +{ + std::lock_guard lock(this->serviceMutex); + return this->cascade->ThinWallCounter(); +} diff --git a/src/gui/plugins/global_illumination_civct/CiVctCascadePrivate.hh b/src/gui/plugins/global_illumination_civct/CiVctCascadePrivate.hh new file mode 100644 index 0000000000..13f53e45dc --- /dev/null +++ b/src/gui/plugins/global_illumination_civct/CiVctCascadePrivate.hh @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#ifndef GZ_SIM_GUI_CIVCTCASCADEPRIVATE_HH_ +#define GZ_SIM_GUI_CIVCTCASCADEPRIVATE_HH_ + +#include +#include + +#include "gz/sim/gui/GuiSystem.hh" +#include "gz/gui/qt.h" + +#include "Tsa.hh" + +namespace gz +{ + namespace rendering + { + inline namespace GZ_SIM_VERSION_NAMESPACE + { + /// Forward declare the only ptr we need + class CiVctCascade; + typedef std::shared_ptr CiVctCascadePtr; + } // namespace GZ_SIM_GAZEBO_VERSION_NAMESPACE + } // namespace rendering +} // namespace gz + +namespace gz +{ +namespace sim +{ +// Inline bracket to help doxygen filtering. +inline namespace GZ_SIM_VERSION_NAMESPACE +{ + class GlobalIlluminationCiVct; + + /// \brief Cascade container for QML to control all of its settings, + /// per cascade. + /// + /// Due to how QML bindings work, we must split Vectors + /// into each component so e.g. the following Javascript code: + /// + /// cascade.areaHalfSizeX = 5.0 + /// cascade.areaHalfSizeY = 6.0 + /// cascade.areaHalfSizeZ = 7.0 + /// + /// Will end up calling: + /// + /// cascade->SetAreaHalfSizeX(5.0f); + /// cascade->SetAreaHalfSizeY(6.0f); + /// cascade->SetAreaHalfSizeZ(7.0f); + /// + /// Even though in C++ we would normally do math::Vector3d(5.0f, 6.0f, 7.0f) + /// The same goes for each property. + class GZ_SIM_HIDDEN CiVctCascadePrivate : public QObject + { + friend class GlobalIlluminationCiVct; + + Q_OBJECT + + Q_PROPERTY( + float areaHalfSizeX + READ AreaHalfSizeX + WRITE SetAreaHalfSizeX + NOTIFY SettingsChanged + ) + Q_PROPERTY( + float areaHalfSizeY + READ AreaHalfSizeY + WRITE SetAreaHalfSizeY + NOTIFY SettingsChanged + ) + Q_PROPERTY( + float areaHalfSizeZ + READ AreaHalfSizeZ + WRITE SetAreaHalfSizeZ + NOTIFY SettingsChanged + ) + + Q_PROPERTY( + int resolutionX + READ ResolutionX + WRITE SetResolutionX + NOTIFY SettingsChanged + ) + Q_PROPERTY( + int resolutionY + READ ResolutionY + WRITE SetResolutionY + NOTIFY SettingsChanged + ) + Q_PROPERTY( + int resolutionZ + READ ResolutionZ + WRITE SetResolutionZ + NOTIFY SettingsChanged + ) + + Q_PROPERTY( + int octantCountX + READ OctantCountX + WRITE SetOctantCountX + NOTIFY SettingsChanged + ) + Q_PROPERTY( + int octantCountY + READ OctantCountY + WRITE SetOctantCountY + NOTIFY SettingsChanged + ) + Q_PROPERTY( + int octantCountZ + READ OctantCountZ + WRITE SetOctantCountZ + NOTIFY SettingsChanged + ) + + Q_PROPERTY( + float thinWallCounter + READ ThinWallCounter + WRITE SetThinWallCounter + // NOTIFY LightingChanged + ) + + /// \brief Constructor + /// \param _serviceMutex Mutex owned by our creator so we don't access + /// this data from the UI thread while the render thread is using it + /// \param _creator Our creator & owner + /// \param _cascade The cascade we will be manipulating via GUI + public: CiVctCascadePrivate(std::mutex &_serviceMutex, + GlobalIlluminationCiVct &_creator, + rendering::CiVctCascadePtr _cascade); + + /// \brief Destructor + public: ~CiVctCascadePrivate() override; + + /// \brief Set VCT resolution + /// \param[in] _axis Axis (width, height, depth). In range [0; 3) + /// \param[in] _res New resolution + public: Q_INVOKABLE void UpdateResolution(int _axis, uint32_t _res) + EXCLUDES(serviceMutex); + + /// \brief Set VCT octant count + /// \param[in] _axis Axis (width, height, depth). In range [0; 3) + /// \param[in] _count New octant count + public: Q_INVOKABLE void UpdateOctantCount(int _axis, uint32_t _count) + EXCLUDES(serviceMutex); + + /// \brief Set the area half size + /// \param[in] _axis Axis (width, height, depth). In range [0; 3) + /// \param[in] _halfSize New half size for that axis + public: Q_INVOKABLE void UpdateAreaHalfSize(int _axis, float _halfSize) + EXCLUDES(serviceMutex); + + /// \brief Notify various properties may have changed + signals: void SettingsChanged(); + + /// \brief See rendering::CiVctCascade::SetAreaHalfSize + /// \param[in] _v See CiVctCascade::SetAreaHalfSize + public: Q_INVOKABLE void SetAreaHalfSizeX(const float _v) + EXCLUDES(serviceMutex); + + /// \brief See rendering::CiVctCascade::AreaHalfSize + /// Affects X component only + /// \return See rendering::CiVctCascade::AreaHalfSize + public: Q_INVOKABLE float AreaHalfSizeX() const + EXCLUDES(serviceMutex); + + /// \brief See rendering::CiVctCascade::SetAreaHalfSize + /// Affects Y component only + /// \param[in] _v See CiVctCascade::SetAreaHalfSize + public: Q_INVOKABLE void SetAreaHalfSizeY(const float _v) + EXCLUDES(serviceMutex); + + /// \brief See rendering::CiVctCascade::AreaHalfSize. + /// Affects Y component only + /// \return See rendering::CiVctCascade::AreaHalfSize + public: Q_INVOKABLE float AreaHalfSizeY() const + EXCLUDES(serviceMutex); + + /// \brief See rendering::CiVctCascade::SetAreaHalfSize + /// Affects Z component only + /// \param[in] _v See CiVctCascade::SetAreaHalfSize + public: Q_INVOKABLE void SetAreaHalfSizeZ(const float _v) + EXCLUDES(serviceMutex); + + /// \brief See rendering::CiVctCascade::AreaHalfSize + /// Affects Z component only + /// \return See rendering::CiVctCascade::AreaHalfSize + public: Q_INVOKABLE float AreaHalfSizeZ() const + EXCLUDES(serviceMutex); + + /// \brief See rendering::CiVctCascade::SetResolution + /// \param[in] _res See CiVctCascadePrivate::SetResolution + public: Q_INVOKABLE void SetResolutionX(const uint32_t _res) + EXCLUDES(serviceMutex); + + /// \brief See rendering::CiVctCascade::Resolution + /// \return See rendering::CiVctCascade::Resolution + public: Q_INVOKABLE uint32_t ResolutionX() const + EXCLUDES(serviceMutex); + + /// \brief See rendering::CiVctCascade::SetResolution + /// \param[in] _res See CiVctCascadePrivate::SetResolution + public: Q_INVOKABLE void SetResolutionY(const uint32_t _res) + EXCLUDES(serviceMutex); + + /// \brief See rendering::CiVctCascade::Resolution + /// \return See rendering::CiVctCascade::Resolution + public: Q_INVOKABLE uint32_t ResolutionY() const + EXCLUDES(serviceMutex); + + /// \brief See rendering::CiVctCascade::SetResolution + /// \param[in] _res See CiVctCascadePrivate::SetResolution + public: Q_INVOKABLE void SetResolutionZ(const uint32_t _res) + EXCLUDES(serviceMutex); + + /// \brief See rendering::CiVctCascade::Resolution + /// \return See rendering::CiVctCascade::Resolution + public: Q_INVOKABLE uint32_t ResolutionZ() const + EXCLUDES(serviceMutex); + + /// \brief See rendering::CiVctCascade::SetOctantCount + /// \param[in] _octantCount See CiVctCascadePrivate::SetOctantCount + public: Q_INVOKABLE void SetOctantCountX(const uint32_t _octantCount) + EXCLUDES(serviceMutex); + + /// \brief See rendering::CiVctCascade::OctantCount + /// \return See rendering::CiVctCascade::OctantCount + public: Q_INVOKABLE uint32_t OctantCountX() const + EXCLUDES(serviceMutex); + + /// \brief See rendering::CiVctCascade::SetOctantCount + /// \param[in] _octantCount See CiVctCascadePrivate::SetOctantCount + public: Q_INVOKABLE void SetOctantCountY(const uint32_t _octantCount) + EXCLUDES(serviceMutex); + + /// \brief See rendering::CiVctCascade::OctantCount + /// \return See rendering::CiVctCascade::OctantCount + public: Q_INVOKABLE uint32_t OctantCountY() const + EXCLUDES(serviceMutex); + + /// \brief See rendering::CiVctCascade::SetOctantCount + /// \param[in] _octantCount See CiVctCascadePrivate::SetOctantCount + public: Q_INVOKABLE void SetOctantCountZ(const uint32_t _octantCount) + EXCLUDES(serviceMutex); + + /// \brief See rendering::CiVctCascade::OctantCount + /// \return See rendering::CiVctCascade::OctantCount + public: Q_INVOKABLE uint32_t OctantCountZ() const + EXCLUDES(serviceMutex); + + /// \brief See rendering::CiVctCascade::SetThinWallCounter + /// \param[in] _thinWallCounter See CiVctCascadePrivate::SetThinWallCounter + public: Q_INVOKABLE void SetThinWallCounter(const float _thinWallCounter) + EXCLUDES(serviceMutex); + + /// \brief See rendering::CiVctCascade::ThinWallCounter + /// \return See rendering::CiVctCascade::ThinWallCounter + public: Q_INVOKABLE float ThinWallCounter() const EXCLUDES(serviceMutex); + + /// \brief Cascade under under control where all settings are stored + private: rendering::CiVctCascadePtr cascade PT_GUARDED_BY(serviceMutex); + + /// \brief Our creator, to avoid updating settings when it's not + /// safe to do so + private: GlobalIlluminationCiVct &creator PT_GUARDED_BY(serviceMutex); + + /// \brief Mutex for variable mutated by the checkbox and spinboxes + /// callbacks. + public: std::mutex &serviceMutex; + }; +} +} +} +#endif diff --git a/src/gui/plugins/global_illumination_civct/CiVctCascadePrivate.qml b/src/gui/plugins/global_illumination_civct/CiVctCascadePrivate.qml new file mode 100644 index 0000000000..7b645a52e6 --- /dev/null +++ b/src/gui/plugins/global_illumination_civct/CiVctCascadePrivate.qml @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +import QtQuick 2.9 +import QtQuick.Controls 2.1 +import QtQuick.Controls.Material 2.2 +import QtQuick.Layouts 1.3 +import "qrc:/qml" + +GridLayout { + columns: 6 + columnSpacing: 10 + Layout.minimumWidth: 350 + Layout.minimumHeight: 400 + Layout.columnSpan: 6 + Layout.fillWidth: true + anchors.left: parent + anchors.right: parent + anchors.leftMargin: 10 + anchors.rightMargin: 10 + + property var cascadePtr + + function isPowerOf2(n) { + return (n & n-1) === 0; + } + + // Returns the closest power of 2, rounding down if need to. + // floorPowerOf2( 4 ) = 4 + // floorPowerOf2( 5 ) = 4 + function floorPowerOf2(n) { + return 1 << (31 - Math.clz32(n)); + } + + // Returns the closest power of 2, rounding up if need to. + // floorPowerOf2( 4 ) = 4 + // floorPowerOf2( 5 ) = 8 + function ceilPowerOf2(n) { + if (isPowerOf2(n)) { + return n; + } + return 1 << (31 - Math.clz32(n) + 1); + } + + function nearestPowerOf2(n, oldValue=undefined) { + if (oldValue === undefined) { + return floorPowerOf2(n); + } + else { + if (oldValue <= n) { + return ceilPowerOf2(n); + } + else { + return floorPowerOf2(n); + } + } + } + + Text { + Layout.columnSpan: 6 + id: resolutionStr + color: "dimgrey" + text: qsTr("Resolution") + } + + GzSpinBox { + property int oldValue: -1 + + Layout.columnSpan: 2 + Layout.fillWidth: true + id: resolutionSpinX + value: cascadePtr.resolutionX + enabled: GlobalIlluminationCiVct.cascadesEditable + minimumValue: 4 + maximumValue: 512 + decimals: 1 + onEditingFinished: { + var tmpValue = value; + tmpValue = nearestPowerOf2(tmpValue, oldValue); + oldValue = tmpValue; + value = tmpValue; + cascadePtr.resolutionX = value; + } + } + GzSpinBox { + property int oldValue: -1 + + Layout.columnSpan: 2 + Layout.fillWidth: true + id: resolutionSpinY + value: cascadePtr.resolutionY + enabled: GlobalIlluminationCiVct.cascadesEditable + minimumValue: 4 + maximumValue: 512 + decimals: 1 + onEditingFinished: { + var tmpValue = value; + tmpValue = nearestPowerOf2(tmpValue, oldValue); + oldValue = tmpValue; + value = tmpValue; + cascadePtr.resolutionY = value; + } + } + GzSpinBox { + property int oldValue: -1 + + Layout.columnSpan: 2 + Layout.fillWidth: true + id: resolutionSpinZ + value: cascadePtr.resolutionZ + enabled: GlobalIlluminationCiVct.cascadesEditable + minimumValue: 4 + maximumValue: 512 + decimals: 1 + onEditingFinished: { + var tmpValue = value; + tmpValue = nearestPowerOf2(tmpValue, oldValue); + oldValue = tmpValue; + value = tmpValue; + cascadePtr.resolutionZ = value; + } + } + + Text { + Layout.columnSpan: 6 + id: octantCountStr + color: "dimgrey" + text: qsTr("OctantCount") + } + + GzSpinBox { + Layout.columnSpan: 2 + Layout.fillWidth: true + id: octantCountX + value: cascadePtr.octantCountX + minimumValue: 1 + maximumValue: 8 + decimals: 1 + onEditingFinished: { + cascadePtr.octantCountX = value + } + } + GzSpinBox { + Layout.columnSpan: 2 + Layout.fillWidth: true + id: octantCountY + value: cascadePtr.octantCountY + minimumValue: 1 + maximumValue: 8 + decimals: 1 + onEditingFinished: { + cascadePtr.octantCountY = value + } + } + GzSpinBox { + Layout.columnSpan: 2 + Layout.fillWidth: true + id: octantCountZ + value: cascadePtr.octantCountZ + minimumValue: 1 + maximumValue: 8 + decimals: 1 + onEditingFinished: { + cascadePtr.octantCountZ = value + } + } + + Text { + Layout.columnSpan: 4 + id: thinWallCounterStr + color: "dimgrey" + text: qsTr("ThinWallCounter") + } + + GzSpinBox { + Layout.columnSpan: 2 + Layout.fillWidth: true + id: thinWallCounter + value: cascadePtr.thinWallCounter + minimumValue: 0 + maximumValue: 5 + decimals: 2 + stepSize: 0.1 + onValueChanged: { + cascadePtr.thinWallCounter = value + } + } + + Text { + Layout.columnSpan: 6 + color: "dimgrey" + text: qsTr("AreaHalfSize") + } + + GzSpinBox { + Layout.columnSpan: 2 + Layout.fillWidth: true + id: areaHalfSizeX + value: cascadePtr.areaHalfSizeX + enabled: GlobalIlluminationCiVct.cascadesEditable + minimumValue: 0.01 + maximumValue: 100000 + decimals: 2 + onEditingFinished: { + cascadePtr.areaHalfSizeX = value + } + } + GzSpinBox { + Layout.columnSpan: 2 + Layout.fillWidth: true + id: areaHalfSizeY + value: cascadePtr.areaHalfSizeY + enabled: GlobalIlluminationCiVct.cascadesEditable + minimumValue: 0.01 + maximumValue: 100000 + decimals: 2 + onEditingFinished: { + cascadePtr.areaHalfSizeY = value + } + } + GzSpinBox { + Layout.columnSpan: 2 + Layout.fillWidth: true + id: areaHalfSizeZ + value: cascadePtr.areaHalfSizeZ + enabled: GlobalIlluminationCiVct.cascadesEditable + minimumValue: 0.01 + maximumValue: 100000 + decimals: 2 + onEditingFinished: { + cascadePtr.areaHalfSizeZ = value + } + } +} + +/*##^## +Designer { + D{i:0;autoSize:true;height:480;width:640}D{i:1}D{i:2}D{i:3}D{i:4}D{i:5}D{i:6}D{i:7} +D{i:8}D{i:9}D{i:10}D{i:11}D{i:12}D{i:13}D{i:14}D{i:15}D{i:16}D{i:17}D{i:18}D{i:19} +} +##^##*/ diff --git a/src/gui/plugins/global_illumination_civct/GlobalIlluminationCiVct.cc b/src/gui/plugins/global_illumination_civct/GlobalIlluminationCiVct.cc new file mode 100644 index 0000000000..d2b8f4c20c --- /dev/null +++ b/src/gui/plugins/global_illumination_civct/GlobalIlluminationCiVct.cc @@ -0,0 +1,828 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#define _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS + +#include "GlobalIlluminationCiVct.hh" + +#include "CiVctCascadePrivate.hh" + +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include "gz/sim/Entity.hh" +#include "gz/sim/EntityComponentManager.hh" +#include "gz/sim/components/Name.hh" +#include "gz/sim/components/World.hh" +#include "gz/sim/rendering/RenderUtil.hh" + +#include "gz/rendering/Camera.hh" +#include "gz/rendering/GlobalIlluminationCiVct.hh" +#include "gz/rendering/RenderEngine.hh" +#include "gz/rendering/RenderTypes.hh" +#include "gz/rendering/RenderingIface.hh" +#include "gz/rendering/Scene.hh" + +#include "gz/sim/Util.hh" + +#include "Tsa.hh" + +// clang-format off +namespace gz +{ +namespace sim +{ +inline namespace GZ_SIM_VERSION_NAMESPACE +{ + /// \brief Private data only used when loading this plugin + /// from XML. We must do this because we require Ogre-Next + /// engine to be loaded and there are also thread synchronization + /// issues (GUI vs internal data) that needs to be accounted + struct GZ_SIM_HIDDEN GiCiVctXmlInitData + { + uint32_t resolution[3]{ 16u, 16u, 16u }; + uint32_t octantCount[3]{ 1u, 1u, 1u }; + float areaHalfSize[3]{ 5.0f, 5.0f, 5.0f }; + float thinWallCounter{ 1.0f }; + }; + + /// \brief Private data class for GlobalIlluminationCiVct + class GZ_SIM_HIDDEN GlobalIlluminationCiVctPrivate + { + /// \brief Transport node + public: transport::Node node; + + /// \brief Scene Pointer + public: rendering::ScenePtr scene GUARDED_BY(serviceMutex); + + /// \brief Each cascade created by GI. + /// We directly access the data in CiVctCascade from UI thread + /// because it's safe to do so: + /// - Ogre2 doesn't invoke any side effect (i.e. build must be called) + /// - Ogre2 won't issue rendering commands (all rendering must + /// happen in the main thread, regardless of whether it's protected) + public: std::vector> cascades + GUARDED_BY(serviceMutex); + + /// \brief Pointer to GlobalIlluminationCiVct + public: rendering::GlobalIlluminationCiVctPtr gi GUARDED_BY(serviceMutex); + + /// \brief Toggles this GI on/off. Only one can be active at the same time. + public: bool enabled GUARDED_BY(serviceMutex){false}; + + /// \brief See GlobalIlluminationCiVct::ResetCascades. + public: bool resetRequested GUARDED_BY(serviceMutex){false}; + + /// \brief See rendering::GlobalIlluminationCiVct::SetBounceCount + public: uint32_t bounceCount GUARDED_BY(serviceMutex){6u}; + + /// \brief See rendering::GlobalIlluminationCiVct::SetHighQuality + public: bool highQuality GUARDED_BY(serviceMutex){true}; + + /// \brief See rendering::GlobalIlluminationCiVct::SetAnisotropic + public: bool anisotropic GUARDED_BY(serviceMutex){true}; + + /// \brief See rendering::GlobalIlluminationCiVct::DebugVisualizationMode + public: uint32_t debugVisMode GUARDED_BY( + serviceMutex){ rendering::GlobalIlluminationCiVct::DVM_None }; + + /// \brief Camera from where the CIVCT cascades are centered around + public: rendering::CameraPtr bindCamera GUARDED_BY(serviceMutex){ nullptr }; + + /// \brief Available cameras for binding + public: QStringList availableCameras; + + /// \brief Mutex for variable mutated by the checkbox and spinboxes + /// callbacks. + public: std::mutex serviceMutex; + + /// \brief Initialization flag + public: bool initialized{false}; + + /// \brief GI visual display dirty flag + public: bool visualDirty GUARDED_BY(serviceMutex){false}; + + /// \brief GI visual display dirty flag; but it is fast/quick to rebuild + public: bool lightingDirty GUARDED_BY(serviceMutex){false}; + + /// \brief GI debug visualization is dirty. Only used by GUI. + /// Not in simulation. + public: bool debugVisualizationDirty GUARDED_BY(serviceMutex){false}; + + /// \brief See GiCiVctXmlInitData. Only used during XML initialization. + /// It's never accessed by multiple threads at the same time. + public: std::vector xmlInitData; + }; +} +} +} +// clang-format on + +using namespace gz; +using namespace sim; + +// Q_DECLARE_METATYPE(CiVctCascadePrivate); + +///////////////////////////////////////////////// +GlobalIlluminationCiVct::GlobalIlluminationCiVct() : + GuiSystem(), + dataPtr(new GlobalIlluminationCiVctPrivate) +{ + // no ops +} + +///////////////////////////////////////////////// +GlobalIlluminationCiVct::~GlobalIlluminationCiVct() +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + this->dataPtr->gi.reset(); +} + +///////////////////////////////////////////////// +void GlobalIlluminationCiVct::LoadGlobalIlluminationCiVct() + REQUIRES(this->dataPtr->serviceMutex) +{ + auto loadedEngNames = rendering::loadedEngines(); + if (loadedEngNames.empty()) + { + return; + } + + // assume there is only one engine loaded + auto engineName = loadedEngNames[0]; + if (loadedEngNames.size() > 1) + { + gzdbg << "More than one engine is available. " + << "GlobalIlluminationCiVct plugin will use engine [" << engineName + << "]" << std::endl; + } + auto engine = rendering::engine(engineName); + if (!engine) + { + gzerr << "Internal error: failed to load engine [" << engineName + << "]. GlobalIlluminationCiVct plugin won't work." << std::endl; + return; + } + + if (engine->SceneCount() == 0) + return; + + // assume there is only one scene + // load scene + auto scene = engine->SceneByIndex(0); + if (!scene) + { + gzerr << "Internal error: scene is null." << std::endl; + return; + } + + if (!scene->IsInitialized() || scene->VisualCount() == 0) + { + return; + } + + // Create visual + gzdbg << "Creating GlobalIlluminationCiVct" << std::endl; + + auto root = scene->RootVisual(); + this->dataPtr->gi = scene->CreateGlobalIlluminationCiVct(); + if (!this->dataPtr->gi) + { + gzerr << "Failed to create GlobalIlluminationCiVct, GI plugin won't work." + << std::endl; + + gz::gui::App()->findChild()->removeEventFilter(this); + } + else + { + this->dataPtr->gi->SetParticipatingVisuals( + rendering::GlobalIlluminationBase::DYNAMIC_VISUALS | + rendering::GlobalIlluminationBase::STATIC_VISUALS); + this->dataPtr->scene = scene; + this->dataPtr->initialized = true; + + if (this->dataPtr->xmlInitData.empty()) + { + // Ensure we initialize with valid settings so the user + // can just Enable us immediately. + emit qmlAddCascade(); + } + else + { + for (const GiCiVctXmlInitData &itor : this->dataPtr->xmlInitData) + { + emit qmlAddCascade2(itor.resolution[0], itor.resolution[1], + itor.resolution[2], itor.octantCount[0], + itor.octantCount[1], itor.octantCount[2], + itor.areaHalfSize[0], itor.areaHalfSize[1], + itor.areaHalfSize[2], itor.thinWallCounter); + } + } + + this->OnRefreshCamerasImpl(); + } +} + +/// \brief XML helper to retrieve values and handle errors +/// \param[in] _elem XML element to read +/// \param[out] _valueToSet Value to set. Left unmodified on error +/// \return True if _valueToSet was successfully set +static bool GetXmlBool(const tinyxml2::XMLElement *_elem, bool &_valueToSet) +{ + bool value = false; + + if (_elem->QueryBoolText(&value) != tinyxml2::XML_SUCCESS) + { + gzerr << "Failed to parse <" << _elem->Name() + << "> value: " << _elem->GetText() << std::endl; + return false; + } + else + { + _valueToSet = value; + return true; + } +} + +/// \brief XML helper to retrieve values and handle errors +/// \param[in] _elem XML element to read +/// \param[out] _valueToSet Value to set. Left unmodified on error +/// \return True if _valueToSet was successfully set +static bool GetXmlFloat(const tinyxml2::XMLElement *_elem, float &_valueToSet) +{ + float value = 0; + + if (_elem->QueryFloatText(&value) != tinyxml2::XML_SUCCESS) + { + gzerr << "Failed to parse <" << _elem->Name() + << "> value: " << _elem->GetText() << std::endl; + return false; + } + else + { + _valueToSet = value; + return true; + } +} + +/// \brief XML helper to retrieve values and handle errors +/// \param[in] _elem XML element to read +/// \param[out] _valueToSet Value to set. Left unmodified on error +/// \return True if _valueToSet was successfully set +static bool GetXmlUint32(const tinyxml2::XMLElement *_elem, + uint32_t &_valueToSet) +{ + int value = 0; + + if (_elem->QueryIntText(&value) != tinyxml2::XML_SUCCESS) + { + gzerr << "Failed to parse <" << _elem->Name() + << "> value: " << _elem->GetText() << std::endl; + return false; + } + else + { + _valueToSet = static_cast(value); + return true; + } +} + +/// \brief XML helper to retrieve values and handle errors +/// \param[in] _elem XML element to read +/// \param[out] _valueToSet Values to set. Left unmodified on error. +/// Its array length must be >= 3 +/// \return True if _valueToSet was successfully set +static bool GetXmlUint32x3(const tinyxml2::XMLElement *_elem, + uint32_t _valueToSet[3]) +{ + std::istringstream stream(_elem->GetText()); + math::Vector3i values3; + stream >> values3; + + _valueToSet[0] = static_cast(values3.X()); + _valueToSet[1] = static_cast(values3.Y()); + _valueToSet[2] = static_cast(values3.Z()); + + return true; +} + +static bool GetXmlFloatx3(const tinyxml2::XMLElement *_elem, + float _valueToSet[3]) +{ + std::istringstream stream(_elem->GetText()); + math::Vector3f values3; + stream >> values3; + + _valueToSet[0] = static_cast(values3.X()); + _valueToSet[1] = static_cast(values3.Y()); + _valueToSet[2] = static_cast(values3.Z()); + + return true; +} + +///////////////////////////////////////////////// +void GlobalIlluminationCiVct::LoadConfig( + const tinyxml2::XMLElement *_pluginElem) +{ + if (this->title.empty()) + this->title = "Global Illumination (VCT)"; + + std::lock_guard lock(this->dataPtr->serviceMutex); + + if (auto elem = _pluginElem->FirstChildElement("enabled")) + { + GetXmlBool(elem, this->dataPtr->enabled); + } + if (auto elem = _pluginElem->FirstChildElement("highQuality")) + { + GetXmlBool(elem, this->dataPtr->highQuality); + } + if (auto elem = _pluginElem->FirstChildElement("anisotropic")) + { + GetXmlBool(elem, this->dataPtr->anisotropic); + } + if (auto elem = _pluginElem->FirstChildElement("bounceCount")) + { + GetXmlUint32(elem, this->dataPtr->bounceCount); + } + if (auto elem = _pluginElem->FirstChildElement("debugVisMode")) + { + const std::string text = elem->GetText(); + if (text == "none") + { + this->dataPtr->debugVisMode = + rendering::GlobalIlluminationCiVct::DVM_None; + } + else if (text == "albedo") + { + this->dataPtr->debugVisMode = + rendering::GlobalIlluminationCiVct::DVM_Albedo; + } + else if (text == "normal") + { + this->dataPtr->debugVisMode = + rendering::GlobalIlluminationCiVct::DVM_Normal; + } + else if (text == "emissive") + { + this->dataPtr->debugVisMode = + rendering::GlobalIlluminationCiVct::DVM_Emissive; + } + else if (text == "lighting") + { + this->dataPtr->debugVisMode = + rendering::GlobalIlluminationCiVct::DVM_Lighting; + } + else + { + GetXmlUint32(elem, this->dataPtr->debugVisMode); + } + } + + this->dataPtr->xmlInitData.clear(); + + for (auto *elemCascade = _pluginElem->FirstChildElement("cascade"); + elemCascade != nullptr; + elemCascade = elemCascade->NextSiblingElement("cascade")) + { + GiCiVctXmlInitData xmlInitData; + if (auto elem = elemCascade->FirstChildElement("resolution")) + { + GetXmlUint32x3(elem, xmlInitData.resolution); + } + if (auto elem = elemCascade->FirstChildElement("octantCount")) + { + GetXmlUint32x3(elem, xmlInitData.octantCount); + } + if (auto elem = elemCascade->FirstChildElement("thinWallCounter")) + { + GetXmlFloat(elem, xmlInitData.thinWallCounter); + } + if (auto elem = elemCascade->FirstChildElement("areaHalfSize")) + { + GetXmlFloatx3(elem, xmlInitData.areaHalfSize); + } + + this->dataPtr->xmlInitData.push_back(xmlInitData); + } + + gz::gui::App()->findChild()->installEventFilter(this); +} + +///////////////////////////////////////////////// +bool GlobalIlluminationCiVct::eventFilter(QObject *_obj, QEvent *_event) +{ + if (_event->type() == gz::gui::events::Render::kType) + { + // This event is called in Scene3d's RenderThread, so it's safe to make + // rendering calls here + + std::lock_guard lock(this->dataPtr->serviceMutex); + if (!this->dataPtr->initialized) + { + this->LoadGlobalIlluminationCiVct(); + } + + if (this->dataPtr->gi) + { + if (!this->dataPtr->visualDirty && !this->dataPtr->gi->Enabled() && + this->dataPtr->enabled) + { + // If we're here, GI was disabled externally. This can happen + // if e.g. another GI solution was enabled (only one can be active) + this->dataPtr->enabled = false; + this->EnabledChanged(); + } + + if (this->dataPtr->visualDirty) + { + this->dataPtr->gi->SetBounceCount(this->dataPtr->bounceCount); + this->dataPtr->gi->SetHighQuality(this->dataPtr->highQuality); + + if (this->dataPtr->gi->Started()) + { + // Ogre-Next may crash if some of the settings above are + // changed while visualizing is enabled. + this->dataPtr->gi->SetDebugVisualization( + rendering::GlobalIlluminationCiVct::DVM_None); + } + + if (this->dataPtr->enabled) + { + if (!this->dataPtr->gi->Started()) + { + this->dataPtr->gi->Bind(this->dataPtr->bindCamera); + this->dataPtr->gi->Start(this->dataPtr->bounceCount, + this->dataPtr->anisotropic); + this->CascadesEditableChanged(); + } + else + { + this->dataPtr->gi->NewSettings(this->dataPtr->bounceCount, + this->dataPtr->anisotropic); + } + this->dataPtr->gi->Build(); + this->dataPtr->scene->SetActiveGlobalIllumination(this->dataPtr->gi); + } + else + { + this->dataPtr->scene->SetActiveGlobalIllumination(nullptr); + } + + if (this->dataPtr->gi->Started()) + { + // Restore debug visualization to desired. + this->dataPtr->gi->SetDebugVisualization( + static_cast< + rendering::GlobalIlluminationCiVct::DebugVisualizationMode>( + this->dataPtr->debugVisMode)); + } + + this->dataPtr->visualDirty = false; + this->dataPtr->lightingDirty = false; + this->dataPtr->debugVisualizationDirty = false; + } + else if (this->dataPtr->lightingDirty) + { + this->dataPtr->gi->SetBounceCount(this->dataPtr->bounceCount); + this->dataPtr->gi->SetHighQuality(this->dataPtr->highQuality); + + if (this->dataPtr->gi->Enabled()) + { + this->dataPtr->gi->SetDebugVisualization( + rendering::GlobalIlluminationCiVct::DVM_None); + + this->dataPtr->gi->LightingChanged(); + + this->dataPtr->gi->SetDebugVisualization( + static_cast< + rendering::GlobalIlluminationCiVct::DebugVisualizationMode>( + this->dataPtr->debugVisMode)); + + this->dataPtr->debugVisualizationDirty = false; + } + this->dataPtr->lightingDirty = false; + } + else if (this->dataPtr->debugVisualizationDirty) + { + if (this->dataPtr->gi->Started()) + { + this->dataPtr->gi->SetDebugVisualization( + static_cast< + rendering::GlobalIlluminationCiVct::DebugVisualizationMode>( + this->dataPtr->debugVisMode)); + } + this->dataPtr->debugVisualizationDirty = false; + } + + if (this->dataPtr->resetRequested) + { + if (this->dataPtr->gi->Enabled()) + { + this->dataPtr->scene->SetActiveGlobalIllumination(nullptr); + this->dataPtr->enabled = false; + this->EnabledChanged(); + } + + this->dataPtr->gi->Reset(); + this->CascadesEditableChanged(); + + this->dataPtr->resetRequested = false; + } + } + else + { + gzerr << "GI pointer is not set" << std::endl; + } + } + + // Standard event processing + return QObject::eventFilter(_obj, _event); +} + +////////////////////////////////////////////////// +void GlobalIlluminationCiVct::UpdateDebugVisualizationMode(int _mode) +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + + rendering::GlobalIlluminationCiVct::DebugVisualizationMode + debugVisualizationMode = rendering::GlobalIlluminationCiVct::DVM_None; + + if (_mode >= rendering::GlobalIlluminationCiVct::DVM_Albedo && + _mode <= rendering::GlobalIlluminationCiVct::DVM_None) + { + debugVisualizationMode = + static_cast( + _mode); + } + + this->dataPtr->gi->SetDebugVisualization(debugVisualizationMode); +} + +////////////////////////////////////////////////// +bool GlobalIlluminationCiVct::SetEnabled(const bool _enabled) +{ + if (_enabled && !this->ValidSettings()) + { + this->EnabledChanged(); + return false; + } + + std::lock_guard lock(this->dataPtr->serviceMutex); + this->dataPtr->enabled = _enabled; + this->dataPtr->visualDirty = true; + + return _enabled; +} + +////////////////////////////////////////////////// +bool GlobalIlluminationCiVct::Enabled() const +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + return this->dataPtr->enabled; +} + +////////////////////////////////////////////////// +bool GlobalIlluminationCiVct::CascadesEditable() const +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + return !this->dataPtr->gi || !this->dataPtr->gi->Started(); +} + +////////////////////////////////////////////////// +void GlobalIlluminationCiVct::SetBounceCount(const uint32_t _bounces) +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + this->dataPtr->bounceCount = _bounces; + this->dataPtr->lightingDirty = true; +} + +////////////////////////////////////////////////// +uint32_t GlobalIlluminationCiVct::BounceCount() const +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + return this->dataPtr->bounceCount; +} + +////////////////////////////////////////////////// +void GlobalIlluminationCiVct::SetHighQuality(const bool _quality) +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + this->dataPtr->highQuality = _quality; + this->dataPtr->lightingDirty = true; +} + +////////////////////////////////////////////////// +bool GlobalIlluminationCiVct::HighQuality() const +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + return this->dataPtr->highQuality; +} + +////////////////////////////////////////////////// +void GlobalIlluminationCiVct::SetAnisotropic(const bool _anisotropic) +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + this->dataPtr->anisotropic = _anisotropic; + this->dataPtr->lightingDirty = true; +} + +////////////////////////////////////////////////// +bool GlobalIlluminationCiVct::Anisotropic() const +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + return this->dataPtr->anisotropic; +} + +////////////////////////////////////////////////// +void GlobalIlluminationCiVct::SetDebugVisualizationMode(const uint32_t _visMode) +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + if (this->dataPtr->debugVisMode != _visMode) + { + this->dataPtr->debugVisMode = _visMode; + this->dataPtr->debugVisualizationDirty = true; + } +} + +////////////////////////////////////////////////// +uint32_t GlobalIlluminationCiVct::DebugVisualizationMode() const +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + return this->dataPtr->debugVisMode; +} + +////////////////////////////////////////////////// +void GlobalIlluminationCiVct::OnCamareBind(const QString &_cameraName) +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + + auto scene = this->dataPtr->scene.get(); + rendering::SensorPtr sensor = scene->SensorByName(_cameraName.toStdString()); + rendering::CameraPtr asCamera = + std::dynamic_pointer_cast(sensor); + + if (asCamera) + { + this->dataPtr->bindCamera = asCamera; + } + else + { + this->CameraListChanged(); + } +} + +////////////////////////////////////////////////// +void GlobalIlluminationCiVct::OnRefreshCamerasImpl() + REQUIRES(this->dataPtr->serviceMutex) +{ + auto scene = this->dataPtr->scene.get(); + const unsigned int sensorCount = scene->SensorCount(); + for (unsigned int i = 0u; i < sensorCount; ++i) + { + rendering::SensorPtr sensor = scene->SensorByIndex(i); + rendering::CameraPtr asCamera = + std::dynamic_pointer_cast(sensor); + + if (asCamera) + { + this->dataPtr->availableCameras.push_back( + QString::fromStdString(asCamera->Name())); + + if (!this->dataPtr->bindCamera) + this->dataPtr->bindCamera = asCamera; + } + } + + this->CameraListChanged(); +} + +////////////////////////////////////////////////// +void GlobalIlluminationCiVct::OnRefreshCameras() +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + this->OnRefreshCamerasImpl(); +} + +////////////////////////////////////////////////// +QStringList GlobalIlluminationCiVct::CameraList() +{ + return this->dataPtr->availableCameras; +} + +////////////////////////////////////////////////// +QObject *GlobalIlluminationCiVct::AddCascade() +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + + if (this->dataPtr->gi && this->dataPtr->gi->Started()) + return nullptr; + + rendering::CiVctCascade const *ref = nullptr; + if (!this->dataPtr->cascades.empty()) + ref = this->dataPtr->cascades.back()->cascade.get(); + + auto cascadeRendering = this->dataPtr->gi->AddCascade(ref); + + this->dataPtr->cascades.push_back( + std::unique_ptr(new CiVctCascadePrivate( + this->dataPtr->serviceMutex, *this, cascadeRendering))); + + if (!ref) + { + this->dataPtr->cascades.back()->cascade->SetAreaHalfSize( + gz::math::Vector3d(5.0, 5.0, 5.0)); + this->dataPtr->cascades.back()->cascade->SetThinWallCounter(1.0f); + } + else + { + this->dataPtr->cascades.back()->cascade->SetAreaHalfSize( + this->dataPtr->cascades[this->dataPtr->cascades.size() - 1u] + ->cascade->AreaHalfSize() * + 2.0); + } + + return this->dataPtr->cascades.back().get(); +} + +////////////////////////////////////////////////// +void GlobalIlluminationCiVct::PopCascade() +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + if (!this->dataPtr->cascades.empty()) + { + if (this->dataPtr->gi && this->dataPtr->gi->Started()) + return; + + this->dataPtr->cascades.pop_back(); + this->dataPtr->gi->PopCascade(); + } +} + +////////////////////////////////////////////////// +QObject *GlobalIlluminationCiVct::GetCascade(int _idx) const +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + return this->dataPtr->cascades[(size_t)_idx].get(); +} + +////////////////////////////////////////////////// +void GlobalIlluminationCiVct::ResetCascades() +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + this->dataPtr->resetRequested = true; +} + +////////////////////////////////////////////////// +bool GlobalIlluminationCiVct::ValidSettings() const +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + + if (this->dataPtr->cascades.empty()) + { + return false; + } + + if (this->dataPtr->bindCamera == nullptr) + { + return false; + } + + return true; +} + +// Register this plugin +GZ_ADD_PLUGIN(gz::sim::GlobalIlluminationCiVct, gz::gui::Plugin) diff --git a/src/gui/plugins/global_illumination_civct/GlobalIlluminationCiVct.hh b/src/gui/plugins/global_illumination_civct/GlobalIlluminationCiVct.hh new file mode 100644 index 0000000000..6ebaee5741 --- /dev/null +++ b/src/gui/plugins/global_illumination_civct/GlobalIlluminationCiVct.hh @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#ifndef GZ_SIM_GUI_GLOBALILLUMINATIONCIVCT_HH_ +#define GZ_SIM_GUI_GLOBALILLUMINATIONCIVCT_HH_ + +#include + +#include "gz/sim/gui/GuiSystem.hh" +#include "gz/gui/qt.h" + +#include + +namespace gz +{ +namespace sim +{ +// Inline bracket to help doxygen filtering. +inline namespace GZ_SIM_VERSION_NAMESPACE +{ + class GlobalIlluminationCiVctPrivate; + class CiVctCascadePrivate; + + /// \brief Enable and configure Global Illumination using CIVCT + /// (Cascaded Image Voxel Cone Tracing) + class GlobalIlluminationCiVct : public gz::sim::GuiSystem + { + Q_OBJECT + + /// \brief Enabled QML binding. + /// Anything that isn't GUI-only (i.e. affects simulation) needs it + Q_PROPERTY( + bool enabled + READ Enabled + WRITE SetEnabled + NOTIFY EnabledChanged + ) + + Q_PROPERTY( + bool cascadesEditable + READ CascadesEditable + NOTIFY CascadesEditableChanged + ) + + Q_PROPERTY( + int bounceCount + READ BounceCount + WRITE SetBounceCount + NOTIFY LightingChanged + ) + + Q_PROPERTY( + bool highQuality + READ HighQuality + WRITE SetHighQuality + NOTIFY LightingChanged + ) + + Q_PROPERTY( + bool anisotropic + READ Anisotropic + WRITE SetAnisotropic + NOTIFY LightingChanged + ) + + Q_PROPERTY( + int debugVisualizationMode + READ DebugVisualizationMode + WRITE SetDebugVisualizationMode + NOTIFY DebugVisualizationModeChanged + ) + + Q_PROPERTY( + QStringList cameraList + READ CameraList + NOTIFY CameraListChanged + ) + + /// \brief Constructor + public: GlobalIlluminationCiVct(); + + /// \brief Destructor + public: ~GlobalIlluminationCiVct() override; + + // Documentation inherited + public: void LoadConfig(const tinyxml2::XMLElement *_pluginElem) override; + + // Documentation Inherited + public: bool eventFilter(QObject *_obj, QEvent *_event) override; + + /// \brief Load the scene and attach LidarVisual to the scene + public: void LoadGlobalIlluminationCiVct(); + + /// \brief Set debug visualization mode GlogbalIllumination + /// \param[in] _mode Index of selected debug visualization mode + public: Q_INVOKABLE void UpdateDebugVisualizationMode(int _mode); + + /// \brief See rendering::GlobalIlluminationCiVct::SetEnabled & + /// rendering::Scene::SetActiveGlobalIllumination + /// \param[in] _enabled See GlobalIlluminationCiVct::SetEnabled + /// \return The new setting. We may fail to enable if settings are invalid + /// See ValidSettings() + public: Q_INVOKABLE bool SetEnabled(const bool _enabled); + + /// \brief See rendering::GlobalIlluminationCiVct::Enabled + /// \return See rendering::GlobalIlluminationCiVct::Enabled + public: Q_INVOKABLE bool Enabled() const; + + /// \brief Returns true when it's possible to add/remove cascades + /// False when it's not. + /// \return True if cascades can be added/removed + public: Q_INVOKABLE bool CascadesEditable() const; + + /// \brief Notify this property has changed + signals: void EnabledChanged(); + + /// \brief Notify this property has changed + signals: void CascadesEditableChanged(); + + /// \brief Notify various properties may have changed + signals: void SettingsChanged(); + + /// \brief Notify fast-to-rebuild properties may have changed + signals: void LightingChanged(); + + /// \brief Notify debug visualization has changed + signals: void DebugVisualizationModeChanged(); + + /// \brief Notify camera list has changed + signals: void CameraListChanged(); + + /// \brief Tells QML to add a cascade from UI thread. MUST start lowercase. + signals: void qmlAddCascade(); + + /// \brief Tells QML to add a cascade from UI thread. MUST start lowercase. + /// \param[in] _resX Resolution X + /// \param[in] _resY Resolution Y + /// \param[in] _resZ Resolution Z + /// \param[in] _octX Octant Count X + /// \param[in] _octY Octant Count Y + /// \param[in] _octZ Octant Count Z + /// \param[in] _ahsX Area Half Size X + /// \param[in] _ahsY Area Half Size Y + /// \param[in] _ahsZ Area Half Size Z + /// \param[in] _thinWallCounter thinWallCounter parameter + signals: void qmlAddCascade2(quint32 _resX, quint32 _resY, quint32 _resZ, + quint32 _octX, quint32 _octY, quint32 _octZ, + float _ahsX, float _ahsY, float _ahsZ, + float _thinWallCounter); + + /// \brief See rendering::GlobalIlluminationCiVct::SetBounceCount + /// \param[in] _bounces See GlobalIlluminationCiVct::SetBounceCount + public: Q_INVOKABLE void SetBounceCount(const uint32_t _bounces); + + /// \brief See rendering::GlobalIlluminationCiVct::BounceCount + /// \return See rendering::GlobalIlluminationCiVct::BounceCount + public: Q_INVOKABLE uint32_t BounceCount() const; + + /// \brief See rendering::GlobalIlluminationCiVct::SetHighQuality + /// \param[in] _quality See GlobalIlluminationCiVct::SetHighQuality + public: Q_INVOKABLE void SetHighQuality(const bool _quality); + + /// \brief See rendering::GlobalIlluminationCiVct::HighQuality + /// \return See rendering::GlobalIlluminationCiVct::HighQuality + public: Q_INVOKABLE bool HighQuality() const; + + /// \brief See rendering::GlobalIlluminationCiVct::SetAnisotropic + /// \param[in] _anisotropic See GlobalIlluminationCiVct::SetAnisotropic + public: Q_INVOKABLE void SetAnisotropic(const bool _anisotropic); + + /// \brief See rendering::GlobalIlluminationCiVct::Anisotropic + /// \return See rendering::GlobalIlluminationCiVct::Anisotropic + public: Q_INVOKABLE bool Anisotropic() const; + + /// \brief See rendering::GlobalIlluminationCiVct::SetDebugVisualizationMode + /// \param[in] _visMode + /// See GlobalIlluminationCiVct::SetDebugVisualizationMode + public: Q_INVOKABLE void SetDebugVisualizationMode(const uint32_t _visMode); + + /// \brief See rendering::GlobalIlluminationCiVct::DebugVisualizationMode + /// \return See rendering::GlobalIlluminationCiVct::DebugVisualizationMode + public: Q_INVOKABLE uint32_t DebugVisualizationMode() const; + + /// \brief Binds the given camera as active for the center of all cascades + /// \param[in] _cameraName Name of an existing camera to track + /// Currently it must be a Camera in the GUI. In the future something + /// more advanced to synchronize with Server will be needed. + public: Q_INVOKABLE void OnCamareBind(const QString &_cameraName); + + /// \brief See OnRefreshCameras. Does not lock. + private: void OnRefreshCamerasImpl(); + + /// \brief Populates available cameras + public: Q_INVOKABLE void OnRefreshCameras(); + + /// \brief Populates available cameras + public: Q_INVOKABLE QStringList CameraList(); + + /// \brief Adds a new cascade based on the previous one (if there's any) + /// \return A CiVctCascade ptr to be used by QML + public: Q_INVOKABLE QObject* AddCascade(); + + /// \brief Pops the last created cascade + public: Q_INVOKABLE void PopCascade(); + + /// \brief Retrieves an existing cascade + /// \param _idx Index of the cascade. Must be in range (you must track how + /// many times AddCascade() & PopCascade() has been called). + /// \return A CiVctCascade ptr to be used by QML + public: Q_INVOKABLE QObject* GetCascade(int _idx) const; + + /// \brief Disables this GI solution and makes cascades editable again + public: Q_INVOKABLE void ResetCascades(); + + /// \brief Returns true if current UI settings are valid. + /// Not all settings are valid, e.g. + /// - Not having any cascade + /// - Not having a camera bound + /// - etc + /// \return Returns true if settings are valid. + private: bool ValidSettings() const; + + /// \internal + /// \brief Pointer to private data + private: std::unique_ptr dataPtr; + }; +} +} +} +#endif diff --git a/src/gui/plugins/global_illumination_civct/GlobalIlluminationCiVct.qml b/src/gui/plugins/global_illumination_civct/GlobalIlluminationCiVct.qml new file mode 100644 index 0000000000..05f9878659 --- /dev/null +++ b/src/gui/plugins/global_illumination_civct/GlobalIlluminationCiVct.qml @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +import QtQuick 2.9 +import QtQuick.Controls 2.1 +import QtQuick.Controls.Material 2.2 +import QtQuick.Layouts 1.3 +import "qrc:/qml" + +GridLayout { + id: mainGridLayout + columns: 6 + columnSpacing: 10 + Layout.minimumWidth: 350 + Layout.minimumHeight: 800 + anchors.fill: parent + anchors.leftMargin: 10 + anchors.rightMargin: 10 + + property var cascades: [] + + function addCascade() { + var cascade = GlobalIlluminationCiVct.AddCascade() + var cascadeComponent = Qt.createComponent("CiVctCascadePrivate.qml"); + var cascadeObj = cascadeComponent.createObject(mainGridLayout, + { "cascadePtr":cascade }); + cascades.push(cascadeObj) + } + + Connections { + target: GlobalIlluminationCiVct + function onQmlAddCascade() { + mainGridLayout.addCascade() + } + + function onQmlAddCascade2(_resX, _resY, _resZ, _octX, _octY, _octZ, + _ahsX, _ahsY, _ahsZ, _thinWallCounter) { + var cascade = GlobalIlluminationCiVct.AddCascade() + var cascadeComponent = Qt.createComponent("CiVctCascadePrivate.qml"); + cascade.resolutionX = _resX + cascade.resolutionY = _resY + cascade.resolutionZ = _resZ + cascade.octantCountX = _octX + cascade.octantCountY = _octY + cascade.octantCountZ = _octZ + cascade.areaHalfSizeX = _ahsX + cascade.areaHalfSizeY = _ahsY + cascade.areaHalfSizeZ = _ahsZ + var cascadeObj = + cascadeComponent.createObject(mainGridLayout, + { "cascadePtr":cascade }); + cascades.push(cascadeObj) + } + } + + Button { + id: removeCascade + text: qsTr("Remove Cascade") + enabled: GlobalIlluminationCiVct.cascadesEditable + Layout.columnSpan: 3 + Layout.fillWidth: true + onClicked: { + if(cascades.length > 0) { + //mainGridLayout.height = 400 + 400 * (cascades.length + 1) + cascades[cascades.length - 1].destroy() + cascades.pop(); + GlobalIlluminationCiVct.PopCascade() + } + } + } + Button { + id: addCascade + text: qsTr("Add Cascade") + enabled: GlobalIlluminationCiVct.cascadesEditable + Layout.columnSpan: 3 + Layout.fillWidth: true + onClicked: { + mainGridLayout.addCascade() + } + } + + Button { + id: resetCascades + text: qsTr("Reset Cascades") + enabled: !GlobalIlluminationCiVct.cascadesEditable + Layout.columnSpan: 6 + Layout.fillWidth: true + onClicked: { + GlobalIlluminationCiVct.ResetCascades() + } + } + + CheckBox { + Layout.alignment: Qt.AlignHCenter + id: displayVisual + Layout.columnSpan: 6 + Layout.fillWidth: true + text: qsTr("Enabled") + checked: GlobalIlluminationCiVct.enabled + onToggled: { + GlobalIlluminationCiVct.enabled = checked + } + } + + Text { + Layout.columnSpan: 4 + id: bounceCountStr + color: "dimgrey" + text: qsTr("BounceCount") + } + + GzSpinBox { + Layout.columnSpan: 2 + Layout.fillWidth: true + id: bounceCount + value: GlobalIlluminationCiVct.bounceCount + minimumValue: 0 + maximumValue: 16 + decimals: 1 + onValueChanged: { + GlobalIlluminationCiVct.bounceCount = value + } + } + + CheckBox { + Layout.alignment: Qt.AlignHCenter + id: highQuality + Layout.columnSpan: 6 + Layout.fillWidth: true + text: qsTr("High Quality") + checked: GlobalIlluminationCiVct.highQuality + onToggled: { + GlobalIlluminationCiVct.highQuality = checked; + } + } + + CheckBox { + Layout.alignment: Qt.AlignHCenter + id: anisotropic + Layout.columnSpan: 6 + Layout.fillWidth: true + text: qsTr("Anisotropic") + checked: GlobalIlluminationCiVct.anisotropic + onToggled: { + GlobalIlluminationCiVct.anisotropic = checked; + } + } + + Text { + Layout.columnSpan: 2 + id: debugVisualizationStr + color: "dimgrey" + text: qsTr("DebugVisualization") + } + + ComboBox { + Layout.columnSpan: 4 + id: debugVisualization + Layout.fillWidth: true + currentIndex: GlobalIlluminationCiVct.debugVisualizationMode + model: ["Albedo", "Normal", "Emissive", "Lighting", "None"] + onCurrentIndexChanged: { + if (currentIndex < 0|| currentIndex > 4) { + return; + } + GlobalIlluminationCiVct.debugVisualizationMode = currentIndex + } + } + + RoundButton { + Layout.columnSpan: 1 + text: "\u21bb" + Material.background: Material.primary + onClicked: { + combo.currentIndex = 0 + GlobalIlluminationCiVct.OnRefreshCameras(); + } + ToolTip.visible: hovered + ToolTip.delay: tooltipDelay + ToolTip.timeout: tooltipTimeout + ToolTip.text: qsTr("Refresh list all available cameras") + } + + ComboBox { + Layout.columnSpan: 5 + id: combo + Layout.fillWidth: true + model: GlobalIlluminationCiVct.cameraList + currentIndex: 0 + onCurrentIndexChanged: { + if (currentIndex < 0) + return; + GlobalIlluminationCiVct.OnCamareBind(textAt(currentIndex)); + } + ToolTip.visible: hovered + ToolTip.delay: tooltipDelay + ToolTip.timeout: tooltipTimeout + ToolTip.text: qsTr("Camera around which all cascades are centered from") + } +} + +/*##^## +Designer { + D{i:0;autoSize:true;height:480;width:640}D{i:1}D{i:2}D{i:3}D{i:4}D{i:5}D{i:6}D{i:7} +D{i:8}D{i:9}D{i:10}D{i:11}D{i:12}D{i:13}D{i:14}D{i:15}D{i:16}D{i:17}D{i:18}D{i:19} +} +##^##*/ diff --git a/src/gui/plugins/global_illumination_civct/GlobalIlluminationCiVct.qrc b/src/gui/plugins/global_illumination_civct/GlobalIlluminationCiVct.qrc new file mode 100644 index 0000000000..e38ebf4eed --- /dev/null +++ b/src/gui/plugins/global_illumination_civct/GlobalIlluminationCiVct.qrc @@ -0,0 +1,6 @@ + + + CiVctCascadePrivate.qml + GlobalIlluminationCiVct.qml + + diff --git a/src/gui/plugins/global_illumination_civct/Tsa.hh b/src/gui/plugins/global_illumination_civct/Tsa.hh new file mode 100644 index 0000000000..d825a531de --- /dev/null +++ b/src/gui/plugins/global_illumination_civct/Tsa.hh @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/// TSA = Clang Thread Safety Analysis +#ifndef THREAD_SAFETY_ANALYSIS_MUTEX_H +#define THREAD_SAFETY_ANALYSIS_MUTEX_H + +// Enable thread safety attributes only with clang. +// The attributes can be safely erased when compiling with other compilers. +#if defined(__clang__) && (!defined(SWIG)) +#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#endif + +#define CAPABILITY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(capability(x)) + +#define SCOPED_CAPABILITY \ + THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) + +#define GUARDED_BY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) + +#define PT_GUARDED_BY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) + +#define ACQUIRED_BEFORE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__)) + +#define ACQUIRED_AFTER(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__)) + +#define REQUIRES(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__)) + +#define REQUIRES_SHARED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(requires_shared_capability(__VA_ARGS__)) + +#define ACQUIRE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(__VA_ARGS__)) + +#define ACQUIRE_SHARED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquire_shared_capability(__VA_ARGS__)) + +#define RELEASE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__)) + +#define RELEASE_SHARED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__)) + +#define RELEASE_GENERIC(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(release_generic_capability(__VA_ARGS__)) + +#define TRY_ACQUIRE(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_capability(__VA_ARGS__)) + +#define TRY_ACQUIRE_SHARED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(try_acquire_shared_capability(__VA_ARGS__)) + +#define EXCLUDES(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) + +#define ASSERT_CAPABILITY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x)) + +#define ASSERT_SHARED_CAPABILITY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x)) + +#define RETURN_CAPABILITY(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) + +#define NO_THREAD_SAFETY_ANALYSIS \ + THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) + + +#ifdef USE_LOCK_STYLE_THREAD_SAFETY_ATTRIBUTES +// The original version of thread safety analysis the following attribute +// definitions. These use a lock-based terminology. They are still in use +// by existing thread safety code, and will continue to be supported. + +// Deprecated. +#define PT_GUARDED_VAR \ + THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_var) + +// Deprecated. +#define GUARDED_VAR \ + THREAD_ANNOTATION_ATTRIBUTE__(guarded_var) + +// Replaced by REQUIRES +#define EXCLUSIVE_LOCKS_REQUIRED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__)) + +// Replaced by REQUIRES_SHARED +#define SHARED_LOCKS_REQUIRED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__)) + +// Replaced by CAPABILITY +#define LOCKABLE \ + THREAD_ANNOTATION_ATTRIBUTE__(lockable) + +// Replaced by SCOPED_CAPABILITY +#define SCOPED_LOCKABLE \ + THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable) + +// Replaced by ACQUIRE +#define EXCLUSIVE_LOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__)) + +// Replaced by ACQUIRE_SHARED +#define SHARED_LOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__)) + +// Replaced by RELEASE and RELEASE_SHARED +#define UNLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__)) + +// Replaced by TRY_ACQUIRE +#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__)) + +// Replaced by TRY_ACQUIRE_SHARED +#define SHARED_TRYLOCK_FUNCTION(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__)) + +// Replaced by ASSERT_CAPABILITY +#define ASSERT_EXCLUSIVE_LOCK(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__)) + +// Replaced by ASSERT_SHARED_CAPABILITY +#define ASSERT_SHARED_LOCK(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__)) + +// Replaced by EXCLUDE_CAPABILITY. +#define LOCKS_EXCLUDED(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__)) + +// Replaced by RETURN_CAPABILITY +#define LOCK_RETURNED(x) \ + THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x)) + +#endif // USE_LOCK_STYLE_THREAD_SAFETY_ATTRIBUTES + +#endif diff --git a/src/gui/plugins/global_illumination_vct/CMakeLists.txt b/src/gui/plugins/global_illumination_vct/CMakeLists.txt new file mode 100644 index 0000000000..50f3330edd --- /dev/null +++ b/src/gui/plugins/global_illumination_vct/CMakeLists.txt @@ -0,0 +1,6 @@ +gz_add_gui_plugin(GlobalIlluminationVct + SOURCES GlobalIlluminationVct.cc GlobalIlluminationVct.qml GlobalIlluminationVct.qrc + QT_HEADERS GlobalIlluminationVct.hh + PRIVATE_LINK_LIBS + ${GZ-RENDERING_LIBRARIES} +) diff --git a/src/gui/plugins/global_illumination_vct/GlobalIlluminationVct.cc b/src/gui/plugins/global_illumination_vct/GlobalIlluminationVct.cc new file mode 100644 index 0000000000..0d6400c1a5 --- /dev/null +++ b/src/gui/plugins/global_illumination_vct/GlobalIlluminationVct.cc @@ -0,0 +1,717 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#define _LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS + +#include "GlobalIlluminationVct.hh" + +#include +#include +#include + +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include "gz/sim/Entity.hh" +#include "gz/sim/EntityComponentManager.hh" +#include "gz/sim/components/Name.hh" +#include "gz/sim/components/World.hh" +#include "gz/sim/rendering/RenderUtil.hh" + +#include "gz/rendering/GlobalIlluminationVct.hh" +#include "gz/rendering/LidarVisual.hh" +#include "gz/rendering/RenderEngine.hh" +#include "gz/rendering/RenderTypes.hh" +#include "gz/rendering/RenderingIface.hh" +#include "gz/rendering/Scene.hh" + +#include "gz/sim/Util.hh" +#include "gz/sim/components/Link.hh" +#include "gz/sim/components/Model.hh" +#include "gz/sim/components/ParentEntity.hh" +#include "gz/sim/components/Pose.hh" +#include "gz/sim/components/Sensor.hh" + +#include "gz/msgs/laserscan.pb.h" + +#if defined(__clang__) +# define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +# define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op +#endif +#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x)) +#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x)) +#define REQUIRES(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__)) + +// clang-format off +namespace gz +{ +namespace sim +{ +inline namespace GZ_SIM_VERSION_NAMESPACE +{ + /// \brief Private data class for GlobalIlluminationVct + class GZ_SIM_HIDDEN GlobalIlluminationVctPrivate + { + /// \brief Transport node + public: transport::Node node; + + /// \brief Scene Pointer + public: rendering::ScenePtr scene; + + /// \brief Pointer to GlobalIlluminationVct + public: rendering::GlobalIlluminationVctPtr gi GUARDED_BY(serviceMutex); + + /// \brief Toggles this GI on/off. Only one can be active at the same time. + public: bool enabled GUARDED_BY(serviceMutex){false}; + + /// \brief See rendering::GlobalIlluminationVct::SetResolution + public: uint32_t resolution[3] GUARDED_BY(serviceMutex){16u, 16u, 16u}; + + /// \brief See rendering::GlobalIlluminationVct::SetOctantCount + public: uint32_t octantCount[3] GUARDED_BY(serviceMutex){1u, 1u, 1u}; + + /// \brief See rendering::GlobalIlluminationVct::SetBounceCount + public: uint32_t bounceCount GUARDED_BY(serviceMutex){6u}; + + /// \brief See rendering::GlobalIlluminationVct::SetHighQuality + public: bool highQuality GUARDED_BY(serviceMutex){true}; + + /// \brief See rendering::GlobalIlluminationVct::SetAnisotropic + public: bool anisotropic GUARDED_BY(serviceMutex){true}; + + /// \brief See rendering::GlobalIlluminationVct::SetConserveMemory + public: bool conserveMemory GUARDED_BY(serviceMutex){false}; + + /// \brief See rendering::GlobalIlluminationVct::DebugVisualizationMode + public: float thinWallCounter GUARDED_BY(serviceMutex){ 1.0f }; + + /// \brief See rendering::GlobalIlluminationVct::DebugVisualizationMode + public: uint32_t debugVisMode GUARDED_BY( + serviceMutex){ rendering::GlobalIlluminationVct::DVM_None }; + + /// \brief Mutex for variable mutated by the checkbox and spinboxes + /// callbacks. + /// The variables are: msg, minVisualRange and + /// maxVisualRange + public: std::mutex serviceMutex; + + /// \brief Initialization flag + public: bool initialized{false}; + + /// \brief Reset visual flag + public: bool resetVisual{false}; + + /// \brief GI visual display dirty flag + public: bool visualDirty GUARDED_BY(serviceMutex){false}; + + /// \brief GI visual display dirty flag; but it is fast/quick to rebuild + public: bool lightingDirty GUARDED_BY(serviceMutex){false}; + + /// \brief GI debug visualization is dirty. Only used by GUI. + /// Not in simulation. + public: bool debugVisualizationDirty GUARDED_BY(serviceMutex){false}; + }; +} +} +} +// clang-format on + +using namespace gz; +using namespace sim; + +///////////////////////////////////////////////// +GlobalIlluminationVct::GlobalIlluminationVct() : + GuiSystem(), + dataPtr(new GlobalIlluminationVctPrivate) +{ + // no ops +} + +///////////////////////////////////////////////// +GlobalIlluminationVct::~GlobalIlluminationVct() +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + this->dataPtr->gi.reset(); +} + +///////////////////////////////////////////////// +void GlobalIlluminationVct::LoadGlobalIlluminationVct() + REQUIRES(this->dataPtr->serviceMutex) +{ + auto loadedEngNames = rendering::loadedEngines(); + if (loadedEngNames.empty()) + { + return; + } + + // assume there is only one engine loaded + auto engineName = loadedEngNames[0]; + if (loadedEngNames.size() > 1) + { + gzdbg << "More than one engine is available. " + << "GlobalIlluminationVct plugin will use engine [" << engineName + << "]" << std::endl; + } + auto engine = rendering::engine(engineName); + if (!engine) + { + gzerr << "Internal error: failed to load engine [" << engineName + << "]. GlobalIlluminationVct plugin won't work." << std::endl; + return; + } + + if (engine->SceneCount() == 0) + return; + + // assume there is only one scene + // load scene + auto scene = engine->SceneByIndex(0); + if (!scene) + { + gzerr << "Internal error: scene is null." << std::endl; + return; + } + + if (!scene->IsInitialized() || scene->VisualCount() == 0) + { + return; + } + + // Create lidar visual + gzdbg << "Creating GlobalIlluminationVct" << std::endl; + + auto root = scene->RootVisual(); + this->dataPtr->gi = scene->CreateGlobalIlluminationVct(); + if (!this->dataPtr->gi) + { + gzwarn << "Failed to create GlobalIlluminationVct, GI plugin won't work." + << std::endl; + + gz::gui::App()->findChild()->removeEventFilter(this); + } + else + { + this->dataPtr->gi->SetParticipatingVisuals( + rendering::GlobalIlluminationBase::DYNAMIC_VISUALS | + rendering::GlobalIlluminationBase::STATIC_VISUALS); + this->dataPtr->scene = scene; + this->dataPtr->initialized = true; + } +} + +/// \brief XML helper to retrieve values and handle errors +/// \param[in] _elem XML element to read +/// \param[out] _valueToSet Value to set. Left unmodified on error +/// \return True if _valueToSet was successfully set +static bool GetXmlBool(const tinyxml2::XMLElement *_elem, bool &_valueToSet) +{ + bool value = false; + + if (_elem->QueryBoolText(&value) != tinyxml2::XML_SUCCESS) + { + gzerr << "Failed to parse <" << _elem->Name() + << "> value: " << _elem->GetText() << std::endl; + return false; + } + else + { + _valueToSet = value; + return true; + } +} + +/// \brief XML helper to retrieve values and handle errors +/// \param[in] _elem XML element to read +/// \param[out] _valueToSet Value to set. Left unmodified on error +/// \return True if _valueToSet was successfully set +static bool GetXmlFloat(const tinyxml2::XMLElement *_elem, float &_valueToSet) +{ + float value = 0; + + if (_elem->QueryFloatText(&value) != tinyxml2::XML_SUCCESS) + { + gzerr << "Failed to parse <" << _elem->Name() + << "> value: " << _elem->GetText() << std::endl; + return false; + } + else + { + _valueToSet = value; + return true; + } +} + +/// \brief XML helper to retrieve values and handle errors +/// \param[in] _elem XML element to read +/// \param[out] _valueToSet Value to set. Left unmodified on error +/// \return True if _valueToSet was successfully set +static bool GetXmlUint32(const tinyxml2::XMLElement *_elem, + uint32_t &_valueToSet) +{ + int value = 0; + + if (_elem->QueryIntText(&value) != tinyxml2::XML_SUCCESS) + { + gzerr << "Failed to parse <" << _elem->Name() + << "> value: " << _elem->GetText() << std::endl; + return false; + } + else + { + _valueToSet = static_cast(value); + return true; + } +} + +/// \brief XML helper to retrieve values and handle errors +/// \param[in] _elem XML element to read +/// \param[out] _valueToSet Values to set. Left unmodified on error. +/// Its array length must be >= 3 +/// \return True if _valueToSet was successfully set +static bool GetXmlUint32x3(const tinyxml2::XMLElement *_elem, + uint32_t _valueToSet[3]) +{ + std::istringstream stream(_elem->GetText()); + math::Vector3i values3; + stream >> values3; + + _valueToSet[0] = static_cast(values3.X()); + _valueToSet[1] = static_cast(values3.Y()); + _valueToSet[2] = static_cast(values3.Z()); + + return true; +} + +///////////////////////////////////////////////// +void GlobalIlluminationVct::LoadConfig(const tinyxml2::XMLElement *_pluginElem) +{ + if (this->title.empty()) + this->title = "Global Illumination (VCT)"; + + std::lock_guard lock(this->dataPtr->serviceMutex); + + if (auto elem = _pluginElem->FirstChildElement("enabled")) + { + GetXmlBool(elem, this->dataPtr->enabled); + } + if (auto elem = _pluginElem->FirstChildElement("highQuality")) + { + GetXmlBool(elem, this->dataPtr->highQuality); + } + if (auto elem = _pluginElem->FirstChildElement("anisotropic")) + { + GetXmlBool(elem, this->dataPtr->anisotropic); + } + if (auto elem = _pluginElem->FirstChildElement("conserveMemory")) + { + GetXmlBool(elem, this->dataPtr->conserveMemory); + } + if (auto elem = _pluginElem->FirstChildElement("resolution")) + { + GetXmlUint32x3(elem, this->dataPtr->resolution); + } + if (auto elem = _pluginElem->FirstChildElement("octantCount")) + { + GetXmlUint32x3(elem, this->dataPtr->octantCount); + } + if (auto elem = _pluginElem->FirstChildElement("bounceCount")) + { + GetXmlUint32(elem, this->dataPtr->bounceCount); + } + if (auto elem = _pluginElem->FirstChildElement("thinWallCounter")) + { + GetXmlFloat(elem, this->dataPtr->thinWallCounter); + } + if (auto elem = _pluginElem->FirstChildElement("debugVisMode")) + { + const std::string text = elem->GetText(); + if (text == "none") + { + this->dataPtr->debugVisMode = rendering::GlobalIlluminationVct::DVM_None; + } + else if (text == "albedo") + { + this->dataPtr->debugVisMode = + rendering::GlobalIlluminationVct::DVM_Albedo; + } + else if (text == "normal") + { + this->dataPtr->debugVisMode = + rendering::GlobalIlluminationVct::DVM_Normal; + } + else if (text == "emissive") + { + this->dataPtr->debugVisMode = + rendering::GlobalIlluminationVct::DVM_Emissive; + } + else if (text == "lighting") + { + this->dataPtr->debugVisMode = + rendering::GlobalIlluminationVct::DVM_Lighting; + } + else + { + GetXmlUint32(elem, this->dataPtr->debugVisMode); + } + } + + gz::gui::App()->findChild()->installEventFilter(this); +} + +///////////////////////////////////////////////// +bool GlobalIlluminationVct::eventFilter(QObject *_obj, QEvent *_event) +{ + if (_event->type() == gz::gui::events::Render::kType) + { + // This event is called in Scene3d's RenderThread, so it's safe to make + // rendering calls here + + std::lock_guard lock(this->dataPtr->serviceMutex); + if (!this->dataPtr->initialized) + { + this->LoadGlobalIlluminationVct(); + } + + if (this->dataPtr->gi) + { + if (this->dataPtr->resetVisual) + { + this->dataPtr->resetVisual = false; + } + + if (!this->dataPtr->visualDirty && !this->dataPtr->gi->Enabled() && + this->dataPtr->enabled) + { + // If we're here, GI was disabled externally. This can happen + // if e.g. another GI solution was enabled (only one can be active) + this->dataPtr->enabled = false; + this->EnabledChanged(); + } + + if (this->dataPtr->visualDirty) + { + this->dataPtr->gi->SetResolution(this->dataPtr->resolution); + this->dataPtr->gi->SetOctantCount(this->dataPtr->octantCount); + this->dataPtr->gi->SetBounceCount(this->dataPtr->bounceCount); + this->dataPtr->gi->SetHighQuality(this->dataPtr->highQuality); + this->dataPtr->gi->SetAnisotropic(this->dataPtr->anisotropic); + this->dataPtr->gi->SetThinWallCounter(this->dataPtr->thinWallCounter); + this->dataPtr->gi->SetConserveMemory(this->dataPtr->conserveMemory); + + // Ogre-Next may crash if some of the settings above are + // changed while visualizing is enabled. + this->dataPtr->gi->SetDebugVisualization( + rendering::GlobalIlluminationVct::DVM_None); + + if (this->dataPtr->enabled) + { + this->dataPtr->gi->Build(); + this->dataPtr->scene->SetActiveGlobalIllumination(this->dataPtr->gi); + } + else + { + this->dataPtr->scene->SetActiveGlobalIllumination(nullptr); + } + + // Restore debug visualization to desired. + this->dataPtr->gi->SetDebugVisualization( + static_cast( + this->dataPtr->debugVisMode)); + + this->dataPtr->visualDirty = false; + this->dataPtr->lightingDirty = false; + this->dataPtr->debugVisualizationDirty = false; + } + else if (this->dataPtr->lightingDirty) + { + this->dataPtr->gi->SetBounceCount(this->dataPtr->bounceCount); + this->dataPtr->gi->SetHighQuality(this->dataPtr->highQuality); + this->dataPtr->gi->SetAnisotropic(this->dataPtr->anisotropic); + this->dataPtr->gi->SetThinWallCounter(this->dataPtr->thinWallCounter); + this->dataPtr->gi->SetConserveMemory(this->dataPtr->conserveMemory); + + if (this->dataPtr->gi->Enabled()) + { + this->dataPtr->gi->SetDebugVisualization( + rendering::GlobalIlluminationVct::DVM_None); + + this->dataPtr->gi->LightingChanged(); + + this->dataPtr->gi->SetDebugVisualization( + static_cast< + rendering::GlobalIlluminationVct::DebugVisualizationMode>( + this->dataPtr->debugVisMode)); + + this->dataPtr->debugVisualizationDirty = false; + } + this->dataPtr->lightingDirty = false; + } + else if (this->dataPtr->debugVisualizationDirty) + { + this->dataPtr->gi->SetDebugVisualization( + static_cast( + this->dataPtr->debugVisMode)); + this->dataPtr->debugVisualizationDirty = false; + } + } + else + { + gzerr << "GI pointer is not set" << std::endl; + } + } + + // Standard event processing + return QObject::eventFilter(_obj, _event); +} + +////////////////////////////////////////////////// +void GlobalIlluminationVct::UpdateDebugVisualizationMode(int _mode) +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + + rendering::GlobalIlluminationVct::DebugVisualizationMode + debugVisualizationMode = rendering::GlobalIlluminationVct::DVM_None; + + if (_mode >= rendering::GlobalIlluminationVct::DVM_Albedo && + _mode <= rendering::GlobalIlluminationVct::DVM_None) + { + debugVisualizationMode = + static_cast( + _mode); + } + + this->dataPtr->gi->SetDebugVisualization(debugVisualizationMode); +} + +////////////////////////////////////////////////// +void GlobalIlluminationVct::UpdateResolution(int _axis, uint32_t _res) +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + this->dataPtr->resolution[_axis] = _res; + this->dataPtr->visualDirty = true; +} + +////////////////////////////////////////////////// +void GlobalIlluminationVct::UpdateOctantCount(int _axis, uint32_t _count) +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + this->dataPtr->octantCount[_axis] = _count; + this->dataPtr->visualDirty = true; +} + +////////////////////////////////////////////////// +void GlobalIlluminationVct::SetEnabled(const bool _enabled) +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + this->dataPtr->enabled = _enabled; + this->dataPtr->visualDirty = true; +} + +////////////////////////////////////////////////// +bool GlobalIlluminationVct::Enabled() const +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + return this->dataPtr->enabled; +} + +////////////////////////////////////////////////// +void GlobalIlluminationVct::SetResolutionX(const uint32_t _res) +{ + this->UpdateResolution(0, _res); +} + +////////////////////////////////////////////////// +uint32_t GlobalIlluminationVct::ResolutionX() const +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + return this->dataPtr->resolution[0]; +} + +////////////////////////////////////////////////// +void GlobalIlluminationVct::SetResolutionY(const uint32_t _res) +{ + this->UpdateResolution(1, _res); +} + +////////////////////////////////////////////////// +uint32_t GlobalIlluminationVct::ResolutionY() const +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + return this->dataPtr->resolution[1]; +} + +////////////////////////////////////////////////// +void GlobalIlluminationVct::SetResolutionZ(const uint32_t _res) +{ + this->UpdateResolution(2, _res); +} + +////////////////////////////////////////////////// +uint32_t GlobalIlluminationVct::ResolutionZ() const +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + return this->dataPtr->resolution[2]; +} + +////////////////////////////////////////////////// +void GlobalIlluminationVct::SetOctantCountX(const uint32_t _octantCount) +{ + this->UpdateOctantCount(0, _octantCount); +} + +////////////////////////////////////////////////// +uint32_t GlobalIlluminationVct::OctantCountX() const +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + return this->dataPtr->octantCount[0]; +} + +////////////////////////////////////////////////// +void GlobalIlluminationVct::SetOctantCountY(const uint32_t _octantCount) +{ + this->UpdateOctantCount(1, _octantCount); +} + +////////////////////////////////////////////////// +uint32_t GlobalIlluminationVct::OctantCountY() const +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + return this->dataPtr->octantCount[1]; +} + +////////////////////////////////////////////////// +void GlobalIlluminationVct::SetOctantCountZ(const uint32_t _octantCount) +{ + this->UpdateOctantCount(2, _octantCount); +} + +////////////////////////////////////////////////// +uint32_t GlobalIlluminationVct::OctantCountZ() const +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + return this->dataPtr->octantCount[2]; +} + +////////////////////////////////////////////////// +void GlobalIlluminationVct::SetBounceCount(const uint32_t _bounces) +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + this->dataPtr->bounceCount = _bounces; + this->dataPtr->lightingDirty = true; +} + +////////////////////////////////////////////////// +uint32_t GlobalIlluminationVct::BounceCount() const +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + return this->dataPtr->bounceCount; +} + +////////////////////////////////////////////////// +void GlobalIlluminationVct::SetHighQuality(const bool _quality) +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + this->dataPtr->highQuality = _quality; + this->dataPtr->lightingDirty = true; +} + +////////////////////////////////////////////////// +bool GlobalIlluminationVct::HighQuality() const +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + return this->dataPtr->highQuality; +} + +////////////////////////////////////////////////// +void GlobalIlluminationVct::SetAnisotropic(const bool _anisotropic) +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + this->dataPtr->anisotropic = _anisotropic; + this->dataPtr->lightingDirty = true; +} + +////////////////////////////////////////////////// +bool GlobalIlluminationVct::Anisotropic() const +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + return this->dataPtr->anisotropic; +} + +////////////////////////////////////////////////// +void GlobalIlluminationVct::SetConserveMemory(const bool _conserveMemory) +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + this->dataPtr->conserveMemory = _conserveMemory; + this->dataPtr->lightingDirty = true; +} + +////////////////////////////////////////////////// +bool GlobalIlluminationVct::ConserveMemory() const +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + return this->dataPtr->conserveMemory; +} + +////////////////////////////////////////////////// +void GlobalIlluminationVct::SetThinWallCounter(const float _thinWallCounter) +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + this->dataPtr->thinWallCounter = _thinWallCounter; + this->dataPtr->lightingDirty = true; +} + +////////////////////////////////////////////////// +float GlobalIlluminationVct::ThinWallCounter() const +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + return this->dataPtr->thinWallCounter; +} + +////////////////////////////////////////////////// +void GlobalIlluminationVct::SetDebugVisualizationMode(const uint32_t _visMode) +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + this->dataPtr->debugVisMode = _visMode; + this->dataPtr->debugVisualizationDirty = true; +} + +////////////////////////////////////////////////// +uint32_t GlobalIlluminationVct::DebugVisualizationMode() const +{ + std::lock_guard lock(this->dataPtr->serviceMutex); + return this->dataPtr->debugVisMode; +} + +// Register this plugin +GZ_ADD_PLUGIN(gz::sim::GlobalIlluminationVct, gz::gui::Plugin) diff --git a/src/gui/plugins/global_illumination_vct/GlobalIlluminationVct.hh b/src/gui/plugins/global_illumination_vct/GlobalIlluminationVct.hh new file mode 100644 index 0000000000..fde6a6979b --- /dev/null +++ b/src/gui/plugins/global_illumination_vct/GlobalIlluminationVct.hh @@ -0,0 +1,300 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +#ifndef GZ_SIM_GUI_GLOBALILLUMINATIONVCT_HH_ +#define GZ_SIM_GUI_GLOBALILLUMINATIONVCT_HH_ + +#include + +#include "gz/sim/gui/GuiSystem.hh" +#include "gz/gui/qt.h" + +namespace gz +{ +namespace sim +{ +// Inline bracket to help doxygen filtering. +inline namespace GZ_SIM_VERSION_NAMESPACE +{ + class GlobalIlluminationVctPrivate; + + /// \brief Enable and configure Global Illumination using VCT + /// (Voxel Cone Tracing) + /// + /// Due to how QML bindings work, we must split Vectors + /// into each component so e.g. the following Javascript code: + /// + /// cascade.resolutionX = 16 + /// cascade.resolutionY = 32 + /// cascade.resolutionZ = 64 + /// + /// Will end up calling: + /// + /// cascade->SetResolutionX(16); + /// cascade->SetResolutionY(32); + /// cascade->SetResolutionZ(64); + /// + /// Even though in C++ we would normally do resolution = {16, 32, 64}; + /// The same goes for each property. + class GlobalIlluminationVct : public gz::sim::GuiSystem + { + Q_OBJECT + + /// \brief Enabled QML binding. + /// Anything that isn't GUI-only (i.e. affects simulation) needs it + Q_PROPERTY( + bool enabled + READ Enabled + WRITE SetEnabled + NOTIFY EnabledChanged + ) + + Q_PROPERTY( + int resolutionX + READ ResolutionX + WRITE SetResolutionX + NOTIFY SettingsChanged + ) + Q_PROPERTY( + int resolutionY + READ ResolutionY + WRITE SetResolutionY + NOTIFY SettingsChanged + ) + Q_PROPERTY( + int resolutionZ + READ ResolutionZ + WRITE SetResolutionZ + NOTIFY SettingsChanged + ) + + Q_PROPERTY( + int octantCountX + READ OctantCountX + WRITE SetOctantCountX + NOTIFY SettingsChanged + ) + Q_PROPERTY( + int octantCountY + READ OctantCountY + WRITE SetOctantCountY + NOTIFY SettingsChanged + ) + Q_PROPERTY( + int octantCountZ + READ OctantCountZ + WRITE SetOctantCountZ + NOTIFY SettingsChanged + ) + + Q_PROPERTY( + int bounceCount + READ BounceCount + WRITE SetBounceCount + NOTIFY LightingChanged + ) + + Q_PROPERTY( + bool highQuality + READ HighQuality + WRITE SetHighQuality + NOTIFY LightingChanged + ) + + Q_PROPERTY( + bool anisotropic + READ Anisotropic + WRITE SetAnisotropic + NOTIFY LightingChanged + ) + + Q_PROPERTY( + bool conserveMemory + READ ConserveMemory + WRITE SetConserveMemory + NOTIFY LightingChanged + ) + + Q_PROPERTY( + float thinWallCounter + READ ThinWallCounter + WRITE SetThinWallCounter + NOTIFY LightingChanged + ) + + Q_PROPERTY( + int debugVisualizationMode + READ DebugVisualizationMode + WRITE SetDebugVisualizationMode + NOTIFY DebugVisualizationModeChanged + ) + + /// \brief Constructor + public: GlobalIlluminationVct(); + + /// \brief Destructor + public: ~GlobalIlluminationVct() override; + + // Documentation inherited + public: void LoadConfig(const tinyxml2::XMLElement *_pluginElem) override; + + // Documentation Inherited + public: bool eventFilter(QObject *_obj, QEvent *_event) override; + + /// \brief Load the scene and attach LidarVisual to the scene + public: void LoadGlobalIlluminationVct(); + + /// \brief Set debug visualization mode GlogbalIllumination + /// \param[in] _mode Index of selected debug visualization mode + public: Q_INVOKABLE void UpdateDebugVisualizationMode(int _mode); + + /// \brief Set VCT resolution + /// \param[in] _axis Axis (width, height, depth). In range [0; 3) + /// \param[in] _res New resolution + public: Q_INVOKABLE void UpdateResolution(int _axis, uint32_t _res); + + /// \brief Set VCT octant count + /// \param[in] _axis Axis (width, height, depth). In range [0; 3) + /// \param[in] _count New octant count + public: Q_INVOKABLE void UpdateOctantCount(int _axis, uint32_t _count); + + /// \brief See rendering::GlobalIlluminationVct::SetEnabled & + /// rendering::Scene::SetActiveGlobalIllumination + /// \param[in] _enabled See GlobalIlluminationVct::SetEnabled + public: Q_INVOKABLE void SetEnabled(const bool _enabled); + + /// \brief See rendering::GlobalIlluminationVct::Enabled + /// \return See rendering::GlobalIlluminationVct::Enabled + public: Q_INVOKABLE bool Enabled() const; + + /// \brief Notify this property has changed + signals: void EnabledChanged(); + + /// \brief Notify various properties may have changed + signals: void SettingsChanged(); + + /// \brief Notify fast-to-rebuild properties may have changed + signals: void LightingChanged(); + + /// \brief Notify debug visualization has changed + signals: void DebugVisualizationModeChanged(); + + /// \brief See rendering::GlobalIlluminationVct::SetResolution + /// \param[in] _res See GlobalIlluminationVct::SetResolution + public: Q_INVOKABLE void SetResolutionX(const uint32_t _res); + + /// \brief See rendering::GlobalIlluminationVct::Resolution + /// \return See rendering::GlobalIlluminationVct::Resolution + public: Q_INVOKABLE uint32_t ResolutionX() const; + + /// \brief See rendering::GlobalIlluminationVct::SetResolution + /// \param[in] _res See GlobalIlluminationVct::SetResolution + public: Q_INVOKABLE void SetResolutionY(const uint32_t _res); + + /// \brief See rendering::GlobalIlluminationVct::Resolution + /// \return See rendering::GlobalIlluminationVct::Resolution + public: Q_INVOKABLE uint32_t ResolutionY() const; + + /// \brief See rendering::GlobalIlluminationVct::SetResolution + /// \param[in] _res See GlobalIlluminationVct::SetResolution + public: Q_INVOKABLE void SetResolutionZ(const uint32_t _res); + + /// \brief See rendering::GlobalIlluminationVct::Resolution + /// \return See rendering::GlobalIlluminationVct::Resolution + public: Q_INVOKABLE uint32_t ResolutionZ() const; + + /// \brief See rendering::GlobalIlluminationVct::SetOctantCount + /// \param[in] _octantCount See GlobalIlluminationVct::SetOctantCount + public: Q_INVOKABLE void SetOctantCountX(const uint32_t _octantCount); + + /// \brief See rendering::GlobalIlluminationVct::OctantCount + /// \return See rendering::GlobalIlluminationVct::OctantCount + public: Q_INVOKABLE uint32_t OctantCountX() const; + + /// \brief See rendering::GlobalIlluminationVct::SetOctantCount + /// \param[in] _octantCount See GlobalIlluminationVct::SetOctantCount + public: Q_INVOKABLE void SetOctantCountY(const uint32_t _octantCount); + + /// \brief See rendering::GlobalIlluminationVct::OctantCount + /// \return See rendering::GlobalIlluminationVct::OctantCount + public: Q_INVOKABLE uint32_t OctantCountY() const; + + /// \brief See rendering::GlobalIlluminationVct::SetOctantCount + /// \param[in] _octantCount See GlobalIlluminationVct::SetOctantCount + public: Q_INVOKABLE void SetOctantCountZ(const uint32_t _octantCount); + + /// \brief See rendering::GlobalIlluminationVct::OctantCount + /// \return See rendering::GlobalIlluminationVct::OctantCount + public: Q_INVOKABLE uint32_t OctantCountZ() const; + + /// \brief See rendering::GlobalIlluminationVct::SetBounceCount + /// \param[in] _bounces See GlobalIlluminationVct::SetBounceCount + public: Q_INVOKABLE void SetBounceCount(const uint32_t _bounces); + + /// \brief See rendering::GlobalIlluminationVct::BounceCount + /// \return See rendering::GlobalIlluminationVct::BounceCount + public: Q_INVOKABLE uint32_t BounceCount() const; + + /// \brief See rendering::GlobalIlluminationVct::SetHighQuality + /// \param[in] _quality See GlobalIlluminationVct::SetHighQuality + public: Q_INVOKABLE void SetHighQuality(const bool _quality); + + /// \brief See rendering::GlobalIlluminationVct::HighQuality + /// \return See rendering::GlobalIlluminationVct::HighQuality + public: Q_INVOKABLE bool HighQuality() const; + + /// \brief See rendering::GlobalIlluminationVct::SetAnisotropic + /// \param[in] _anisotropic See GlobalIlluminationVct::SetAnisotropic + public: Q_INVOKABLE void SetAnisotropic(const bool _anisotropic); + + /// \brief See rendering::GlobalIlluminationVct::Anisotropic + /// \return See rendering::GlobalIlluminationVct::Anisotropic + public: Q_INVOKABLE bool Anisotropic() const; + + /// \brief See rendering::GlobalIlluminationVct::SetConserveMemory + /// \param[in] _conserveMemory See GlobalIlluminationVct::SetConserveMemory + public: Q_INVOKABLE void SetConserveMemory(const bool _conserveMemory); + + /// \brief See rendering::GlobalIlluminationVct::ConserveMemory + /// \return See rendering::GlobalIlluminationVct::ConserveMemory + public: Q_INVOKABLE bool ConserveMemory() const; + + /// \brief See rendering::GlobalIlluminationVct::SetThinWallCounter + /// \param[in] _thinWallCounter See + /// GlobalIlluminationVct::SetThinWallCounter + public: Q_INVOKABLE void SetThinWallCounter(const float _thinWallCounter); + + /// \brief See rendering::GlobalIlluminationVct::ThinWallCounter + /// \return See rendering::GlobalIlluminationVct::ThinWallCounter + public: Q_INVOKABLE float ThinWallCounter() const; + + /// \brief See rendering::GlobalIlluminationVct::SetDebugVisualizationMode + /// \param[in] _visMode See GlobalIlluminationVct::SetDebugVisualizationMode + public: Q_INVOKABLE void SetDebugVisualizationMode(const uint32_t _visMode); + + /// \brief See rendering::GlobalIlluminationVct::DebugVisualizationMode + /// \return See rendering::GlobalIlluminationVct::DebugVisualizationMode + public: Q_INVOKABLE uint32_t DebugVisualizationMode() const; + + /// \internal + /// \brief Pointer to private data + private: std::unique_ptr dataPtr; + }; +} +} +} +#endif diff --git a/src/gui/plugins/global_illumination_vct/GlobalIlluminationVct.qml b/src/gui/plugins/global_illumination_vct/GlobalIlluminationVct.qml new file mode 100644 index 0000000000..e0f614be67 --- /dev/null +++ b/src/gui/plugins/global_illumination_vct/GlobalIlluminationVct.qml @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2022 Open Source Robotics Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * +*/ + +import QtQuick 2.9 +import QtQuick.Controls 2.1 +import QtQuick.Controls.Material 2.2 +import QtQuick.Layouts 1.3 +import "qrc:/qml" + +GridLayout { + columns: 6 + columnSpacing: 10 + Layout.minimumWidth: 350 + Layout.minimumHeight: 800 + anchors.fill: parent + anchors.leftMargin: 10 + anchors.rightMargin: 10 + + function isPowerOf2(n) { + return (n & n-1) === 0; + } + + // Returns the closest power of 2, rounding down if need to. + // floorPowerOf2( 4 ) = 4 + // floorPowerOf2( 5 ) = 4 + function floorPowerOf2(n) { + return 1 << (31 - Math.clz32(n)); + } + + // Returns the closest power of 2, rounding up if need to. + // floorPowerOf2( 4 ) = 4 + // floorPowerOf2( 5 ) = 8 + function ceilPowerOf2(n) { + if (isPowerOf2(n)) { + return n; + } + return 1 << (31 - Math.clz32(n) + 1); + } + + function nearestPowerOf2(n, oldValue=undefined) { + if (oldValue === undefined) { + return floorPowerOf2(n); + } + else { + if (oldValue <= n) { + return ceilPowerOf2(n); + } + else { + return floorPowerOf2(n); + } + } + } + + CheckBox { + Layout.alignment: Qt.AlignHCenter + id: displayVisual + Layout.columnSpan: 6 + Layout.fillWidth: true + text: qsTr("Enabled") + checked: GlobalIlluminationVct.enabled + onToggled: { + GlobalIlluminationVct.enabled = checked + } + } + + Text { + Layout.columnSpan: 4 + id: bounceCountStr + color: "dimgrey" + text: qsTr("BounceCount") + } + + GzSpinBox { + Layout.columnSpan: 2 + Layout.fillWidth: true + id: bounceCount + value: GlobalIlluminationVct.bounceCount + minimumValue: 0 + maximumValue: 16 + decimals: 1 + onValueChanged: { + GlobalIlluminationVct.bounceCount = value + } + } + + Text { + Layout.columnSpan: 6 + id: resolutionStr + color: "dimgrey" + text: qsTr("Resolution") + } + + GzSpinBox { + property int oldValue: -1 + + Layout.columnSpan: 2 + Layout.fillWidth: true + id: resolutionSpinX + value: GlobalIlluminationVct.resolutionX + minimumValue: 4 + maximumValue: 512 + decimals: 1 + onEditingFinished: { + var tmpValue = value; + tmpValue = nearestPowerOf2(tmpValue, oldValue); + oldValue = tmpValue; + value = tmpValue; + GlobalIlluminationVct.resolutionX = value; + } + } + GzSpinBox { + property int oldValue: -1 + + Layout.columnSpan: 2 + Layout.fillWidth: true + id: resolutionSpinY + value: GlobalIlluminationVct.resolutionY + minimumValue: 4 + maximumValue: 512 + decimals: 1 + onEditingFinished: { + var tmpValue = value; + tmpValue = nearestPowerOf2(tmpValue, oldValue); + oldValue = tmpValue; + value = tmpValue; + GlobalIlluminationVct.resolutionY = value; + } + } + GzSpinBox { + property int oldValue: -1 + + Layout.columnSpan: 2 + Layout.fillWidth: true + id: resolutionSpinZ + value: GlobalIlluminationVct.resolutionZ + minimumValue: 4 + maximumValue: 512 + decimals: 1 + onEditingFinished: { + var tmpValue = value; + tmpValue = nearestPowerOf2(tmpValue, oldValue); + oldValue = tmpValue; + value = tmpValue; + GlobalIlluminationVct.resolutionZ = value; + } + } + + Text { + Layout.columnSpan: 6 + id: octantCountStr + color: "dimgrey" + text: qsTr("OctantCount") + } + + GzSpinBox { + Layout.columnSpan: 2 + Layout.fillWidth: true + id: octantCountX + value: GlobalIlluminationVct.octantCountX + minimumValue: 1 + maximumValue: 8 + decimals: 1 + onEditingFinished: { + GlobalIlluminationVct.octantCountX = value + } + } + GzSpinBox { + Layout.columnSpan: 2 + Layout.fillWidth: true + id: octantCountY + value: GlobalIlluminationVct.octantCountY + minimumValue: 1 + maximumValue: 8 + decimals: 1 + onEditingFinished: { + GlobalIlluminationVct.octantCountY = value + } + } + GzSpinBox { + Layout.columnSpan: 2 + Layout.fillWidth: true + id: octantCountZ + value: GlobalIlluminationVct.octantCountZ + minimumValue: 1 + maximumValue: 8 + decimals: 1 + onEditingFinished: { + GlobalIlluminationVct.octantCountZ = value + } + } + + CheckBox { + Layout.alignment: Qt.AlignHCenter + id: conserveMemory + Layout.columnSpan: 6 + Layout.fillWidth: true + text: qsTr("Conserve Memory") + checked: GlobalIlluminationVct.conserveMemory + onToggled: { + GlobalIlluminationVct.conserveMemory = checked; + } + } + + CheckBox { + Layout.alignment: Qt.AlignHCenter + id: highQuality + Layout.columnSpan: 6 + Layout.fillWidth: true + text: qsTr("High Quality") + checked: GlobalIlluminationVct.highQuality + onToggled: { + GlobalIlluminationVct.highQuality = checked; + } + } + + CheckBox { + Layout.alignment: Qt.AlignHCenter + id: anisotropic + Layout.columnSpan: 6 + Layout.fillWidth: true + text: qsTr("Anisotropic") + checked: GlobalIlluminationVct.anisotropic + onToggled: { + GlobalIlluminationVct.anisotropic = checked; + } + } + + Text { + Layout.columnSpan: 4 + id: thinWallCounterStr + color: "dimgrey" + text: qsTr("ThinWallCounter") + } + + GzSpinBox { + Layout.columnSpan: 2 + Layout.fillWidth: true + id: thinWallCounter + value: GlobalIlluminationVct.thinWallCounter + minimumValue: 0 + maximumValue: 5 + decimals: 2 + stepSize: 0.1 + onValueChanged: { + GlobalIlluminationVct.thinWallCounter = value + } + } + + Text { + Layout.columnSpan: 2 + id: debugVisualizationStr + color: "dimgrey" + text: qsTr("DebugVisualization") + } + + ComboBox { + Layout.columnSpan: 4 + id: debugVisualization + Layout.fillWidth: true + currentIndex: GlobalIlluminationVct.debugVisualizationMode + model: ["Albedo", "Normal", "Emissive", "Lighting", "None"] + onCurrentIndexChanged: { + if (currentIndex < 0|| currentIndex > 4) { + return; + } + GlobalIlluminationVct.debugVisualizationMode = currentIndex + } + } +} diff --git a/src/gui/plugins/global_illumination_vct/GlobalIlluminationVct.qrc b/src/gui/plugins/global_illumination_vct/GlobalIlluminationVct.qrc new file mode 100644 index 0000000000..a4fa2b20cd --- /dev/null +++ b/src/gui/plugins/global_illumination_vct/GlobalIlluminationVct.qrc @@ -0,0 +1,5 @@ + + + GlobalIlluminationVct.qml + +