├── cmd ├── CMakeLists.txt └── moonray_gui │ ├── resources.qrc │ ├── data │ ├── moonray_rndr_gui_tex_3dlut_3d.bin │ ├── moonray_rndr_gui_tex_3dlut_post1d.bin │ └── moonray_rndr_gui_tex_3dlut_pre1d.bin │ ├── FrameUpdateEvent.cc │ ├── QtQuirks.h │ ├── ColorPicker.h │ ├── FrameUpdateEvent.h │ ├── PathVisualizerGui.qss │ ├── GuiTypes.h │ ├── MainWindow.h │ ├── GlslBuffer.h │ ├── NavigationCam.h │ ├── ColorPicker.cc │ ├── ColorManager.h │ ├── FreeCam.h │ ├── PathVisualizerGui.h │ ├── OrbitCam.h │ ├── CMakeLists.txt │ ├── RenderViewport.h │ ├── RenderGui.h │ ├── MainWindow.cc │ ├── FreeCam.cc │ ├── ColorManager.cc │ ├── OrbitCam.cc │ ├── GlslBuffer.cc │ ├── PathVisualizerGui.cc │ └── moonray_gui.cc ├── CODE_OF_CONDUCT.md ├── .github ├── workflows │ ├── manual.yml │ ├── showlock_cd.yml │ ├── testmap.yml │ ├── OnMerge.yml │ └── trigger_CI.yml └── pull_request_template.md ├── cmake ├── MoonrayGuiLinkOptions.cmake ├── MoonrayGuiCompileFeatures.cmake ├── MoonrayGuiConfig.cmake.in ├── MoonrayGuiCompileDefinitions.cmake └── MoonrayGuiCompileOptions.cmake ├── MAINTAINERS.md ├── README.md ├── .gitignore ├── tsc ├── icla.md ├── ccla.md └── charter.md ├── package.py ├── flowpipeline.yaml ├── CMakeLists.txt ├── CONTRIBUTING.md └── LICENSE /cmd/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2023-2024 DreamWorks Animation LLC 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | add_subdirectory(moonray_gui) 5 | -------------------------------------------------------------------------------- /cmd/moonray_gui/resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | PathVisualizerGui.qss 4 | 5 | 6 | -------------------------------------------------------------------------------- /cmd/moonray_gui/data/moonray_rndr_gui_tex_3dlut_3d.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamworksanimation/moonray_gui/HEAD/cmd/moonray_gui/data/moonray_rndr_gui_tex_3dlut_3d.bin -------------------------------------------------------------------------------- /cmd/moonray_gui/data/moonray_rndr_gui_tex_3dlut_post1d.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamworksanimation/moonray_gui/HEAD/cmd/moonray_gui/data/moonray_rndr_gui_tex_3dlut_post1d.bin -------------------------------------------------------------------------------- /cmd/moonray_gui/data/moonray_rndr_gui_tex_3dlut_pre1d.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dreamworksanimation/moonray_gui/HEAD/cmd/moonray_gui/data/moonray_rndr_gui_tex_3dlut_pre1d.bin -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | All participants agree to abide by LF Projects Code of Conduct (as defined in the [charter](tsc/charter.md)) available at https://lfprojects.org/policies/code-of-conduct/ -------------------------------------------------------------------------------- /.github/workflows/manual.yml: -------------------------------------------------------------------------------- 1 | name: Manual Harmony Pipeline CD Trigger 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | call_workflow: 8 | uses: dwanim/.github/.github/workflows/Harmony_CICD.yml@master 9 | with: 10 | pipe_config: 'release' 11 | rez_config: 'rez-2' 12 | -------------------------------------------------------------------------------- /cmake/MoonrayGuiLinkOptions.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2023-2024 DreamWorks Animation LLC 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | function(${PROJECT_NAME}_link_options target) 5 | target_link_options(${target} 6 | PRIVATE 7 | ${GLOBAL_LINK_FLAGS} 8 | ) 9 | endfunction() 10 | -------------------------------------------------------------------------------- /.github/workflows/showlock_cd.yml: -------------------------------------------------------------------------------- 1 | name: Show Lock Harmony Pipeline CD Trigger 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - '**lock' 8 | 9 | jobs: 10 | call_workflow: 11 | uses: dwanim/.github/.github/workflows/Harmony_CICD.yml@master 12 | with: 13 | pipe_config: 'release' 14 | rez_config: 'rez-2' 15 | -------------------------------------------------------------------------------- /cmd/moonray_gui/FrameUpdateEvent.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023-2024 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include "FrameUpdateEvent.h" 5 | 6 | #include 7 | namespace moonray_gui { 8 | 9 | QEvent::Type FrameUpdateEvent::sEventType = 10 | static_cast(QEvent::registerEventType()); 11 | 12 | } // namespace moonray_gui 13 | 14 | -------------------------------------------------------------------------------- /cmake/MoonrayGuiCompileFeatures.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2023-2024 DreamWorks Animation LLC 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | function(${PROJECT_NAME}_cxx_compile_features target) 5 | if (NOT CMAKE_CXX_COMPILER_ID STREQUAL Intel) 6 | target_compile_features(${target} 7 | PRIVATE 8 | cxx_std_17 9 | ) 10 | endif() 11 | endfunction() 12 | -------------------------------------------------------------------------------- /cmd/moonray_gui/QtQuirks.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023-2024 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | #pragma once 6 | 7 | // Include this header before any Qt headers to compile with icc14/gcc48. 8 | 9 | // These should totally not be necessary, but BaRT is black magic and I have 10 | // absolutely no idea WTF it's doing. 11 | #define slots Q_SLOTS 12 | #define signals Q_SIGNALS 13 | 14 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # MoonRay Committers 5 | 6 | The current MoonRay maintainers are: 7 | 8 | 9 | | Name | Email | 10 | | --------------------------------| ---------------------------------- | 11 | | DreamWorks MoonRay Contributors | MoonRayContributors@dreamworks.com | 12 | 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # moonray_gui 2 | This repository contains the moonray_gui command-line QT application. It can be used to view render progress and final results. 3 | It has been separated from moonray so that moonray does not need a Qt requirement. 4 | 5 | This repository is part of the larger MoonRay/Arras codebase. It is included as a submodule in the top-level 6 | OpenMoonRay repository located here: [OpenMoonRay](https://github.com/dreamworksanimation/openmoonray) 7 | 8 | -------------------------------------------------------------------------------- /.github/workflows/testmap.yml: -------------------------------------------------------------------------------- 1 | name: Create Test Map 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | testmappath: 7 | description: 'Path to release test map under.' 8 | required: true 9 | type: string 10 | 11 | jobs: 12 | call_workflow: 13 | uses: dwanim/.github/.github/workflows/Harmony_CICD.yml@master 14 | with: 15 | pipe_config: 'pre-release' 16 | rez_config: 'rez-2' 17 | pipe_args: 'custom_release_packages_path=${{ github.event.inputs.testmappath }} release_version=niner' 18 | -------------------------------------------------------------------------------- /cmake/MoonrayGuiConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | # @PACKAGE_cmakeModulesDir@ 4 | # The variables to given as PATH_VARS are the variables which contain install destinations. 5 | # For each of them the macro will create a helper variable PACKAGE_. 6 | # These helper variables must be used in the FooConfig.cmake.in file for setting the installed location. 7 | 8 | include(CMakeFindDependencyMacro) 9 | 10 | set(BUILD_SHARED_LIBS ON) 11 | find_dependency(Boost 12 | COMPONENTS 13 | regex) 14 | 15 | find_dependency(Qt5 16 | COMPONENTS 17 | Core 18 | Gui 19 | OpenGL) 20 | 21 | find_dependency(OpenGL) 22 | find_dependency(Moonray) 23 | find_dependency(Mcrt_Denoise) 24 | find_dependency(OpenColorIO) 25 | find_dependency(Mkl) 26 | 27 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") 28 | 29 | check_required_components(@PROJECT_NAME@) 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.os 3 | *.pyc 4 | *.pydevproject 5 | *.project 6 | *.cproject 7 | *.settings 8 | *.autotools 9 | .*.swp 10 | Makefile 11 | CMakeFiles 12 | CMakeCache.txt 13 | cppcheck_results.xml 14 | install_manifest.txt 15 | *.ui.h 16 | *.cxx 17 | .gdb_history 18 | *.vcxproj* 19 | *.sln 20 | *.db 21 | *.vs 22 | Win32 23 | Win64 24 | *.dir 25 | *.suo 26 | *.sdf 27 | *.opendb 28 | x64 29 | Testing 30 | renderdev/Dockerfile.out 31 | 32 | 33 | cmd/client_apps/Android/gen/ 34 | cmd/client_apps/Android/bin/ 35 | 36 | *.old 37 | *.bak 38 | *.classpath 39 | *target/ 40 | 41 | # ignore build/install/build_env dirs 42 | /build 43 | /build_env 44 | /install 45 | /Testing 46 | html 47 | latex 48 | Doxyfile 49 | 50 | /.config.xml 51 | /.metadata_src_project 52 | /.src_project* 53 | 54 | profiling/build/ 55 | profiling/latest_results/ 56 | profiling/profile_reports/ 57 | profiling/profiles/ 58 | 59 | /SConstruct 60 | /boot 61 | /site_scons 62 | /bamboo_tools 63 | 64 | # gha created files 65 | .ci_githooks 66 | -------------------------------------------------------------------------------- /cmake/MoonrayGuiCompileDefinitions.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2023-2024 DreamWorks Animation LLC 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | function(${PROJECT_NAME}_cxx_compile_definitions target) 5 | target_compile_definitions(${target} 6 | PRIVATE 7 | $<$: 8 | DEBUG # Enables extra validation/debugging code 9 | > 10 | $<$: 11 | BOOST_DISABLE_ASSERTS # Disable BOOST_ASSERT macro 12 | > 13 | $<$: 14 | BOOST_DISABLE_ASSERTS # Disable BOOST_ASSERT macro 15 | > 16 | 17 | PUBLIC 18 | ${GLOBAL_COMPILE_DEFINITIONS} 19 | GL_GLEXT_PROTOTYPES=1 # This define makes function symbols to be available as extern declarations. 20 | TBB_SUPPRESS_DEPRECATED_MESSAGES # Suppress 'deprecated' messages from TBB 21 | ) 22 | endfunction() 23 | -------------------------------------------------------------------------------- /cmd/moonray_gui/ColorPicker.h: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | class QPushButton; 11 | 12 | namespace moonray_gui { 13 | 14 | /// The ColorPicker is a widget that displays a square swatch that 15 | /// the user can click on to select a new color. 16 | class ColorPicker : public QWidget 17 | { 18 | Q_OBJECT 19 | 20 | public: 21 | explicit ColorPicker(QWidget* parent, QString label, QColor initialColor); 22 | ~ColorPicker() {} 23 | 24 | int red() { return mColor.red(); } 25 | int green() { return mColor.green(); } 26 | int blue() { return mColor.blue(); } 27 | 28 | private: 29 | QColor mColor; 30 | QColorDialog* mColorDialog; 31 | QPushButton* mSelector; 32 | 33 | signals: 34 | void sig_colorChanged(const QColor& color); 35 | 36 | public slots: 37 | void slot_changeColor(const QColor& color); 38 | void slot_openColorDialog(); 39 | }; 40 | 41 | } // namespace moonray_gui -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | ## Compatibility: 3 | 4 | 5 | ## Issues/Tickets: 6 | 7 | 8 | ## Release notes comment: 9 | 10 | 11 | 12 | ## Comments for the reviewer: 13 | 14 | 15 | ## Look or scene setup change: 16 | 17 | 18 | 19 | ## Special notes for production: 20 | 21 | 22 | 23 | ## Attention/Reviewers: 24 | 25 | 26 | ## Checklist: 27 | - [ ] Documentation has been updated. 28 | - [ ] Includes new unit tests. 29 | - [ ] Includes new RATS tests. 30 | 31 | -------------------------------------------------------------------------------- /cmd/moonray_gui/FrameUpdateEvent.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023-2024 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | #include "GuiTypes.h" 9 | 10 | #include 11 | namespace moonray_gui { 12 | 13 | class FrameUpdateEvent : public QEvent 14 | { 15 | public: 16 | FrameUpdateEvent(const FrameBuffer &frame, FrameType frameType, DebugMode mode, float exposure, float gamma): 17 | QEvent(FrameUpdateEvent::type()), 18 | mFrame(frame), 19 | mFrameType(frameType), 20 | mDebugMode(mode), 21 | mExposure(exposure), 22 | mGamma(gamma) 23 | { 24 | } 25 | 26 | const FrameBuffer &getFrame() const { return mFrame; } 27 | FrameType getFrameType() const { return mFrameType; } 28 | DebugMode getDebugMode() const { return mDebugMode; } 29 | float getExposure() const { return mExposure; } 30 | float getGamma() const { return mGamma; } 31 | static QEvent::Type type() { return sEventType; } 32 | 33 | private: 34 | FrameBuffer mFrame; 35 | FrameType mFrameType; 36 | DebugMode mDebugMode; 37 | float mExposure; 38 | float mGamma; 39 | static QEvent::Type sEventType; 40 | }; 41 | 42 | } // namespace moonray_gui 43 | 44 | -------------------------------------------------------------------------------- /.github/workflows/OnMerge.yml: -------------------------------------------------------------------------------- 1 | name: After Merge 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | branch: 7 | description: "Target branch for merge operations (defaults to main)" 8 | required: false 9 | type: string 10 | default: "main" 11 | 12 | push: 13 | branches: 14 | - main 15 | paths-ignore: 16 | - '**.md' 17 | - '.github/**' 18 | - 'flowpipeline.yaml' 19 | - 'LICENSE' 20 | - 'tsc/**' 21 | 22 | concurrency: 23 | group: OnMergeUpdate-${{ github.event.inputs.branch || github.ref }} 24 | cancel-in-progress: true 25 | 26 | env: 27 | CHATROOM: ${{ secrets.TRIGGER_RATS_CHATROOM }} 28 | 29 | jobs: 30 | MergeMessage: 31 | # Restrict to run on only those EL9 hosts that are rez hosts as well. 32 | runs-on: [EL9, rez] 33 | env: 34 | MESSAGE: "A push to the ${{ github.event.inputs.branch || github.ref_name }} branch of the ${{ github.event.repository.name }} repo. A CI/RaTS test run should start in the next 5 minutes." 35 | steps: 36 | - name: Report to Chat Room 37 | run: | 38 | curl -X POST -H 'Content-Type: application/json' --url ${CHATROOM} \ 39 | -d '{"text": "'"${MESSAGE}"'"}' 40 | 41 | MergeAction: 42 | uses: dwanim/openmoonray/.github/workflows/SubmoduleUpdate.yml@main 43 | with: 44 | repo: "openmoonray" 45 | branch: ${{ github.event.inputs.branch || github.ref_name }} 46 | secrets: inherit 47 | -------------------------------------------------------------------------------- /cmd/moonray_gui/PathVisualizerGui.qss: -------------------------------------------------------------------------------- 1 | QWidget { 2 | color: white; 3 | font-size: 15px; 4 | } 5 | 6 | QSpinBox { 7 | background-color: #333333; 8 | width: 50px; 9 | } 10 | 11 | QSpinBox:disabled { 12 | background-color: #121212; 13 | color: grey; 14 | width: 50px; 15 | } 16 | 17 | *[class~="button"] { 18 | background-color: #b8d3ff; 19 | color: black; 20 | border-radius: 5px; 21 | margin: 10px; 22 | width: 70px; 23 | padding: 5px; 24 | } 25 | 26 | QPushButton:hover { 27 | background-color: #98b9ed; 28 | } 29 | 30 | *[class~="header"] { 31 | background-color: #787878; 32 | padding:5px; 33 | font: bold 16px; 34 | margin-top: 5px; 35 | } 36 | 37 | QSlider { 38 | height: 25px; 39 | } 40 | 41 | QSlider::handle:horizontal { 42 | background-color: #51A0D5; 43 | width: 15px; 44 | height: 25px; 45 | border-radius: 4px; 46 | margin: -5px 0px; /* Adjust margin if handle extends beyond groove */ 47 | } 48 | 49 | QSlider::groove { 50 | border: 1px solid #808080; 51 | background: #2e2e2e; 52 | height: 20px; 53 | border-radius: 4px; 54 | } 55 | 56 | QCheckBox { 57 | margin: 3px; 58 | } 59 | 60 | QCheckBox::indicator { 61 | border: 1px solid #808080; 62 | border-radius: 4px; 63 | width: 15px; 64 | height: 15px; 65 | background-color: #2e2e2e; 66 | } 67 | 68 | QCheckBox::indicator::checked { 69 | background-color: #789c81; 70 | border: 1px solid #85eda1; 71 | } 72 | -------------------------------------------------------------------------------- /cmd/moonray_gui/GuiTypes.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023-2024 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | using namespace scene_rdl2; 9 | 10 | namespace moonray_gui { 11 | 12 | class MainWindow; 13 | class NavigationCam; 14 | class RenderViewport; 15 | 16 | enum CameraType 17 | { 18 | ORBIT_CAM, 19 | FREE_CAM, 20 | NUM_CAMERA_TYPES, 21 | }; 22 | 23 | enum DebugMode 24 | { 25 | RGB, 26 | RED, 27 | GREEN, 28 | BLUE, 29 | ALPHA, 30 | LUMINANCE, 31 | SATURATION, 32 | RGB_NORMALIZED, 33 | NUM_SAMPLES, 34 | 35 | NUM_DEBUG_MODES, 36 | }; 37 | 38 | enum InspectorMode 39 | { 40 | INSPECT_NONE, 41 | INSPECT_LIGHT_CONTRIBUTIONS, 42 | INSPECT_GEOMETRY, 43 | INSPECT_GEOMETRY_PART, 44 | INSPECT_MATERIAL, 45 | NUM_INSPECTOR_MODES 46 | }; 47 | 48 | enum FrameType 49 | { 50 | FRAME_TYPE_IS_RGB8 = 0, 51 | FRAME_TYPE_IS_XYZW32, 52 | FRAME_TYPE_IS_XYZ32 53 | }; 54 | 55 | // Which additional buffers do we want to use for denoising. 56 | enum DenoisingBufferMode 57 | { 58 | DN_BUFFERS_BEAUTY, 59 | DN_BUFFERS_BEAUTY_ALBEDO, 60 | DN_BUFFERS_BEAUTY_ALBEDO_NORMALS, 61 | NUM_DENOISING_BUFFER_MODES, 62 | }; 63 | 64 | union FrameBuffer 65 | { 66 | const fb_util::Rgb888Buffer *rgb8; 67 | const fb_util::RenderBuffer *xyzw32; 68 | const fb_util::Float3Buffer *xyz32; 69 | }; 70 | 71 | } // namespace moonray_gui 72 | 73 | -------------------------------------------------------------------------------- /cmd/moonray_gui/MainWindow.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023-2024 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #pragma once 5 | 6 | #include "GuiTypes.h" 7 | #include "QtQuirks.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | class QEvent; 14 | class QCloseEvent; 15 | 16 | namespace moonray_gui { 17 | 18 | class MainWindow : public QMainWindow 19 | { 20 | Q_OBJECT 21 | 22 | public: 23 | explicit MainWindow(QWidget* parent, CameraType initialType, const char *crtOverride, const std::string& snapPath); 24 | 25 | ~MainWindow(); 26 | 27 | bool event(QEvent* event); 28 | void closeEvent(QCloseEvent* event); 29 | 30 | RenderViewport* getRenderViewport() const { return mRenderViewport; } 31 | const QLabel* getSettings() const { return mSettings; } 32 | 33 | private: 34 | void setupUi(CameraType initialType, const char *crtOverride, const std::string& snapPath); 35 | 36 | void setFastModeText(); 37 | 38 | RenderViewport* mRenderViewport; 39 | 40 | QLabel* mFastMode; 41 | QLabel* mGuide; 42 | QLabel* mSettings; 43 | 44 | QTimer* mTimer; 45 | 46 | 47 | public slots: 48 | void hideTextOverlay(); 49 | signals: 50 | void sig_hideRecordingOverlay(); 51 | }; 52 | 53 | class Handler : public QObject 54 | { 55 | Q_OBJECT 56 | public: 57 | Handler(QObject* parent = 0) : QObject(parent), mIsActive(false) {}; 58 | bool mIsActive; 59 | 60 | public Q_SLOTS: 61 | void quitApp(); 62 | 63 | }; 64 | 65 | } // namespace moonray_gui 66 | 67 | -------------------------------------------------------------------------------- /.github/workflows/trigger_CI.yml: -------------------------------------------------------------------------------- 1 | name: Trigger the CI Plan Run 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | paths-ignore: 9 | - '**.md' 10 | - '.github/**' 11 | - 'flowpipeline.yaml' 12 | - 'LICENSE' 13 | - 'tsc/**' 14 | 15 | jobs: 16 | trigger_CI_plan: 17 | env: 18 | CHATROOM: ${{ secrets.TRIGGER_CI_CHATROOM }} 19 | runs-on: [self-hosted, rez] 20 | steps: 21 | - name: Add Dynamic Environment Variable 22 | run: | 23 | echo "FILENAME=LastTrigger_$(basename ${{ github.repository }})" >> ${GITHUB_ENV} 24 | echo "MESSAGE=A push to the main branch of the $(basename ${{ github.repository }}) repo. A CI test run should start in the next 5 minutes." >> ${GITHUB_ENV} 25 | - name: Checkout 26 | uses: actions/checkout@v4 27 | with: 28 | repository: dwanim/CITrigger 29 | token: ${{ secrets.BART_TOKEN }} 30 | - name: Update File 31 | run: echo $(date) > ${FILENAME} 32 | - name: Commit and Push 33 | run: | 34 | git add ${FILENAME} 35 | git commit -m 'New triggered run.' 36 | git push --force || true 37 | - name: Report to Chat Room 38 | run: | 39 | curl -X POST -H 'Content-Type: application/json' --url ${CHATROOM} -d '{"text": "'"${MESSAGE}"'"}' 40 | - name: Cleanup 41 | shell: bash 42 | run: | 43 | shopt -s dotglob 44 | echo Removing ${{ github.workspace }} 45 | rm -rf ${{ github.workspace }}/* 46 | -------------------------------------------------------------------------------- /cmd/moonray_gui/GlslBuffer.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023-2024 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// @file GlslBuffer.h 5 | 6 | #pragma once 7 | 8 | #include "GuiTypes.h" 9 | 10 | #include 11 | 12 | namespace moonray_gui { 13 | 14 | class GlslBuffer 15 | { 16 | public: 17 | // create a glsl buffer for off-screen rendering 18 | GlslBuffer(int width, int height, const float *lutOverride); 19 | ~GlslBuffer(); 20 | 21 | // LINEAR RGBA -> CRT -> GAMMA -> RGB 22 | void makeCrtGammaProgram(); 23 | 24 | // render to pixel buffer, input should 25 | // be a linear RenderBuffer 26 | void render(const FrameBuffer &frame, FrameType frameType, DebugMode mode, float exposure, float gamma); 27 | 28 | // return pixel buffer as a QImage 29 | QImage asImage() const; 30 | 31 | private: 32 | int mWidth; 33 | int mHeight; 34 | QGLPixelBuffer mPixelBuffer; 35 | GLuint mVertexBuffer; 36 | GLuint mUvBuffer; 37 | GLint mTexture; 38 | GLuint mVertexShaderID; 39 | GLuint mProgram; 40 | GLuint mChannel; 41 | GLfloat mExposure; 42 | GLfloat mGamma; 43 | 44 | // Color render override LUT. Set to nullptr if we aren't overriding 45 | // the LUT. This binary blob is assumed to contain 64*64*64 * RGB float 46 | // OpenGL compatible volume texture data. 47 | // This class doesn't own this data and isn't responsible for deleting it. 48 | const float * mLutOverride; 49 | }; 50 | 51 | } // namespace moonray_gui 52 | 53 | -------------------------------------------------------------------------------- /cmd/moonray_gui/NavigationCam.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023-2024 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | #pragma once 6 | #include 7 | 8 | class QKeyEvent; 9 | class QMouseEvent; 10 | 11 | namespace moonray { namespace rndr { class RenderContext; } } 12 | 13 | namespace moonray_gui { 14 | /// 15 | /// Pure virtual base class which further navigation models may be implemented 16 | /// on top of. 17 | /// 18 | class NavigationCam 19 | { 20 | public: 21 | NavigationCam() {} 22 | virtual ~NavigationCam() {} 23 | 24 | // Certain types of camera may want to intersect with the scene, in which 25 | // case they'll need more information about the scene. This function does 26 | // nothing by default. 27 | virtual void setRenderContext(const moonray::rndr::RenderContext &context) {} 28 | 29 | // If this camera model imposes any constraints on the input matrix, then 30 | // the constrained matrix is returned, otherwise the output will equal in 31 | // input. 32 | // If makeDefault is set to true then this xform is designated a the new 33 | // default transform when/if the camera is reset. 34 | virtual scene_rdl2::math::Mat4f resetTransform(const scene_rdl2::math::Mat4f &xform, bool makeDefault) = 0; 35 | 36 | // Returns the latest camera matrix. 37 | virtual scene_rdl2::math::Mat4f update(float dt) = 0; 38 | 39 | /// Returns true if the input was used, false to pass the input to a higher 40 | /// level handler. 41 | virtual bool processKeyboardEvent(QKeyEvent *event, bool pressed) { return false; } 42 | virtual bool processMousePressEvent(QMouseEvent *event, int key) { return false; } 43 | virtual bool processMouseReleaseEvent(QMouseEvent *event) { return false; } 44 | virtual bool processMouseMoveEvent(QMouseEvent *event) { return false; } 45 | virtual void clearMovementState() {}; 46 | }; 47 | 48 | } // namespace moonray_gui 49 | 50 | 51 | -------------------------------------------------------------------------------- /tsc/icla.md: -------------------------------------------------------------------------------- 1 | Individual Contributor License Agreement ("Agreement") 2 | 3 | Thank you for your interest in the MoonRay (aka OpenMoonRay) Project (hereinafter "Project") 4 | which has selected the Apache License, Version 2.0 (hereinafter "Apache 2.0 License") for its 5 | inbound contributions. The terms You, Contributor and Contribution are used here as defined in 6 | the Apache 2.0 License. 7 | 8 | The Project is required to have a Contributor License Agreement (CLA) on file that binds each 9 | Contributor. 10 | 11 | You agree that all Contributions to the Project made by You shall be licensed to the Project 12 | under the Apache 2.0 License, and that You agree to, and shall be bound by, the terms of the 13 | Apache 2.0 License. 14 | 15 | By making a Contribution to the Project, You certify that: 16 | 17 |  (a) The Contribution was created in whole or in part by You and You have the right to 18 |    submit it under the Apache 2.0 License; or 19 | 20 |  (b) The Contribution is based upon previous work that, to the best of Your knowledge, is 21 |    covered under an appropriate open source license and You have the right under that 22 |    license to submit that work with modifications, whether created in whole or in part by 23 |    You, under the Apache 2.0 License; or 24 | 25 |  (c) The Contribution was provided directly to You by some other person who certified (a) 26 |    or (b) and You have not modified it. 27 | 28 | By making a Contribution to the Project, You acknowledge and agree that the Project and the 29 | Contribution are public and that a record of the Contribution (including all personal information 30 | You submit with it, including Your sign-off) is maintained indefinitely and may be redistributed 31 | consistent with the Project or the Apache 2.0 License. 32 | 33 | Signature: __________________________________________ 34 | 35 | Name: _______________________________________________ 36 | 37 | Date: _______________________________________________ 38 | -------------------------------------------------------------------------------- /cmd/moonray_gui/ColorPicker.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include "ColorPicker.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace moonray_gui { 12 | 13 | ColorPicker::ColorPicker(QWidget* parent, QString labelString, QColor initialColor) : 14 | QWidget(parent), mColor(initialColor) 15 | { 16 | // Set up the layout 17 | QHBoxLayout* layout = new QHBoxLayout(this); 18 | layout->setContentsMargins(5, 5, 5, 5); 19 | 20 | // Set up the label 21 | QLabel* label = new QLabel(labelString, this); 22 | layout->addWidget(label); 23 | 24 | // Set up the color swatch button 25 | mSelector = new QPushButton(this); 26 | mSelector->setFixedWidth(20); 27 | mSelector->setFixedHeight(20); 28 | mSelector->setCursor(Qt::PointingHandCursor); 29 | 30 | QString style = QString("QPushButton { background-color: rgb(%1, %2, %3); }").arg(initialColor.red()) 31 | .arg(initialColor.green()) 32 | .arg(initialColor.blue()); 33 | mSelector->setStyleSheet(style); 34 | connect(mSelector, SIGNAL(clicked()), this, SLOT(slot_openColorDialog())); 35 | layout->addWidget(mSelector); 36 | 37 | mColorDialog = new QColorDialog(initialColor, this); 38 | mColorDialog->hide(); 39 | connect(mColorDialog, SIGNAL(colorSelected(const QColor&)), this, SLOT(slot_changeColor(const QColor&))); 40 | 41 | // Set the layout for this widget 42 | setLayout(layout); 43 | } 44 | 45 | void ColorPicker::slot_openColorDialog() 46 | { 47 | mColorDialog->show(); 48 | } 49 | 50 | void ColorPicker::slot_changeColor(const QColor& color) 51 | { 52 | mColor = color; 53 | QString style = QString("QPushButton { background-color: rgb(%1, %2, %3); }").arg(mColor.red()) 54 | .arg(mColor.green()) 55 | .arg(mColor.blue()); 56 | mSelector->setStyleSheet(style); 57 | 58 | emit sig_colorChanged(mColor); 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /cmd/moonray_gui/ColorManager.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023-2024 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | #pragma once 6 | 7 | #include "MainWindow.h" 8 | #include 9 | 10 | #if !defined(DISABLE_OCIO) 11 | #include 12 | 13 | namespace OCIO = OCIO_NAMESPACE; 14 | #endif 15 | 16 | namespace moonray_gui { 17 | class ColorManager 18 | { 19 | public: 20 | ColorManager(); 21 | ~ColorManager(); 22 | 23 | void applyCRT(const MainWindow* mainWindow, 24 | const bool useOCIO, 25 | int renderOutput, 26 | const fb_util::RenderBuffer& renderBuffer, 27 | const fb_util::VariablePixelBuffer& renderOutputBuffer, 28 | fb_util::Rgb888Buffer* displayBuffer, 29 | fb_util::PixelBufferUtilOptions options, 30 | bool parallel) const; 31 | 32 | void setupConfig(); 33 | 34 | private: 35 | #if !defined(DISABLE_OCIO) 36 | 37 | OCIO::ConstConfigRcPtr mConfig; 38 | bool mConfigIsRaw; 39 | 40 | // read config, define OCIO transforms, initialize processors 41 | void configureOcio(double exposure, 42 | double gamma, 43 | DebugMode mode, 44 | OCIO::ConstCPUProcessorRcPtr& cpuProcessor) const; 45 | 46 | // apply color transformations using OCIO 47 | static void applyCRT_Ocio(const OCIO::ConstCPUProcessorRcPtr& cpuProcessor, 48 | void* srcData, 49 | scene_rdl2::fb_util::Rgb888Buffer* destBuf, 50 | int w, int h, 51 | int channels); 52 | #endif 53 | 54 | // apply color transformations using previous non-OCIO code 55 | static void applyCRT_Legacy(const fb_util::RenderBuffer& renderBuffer, 56 | const fb_util::VariablePixelBuffer& renderOutputBuffer, 57 | fb_util::Rgb888Buffer* displayBuffer, 58 | int renderOutput, 59 | double exposure, 60 | double gamma, 61 | DebugMode mode, 62 | fb_util::PixelBufferUtilOptions options, 63 | bool parallel); 64 | }; 65 | } 66 | 67 | -------------------------------------------------------------------------------- /cmd/moonray_gui/FreeCam.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023-2024 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | /// 6 | /// Controls: 7 | /// 8 | /// LMB + Mouse move - rotate around camera position 9 | /// alt + LMB + RMB - roll 10 | /// 11 | /// W - forward 12 | /// S - backward 13 | /// A - left 14 | /// D - right 15 | /// Space - up 16 | /// C - down 17 | /// Q - slow down 18 | /// E - speed up 19 | /// R - reset to original startup location in world 20 | /// U - upright camera (remove roll) 21 | /// T - print current camera matrix to console in lua format 22 | /// 23 | 24 | #pragma once 25 | #include "NavigationCam.h" 26 | 27 | namespace moonray_gui { 28 | 29 | class FreeCam : public NavigationCam 30 | { 31 | public: 32 | FreeCam(); 33 | ~FreeCam(); 34 | 35 | /// Returns a matrix with only pitch and yaw (no roll). 36 | scene_rdl2::math::Mat4f resetTransform(const scene_rdl2::math::Mat4f &xform, bool makeDefault) override; 37 | 38 | scene_rdl2::math::Mat4f update(float dt) override; 39 | 40 | /// Returns true if the input was used, false if discarded. 41 | bool processKeyboardEvent(QKeyEvent *event, bool pressed) override; 42 | bool processMousePressEvent(QMouseEvent *event, int key) override; 43 | bool processMouseReleaseEvent(QMouseEvent *event) override; 44 | bool processMouseMoveEvent(QMouseEvent *event) override; 45 | void clearMovementState() override; 46 | 47 | private: 48 | enum MouseMode 49 | { 50 | NONE, 51 | MOVE, 52 | ROLL, 53 | }; 54 | 55 | void printCameraMatrices() const; 56 | 57 | scene_rdl2::math::Vec3f mPosition; 58 | scene_rdl2::math::Vec3f mVelocity; 59 | float mYaw; 60 | float mPitch; 61 | float mRoll; 62 | float mSpeed; 63 | float mDampening; ///< the amount by which mVelocity is dampened each update 64 | float mMouseSensitivity; 65 | uint32_t mInputState; 66 | MouseMode mMouseMode; 67 | int mMouseX; 68 | int mMouseY; 69 | int mMouseDeltaX; 70 | int mMouseDeltaY; 71 | 72 | bool mInitialTransformSet; 73 | scene_rdl2::math::Mat4f mInitialTransform; 74 | }; 75 | 76 | } // namespace moonray_gui 77 | 78 | -------------------------------------------------------------------------------- /cmd/moonray_gui/PathVisualizerGui.h: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | class QGridLayout; 9 | class QPushButton; 10 | class QSpinBox; 11 | class QLabel; 12 | 13 | namespace moonray { namespace rndr { class PathVisualizerManager; }} 14 | 15 | namespace moonray_gui { 16 | 17 | class ColorPicker; 18 | class RenderViewport; 19 | 20 | class PathVisualizerGui : public QWidget 21 | { 22 | Q_OBJECT 23 | 24 | public: 25 | explicit PathVisualizerGui(QWidget* parent, moonray::rndr::PathVisualizerManager* manager); 26 | 27 | ~PathVisualizerGui(); 28 | 29 | public slots: 30 | 31 | void slot_togglePathVisualizer(); 32 | 33 | void slot_processPixelXValue(const int x); 34 | void slot_processPixelYValue(const int y); 35 | void slot_processPixel(const int x, const int y); 36 | 37 | void slot_processMaxDepth(const int depth); 38 | 39 | void slot_processUseSceneSamples(const int useSceneSamples); 40 | void slot_processPixelSamples(const int samples); 41 | void slot_processLightSamples(const int samples); 42 | void slot_processBsdfSamples(const int samples); 43 | 44 | void slot_processDiffuseRayFlag(const int flag); 45 | void slot_processSpecularRayFlag(const int flag); 46 | void slot_processBsdfSampleFlag(const int flag); 47 | void slot_processLightSampleFlag(const int flag); 48 | 49 | void slot_setLineWidth(const int width); 50 | void slot_setDiffuseRayColor(const QColor& color); 51 | void slot_setSpecularRayColor(const QColor& color); 52 | void slot_setBsdfSampleColor(const QColor& color); 53 | void slot_setLightSampleColor(const QColor& color); 54 | void slot_setCameraRayColor(const QColor& color); 55 | 56 | signals: 57 | void sig_styleParamChanged(); 58 | 59 | private: 60 | 61 | void setupOnBtn(QGridLayout* layout); 62 | void setupPixelUI(QGridLayout* layout); 63 | void setupSamplingUI(QGridLayout* layout); 64 | void setupDepthUI(QGridLayout* layout); 65 | void setupVisibilityUI(QGridLayout* layout); 66 | void setupStyleUI(QGridLayout* layout); 67 | 68 | moonray::rndr::PathVisualizerManager* mPathVisualizerManager; 69 | 70 | int mCurrentRow; 71 | 72 | QPushButton* mOnBtn; 73 | 74 | QSpinBox* mPixelXSpinBox; 75 | QSpinBox* mPixelYSpinBox; 76 | 77 | QSpinBox* mPixelSamples; 78 | QSpinBox* mLightSamples; 79 | QSpinBox* mBsdfSamples; 80 | 81 | QLabel* mLineWidthValue; 82 | 83 | ColorPicker* mDiffuseRayColorPicker; 84 | ColorPicker* mSpecularRayColorPicker; 85 | ColorPicker* mBsdfSampleColorPicker; 86 | ColorPicker* mLightSampleColorPicker; 87 | ColorPicker* mCameraRayColorPicker; 88 | }; 89 | 90 | } // namespace moonray_gui 91 | -------------------------------------------------------------------------------- /tsc/ccla.md: -------------------------------------------------------------------------------- 1 | Corporate Contributor License Agreement ("Agreement") 2 | 3 | Thank you for your interest in the MoonRay (aka OpenMoonRay) Project (hereinafter "Project") 4 | which has selected the Apache License, Version 2.0 (hereinafter "Apache 2.0 License") for its 5 | inbound contributions. The terms You, Contributor and Contribution are used here as defined in 6 | the Apache 2.0 License. 7 | 8 | The Project is required to have a Contributor License Agreement (CLA) on file that binds each 9 | Contributor. 10 | 11 | You agree that all Contributions to the Project made by You or by Your designated employees 12 | shall be licensed to the Project under the Apache 2.0 License, and that You agree to, and shall be 13 | bound by, the terms of the Apache 2.0 License for all contributions made by You and Your 14 | employees. Your initial list of designated employees who are authorized to make Contributions 15 | shall be as set forth in Schedule A. You agree to identify Your initial CLA Manager below and 16 | thereafter provide any updates to the identity of your CLA Manager or employees eligible to 17 | make Contributions by providing updated lists to MoonRay@dreamworks.com. 18 | 19 | You certify that, for all Contributions to the Project made by You or by Your designated 20 | employees: 21 | 22 |  (a) The Contribution was created in whole or in part by You or Your designated 23 |    employees and You have the right to submit it under the Apache 2.0 License; or 24 | 25 |  (b) The Contribution is based upon previous work that, to the best of Your knowledge, is 26 |    covered under an appropriate open source license and You have the right under that 27 |    license to submit that work with modifications, whether created in whole or in part by 28 |    You or Your designated employees, under the Apache 2.0 License; or 29 | 30 |  (c) The Contribution was provided directly to You or Your designated employees by 31 |    some other person who certified (a) or (b) and You or Your designated employees have 32 |    not modified it. 33 | 34 | You acknowledge and agree that the Project and the Contribution are public and that a record of 35 | the Contribution (including all personal information You or Your designated employees submit 36 | with it, including Your or Your designated employees’ sign-off) is maintained indefinitely and 37 | may be redistributed consistent with the Project or the Apache 2.0 License. 38 | 39 | Initial CLA Manager (Name and Email): ______________________________________ 40 | 41 | Company Name: ____________________________________________ 42 | 43 | Signature: _______________________________________________ 44 | 45 | Name: _______________________________________________ 46 | 47 | Title _______________________________________________ 48 | 49 | Date: _______________________________________________ 50 | 51 | 52 | \**************************************************************************** 53 |             **Schedule A** 54 | 55 | 56 | \[Initial list of designated employees\] 57 | -------------------------------------------------------------------------------- /package.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024-2025 DreamWorks Animation LLC 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | # -*- coding: utf-8 -*- 5 | import os 6 | 7 | name = 'moonray_gui' 8 | 9 | if 'early' not in locals() or not callable(early): 10 | def early(): return lambda x: x 11 | 12 | @early() 13 | def version(): 14 | """ 15 | Increment the build in the version. 16 | """ 17 | _version = '17.31' 18 | from rezbuild import earlybind 19 | return earlybind.version(this, _version) 20 | 21 | description = 'Arras moonray_gui package' 22 | 23 | authors = [ 24 | 'PSW Rendering and Shading', 25 | 'moonbase-dev@dreamworks.com' 26 | ] 27 | 28 | help = ('For assistance, ' 29 | "please contact the folio's owner at: moonbase-dev@dreamworks.com") 30 | 31 | variants = [ 32 | [ # variant 0 33 | 'os-rocky-9', 34 | 'opt_level-optdebug', 35 | 'refplat-vfx2023.1', 36 | 'gcc-11.x' 37 | ], 38 | [ # variant 1 39 | 'os-rocky-9', 40 | 'opt_level-debug', 41 | 'refplat-vfx2023.1', 42 | 'gcc-11.x' 43 | ], 44 | [ # variant 2 45 | 'os-rocky-9', 46 | 'opt_level-optdebug', 47 | 'refplat-vfx2023.1', 48 | 'clang-17.0.6.x' 49 | ], 50 | # Won't build until we upgrade to Qt 6 51 | # [ 52 | # 'os-rocky-9', 53 | # 'opt_level-optdebug', 54 | # 'refplat-vfx2024.0', 55 | # 'gcc-11.x' 56 | # ], 57 | # [ 58 | # 'os-rocky-9', 59 | # 'opt_level-optdebug', 60 | # 'refplat-vfx2025.0', 61 | # 'gcc-11.x' 62 | # ], 63 | # [ 64 | # 'os-rocky-9', 65 | # 'opt_level-optdebug', 66 | # 'refplat-houdini21.0', 67 | # 'gcc-11.x' 68 | # ], 69 | [ # variant 3 70 | 'os-rocky-9', 71 | 'opt_level-optdebug', 72 | 'refplat-vfx2022.0', 73 | 'gcc-9.3.x.1' 74 | ], 75 | ] 76 | 77 | conf_rats_variants = variants[0:2] 78 | conf_CI_variants = variants 79 | 80 | # Add ephemeral package to each variant. 81 | for i, variant in enumerate(variants): 82 | variant.insert(0, '.moonray_gui_variant-%d' % i) 83 | 84 | requires = [ 85 | 'mkl', 86 | 'moonray-17.31', 87 | 'mcrt_denoise-6.19', 88 | 'opencolorio-2', 89 | 'qt', 90 | ] 91 | 92 | private_build_requires = [ 93 | 'cmake_modules-1.0', 94 | 'cppunit', 95 | ] 96 | 97 | commandstr = lambda i: "cd build/"+os.path.join(*variants[i])+"; ctest -j $(nproc)" 98 | testentry = lambda i: ("variant%d" % i, 99 | { "command": commandstr(i), 100 | "requires": ["cmake"], 101 | "on_variants": { 102 | "type": "requires", 103 | "value": [".moonray_gui_variant-%d" % i] 104 | }, 105 | "run_on": "explicit", 106 | }, ) 107 | testlist = [testentry(i) for i in range(len(variants))] 108 | tests = dict(testlist) 109 | 110 | def commands(): 111 | prependenv('CMAKE_MODULE_PATH', '{root}/lib64/cmake') 112 | prependenv('CMAKE_PREFIX_PATH', '{root}') 113 | prependenv('PATH', '{root}/bin') 114 | 115 | uuid = '470184d5-82cf-4d9b-a94b-456f75bb97a1' 116 | 117 | config_version = 0 118 | -------------------------------------------------------------------------------- /cmd/moonray_gui/OrbitCam.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023-2024 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// 5 | /// Controls: 6 | /// 7 | /// alt + LMB - orbit around pivot point 8 | /// alt + MMB - pan 9 | /// alt + RMB - dolly (zoom in and out) 10 | /// alt + LMB + RMB - roll 11 | /// ctrl + LMB - refocus on point under mouse cursor 12 | /// 13 | /// W - forward 14 | /// S - backward 15 | /// A - left 16 | /// D - right 17 | /// Space - up 18 | /// C - down 19 | /// Q - slow down 20 | /// E - speed up 21 | /// R - reset to original startup location in world 22 | /// U - upright camera (remove roll) 23 | /// T - print current camera matrix to console in lua format 24 | /// F - alternate key to refocus on point under mouse cursor 25 | /// 26 | 27 | #pragma once 28 | #include "NavigationCam.h" 29 | 30 | namespace moonray_gui { 31 | 32 | struct Camera; 33 | 34 | class OrbitCam : public NavigationCam 35 | { 36 | public: 37 | OrbitCam(); 38 | ~OrbitCam(); 39 | 40 | void setRenderContext(const moonray::rndr::RenderContext &context) override; 41 | 42 | /// The active render context should be set before calling this function. 43 | scene_rdl2::math::Mat4f resetTransform(const scene_rdl2::math::Mat4f &xform, bool makeDefault) override; 44 | 45 | scene_rdl2::math::Mat4f update(float dt) override; 46 | 47 | /// Returns true if the input was used, false if discarded. 48 | bool processKeyboardEvent(QKeyEvent *event, bool pressed) override; 49 | bool processMousePressEvent(QMouseEvent *event, int key) override; 50 | bool processMouseMoveEvent(QMouseEvent *event) override; 51 | void clearMovementState() override; 52 | 53 | private: 54 | enum MouseMode 55 | { 56 | NONE, 57 | ORBIT, 58 | PAN, 59 | DOLLY, 60 | ROLL, 61 | ROTATE_CAMERA, 62 | }; 63 | 64 | // Run a center-pixel "pick" operation to compute camera focus 65 | void pickFocusPoint(); 66 | 67 | void recenterCamera(); 68 | bool pick(int x, int y, scene_rdl2::math::Vec3f *hitPoint) const; 69 | scene_rdl2::math::Mat4f makeMatrix(const Camera &camera) const; 70 | void printCameraMatrices() const; 71 | 72 | const moonray::rndr::RenderContext *mRenderContext; 73 | Camera * mCamera; 74 | 75 | float mSpeed; 76 | uint32_t mInputState; 77 | MouseMode mMouseMode; 78 | int mMouseX; 79 | int mMouseY; 80 | 81 | bool mInitialTransformSet; 82 | bool mInitialFocusSet; 83 | scene_rdl2::math::Vec3f mInitialPosition; 84 | scene_rdl2::math::Vec3f mInitialViewDir; 85 | scene_rdl2::math::Vec3f mInitialUp; 86 | float mInitialFocusDistance; 87 | }; 88 | 89 | } // namespace moonray_gui 90 | 91 | -------------------------------------------------------------------------------- /cmd/moonray_gui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2023-2024 DreamWorks Animation LLC 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | set(target moonray_gui) 5 | 6 | set(CMAKE_AUTOMOC TRUE) 7 | 8 | add_executable(${target}) 9 | 10 | # ----------------------------------------- 11 | # CRT objects 12 | # color render transform tables are just large float arrays 13 | 14 | set(crtFiles 15 | moonray_rndr_gui_tex_3dlut_post1d 16 | moonray_rndr_gui_tex_3dlut_pre1d 17 | moonray_rndr_gui_tex_3dlut_3d 18 | ) 19 | 20 | set(crtBinDir ${CMAKE_CURRENT_SOURCE_DIR}/data) 21 | file(RELATIVE_PATH relBinDir ${PROJECT_SOURCE_DIR} ${crtBinDir}) 22 | 23 | # TODO: actually check that the string produced by xxd matches the expectation. i.e. Test this beyond building it, which works 24 | # translate ".bin" source files to ".o" files 25 | foreach(crtFile ${crtFiles}) 26 | if(IsDarwinPlatform) 27 | add_custom_command( 28 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${crtFile}.o 29 | DEPENDS ${crtBinDir}/${crtFile}.bin 30 | COMMAND xxd -i -n _binary_${crtFile} "${crtBinDir}/${crtFile}.bin" "${crtBinDir}/${crtFile}.c" && cc -c "${crtBinDir}/${crtFile}.c" -o "${CMAKE_CURRENT_BINARY_DIR}/${crtFile}.o") 31 | else() 32 | add_custom_command( 33 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${crtFile}.o 34 | DEPENDS ${crtBinDir}/${crtFile}.bin 35 | COMMAND cd ${PROJECT_SOURCE_DIR} && 36 | objcopy --input-target=binary --binary-architecture=i386 37 | --output-target=elf64-x86-64 38 | "${relBinDir}/${crtFile}.bin" 39 | "${CMAKE_CURRENT_BINARY_DIR}/${crtFile}.o") 40 | endif() 41 | # build a list of the results 42 | list(APPEND crtObjs ${CMAKE_CURRENT_BINARY_DIR}/${crtFile}.o) 43 | endforeach() 44 | 45 | # -------------------------------------------------------------- 46 | 47 | target_sources(${target} 48 | PRIVATE 49 | ColorManager.cc 50 | ColorPicker.cc 51 | FrameUpdateEvent.cc 52 | FreeCam.cc 53 | GlslBuffer.cc 54 | MainWindow.cc 55 | moonray_gui.cc 56 | OrbitCam.cc 57 | PathVisualizerGui.cc 58 | RenderGui.cc 59 | RenderViewport.cc 60 | ${crtObjs} 61 | ) 62 | 63 | if (NOT IsDarwinPlatform) 64 | set(PlatformSpecificLibs atomic) 65 | endif() 66 | 67 | target_link_libraries(${target} 68 | PRIVATE 69 | Boost::regex 70 | McrtDenoise::denoiser 71 | ${MKL_target} 72 | ${OCIO} 73 | Moonray::application 74 | Moonray::rendering_rndr 75 | SceneRdl2::common_fb_util 76 | SceneRdl2::common_math 77 | SceneRdl2::common_platform 78 | SceneRdl2::render_logging 79 | SceneRdl2::render_util 80 | SceneRdl2::scene_rdl2 81 | OpenGL::GL 82 | Qt5::Core 83 | Qt5::Gui 84 | Qt5::OpenGL 85 | ${PlatformSpecificLibs} 86 | ) 87 | 88 | # Set standard compile/link options 89 | MoonrayGui_cxx_compile_definitions(${target}) 90 | MoonrayGui_cxx_compile_features(${target}) 91 | MoonrayGui_cxx_compile_options(${target}) 92 | MoonrayGui_link_options(${target}) 93 | 94 | if(Qt5Core_VERSION VERSION_GREATER_EQUAL "5.15.0") 95 | qt_add_resources(APP_RESOURCES resources.qrc) 96 | else() 97 | qt5_add_resources(APP_RESOURCES resources.qrc) 98 | endif() 99 | 100 | target_sources(${target} PRIVATE ${APP_RESOURCES}) 101 | 102 | # Disable OCIO if not found 103 | if (NOT OpenColorIO_FOUND) 104 | target_compile_definitions(${target} PRIVATE DISABLE_OCIO) 105 | endif() 106 | 107 | install(TARGETS ${target} 108 | RUNTIME DESTINATION bin) 109 | -------------------------------------------------------------------------------- /flowpipeline.yaml: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | syntax: 1 3 | 4 | pipeline-configurations: 5 | 6 | continuous-integration: 7 | checkout: 8 | fresh: true 9 | build: 10 | rez_args: -b cmake 11 | prepend_release_dir: true 12 | resource: 13 | os-rocky-9: EL9 14 | os-CentOS-7: EL7_RaTS_build 15 | test: 16 | enabled: true 17 | cmd: 18 | - rez-test _packagename_ variant0 19 | - rez-test _packagename_ variant1 20 | - rez-test _packagename_ variant2 21 | - rez-test _packagename_ variant3 22 | ignore_failures: false 23 | ignore_failures_email: true 24 | resource: 25 | - EL9_CI 26 | - EL9_CI 27 | - EL9_CI 28 | - EL9_CI 29 | postp: /opt/electriccloud/electriccommander/bin/postp 30 | name_filter: foobar 31 | release: 32 | enabled: true 33 | finalize: 34 | email_enabled: false 35 | zulip_enabled: false 36 | initialize: 37 | email_enabled: true 38 | zulip_enabled: false 39 | 40 | pre-release: 41 | checkout: 42 | fresh: true 43 | build: 44 | rez_args: -b cmake 45 | prepend_release_dir: true 46 | resource: 47 | os-rocky-9: EL9 48 | os-CentOS-7: EL7_RaTS_build 49 | test: 50 | enabled: false 51 | finalize: 52 | zulip_enabled: false 53 | initialize: 54 | zulip_enabled: false 55 | 56 | release: 57 | build: 58 | rez_args: -b cmake 59 | resource: 60 | os-rocky-9: EL9 61 | os-CentOS-7: EL7_RaTS_build 62 | test: 63 | enabled: false 64 | finalize: 65 | zulip_enabled: false 66 | initialize: 67 | email_enabled: false 68 | zulip_enabled: false 69 | 70 | rats: 71 | checkout: 72 | enabled: true 73 | fresh: true 74 | clone: 0 75 | merge_enabled: false 76 | merge_target: develop 77 | merge_target_type: branch 78 | build: 79 | enabled: true 80 | resource: 81 | os-rocky-9: EL9 82 | os-CentOS-7: EL7_RaTS_build 83 | post_cmd: 84 | rez_args: -b cmake 85 | postp: /opt/electriccloud/electriccommander/bin/postp 86 | build_directory: ../build 87 | parallel_variants: true 88 | prepend_release_dir: true 89 | scan: 90 | enabled: false 91 | abi_checker: false 92 | checkmarx: false 93 | fossID: false 94 | dwa_policy: false 95 | test: 96 | enabled: false 97 | release: 98 | enabled: true 99 | ask_approval: false 100 | update_git: false 101 | update_jira: false 102 | finalize: 103 | email_enabled: false 104 | email_recipients_failure: 105 | - _submitter_ 106 | - _contributor_ 107 | email_recipients_success: 108 | - _submitter_ 109 | - _contributor_ 110 | chat_enabled: false 111 | zulip_enabled: false 112 | zulip_stream: CM DevOps Updates 113 | zulip_topic: Continuous Integration 114 | initialize: 115 | email_enabled: true 116 | email_recipients: 117 | - _submitter_ 118 | - _contributor_ 119 | chat_enabled: false 120 | zulip_enabled: false 121 | zulip_stream: CM DevOps Updates 122 | zulip_topic: Continuous Integration 123 | 124 | 125 | package-configurations: 126 | 127 | rez-2-CI: 128 | build_context: 129 | - buildtools-2 130 | environment: 131 | BROKEN_CUSTOM_ARGS_UNITTESTS: 1 132 | REZ_ALTERNATE_CONFIGURATION: CI 133 | packages_path: 134 | - /rel/rez/dwa 135 | - /rel/rez/third_party 136 | - /rel/rez/pypi 137 | - /rel/lang/python/packages 138 | release_packages_path: /rel/rez/dwa 139 | symlink_directory: null 140 | symlinks: false 141 | variants: true 142 | yaml: false 143 | 144 | rez-2-RaTS: 145 | build_context: 146 | - buildtools-2 147 | environment: 148 | REZ_ALTERNATE_CONFIGURATION: rats 149 | packages_path: 150 | - /rel/rez/dwa 151 | - /rel/rez/third_party 152 | - /rel/rez/pypi 153 | - /rel/lang/python/packages 154 | release_packages_path: /rel/rez/dwa 155 | symlink_directory: null 156 | symlinks: false 157 | variants: true 158 | yaml: false 159 | 160 | rez-2-testmap: 161 | build_context: 162 | - buildtools-2 163 | environment: {} 164 | packages_path: 165 | - /rel/rez/dwa 166 | - /rel/rez/third_party 167 | - /rel/rez/pypi 168 | - /rel/lang/python/packages 169 | release_packages_path: /rel/rez/dwa 170 | symlink_directory: null 171 | symlinks: false 172 | variants: true 173 | yaml: false 174 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2023-2024 DreamWorks Animation LLC 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | cmake_minimum_required (VERSION 3.23.1) 5 | 6 | include(OMR_PackageVersion) # Sets versionString, projectString and PACKAGE_NAME 7 | project(${projectString} 8 | VERSION ${versionString} 9 | LANGUAGES CXX) 10 | 11 | string(TOUPPER ${PROJECT_NAME} PROJECT_NAME_UPPER) 12 | list(APPEND CMAKE_MESSAGE_CONTEXT ${PROJECT_NAME}) 13 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) 14 | 15 | include(OMR_Platform) 16 | 17 | option(ABI_SET_VERSION "Enable the abi-version option" OFF) 18 | if(ABI_SET_VERSION) 19 | set(ABI_VERSION "6" CACHE STRING "If ABI_SET_VERSION is on, which version to set") 20 | endif() 21 | 22 | # ================================================ 23 | # Find dependencies 24 | # ================================================ 25 | # TODO: setting this/these should not be necessary before calling find_package. 26 | # see: https://stackoverflow.com/questions/55217771/boostcmake-no-suitable-build-variant 27 | set(BUILD_SHARED_LIBS ON) 28 | find_package(Boost REQUIRED 29 | COMPONENTS 30 | regex 31 | ) 32 | 33 | find_package(Qt5 REQUIRED 34 | COMPONENTS 35 | Core 36 | Gui 37 | OpenGL) 38 | 39 | find_package(OpenGL REQUIRED) 40 | find_package(CppUnit REQUIRED) 41 | 42 | # Intel Math Kernel Library is not required by Moonray itself, but currently it has to 43 | # be linked into the application if any dsos require it. Therefore we link it if it is available. 44 | set(MKL_THREADING tbb_thread) # Override the default MKL threading (intel_thread, which requires OMP) to use tbb_thread 45 | find_package(MKL CONFIG QUIET) # Look for an official MKL package first 46 | if(NOT TARGET MKL::MKL) 47 | find_package(MKL QUIET) # Fallback to the FindMKL module 48 | endif() 49 | if(TARGET MKL::MKL) 50 | set(MKL_target "MKL::MKL") # we'll use this in the target_link_libraries calls 51 | else() 52 | message(STATUS "MKL not found, skipping") 53 | endif() 54 | 55 | if("${PROJECT_NAME}" STREQUAL "${CMAKE_PROJECT_NAME}") 56 | find_package(Moonray REQUIRED) 57 | find_package(McrtDenoise REQUIRED) 58 | endif() 59 | find_package(OpenColorIO 2) 60 | if(${OpenColorIO_FOUND}) 61 | if(TARGET OpenColorIO::OpenColorIO) 62 | set(OCIO OpenColorIO::OpenColorIO) 63 | endif() 64 | endif() 65 | 66 | # Set the RPATH for binaries in the install tree 67 | set(CMAKE_INSTALL_RPATH ${GLOBAL_INSTALL_RPATH}) 68 | 69 | if(NOT IsDarwinPlatform) 70 | set(CMAKE_BUILD_RPATH ${COMPILER_LIBRARY_DIR}) 71 | endif() 72 | 73 | set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) 74 | 75 | # if OPT_LEVEL is set use its value to override the CMAKE_BUILD_TYPE because the 76 | # rez cmake plugin does not have an easy way to set the build_target. 77 | set(opt_level $ENV{OPT_LEVEL}) 78 | if(opt_level STREQUAL opt-debug) 79 | set(CMAKE_BUILD_TYPE RelWithDebInfo) 80 | elseif(opt_level STREQUAL debug) 81 | set(CMAKE_BUILD_TYPE Debug) 82 | elseif(opt_level STREQUAL opt) 83 | set(CMAKE_BUILD_TYPE Release) 84 | endif() 85 | 86 | # default to Release if no build type specified 87 | if(NOT CMAKE_BUILD_TYPE) 88 | set(CMAKE_BUILD_TYPE Release) 89 | endif() 90 | 91 | # Create include/${PACKAGE_NAME} link in the build directory for generated headers. 92 | file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/include) 93 | file(CREATE_LINK ../lib ${PROJECT_BINARY_DIR}/include/${PACKAGE_NAME} SYMBOLIC) 94 | 95 | include(${PROJECT_NAME}CompileDefinitions) 96 | include(${PROJECT_NAME}CompileFeatures) 97 | include(${PROJECT_NAME}CompileOptions) 98 | include(${PROJECT_NAME}LinkOptions) 99 | include(SConscriptStub) 100 | 101 | # ================================================ 102 | # Add project files 103 | # ================================================ 104 | add_subdirectory(cmd) 105 | 106 | # ================================================ 107 | # Install 108 | # ================================================ 109 | include(GNUInstallDirs) 110 | # install(# EXPORT ${PROJECT_NAME}Targets 111 | # FILE ${PROJECT_NAME}Targets.cmake 112 | # DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}-${PROJECT_VERSION} 113 | # NAMESPACE ${PROJECT_NAME}::) 114 | 115 | include(CMakePackageConfigHelpers) 116 | 117 | configure_package_config_file( 118 | ${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}Config.cmake.in 119 | ${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake 120 | INSTALL_DESTINATION 121 | ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}-${PROJECT_VERSION} 122 | ) 123 | 124 | write_basic_package_version_file( 125 | ${PROJECT_NAME}ConfigVersion.cmake 126 | VERSION ${PROJECT_VERSION} 127 | COMPATIBILITY SameMinorVersion 128 | ) 129 | 130 | install( 131 | FILES 132 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake 133 | ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake 134 | DESTINATION 135 | ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}-${PROJECT_VERSION} 136 | ) 137 | 138 | -------------------------------------------------------------------------------- /cmake/MoonrayGuiCompileOptions.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2023-2024 DreamWorks Animation LLC 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | function(${PROJECT_NAME}_cxx_compile_options target) 5 | if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 6 | target_compile_options(${target} 7 | PRIVATE 8 | $<$: 9 | -fabi-version=${ABI_VERSION} # corrects the promotion behavior of C++11 scoped enums and the mangling of template argument packs. 10 | > 11 | -fexceptions # Enable exception handling. 12 | -fno-omit-frame-pointer # TODO: add a note 13 | -fno-strict-aliasing # TODO: add a note 14 | -fpermissive # Downgrade some diagnostics about nonconformant code from errors to warnings. 15 | -march=core-avx2 # Specify the name of the target architecture 16 | -mavx # x86 options 17 | -pipe # Use pipes rather than intermediate files. 18 | -pthread # Define additional macros required for using the POSIX threads library. 19 | -w # Inhibit all warning messages. 20 | -Wall # Enable most warning messages. 21 | -Wcast-align # Warn about pointer casts which increase alignment. 22 | -Wcast-qual # Warn about casts which discard qualifiers. 23 | -Wdisabled-optimization # Warn when an optimization pass is disabled. 24 | -Wextra # This enables some extra warning flags that are not enabled by -Wall 25 | -Woverloaded-virtual # Warn about overloaded virtual function names. 26 | -Wno-conversion # Disable certain warnings that are enabled by -Wall 27 | -Wno-sign-compare # Disable certain warnings that are enabled by -Wall 28 | -Wno-switch # Disable certain warnings that are enabled by -Wall 29 | -Wno-system-headers # Disable certain warnings that are enabled by -Wall 30 | -Wno-unused-parameter # Disable certain warnings that are enabled by -Wall 31 | 32 | $<$: 33 | -O3 # the default is -O2 for RELWITHDEBINFO 34 | > 35 | ) 36 | elseif (CMAKE_CXX_COMPILER_ID STREQUAL Clang) 37 | target_compile_options(${target} 38 | # TODO: Some if not all of these should probably be PUBLIC 39 | PRIVATE 40 | -march=core-avx2 # Specify the name of the target architecture 41 | -fdelayed-template-parsing # Shader.h has a template method that uses a moonray class which is no available to scene_rdl2 and is only used in moonray+ 42 | -Wno-deprecated-declarations # disable auto_ptr deprecated warnings from log4cplus-1. 43 | -Wno-unused-value # caused by opt-debug build and MNRY_VERIFY. 44 | -Wno-switch # Disable certain warnings that are enabled by -Wall 45 | -Wno-uninitialized # Disable warning for Interpolater 46 | -Wno-register # Flex uses deprecated register keyword 47 | ) 48 | elseif (CMAKE_CXX_COMPILER_ID STREQUAL Intel) 49 | target_compile_options(${target} 50 | # TODO: Some if not all of these should probably be PUBLIC 51 | PRIVATE 52 | -march=core-avx2 # Specify the name of the target architecture 53 | -mavx 54 | -Qoption,cpp,--print_include_stack 55 | -fmerge-debug-strings 56 | -fexceptions # Enable exception handling. 57 | -fno-strict-aliasing # TODO: add a note 58 | -pthread 59 | -restrict 60 | ) 61 | endif() 62 | endfunction() 63 | 64 | # ISPC compiler 65 | function(${PROJECT_NAME}_ispc_compile_options target) 66 | target_compile_options(${target} 67 | PRIVATE 68 | --opt=force-aligned-memory # always issue "aligned" vector load and store instructions 69 | --pic # Generate position-independent code. Ignored for Windows target 70 | --werror # Treat warnings as errors 71 | --wno-perf # Don't issue warnings related to performance-related issues 72 | 73 | $<$: 74 | --dwarf-version=2 # use DWARF version 2 for debug symbols 75 | > 76 | 77 | $<$: 78 | -O3 # the default is -O2 for RELWITHDEBINFO 79 | --dwarf-version=2 # use DWARF version 2 for debug symbols 80 | --opt=disable-assertions # disable all of the assertions 81 | > 82 | 83 | $<$: 84 | --opt=disable-assertions # disable all of the assertions 85 | > 86 | ) 87 | endfunction() 88 | -------------------------------------------------------------------------------- /cmd/moonray_gui/RenderViewport.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023-2025 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #pragma once 5 | 6 | #ifndef Q_MOC_RUN 7 | #include "QtQuirks.h" 8 | #include "FrameUpdateEvent.h" 9 | #include "FreeCam.h" 10 | #include "GlslBuffer.h" 11 | #include "GuiTypes.h" 12 | #include "OrbitCam.h" 13 | #include "RenderGui.h" 14 | 15 | #include 16 | #endif 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | class QLabel; 24 | 25 | namespace moonray_gui { 26 | 27 | class PathVisualizerGui; 28 | 29 | /** 30 | * The RenderViewport class will just display a frame buffer. 31 | */ 32 | class RenderViewport : public QWidget 33 | { 34 | Q_OBJECT 35 | 36 | public: 37 | RenderViewport(QWidget* parent, CameraType initialType, const char *crtOverride, const std::string& snapPath); 38 | ~RenderViewport(); 39 | 40 | /// Navigation camera access. 41 | void setCameraRenderContext(const moonray::rndr::RenderContext &context); 42 | void setDefaultCameraTransform(const scene_rdl2::math::Mat4f &xform); 43 | NavigationCam *getNavigationCam(); 44 | 45 | void setShowTileProgress(bool tileProgress) { mShowTileProgress = tileProgress; } 46 | bool getShowTileProgress() const { return mShowTileProgress; } 47 | void setApplyColorRenderTransform(bool applyCrt) { mApplyColorRenderTransform = applyCrt; } 48 | bool getApplyColorRenderTransform() const { return mApplyColorRenderTransform; } 49 | bool getDenoisingEnabled() const { return mDenoise; } 50 | moonray::denoiser::DenoiserMode getDenoiserMode() const { return mDenoiserMode; } 51 | DenoisingBufferMode getDenoisingBufferMode() const { return mDenoisingBufferMode; } 52 | DebugMode getDebugMode() const { return mDebugMode; } 53 | int getRenderOutputIndx() const { return mRenderOutputIndx; } 54 | 55 | bool getUpdateExposure() const { return mUpdateExposure; } 56 | bool getUpdateGamma() const { return mUpdateGamma; } 57 | float getExposure() const { return mExposure; } 58 | float getGamma() const { return mGamma; } 59 | 60 | bool isFastProgressive() const { return mProgressiveFast; } 61 | 62 | moonray::rndr::FastRenderMode getFastMode() const { return mFastMode; } 63 | void setFastMode(moonray::rndr::FastRenderMode mode) { mFastMode = mode; } 64 | 65 | bool getNeedsRefresh() const { return mNeedsRefresh; } 66 | void setNeedsRefresh(bool refresh) { mNeedsRefresh = refresh; } 67 | 68 | int getKey() const { return mKey; } 69 | void setKey(int key) { mKey = key; } 70 | 71 | bool getUseOCIO() const { return mUseOCIO; } 72 | 73 | /// Called by the main application to update the frame which is displayed. 74 | void updateFrame(FrameUpdateEvent* event); 75 | 76 | // Get status string 77 | QString getSettings() const { return "Exposure: " + QString::number(mExposure) + 78 | "\nGamma: " + QString::number(mGamma); } 79 | 80 | void setRenderGui(RenderGui* renderGui) { mRenderGui = renderGui; } 81 | 82 | void processPixel(int x, int y); 83 | 84 | static const char* mHelp; 85 | 86 | public slots: 87 | void slot_forceFrameRedraw(); 88 | 89 | signals: 90 | void sig_pixelSelected(int x, int y); 91 | 92 | protected: 93 | void keyPressEvent(QKeyEvent *event) override; 94 | void keyReleaseEvent(QKeyEvent *event) override; 95 | void mousePressEvent(QMouseEvent *event) override; 96 | void mouseReleaseEvent(QMouseEvent *event) override; 97 | void mouseMoveEvent(QMouseEvent *event) override; 98 | 99 | private: 100 | void setupUi(); 101 | 102 | RenderGui* mRenderGui; 103 | 104 | moonray_gui::PathVisualizerGui* mPathVisualizerGui; 105 | bool mPathVisualizerGuiInitialized; 106 | 107 | QLabel* mImageLabel; 108 | QLabel* mImageOverlay; 109 | QLabel* mRecordingText; 110 | 111 | QGridLayout* mLayout; 112 | 113 | // OpenGL CRT 114 | GlslBuffer *mGlslBuffer; 115 | 116 | int mWidth; 117 | int mHeight; 118 | 119 | CameraType mActiveCameraType; 120 | OrbitCam mOrbitCam; 121 | FreeCam mFreeCam; 122 | 123 | bool mShowTileProgress; 124 | bool mApplyColorRenderTransform; 125 | bool mDenoise; 126 | moonray::denoiser::DenoiserMode mDenoiserMode; 127 | DenoisingBufferMode mDenoisingBufferMode; 128 | std::vector mValidDenoisingBufferModes; 129 | DebugMode mDebugMode; 130 | int mRenderOutputIndx; 131 | bool mNeedsRefresh; 132 | bool mUpdateExposure; // is exposure being updated? 133 | bool mUpdateGamma; // is gamma being updated? 134 | float mExposure; 135 | float mGamma; 136 | int mMousePos; // x position of the mouse 137 | int mKey; // index of current pressed key 138 | int mKeyTime; // elapsed time between key press and release 139 | int mMouseTime; // elapsed time between mouse button press and release 140 | int mSnapIdx; 141 | std::string mSnapshotPath; 142 | int mInspectorMode; 143 | const moonray::rndr::RenderContext *mRenderContext; 144 | bool mProgressiveFast; 145 | moonray::rndr::FastRenderMode mFastMode; 146 | bool mUseOCIO; // toggles on/off OCIO support 147 | 148 | // Color render override LUT. Set to nullptr if we aren't overriding 149 | // the LUT. This binary blob is assumed to contain 64*64*64 * RGB float 150 | // OpenGL compatible volume texture data. 151 | // This class owns this data and is responsible for deleting it. 152 | float * mLutOverride; 153 | }; 154 | 155 | } // namespace moonray_gui 156 | 157 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This project aims to be governed in a transparent, accessible way for the benefit of the community. All participation in this project is open and not bound to corporate affiliation. Participants are all bound to the [Code of Conduct](CODE_OF_CONDUCT.md). 4 | 5 | # Project roles 6 | 7 | ## Contributor 8 | 9 | The contributor role is the starting role for anyone participating in the project and wishing to contribute code. 10 | 11 | ### Process for becoming a contributor 12 | 13 | * Review the [coding standards](https://docs.openmoonray.org/developer-reference/coding-standards/) to ensure your contribution is in line with the project's coding and styling guidelines. 14 | * Have a signed CLA on file ( see [below](#contributor-license-agreements) ) 15 | * Have your submission approved by the [committer(s)](#committer) and merged into the codebase. 16 | 17 | #### License 18 | 19 | MoonRay is licensed under the [Apache, version 2.0](LICENSE.md) 20 | license. Contributions to MoonRay should abide by that standard 21 | license. 22 | 23 | #### Contributor License Agreements 24 | 25 | Developers who wish to contribute code to be considered for inclusion in MoonRay must first complete 26 | a **Contributor License Agreement**, and email it to MoonRay@dreamworks.com and be sure to include 27 | your GitHub username(s). 28 | 29 | * If you are an individual writing the code on your own time and 30 | you're SURE you are the sole owner of any intellectual property you 31 | contribute, you can [sign the CLA as an individual contributor](https://github.com/dreamworksanimation/openmoonray/blob/main/tsc/icla.md). 32 | 33 | * If you are writing the code as part of your job, or if there is any 34 | possibility that your employers might think they own any 35 | intellectual property you create, then you should use the [Corporate 36 | Contributor Licence 37 | Agreement](https://github.com/dreamworksanimation/openmoonray/blob/main/tsc/ccla.md). 38 | 39 | The MoonRay CLAs are in the [OpenMoonRay repo](https://github.com/dreamworksanimation/openmoonray/tree/main/tsc). 40 | 41 | #### Commit Sign-Off 42 | 43 | Every commit must be signed off. That is, every commit log message 44 | must include a “`Signed-off-by`” line (generated, for example, with 45 | “`git commit --signoff`”), indicating that the committer wrote the 46 | code and has the right to release it under the 47 | [Apache License, version 2.0](LICENSE) 48 | license. See [http://developercertificate.org](http://developercertificate.org/) for more information on this requirement. 49 | 50 | ## Committer 51 | 52 | The committer role enables the participant to commit code directly to the repository, but also comes with the obligation to be a responsible leader in the community. 53 | 54 | ### Process for becoming a committer 55 | 56 | * Show your experience with the codebase through contributions and engagement on the community channels. 57 | * Request to become a committer. 58 | * Have the majority of committers approve you becoming a committer. 59 | * Your name and email is added to the [MAINTAINERS](MAINTAINERS.md) file for the project. 60 | 61 | ### Committer responsibilities 62 | 63 | * Monitor email aliases. 64 | * Monitor Forums (delayed response is perfectly acceptable). 65 | * Triage GitHub issues and perform pull request reviews for other committers and the community. 66 | * Make sure that ongoing PRs are moving forward at the right pace or close them. 67 | * Remain an active contributor to the project in general and the code base in particular. 68 | 69 | ### When does a committer lose committer status? 70 | 71 | If a committer is no longer interested or cannot perform the committer duties listed above, they 72 | should volunteer to be moved to emeritus status. In extreme cases this can also occur by a vote of 73 | the committers per the voting process below. 74 | 75 | ## Technical Steering Committee (TSC) member 76 | 77 | The Technical Steering Committee (TSC) oversees the overall technical direction of MoonRay, as defined in the [charter](charter.md). 78 | 79 | TSC voting members consist of committers that have been nominated by the committers, with a supermajority of voting members required to have a committer elected to be a TSC voting member. TSC voting members term and succession is defined in the [charter](charter.md). 80 | 81 | All meetings of the TSC are open to participation by any member of the MoonRay community. Meeting times are listed in the [MoonRay technical community calendar](https://calendar.google.com/calendar/embed?src=c_0104aeaceaad2fdc2db4264d1b1211ed56c33cb51086cd5a2a8df324158d21c5%40group.calendar.google.com&ctz=America%2FLos_Angeles). 82 | 83 | ## Current TSC members 84 | 85 | * Jon Lanz, Chair / DreamWorks 86 | * Toshi Kato / DreamWorks 87 | * Rob Wilson / DreamWorks 88 | 89 | # Release Process 90 | 91 | Project releases will occur on a scheduled basis as agreed to by the TSC. 92 | 93 | # Conflict resolution and voting 94 | 95 | In general, we prefer that technical issues and committer status/TSC membership are amicably worked out 96 | between the persons involved. If a dispute cannot be decided independently, the TSC can be 97 | called in to decide an issue. If the TSC themselves cannot decide an issue, the issue will 98 | be resolved by voting. The voting process is a simple majority in which each TSC receives one vote. 99 | 100 | # Communication 101 | 102 | This project, just like all of open source, is a global community. In addition to the [Code of Conduct](CODE_OF_CONDUCT.md), this project will: 103 | 104 | * Keep all communication on open channels ( mailing list, forums, chat ). 105 | * Be respectful of time and language differences between community members ( such as scheduling meetings, email/issue responsiveness, etc ). 106 | * Ensure tools are able to be used by community members regardless of their region. 107 | 108 | If you have concerns about communication challenges for this project, please contact the [TSC](mailto:MoonRay_TSC@dreamworks.com). 109 | -------------------------------------------------------------------------------- /cmd/moonray_gui/RenderGui.h: -------------------------------------------------------------------------------- 1 | // Copyright 2023-2025 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #pragma once 5 | 6 | #include "ColorManager.h" 7 | #include "GuiTypes.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | #define NUM_TILE_FADE_STEPS 4 16 | 17 | namespace moonray_gui { 18 | 19 | class Handler; 20 | 21 | /** 22 | * The RenderGui class handles spinning off a thread for Qt, as well as booting 23 | * up the GUI interface and communicating updates from the renderer to the GUI. 24 | */ 25 | class RenderGui 26 | { 27 | public: 28 | RenderGui(CameraType initialCamType, bool showTileProgress, bool applyCrt, 29 | const char *crtOverride, const std::string& snapPath); 30 | ~RenderGui(); 31 | 32 | void setContext(moonray::rndr::RenderContext *ctx) { mRenderContext = ctx; } 33 | 34 | /// Submits a new frame to the GUI for display. 35 | void updateFrame(const scene_rdl2::fb_util::RenderBuffer *renderBuffer, 36 | const scene_rdl2::fb_util::VariablePixelBuffer *renderOutputBuffer, 37 | bool showTileProgress, 38 | bool parallel); 39 | 40 | /// Snapshots the current output buffers based on the 41 | /// user's mRenderOutput selection. 42 | /// heatMapBuffer is a scratch buffer. Final results 43 | /// will be in either renderBuffer or renderOutputBuffer 44 | void snapshotFrame(scene_rdl2::fb_util::RenderBuffer *renderBuffer, 45 | scene_rdl2::fb_util::HeatMapBuffer *heatMapBuffer, 46 | scene_rdl2::fb_util::FloatBuffer *weightBuffer, 47 | scene_rdl2::fb_util::RenderBuffer *renderBufferOdd, 48 | scene_rdl2::fb_util::VariablePixelBuffer *renderOutputBuffer, 49 | bool untile, bool parallel); 50 | 51 | /// APIs to handle interactive rendering logic. 52 | /// All calls to updateInteractiveRendering should only be done inside of 53 | /// a beginInteractiveRendering/endInteractiveRendering pair. 54 | /// A call to beginInteractiveRendering takes an initial camera transform. 55 | /// A call to endInteractiveRendering returns to latest camera transform 56 | /// in case you later want to continue interactive rendering at that same 57 | /// location. 58 | /// updateInteractiveRendering returns the current "render frame" we're in 59 | /// the process of rendering. 60 | void beginInteractiveRendering(const scene_rdl2::math::Mat4f& cameraXform, 61 | bool makeDefaultXform); 62 | uint32_t updateInteractiveRendering(); 63 | scene_rdl2::math::Mat4f endInteractiveRendering(); 64 | 65 | /// Once we are within a beginInteractiveRendering/endInteractiveRendering, 66 | /// This function may be called from anywhere or any thread to discard the 67 | /// current interactive frame being rendered and to kick off another one. 68 | void forceNewInteractiveFrame(); 69 | 70 | /// True: fast progressive, False: regular progressive 71 | bool isFastProgressive() const; 72 | 73 | /// Get the current fast progressive render mode 74 | moonray::rndr::FastRenderMode getFastRenderMode() const; 75 | 76 | bool isActive(); 77 | 78 | bool close(); 79 | 80 | private: 81 | uint32_t updateProgressiveRendering(); 82 | uint32_t updateRealTimeRendering(); 83 | 84 | void computeCameraMotionXformOffset(); 85 | void setCameraXform(const scene_rdl2::math::Mat4f& cameraXform); 86 | 87 | scene_rdl2::math::Mat4f updateNavigationCam(double currentTime); 88 | 89 | enum DisplayBuffer { 90 | DISPLAY_BUFFER_IS_DISPLAY_BUFFER = 0, 91 | DISPLAY_BUFFER_IS_RENDER_BUFFER, 92 | DISPLAY_BUFFER_IS_RENDER_OUTPUT_BUFFER 93 | }; 94 | 95 | void drawTileOutlines(DisplayBuffer buf, const std::vector &tiles, 96 | float tileColor, int fadeLevelIdx); 97 | void showTileProgress(DisplayBuffer buf); 98 | bool updateRenderOutput(); 99 | 100 | CameraType mInitialCameraType; 101 | 102 | MainWindow* mMainWindow; 103 | 104 | bool mPathVisualizerAttached; 105 | bool mPathVisualizerProgressiveDraw; 106 | 107 | moonray::rndr::RenderContext *mRenderContext = nullptr; 108 | scene_rdl2::fb_util::RenderBuffer mRenderBuffer; 109 | scene_rdl2::fb_util::RenderBuffer mDenoisedRenderBuffer; 110 | scene_rdl2::fb_util::RenderBuffer mAlbedoBuffer; 111 | scene_rdl2::fb_util::RenderBuffer mNormalBuffer; 112 | scene_rdl2::fb_util::HeatMapBuffer mHeatMapBuffer; 113 | scene_rdl2::fb_util::FloatBuffer mWeightBuffer; 114 | scene_rdl2::fb_util::RenderBuffer mRenderBufferOdd; 115 | scene_rdl2::fb_util::VariablePixelBuffer mRenderOutputBuffer; 116 | scene_rdl2::fb_util::Rgb888Buffer mDisplayBuffer; 117 | 118 | // 119 | // Interactive rendering related members: 120 | // 121 | 122 | /// Increment whenever any inputs which will affect the render change. 123 | /// The renderering code will strive to render this frame. If it's rendering 124 | /// a frame with a lower timestamp then we know the frame it's currently 125 | /// rendering is old. 126 | tbb::atomic mMasterTimestamp; 127 | 128 | /// The timestamp of the frame the renderer is currently processing. 129 | uint32_t mRenderTimestamp; 130 | 131 | /// This variable contains the timestamp of the most recent frame 132 | /// snap-shotted for display. 133 | uint32_t mLastSnapshotTimestamp; 134 | 135 | /// This variable contains the absolute time of the most recent frame 136 | /// snap-shotted for display. 137 | double mLastSnapshotTime; 138 | 139 | /// Used to check if the Film has changed since the last time we 140 | /// checked. Only touched on the main thread. 141 | unsigned mLastFilmActivity; 142 | 143 | /// This variable contains the absolute time of the most recent call to 144 | /// NavigationCam::update. 145 | double mLastCameraUpdateTime; 146 | 147 | /// The most recent camera transform. Stored to avoid kicking off new frame 148 | /// if the camera hasn't moved. 149 | scene_rdl2::math::Mat4f mLastCameraXform; 150 | 151 | /// The "offset" camera xform to go from TIMESTEP_END to TIMESTEP_BEGIN 152 | /// See computeCameraMotionXformOffset() for details 153 | scene_rdl2::math::Mat4f mC12C0; 154 | 155 | /// The viewport maintains an integer (renderOutputIndex) that increases 156 | /// when the user presses the '.' key and decreases when he presses 157 | /// the ',' key. This member keeps track of the last value read from 158 | /// the viewport - so we can increment to the next render output or 159 | /// decrement to the previous. 160 | int mLastRenderOutputGuiIndx; 161 | 162 | /// < 0 mean show the main render buffer, otherwise this 163 | /// value stores a RenderOutput indx for the RenderOutputDriver 164 | int mRenderOutput; 165 | int mLastTotalRenderOutputs; 166 | std::string mLastRenderOutputName; 167 | 168 | /// Small class for handling interactions between Qt Widgets and the Render GUI 169 | Handler* mHandler; 170 | 171 | /// Tile progress: 172 | bool mOkToRenderTiles; 173 | util::BitArray mFadeLevels[NUM_TILE_FADE_STEPS]; 174 | 175 | /// Denoiser 176 | std::unique_ptr mDenoiser; 177 | 178 | /// Color Manager 179 | ColorManager mColorManager; 180 | 181 | std::shared_ptr mShmFbOutput; 182 | }; 183 | 184 | } // namespace moonray_gui 185 | 186 | -------------------------------------------------------------------------------- /cmd/moonray_gui/MainWindow.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023-2024 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include "MainWindow.h" 5 | 6 | #include "FrameUpdateEvent.h" 7 | #include "RenderViewport.h" 8 | 9 | #include 10 | 11 | #define NO_KEY -1 12 | 13 | namespace moonray_gui { 14 | 15 | void 16 | Handler::quitApp() 17 | { 18 | mIsActive = false; 19 | } 20 | 21 | MainWindow::MainWindow(QWidget* parent, CameraType initialType, const char *crtOverride, const std::string& snapPath): 22 | QMainWindow(parent), 23 | mRenderViewport(nullptr), 24 | mFastMode(nullptr), 25 | mGuide(nullptr), 26 | mSettings(nullptr), 27 | mTimer(nullptr) 28 | { 29 | setupUi(initialType, crtOverride, snapPath); 30 | 31 | // Change color of widget 32 | this->setStyleSheet("background-color:rgba(0.5, 0.5, 0.5, 1.0);"); 33 | 34 | // Setup fast progressive mode text overlay 35 | mFastMode = new QLabel(this); 36 | mFastMode->setStyleSheet(QString("QLabel { margin: 10; padding: 5; background-color : rgba(0.0, 0.0, 0.0, 0.5);") + 37 | QString("color: rgba(255.0, 255.0, 255.0, 0.5); }")); 38 | constexpr int widthResize = 175; 39 | constexpr int heightResize = 50; 40 | mFastMode->resize(widthResize, heightResize); 41 | mFastMode->hide(); // hide text overlay at the start of application 42 | 43 | // Setup exposure/gamma values text overlay 44 | mSettings = new QLabel(this); 45 | mSettings->setStyleSheet(QString::fromStdString("QLabel { margin : 10; padding : 5; background-color : ") + 46 | QString::fromStdString("rgba(0.0, 0.0, 0.0, 0.5); color : ") + 47 | QString::fromStdString("rgba(255.0, 255.0, 255.0, 1.0); }")); 48 | mSettings->setText(mRenderViewport->getSettings()); 49 | mSettings->resize(width() / 4, height() / 10); 50 | mSettings->hide(); // hide text overlay at the start of application 51 | 52 | // Setup hotkey guide text overlay 53 | mGuide = new QLabel(this); 54 | mGuide->setText(QString(RenderViewport::mHelp)); 55 | mGuide->setStyleSheet(QString::fromStdString("QLabel { margin : 10; padding : 5; font : 9.5pt;") + 56 | QString::fromStdString("background-color : rgba(0.0, 0.0, 0.0, 0.5); ") + 57 | QString::fromStdString("color : rgba(255.0, 255.0, 255.0, 1.0); }")); 58 | 59 | mGuide->resize(width() / 2 , height()); 60 | mGuide->hide(); 61 | 62 | // Setup the timer for text overlay 63 | mTimer = new QTimer(this); 64 | connect(mTimer, SIGNAL(timeout()), this, SLOT(hideTextOverlay())); 65 | 66 | // Print welcome message to console 67 | std::cout << "Welcome to Moonray GUI. Press H while running the application to open the hotkey guide." << std::endl; 68 | } 69 | 70 | MainWindow::~MainWindow() 71 | { 72 | if (mRenderViewport) delete mRenderViewport; 73 | if (mFastMode) delete mFastMode; 74 | if (mGuide) delete mGuide; 75 | if (mSettings) delete mSettings; 76 | if (mTimer) delete mTimer; 77 | } 78 | 79 | void 80 | MainWindow::setupUi(CameraType initialType, const char *crtOverride, const std::string& snapPath) 81 | { 82 | // We don't support window maximization. 83 | setWindowFlags(windowFlags() ^ Qt::WindowMaximizeButtonHint); 84 | //setAttribute(Qt::WA_DeleteOnClose); 85 | 86 | // The RenderViewport is our only widget for now. 87 | mRenderViewport = new RenderViewport(this, initialType, crtOverride, snapPath); 88 | setCentralWidget(mRenderViewport); 89 | 90 | // Restore previous window position if we have it. 91 | QSettings settings("DWA", "moonray_gui"); 92 | settings.beginGroup("MainWindow"); 93 | restoreGeometry(settings.value("geometry").toByteArray()); 94 | restoreState(settings.value("windowState").toByteArray()); 95 | settings.endGroup(); 96 | } 97 | 98 | void 99 | MainWindow::setFastModeText() 100 | { 101 | if (mRenderViewport == nullptr) return; 102 | moonray::rndr::FastRenderMode currentMode = mRenderViewport->getFastMode(); 103 | switch(currentMode) { 104 | case moonray::rndr::FastRenderMode::NORMALS: 105 | mFastMode->setText("Geometric normals"); 106 | std::cout << "Fast render mode: Geometric normals" << std::endl; 107 | break; 108 | case moonray::rndr::FastRenderMode::NORMALS_SHADING: 109 | mFastMode->setText("Shading normals"); 110 | std::cout << "Fast render mode: Shading normals" << std::endl; 111 | break; 112 | case moonray::rndr::FastRenderMode::FACING_RATIO: 113 | mFastMode->setText("Facing ratio"); 114 | std::cout << "Fast render mode: Facing ratio" << std::endl; 115 | break; 116 | case moonray::rndr::FastRenderMode::FACING_RATIO_INVERSE: 117 | mFastMode->setText("Inverse facing ratio"); 118 | std::cout << "Fast render mode: Inverse facing ratio" << std::endl; 119 | break; 120 | case moonray::rndr::FastRenderMode::UVS: 121 | mFastMode->setText("UVs"); 122 | std::cout << "Fast render mode: UVs" << std::endl; 123 | break; 124 | default: 125 | break; 126 | } 127 | } 128 | 129 | bool 130 | MainWindow::event(QEvent* event) 131 | { 132 | // Handle frame updates by handling them off to the RenderViewport and 133 | // resizing the window to account for viewport changes. 134 | if (event->type() == FrameUpdateEvent::type()) { 135 | mRenderViewport->updateFrame(static_cast(event)); 136 | mSettings->setText(mRenderViewport->getSettings()); 137 | resize(minimumSizeHint()); 138 | return true; 139 | } 140 | 141 | else if (event->type() == QEvent::KeyPress) { 142 | QKeyEvent *key = static_cast(event); 143 | // ESC key closes interactive viewport. 144 | if (key->key() == Qt::Key_Escape) { 145 | close(); 146 | return true; 147 | } else if (key->key() == Qt::Key_H) { 148 | mGuide->show(); 149 | } else if (key->key() == Qt::Key_X || key->key() == Qt::Key_Y) { 150 | if ((mRenderViewport->getUpdateGamma() || mRenderViewport->getUpdateExposure())) { 151 | mSettings->show(); 152 | } 153 | } 154 | } 155 | 156 | else if (event->type() == QEvent::KeyRelease) { 157 | QKeyEvent *key = static_cast(event); 158 | if (!key->isAutoRepeat()) { 159 | // set text overlay timeouts in milliseconds 160 | constexpr int hideExposureGamma = 2000; 161 | constexpr int hideHelp = 3500; 162 | constexpr int hideFastMode = 3500; 163 | if (key->modifiers() == Qt::NoModifier) { 164 | if ((key->key() == Qt::Key_X || key->key() == Qt::Key_Y) && 165 | QGuiApplication::mouseButtons() == Qt::NoButton) { 166 | mTimer->start(hideExposureGamma); 167 | } else if (key->key() == Qt::Key_H) { 168 | mTimer->start(hideHelp); 169 | } else if (key->key() == Qt::Key_L) { 170 | // Check if fast mode is enabled 171 | if (mRenderViewport->isFastProgressive()) { 172 | setFastModeText(); 173 | mFastMode->show(); 174 | mTimer->start(hideFastMode); 175 | } 176 | } 177 | } else if (key->modifiers() == Qt::ShiftModifier) { 178 | if (key->key() == Qt::Key_X || key->key() == Qt::Key_Y || 179 | key->key() == Qt::Key_Up || key->key() == Qt::Key_Down) { 180 | mSettings->show(); 181 | mTimer->start(hideExposureGamma); 182 | } 183 | } else if (key->modifiers() == Qt::AltModifier) { 184 | if (mRenderViewport->isFastProgressive()) { 185 | if (key->key() == Qt::Key_Up || key->key() == Qt::Key_Down) { 186 | setFastModeText(); 187 | mFastMode->show(); 188 | mTimer->start(hideFastMode); 189 | } 190 | } 191 | } 192 | } 193 | } 194 | 195 | else if (event->type() == QEvent::MouseButtonRelease) { 196 | QMouseEvent *button = static_cast(event); 197 | // set text overlay timeout in millsssssiseconds 198 | constexpr int hideExposureGamma = 2000; 199 | if (button->button() == Qt::LeftButton && mRenderViewport->getKey() == NO_KEY) { 200 | mTimer->start(hideExposureGamma); 201 | } 202 | } 203 | 204 | return QMainWindow::event(event); 205 | } 206 | 207 | void 208 | MainWindow::closeEvent(QCloseEvent* event) 209 | { 210 | // Save the window position when we exit. 211 | QSettings settings("DWA", "moonray_gui"); 212 | settings.beginGroup("MainWindow"); 213 | settings.setValue("geometry", saveGeometry()); 214 | settings.setValue("windowState", saveState()); 215 | settings.endGroup(); 216 | 217 | QMainWindow::closeEvent(event); 218 | } 219 | 220 | void 221 | MainWindow::hideTextOverlay() 222 | { 223 | // hide the text overlay when timer goes off 224 | mSettings->hide(); 225 | mGuide->hide(); 226 | mFastMode->hide(); 227 | } 228 | 229 | } // namespace moonray_gui 230 | 231 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | -------------------------------------------------------------------------------- /cmd/moonray_gui/FreeCam.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023-2024 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | #include "FreeCam.h" 6 | #include 7 | #include 8 | 9 | // must be between 0 and 1 10 | #define FREECAM_MAX_DAMPENING 0.1f 11 | #define NO_KEY -1 12 | 13 | using namespace scene_rdl2::math; 14 | 15 | namespace { 16 | 17 | Mat4f 18 | makeMatrix(float yaw, float pitch, float roll, const Vec3f &pos) 19 | { 20 | Mat4f rotYaw, rotPitch, rotRoll; 21 | rotYaw.setToRotation(Vec4f(0.0f, 1.0f, 0.0f, 0.0f), yaw); 22 | rotPitch.setToRotation(Vec4f(1.0f, 0.0f, 0.0f, 0.0f), pitch); 23 | rotRoll.setToRotation(Vec4f(0.0f, 0.0f, 1.0f, 0.0f), roll); 24 | Mat4f rotation = rotRoll * rotPitch * rotYaw; 25 | 26 | return rotation * Mat4f::translate(Vec4f(pos.x, pos.y, pos.z, 1.0f)); 27 | } 28 | 29 | // Print out matrix in lua format so it can be pasted into an rdla file. 30 | void printMatrix(const char *comment, const Mat4f &m) 31 | { 32 | std::cout << "-- " << comment << "\n" 33 | << "[\"node xform\"] = Mat4(" 34 | << m.vx.x << ", " << m.vx.y << ", " << m.vx.z << ", " << m.vx.w << ", " 35 | << m.vy.x << ", " << m.vy.y << ", " << m.vy.z << ", " << m.vy.w << ", " 36 | << m.vz.x << ", " << m.vz.y << ", " << m.vz.z << ", " << m.vz.w << ", " 37 | << m.vw.x << ", " << m.vw.y << ", " << m.vw.z << ", " << m.vw.w << "),\n" 38 | << std::endl; 39 | } 40 | 41 | } // end of anon namespace 42 | 43 | 44 | namespace moonray_gui { 45 | 46 | enum 47 | { 48 | FREECAM_FORWARD = 0x0001, 49 | FREECAM_BACKWARD = 0x0002, 50 | FREECAM_LEFT = 0x0004, 51 | FREECAM_RIGHT = 0x0008, 52 | FREECAM_UP = 0x0010, 53 | FREECAM_DOWN = 0x0020, 54 | FREECAM_SLOW_DOWN = 0x0040, 55 | FREECAM_SPEED_UP = 0x0080 56 | }; 57 | 58 | //---------------------------------------------------------------------------- 59 | 60 | FreeCam::FreeCam() : 61 | mPosition(0.0f, 0.0f, 0.0f), 62 | mVelocity(0.0f, 0.0f, 0.0f), 63 | mYaw(0.0f), 64 | mPitch(0.0f), 65 | mRoll(0.0f), 66 | mSpeed(10.0f), 67 | mDampening(1.0f), 68 | mMouseSensitivity(0.004f), 69 | mInputState(0), 70 | mMouseMode(NONE), 71 | mMouseX(0), 72 | mMouseY(0), 73 | mMouseDeltaX(0), 74 | mMouseDeltaY(0), 75 | mInitialTransformSet(false) 76 | { 77 | } 78 | 79 | FreeCam::~FreeCam() 80 | { 81 | } 82 | 83 | Mat4f 84 | FreeCam::resetTransform(const Mat4f &xform, bool makeDefault) 85 | { 86 | if (!mInitialTransformSet || makeDefault) { 87 | mInitialTransform = xform; 88 | mInitialTransformSet = true; 89 | } 90 | 91 | mPosition = asVec3(xform.row3()); 92 | mVelocity = Vec3f(zero); 93 | 94 | Vec3f viewDir = -normalize(asVec3(xform.row2())); 95 | 96 | mYaw = 0.0f; 97 | if (viewDir.x * viewDir.x + viewDir.z * viewDir.z > 0.00001f) { 98 | mYaw = scene_rdl2::math::atan2(-viewDir.x, -viewDir.z); 99 | } 100 | 101 | // We aren't extracting the entire range of possible pitches here, just the 102 | // ones which the freecam can natively handle. Because of this, not all camera 103 | // orientations are supported. 104 | mPitch = scene_rdl2::math::asin(viewDir.y); 105 | 106 | // Compute a matrix which only contains the roll so we can extract it out. 107 | Mat4f noRoll = makeMatrix(mYaw, mPitch, 0.0f, Vec3f(0.0f, 0.0f, 0.0f)); 108 | Mat4f rollOnly = xform * noRoll.transposed(); 109 | Vec3f xAxis = normalize(asVec3(rollOnly.row0())); 110 | mRoll = scene_rdl2::math::atan2(xAxis.y, xAxis.x); 111 | 112 | mInputState = 0; 113 | mMouseMode = NONE; 114 | mMouseX = mMouseY = 0; 115 | mMouseDeltaX = mMouseDeltaY = 0; 116 | 117 | return makeMatrix(mYaw, mPitch, mRoll, mPosition); 118 | } 119 | 120 | Mat4f 121 | FreeCam::update(float dt) 122 | { 123 | // Compute some amount to change our current velocity. 124 | Vec3f deltaVelocity = Vec3f(zero); 125 | float movement = mSpeed * 0.5f; 126 | 127 | // Process keyboard input. 128 | if (mInputState & FREECAM_FORWARD) { 129 | deltaVelocity += Vec3f(0.0f, 0.0f, -movement); 130 | } 131 | if (mInputState & FREECAM_BACKWARD) { 132 | deltaVelocity += Vec3f(0.0f, 0.0f, movement); 133 | } 134 | if (mInputState & FREECAM_LEFT) { 135 | deltaVelocity += Vec3f(-movement, 0.0f, 0.0f); 136 | } 137 | if (mInputState & FREECAM_RIGHT) { 138 | deltaVelocity += Vec3f(movement, 0.0f, 0.0f); 139 | } 140 | if (mInputState & FREECAM_UP) { 141 | deltaVelocity += Vec3f(0.0f, movement, 0.0f); 142 | } 143 | if (mInputState & FREECAM_DOWN) { 144 | deltaVelocity += Vec3f(0.0f, -movement, 0.0f); 145 | } 146 | if (mInputState & FREECAM_SLOW_DOWN) { 147 | mSpeed += -mSpeed * dt; 148 | } 149 | if (mInputState & FREECAM_SPEED_UP) { 150 | mSpeed += mSpeed * dt; 151 | } 152 | 153 | // Update the camera angles by the rotation amounts (ignore dt for this 154 | // since it should be instant). 155 | if (mMouseMode == MOVE) { 156 | 157 | // rotate mouse movement by roll before updating yaw and pitch 158 | float c, s; 159 | sincos(-mRoll, &s, &c); 160 | 161 | float dx = float(mMouseDeltaX) * c - float(mMouseDeltaY) * s; 162 | float dy = float(mMouseDeltaY) * c + float(mMouseDeltaX) * s; 163 | 164 | mYaw -= dx * mMouseSensitivity; 165 | mPitch -= dy * mMouseSensitivity; 166 | 167 | } else if (mMouseMode == ROLL) { 168 | mRoll += float(mMouseDeltaX) * mMouseSensitivity; 169 | } 170 | mMouseDeltaX = mMouseDeltaY = 0; 171 | 172 | // Clip camera pitch to prevent Gimbal Lock. 173 | const float halfPi = sHalfPi; 174 | mPitch = clamp(mPitch, -halfPi, halfPi); 175 | 176 | // Transform deltaVelocity into current camera coordinate system. 177 | Mat4f rotation = makeMatrix(mYaw, mPitch, mRoll, zero); 178 | deltaVelocity = transform3x3(rotation, deltaVelocity); 179 | 180 | mVelocity += deltaVelocity; 181 | 182 | // Scale back velocity to mSpeed if too big. 183 | float len = mVelocity.length(); 184 | if (len > mSpeed) { 185 | mVelocity *= (mSpeed / len); 186 | } 187 | 188 | // Integrate position. 189 | mPosition += mVelocity * dt; 190 | 191 | // Apply dampening to velocity. 192 | mVelocity *= min(mDampening * dt, FREECAM_MAX_DAMPENING); 193 | 194 | return makeMatrix(mYaw, mPitch, mRoll, mPosition); 195 | } 196 | 197 | bool 198 | FreeCam::processKeyboardEvent(QKeyEvent *event, bool pressed) 199 | { 200 | bool used = false; 201 | 202 | if (event->modifiers() == Qt::NoModifier) { 203 | 204 | used = true; 205 | 206 | if (pressed) { 207 | // Check for pressed keys. 208 | switch (event->key()) { 209 | case Qt::Key_W: mInputState |= FREECAM_FORWARD; break; 210 | case Qt::Key_S: mInputState |= FREECAM_BACKWARD; break; 211 | case Qt::Key_A: mInputState |= FREECAM_LEFT; break; 212 | case Qt::Key_D: mInputState |= FREECAM_RIGHT; break; 213 | case Qt::Key_Space: mInputState |= FREECAM_UP; break; 214 | case Qt::Key_C: mInputState |= FREECAM_DOWN; break; 215 | case Qt::Key_Q: mInputState |= FREECAM_SLOW_DOWN; break; 216 | case Qt::Key_E: mInputState |= FREECAM_SPEED_UP; break; 217 | case Qt::Key_T: printCameraMatrices(); break; 218 | case Qt::Key_U: mRoll = 0.0f; break; 219 | case Qt::Key_R: 220 | if(mInitialTransformSet) { 221 | clearMovementState(); 222 | resetTransform(mInitialTransform, false); 223 | } 224 | break; 225 | default: used = false; 226 | } 227 | } else { 228 | // Check for released keys. 229 | switch (event->key()) { 230 | case Qt::Key_W: mInputState &= ~FREECAM_FORWARD; break; 231 | case Qt::Key_S: mInputState &= ~FREECAM_BACKWARD; break; 232 | case Qt::Key_A: mInputState &= ~FREECAM_LEFT; break; 233 | case Qt::Key_D: mInputState &= ~FREECAM_RIGHT; break; 234 | case Qt::Key_Space: mInputState &= ~FREECAM_UP; break; 235 | case Qt::Key_C: mInputState &= ~FREECAM_DOWN; break; 236 | case Qt::Key_Q: mInputState &= ~FREECAM_SLOW_DOWN; break; 237 | case Qt::Key_E: mInputState &= ~FREECAM_SPEED_UP; break; 238 | default: used = false; 239 | } 240 | } 241 | } 242 | 243 | return used; 244 | } 245 | 246 | bool 247 | FreeCam::processMousePressEvent(QMouseEvent *event, int key) 248 | { 249 | mMouseMode = NONE; 250 | if (event->buttons() == Qt::LeftButton && 251 | event->modifiers() == Qt::NoModifier && key == NO_KEY) { 252 | mMouseMode = MOVE; 253 | } else if (event->buttons() == (Qt::LeftButton | Qt::RightButton) && 254 | event->modifiers() == Qt::AltModifier) { 255 | mMouseMode = ROLL; 256 | } 257 | 258 | if (mMouseMode != NONE) { 259 | mMouseX = event->x(); 260 | mMouseY = event->y(); 261 | mMouseDeltaX = mMouseDeltaY = 0; 262 | return true; 263 | } 264 | 265 | return false; 266 | } 267 | 268 | bool 269 | FreeCam::processMouseReleaseEvent(QMouseEvent *event) 270 | { 271 | if (event->button() == Qt::LeftButton) { 272 | mMouseMode = NONE; 273 | return true; 274 | } 275 | return false; 276 | } 277 | 278 | bool 279 | FreeCam::processMouseMoveEvent(QMouseEvent *event) 280 | { 281 | if (mMouseMode == MOVE || mMouseMode == ROLL) { 282 | mMouseDeltaX += (event->x() - mMouseX); 283 | mMouseDeltaY += (event->y() - mMouseY); 284 | mMouseX = event->x(); 285 | mMouseY = event->y(); 286 | return true; 287 | } 288 | return false; 289 | } 290 | 291 | void 292 | FreeCam::clearMovementState() 293 | { 294 | mVelocity = Vec3f(0.0f, 0.0f, 0.0f); 295 | mInputState = 0; 296 | mMouseMode = NONE; 297 | mMouseX = 0; 298 | mMouseY = 0; 299 | } 300 | 301 | void 302 | FreeCam::printCameraMatrices() const 303 | { 304 | Mat4f fullMat = makeMatrix(mYaw, mPitch, mRoll, mPosition); 305 | Mat4f zeroPitchMat = makeMatrix(mYaw, 0.0f, 0.0f, mPosition); 306 | 307 | printMatrix("Full matrix containing rotation and position.", fullMat); 308 | printMatrix("Matrix containing world xz rotation and position.", zeroPitchMat); 309 | } 310 | 311 | //---------------------------------------------------------------------------- 312 | 313 | } // namespace moonray_gui 314 | 315 | -------------------------------------------------------------------------------- /tsc/charter.md: -------------------------------------------------------------------------------- 1 | **Technical Charter (the “Charter”) for MoonRay** 2 | 3 | **[ \_ ] [ \_ ], 2023** 4 | 5 | 6 | This charter (the “Charter”) sets forth the responsibilities and procedures for technical contribution 7 |  to, and oversight of, the **MoonRay (aka OpenMoonRay)** project (the “Project”), 8 |  which has been established as **MoonRay** by DreamWorks Animation L.L.C. (“DreamWorks”). 9 |  All Contributors to the Project must comply with the terms of this Charter. 10 | 11 | **1. Mission and Scope of the Project** 12 | 13 |  a. The mission of the Project is to continue maintenance and development of an 14 |    open source project with the goals indicated in the “README” file within the 15 |    Project’s code repository. 16 | 17 |  b. The scope of the Project includes using existing MoonRay repositories to seed the 18 |    Project, and software development under an OSI-approved open source license 19 |    supporting the mission, including documentation, testing, integration and the 20 |    creation of other artifacts that aid the development, deployment, operation or 21 |    adoption of the open source software project. 22 | 23 | **2. Technical Steering Committee** 24 | 25 |  a. The Technical Steering Committee (the “TSC”) will be responsible for all 26 |    technical oversight of the open source Project. 27 | 28 |  b. The TSC voting members shall be as set forth within the “CONTRIBUTING” file 29 |    within the Project’s code repository. A voting member of the TSC may nominate 30 |    a successor in the event that such voting member decides to leave the TSC, and 31 |    the TSC, including the departing member, shall confirm or reject such nomination 32 |    by a vote. In the event that the departing member’s nomination for successor is 33 |    rejected by vote of the TSC, the departing member shall be entitled to continue 34 |    nominating successors until one such successor is confirmed by vote of the TSC. 35 |    If the departing member fails or is unable to nominate a successor, the TSC may 36 |    nominate one on the departing member’s behalf. The TSC may also determine if 37 |    and how additional voting members of the TSC are chosen, and any such 38 |    approach will be documented in the CONTRIBUTING file, provided that such 39 |    approach does not conflict with this Charter. Any meetings of the TSC are 40 |    intended to be open to the public, except where there is a reasonable need for 41 |    privacy, and can be conducted electronically, via teleconference, or in person. 42 | 43 |  c. TSC projects generally will involve Contributors and Committers. The TSC may 44 |    adopt or modify roles so long as the roles are documented in the 45 |    CONTRIBUTING file. Unless otherwise documented: 46 | 47 |     i. Contributors include anyone in the technical community that contributes 48 |        code, documentation, or other technical artifacts to the Project; 49 | 50 |     ii. Committers are Contributors who have earned the ability to modify 51 |        (“commit”) source code, documentation or other technical artifacts in the 52 |        Project’s repository; and 53 | 54 |     iii. A Contributor may become a Committer by a majority approval of 55 |        the existing Committers or at the discretion of the TSC. A Committer may be 56 |        removed by a majority approval of the other existing Committers, or at the 57 |        the discretion of the TSC. 58 | 59 |  d. Participation in the Project through becoming a Contributor and Committer is 60 |    open to anyone so long as they abide by the terms of this Charter. 61 | 62 |  e. The TSC may (1) establish workflow procedures for the submission, approval, 63 |    and closure/archiving of projects, (2) set requirements for the promotion of 64 |    Contributors to Committer status, as applicable, and (3) amend, adjust, refine 65 |    and/or eliminate the roles of Contributors, and Committers, and create new roles, 66 |    and publicly document any TSC roles, as it sees fit. 67 | 68 |  f. The TSC may elect a TSC Chair, who will preside over meetings of the TSC and 69 |    will serve until his or her resignation or replacement by the TSC. 70 | 71 |  g. Responsibilities: The TSC will be responsible for all aspects of oversight relating 72 |    to the Project, which may include: 73 | 74 |     i. coordinating the technical direction of the Project; 75 | 76 |     ii. approving project or system proposals (including, but not limited 77 |        to, incubation, deprecation, and changes to a sub-project’s scope); 78 | 79 |     ii. organizing sub-projects and removing projects; 80 | 81 |     iv. creating sub-committees or working groups to focus on cross-project 82 |        technical issues and requirements; 83 | 84 |     v. appointing representatives to work with other open source or open 85 |        standards communities; 86 | 87 |     vi. establishing community norms, workflows, issuing releases, and security 88 |        issue reporting policies; 89 | 90 |     vii. approving and implementing policies and processes for contributing (to be 91 |        published in the CONTRIBUTING file) and coordinating with the Series 92 |        Manager or its designee to resolve matters or concerns that may arise as 93 |        set forth in Section 7 of this Charter; 94 | 95 |     viii. discussions, seeking consensus, and where necessary, voting on technical 96 |        matters relating to the code base that affect multiple projects; 97 | 98 |     ix. coordinating any marketing, events, or communications regarding the 99 |        Project; and 100 | 101 |     x. ensuring that the Project (x) is developed and maintained in a professional 102 |        manner consistent with maintaining a cohesive community, while also 103 |        maintaining the goodwill and esteem of [entity] and other partner 104 |        organizations in the open source software community, and (y) respects the 105 |        rights of all trademark owners, including any branding and trademark 106 |        usage guidelines. 107 | 108 | **3. TSC Voting** 109 | 110 |  a. Although the Project aims to operate as a consensus-based community, if any 111 |    TSC decision requires a vote to move the Project forward, the voting members of 112 |    the TSC will vote on a one vote per voting member basis. 113 | 114 |  b. At least fifty percent of all voting members of the TSC must be present at a TSC 115 |    meeting in order to establish a quorum. The TSC may continue to meet if a 116 |    quorum is not met, but will be prevented from making any decisions at any such 117 |    meeting. 118 | 119 |  c. Except as provided in Section 7.c. and 8.a, decisions by vote at a meeting require 120 |    a majority vote of those in attendance, provided a quorum is met. Decisions made 121 |    by electronic vote without a meeting require a majority vote of all voting 122 |    members of the TSC. 123 | 124 |  d. In the event a vote cannot be resolved by the TSC, any voting member of the TSC 125 |    may refer the matter to the Series Manager or its designee for assistance in 126 |    reaching a resolution. 127 | 128 | **4. Compliance with Policies** 129 | 130 |  a. The TSC may adopt a code of conduct (“CoC”) for the Project, which is subject to 131 |    approval by DreamWorks. Contributors to the Project will comply with the CoC. 132 | 133 |  b. When amending or adopting any policy applicable to the Project, the TSC will 134 |    publish such policy, as to be amended or adopted, on its web site at least 30 days 135 |    prior to such policy taking effect. 136 | 137 |  c. All participants must allow open participation from any individual or organization 138 |    meeting the requirements for contributing under this Charter and any policies 139 |    adopted for all participants by the TSC, regardless of competitive interests. Put 140 |    another way, the Project community must not seek to exclude any participant 141 |    based on any criteria, requirement, or reason other than those that are reasonable 142 |    and applied on a non-discriminatory basis to all participants in the Project 143 |    community. 144 | 145 |  d. The Project will operate in a transparent, open, collaborative, and ethical manner 146 |    at all times. The output of all Project discussions, proposals, timelines, decisions, 147 |    and status should be made open and easily visible to all. 148 | 149 |  d. Any violations of the CoC or the requirements above should be reported to 150 |    DreamWorks. 151 | 152 | **5. Project Assets** 153 | 154 |  a. DreamWorks will hold title to all trade or service marks used by the Project 155 |    (“Project Trademarks”), whether based on common law or registered rights. Any 156 |    new Project Trademarks will be transferred and assigned to DreamWorks to hold 157 |    on behalf of the Project. Any use of any Project Trademarks by participants in the 158 |    Project will be in accordance with the license from DreamWorks and inure to the 159 |    benefit of DreamWorks. 160 | 161 |  b. The Project will develop and own all Project GitHub and social media accounts, 162 |    and domain name registrations created by the Project community. 163 | 164 | 165 | **6. Intellectual Property Policy** 166 | 167 |  a. Participants acknowledge that the copyright in all new contributions will be 168 |    retained by the copyright holder as independent works of authorship and that no 169 |    contributor or copyright holder will be required to assign copyrights to any other 170 |    party. 171 | 172 |  b. Except as described in Section 6.c., all code contributions to the Project are 173 |    subject to the following: 174 | 175 |     i. All new inbound code contributions to the Project must be made using an 176 |        OSI-approved open source license specified for the Project within the 177 |        “LICENSE” file within the Project’s code repository (the “Project 178 |        License”). 179 | 180 |     ii. All new inbound code contributions must: 181 | 182 |       1. Be made pursuant to a binding Project Contribution License 183 |          Agreement (the “CLA”) available on the Project’s web site; and 184 | 185 |       2. Be accompanied by a Developer Certificate of Origin 186 |          (http://developercertificate.org) sign-off in the source code system 187 |          that is submitted through a TSC-approved contribution process 188 |          which will bind the authorized contributor and, if not self- 189 |          employed, their employer to the applicable license; 190 | 191 |     iii. All outbound code will be made available under the Project License 192 | 193 |     iv. Documentation will be received and made available by the Project under 194 |         the Creative Commons Attribution 4.0 International License (available at 195 |         http://creativecommons.org/licenses/by/4.0/). 196 | 197 |     v. The TSC may seek to integrate and contribute back elements of the 198 |         Project to other open source projects (“Upstream Projects”). In such cases, the 199 |         contributed elements will conform to all license requirements of the 200 |         Upstream Projects, including dependencies, leveraged by those elements. 201 |         Upstream Project code contributions not stored within the Project’s main 202 |         code repository will comply with the contribution process and license 203 |         terms for the applicable Upstream Project. 204 | 205 |  c. If an alternative inbound or outbound license is required for compliance with the 206 |    license for a leveraged open source project or is otherwise required to achieve the 207 |    Project’s mission, the TSC (but only with DreamWorks’ approval) may approve 208 |    the use of an alternative license for specific inbound or outbound contributions on 209 |    an exception basis. Any exceptions must be limited in scope to what is required 210 |    for such a purpose. To request an exception, please describe the contribution, the 211 |    alternative open source license(s), and the justification for using an alternative 212 |    open source license for the Project. 213 | 214 |  c. Contributed files should contain license information, such as SPDX short form 215 |    identifiers, indicating the open source license or licenses pertaining to the file. 216 | 217 | **7. Amendments** 218 | 219 |  a. This charter may be amended by a two-thirds vote of the entire TSC, subject to 220 |    reasonable approval by DreamWorks Animation L.L.C. 221 | -------------------------------------------------------------------------------- /cmd/moonray_gui/ColorManager.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023-2024 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | 5 | #include "ColorManager.h" 6 | #include "RenderViewport.h" 7 | 8 | #if !defined(DISABLE_OCIO) 9 | #include 10 | #endif 11 | #include 12 | 13 | #include 14 | 15 | constexpr double DEFAULT_GAMMA = 2.2; 16 | 17 | constexpr double SRGB_LUMA_COEF1 = 0.2126; 18 | constexpr double SRGB_LUMA_COEF2 = 0.7152; 19 | constexpr double SRGB_LUMA_COEF3 = 0.0722; 20 | 21 | namespace moonray_gui { 22 | using namespace scene_rdl2::math; 23 | using namespace scene_rdl2::fb_util; 24 | 25 | /// -------------------------------- OCIO Helpers -------------------------------------- 26 | 27 | #if !defined(DISABLE_OCIO) 28 | OCIO::ExposureContrastTransformRcPtr createExposureTransform(double exposure) 29 | { 30 | OCIO::ExposureContrastTransformRcPtr exposureTransform = OCIO::ExposureContrastTransform::Create(); 31 | exposureTransform->setStyle(OCIO::EXPOSURE_CONTRAST_LINEAR); 32 | exposureTransform->setExposure(exposure); 33 | return exposureTransform; 34 | } 35 | 36 | OCIO::ExponentTransformRcPtr createGammaTransform(double gamma) 37 | { 38 | OCIO::ExponentTransformRcPtr gammaTransform = OCIO::ExponentTransform::Create(); 39 | const double gammaExponent = 1.0 / gamma; 40 | MNRY_ASSERT(gamma > 0.0f); 41 | const double gammaArr[4] = { gammaExponent, gammaExponent, gammaExponent, gammaExponent }; 42 | gammaTransform->setValue(gammaArr); 43 | return gammaTransform; 44 | } 45 | 46 | OCIO::RangeTransformRcPtr createClampTransform(double minClamp, double maxClamp) 47 | { 48 | OCIO::RangeTransformRcPtr rangeTransform = OCIO::RangeTransform::Create(); 49 | rangeTransform->setStyle(OCIO::RANGE_CLAMP); 50 | rangeTransform->setMinInValue(minClamp); 51 | rangeTransform->setMaxInValue(maxClamp); 52 | rangeTransform->setMinOutValue(minClamp); 53 | rangeTransform->setMaxOutValue(maxClamp); 54 | return rangeTransform; 55 | } 56 | 57 | OCIO::MatrixTransformRcPtr createChannelViewTransform(const OCIO::ConstConfigRcPtr& config, std::array& channel) 58 | { 59 | // Channel swizzling 60 | double lumaCoef1 = scene_rdl2::util::getenv("LUMA_COEF1", SRGB_LUMA_COEF1); 61 | double lumaCoef2 = scene_rdl2::util::getenv("LUMA_COEF2", SRGB_LUMA_COEF2); 62 | double lumaCoef3 = scene_rdl2::util::getenv("LUMA_COEF3", SRGB_LUMA_COEF3); 63 | double lumacoef[3] = { lumaCoef1, lumaCoef2, lumaCoef3 }; 64 | double m44[16]; 65 | double offset[4]; 66 | 67 | OCIO::MatrixTransform::View(m44, offset, channel.data(), lumacoef); 68 | OCIO::MatrixTransformRcPtr swizzle = OCIO::MatrixTransform::Create(); 69 | swizzle->setMatrix(m44); 70 | swizzle->setOffset(offset); 71 | return swizzle; 72 | } 73 | 74 | OCIO::DisplayViewTransformRcPtr createDisplayViewTransform(const OCIO::ConstConfigRcPtr config, bool configIsRaw) 75 | { 76 | // Lookup the display ColorSpace 77 | const char* display = config->getDefaultDisplay(); 78 | const char* view = config->getDefaultView(display); 79 | 80 | // Create a DisplayViewTransform, and set the input and display ColorSpaces 81 | OCIO::DisplayViewTransformRcPtr transform = OCIO::DisplayViewTransform::Create(); 82 | transform->setSrc( configIsRaw ? OCIO::ROLE_DEFAULT : OCIO::ROLE_SCENE_LINEAR ); 83 | transform->setDisplay( display ); 84 | transform->setView( view ); 85 | return transform; 86 | } 87 | #endif 88 | 89 | /// ------------------------------------ General Helpers ----------------------------------------------- 90 | 91 | void setHotChannel(DebugMode mode, std::array& channelHot) 92 | { 93 | switch (mode) { 94 | case RGB: 95 | channelHot = {1, 1, 1, 1}; 96 | break; 97 | case RED: 98 | channelHot = {1, 0, 0, 0}; 99 | break; 100 | case GREEN: 101 | channelHot = {0, 1, 0, 0}; 102 | break; 103 | case BLUE: 104 | channelHot = {0, 0, 1, 0}; 105 | break; 106 | case ALPHA: 107 | channelHot = {0, 0, 0, 1}; 108 | break; 109 | case LUMINANCE: 110 | channelHot = {1, 1, 1, 0}; 111 | break; 112 | default: 113 | channelHot = {1, 1, 1, 1}; 114 | break; 115 | } 116 | } 117 | 118 | void floatBufferToRgb888(const float* src, int w, int h, Rgb888Buffer* dst, int channels) 119 | { 120 | for (int y = 0; y < h; ++y) { 121 | for (int x = 0; x < w; ++x) { 122 | ByteColor col8; 123 | col8.r = static_cast(src[y * w * channels + x * channels] * 255); 124 | col8.g = static_cast(src[y * w * channels + x * channels + 1] * 255); 125 | col8.b = static_cast(src[y * w * channels + x * channels + 2] * 255); 126 | dst->setPixel(x, y, col8); 127 | } 128 | } 129 | } 130 | 131 | /// --------------------------------- ColorManager Class ------------------------------------------- 132 | 133 | #if !defined(DISABLE_OCIO) 134 | ColorManager::ColorManager() : mConfig(nullptr), mConfigIsRaw(false) {}; 135 | #else 136 | ColorManager::ColorManager() = default; 137 | #endif 138 | ColorManager::~ColorManager() = default; 139 | 140 | /// Applies color render transform to a render buffer, performing the following ops (not necessarily in order): 141 | /// - transforms from scene-referred space to display-referred 142 | /// - applies other pre-defined transforms, like exposure and user gamma 143 | /// - allows for swizzling between debug modes 144 | /// - quantizes the data to 8-bit (RGB888) 145 | /// 146 | /// This function decides whether to use OpenColorIO for these operations, or the code path we were using prior 147 | /// 148 | void ColorManager::applyCRT(const MainWindow* mainWindow, 149 | const bool useOCIO, 150 | int renderOutput, 151 | const RenderBuffer& renderBuffer, 152 | const VariablePixelBuffer& renderOutputBuffer, 153 | Rgb888Buffer* displayBuffer, 154 | PixelBufferUtilOptions options, 155 | bool parallel) const 156 | { 157 | const double exposure = mainWindow->getRenderViewport()->getExposure(); 158 | const double gamma = mainWindow->getRenderViewport()->getGamma(); 159 | const DebugMode mode = mainWindow->getRenderViewport()->getDebugMode(); 160 | 161 | #if !defined(DISABLE_OCIO) 162 | 163 | OCIO::ConstCPUProcessorRcPtr cpuProcessor; 164 | if (useOCIO) { 165 | configureOcio(exposure, gamma, mode, cpuProcessor); 166 | } 167 | 168 | int numChannels = renderOutputBuffer.getFormat() == VariablePixelBuffer::FLOAT4 || renderOutput < 0 ? 4 : 169 | renderOutputBuffer.getFormat() == VariablePixelBuffer::FLOAT3 ? 3 : -1; 170 | 171 | if (useOCIO && mode != RGB_NORMALIZED && mode != NUM_SAMPLES && numChannels >= 3) { 172 | // OCIO code path for RenderBuffer 173 | if (renderOutput < 0) { 174 | 175 | Vec4* bufConst = const_cast*>(renderBuffer.getData()); 176 | applyCRT_Ocio(cpuProcessor, 177 | bufConst, 178 | displayBuffer, 179 | renderBuffer.getWidth(), 180 | renderBuffer.getHeight(), 4); 181 | } 182 | // OCIO code path for VariablePixelBuffer 183 | else if ((renderOutputBuffer.getFormat() == VariablePixelBuffer::FLOAT3 || 184 | renderOutputBuffer.getFormat() == VariablePixelBuffer::FLOAT4)) { 185 | 186 | unsigned char* bufConst = const_cast(renderOutputBuffer.getData()); 187 | applyCRT_Ocio(cpuProcessor, 188 | bufConst, 189 | displayBuffer, 190 | renderOutputBuffer.getWidth(), 191 | renderOutputBuffer.getHeight(), 192 | numChannels); 193 | } 194 | } 195 | // Applies the old color management code if useOCIO is false, mode is RGB_NORMALIZED or NUM_SAMPLES OR 196 | // if VariablePixelBuffer number of channels < 3 (I haven't found a case where this is true) 197 | else { 198 | applyCRT_Legacy(renderBuffer, 199 | renderOutputBuffer, 200 | displayBuffer, 201 | renderOutput, 202 | exposure, 203 | gamma, 204 | mode, 205 | options, 206 | parallel); 207 | } 208 | // if < OCIO v2, just use old code path 209 | #else 210 | applyCRT_Legacy(renderBuffer, 211 | renderOutputBuffer, 212 | displayBuffer, 213 | renderOutput, 214 | exposure, 215 | gamma, 216 | mode, 217 | options, 218 | parallel); 219 | #endif 220 | } 221 | 222 | void ColorManager::setupConfig() 223 | { 224 | #if !defined(DISABLE_OCIO) 225 | try { 226 | mConfig = OCIO::Config::CreateFromEnv(); 227 | mConfigIsRaw = scene_rdl2::util::getenv("OCIO", "").empty(); 228 | } 229 | catch (const OCIO::Exception & exception) { 230 | std::cerr << "OpenColorIO Error: Invalid filepath provided. A default color profile will be used. " << 231 | "\nMore Info: " << exception.what() << "\n"; 232 | mConfig = OCIO::Config::CreateRaw(); 233 | mConfigIsRaw = true; 234 | } 235 | #endif 236 | } 237 | 238 | #if !defined(DISABLE_OCIO) 239 | 240 | /// Configures the OpenColorIO transforms to be applied in the following order: 241 | /// 1. Exposure 242 | /// 2. User-defined gamma 243 | /// 3. Swizzle between debug modes 244 | /// 4. Transforms from scene-referred to display-referred by either: 245 | /// - Applying the default display/view provided in an ocio config file 246 | /// - Applying a 1/2.2 default gamma if no config file provided 247 | /// 5. Clamp [0,1] 248 | /// 249 | void ColorManager::configureOcio(double exposure, 250 | double gamma, 251 | DebugMode mode, 252 | OCIO::ConstCPUProcessorRcPtr& cpuProcessor) const 253 | { 254 | OCIO::ExposureContrastTransformRcPtr exposureTransform = createExposureTransform(exposure); 255 | OCIO::ExponentTransformRcPtr userGammaTransform = createGammaTransform(gamma); 256 | OCIO::RangeTransformRcPtr rangeTransform = createClampTransform(0.0, 1.0); 257 | 258 | // Configure the color channel toggle transform 259 | std::array channelHot; 260 | setHotChannel(mode, channelHot); 261 | OCIO::MatrixTransformRcPtr channelViewTransform = createChannelViewTransform(mConfig, channelHot); 262 | // Create a DisplayViewTransform, and set the input and display ColorSpaces 263 | OCIO::DisplayViewTransformRcPtr transform = createDisplayViewTransform(mConfig, mConfigIsRaw); 264 | 265 | // Create group transform to wrap all of the transforms 266 | OCIO::GroupTransformRcPtr groupTransform = OCIO::GroupTransform::Create(); 267 | groupTransform->appendTransform(exposureTransform); 268 | groupTransform->appendTransform(userGammaTransform); 269 | groupTransform->appendTransform(channelViewTransform); 270 | groupTransform->appendTransform(transform); 271 | if (mConfigIsRaw) { 272 | OCIO::ExponentTransformRcPtr gammaTransform = createGammaTransform(DEFAULT_GAMMA); 273 | groupTransform->appendTransform(gammaTransform); 274 | } 275 | groupTransform->appendTransform(rangeTransform); 276 | 277 | // Create processor for view transform 278 | OCIO::ConstProcessorRcPtr processor = mConfig->getProcessor(groupTransform); 279 | cpuProcessor = processor->getDefaultCPUProcessor(); 280 | } 281 | 282 | void ColorManager::applyCRT_Ocio(const OCIO::ConstCPUProcessorRcPtr& cpuProcessor, 283 | void* srcData, 284 | Rgb888Buffer* destBuf, 285 | int w, int h, 286 | int channels) 287 | { 288 | // Apply color transforms 289 | OCIO::PackedImageDesc img(srcData, w, h, channels); 290 | cpuProcessor->apply(img); 291 | float* imgOutput = reinterpret_cast(img.getData()); 292 | 293 | destBuf->init(w, h); 294 | floatBufferToRgb888(imgOutput, w, h, destBuf, channels); 295 | } 296 | #endif 297 | 298 | void ColorManager::applyCRT_Legacy(const RenderBuffer& renderBuffer, 299 | const VariablePixelBuffer& renderOutputBuffer, 300 | Rgb888Buffer* displayBuffer, 301 | int renderOutput, 302 | double exposure, 303 | double gamma, 304 | DebugMode mode, 305 | PixelBufferUtilOptions options, 306 | bool parallel) 307 | { 308 | switch (mode) { 309 | case RGB: 310 | // Convert the frame to RGB888 on the current thread (this is called from 311 | // the main rendering thread). This ensures that the renderer doesn't 312 | // start writing into this RenderBuffer before we've finished prepping it 313 | // for display. 314 | options |= scene_rdl2::fb_util::PIXEL_BUFFER_UTIL_OPTIONS_APPLY_GAMMA; 315 | renderOutput < 0? 316 | scene_rdl2::fb_util::gammaAndQuantizeTo8bit(*displayBuffer, renderBuffer, options, exposure, gamma) : 317 | scene_rdl2::fb_util::gammaAndQuantizeTo8bit(*displayBuffer, renderOutputBuffer, options, exposure, gamma); 318 | break; 319 | 320 | case RED: 321 | renderOutput < 0? 322 | scene_rdl2::fb_util::extractRedChannel(*displayBuffer, renderBuffer, options, exposure, gamma) : 323 | scene_rdl2::fb_util::extractRedChannel(*displayBuffer, renderOutputBuffer, options, exposure, gamma); 324 | break; 325 | 326 | case GREEN: 327 | renderOutput < 0? 328 | scene_rdl2::fb_util::extractGreenChannel(*displayBuffer, renderBuffer, options, exposure, gamma) : 329 | scene_rdl2::fb_util::extractGreenChannel(*displayBuffer, renderOutputBuffer, options, exposure, gamma); 330 | break; 331 | 332 | case BLUE: 333 | renderOutput < 0? 334 | scene_rdl2::fb_util::extractBlueChannel(*displayBuffer, renderBuffer, options, exposure, gamma) : 335 | scene_rdl2::fb_util::extractBlueChannel(*displayBuffer, renderOutputBuffer, options, exposure, gamma); 336 | break; 337 | 338 | case ALPHA: 339 | renderOutput < 0? 340 | scene_rdl2::fb_util::extractAlphaChannel(*displayBuffer, renderBuffer, options, exposure, gamma) : 341 | scene_rdl2::fb_util::extractAlphaChannel(*displayBuffer, renderOutputBuffer, options); 342 | break; 343 | 344 | case LUMINANCE: 345 | renderOutput < 0? 346 | scene_rdl2::fb_util::extractLuminance(*displayBuffer, renderBuffer, options, exposure, gamma) : 347 | scene_rdl2::fb_util::extractLuminance(*displayBuffer, renderOutputBuffer, options, exposure, gamma); 348 | break; 349 | 350 | case RGB_NORMALIZED: 351 | options |= scene_rdl2::fb_util::PIXEL_BUFFER_UTIL_OPTIONS_APPLY_GAMMA; 352 | options |= scene_rdl2::fb_util::PIXEL_BUFFER_UTIL_OPTIONS_NORMALIZE; 353 | renderOutput < 0? 354 | scene_rdl2::fb_util::gammaAndQuantizeTo8bit(*displayBuffer, renderBuffer, options, exposure, gamma): 355 | scene_rdl2::fb_util::gammaAndQuantizeTo8bit(*displayBuffer, renderOutputBuffer, options, exposure, gamma); 356 | break; 357 | 358 | case NUM_SAMPLES: 359 | scene_rdl2::fb_util::visualizeSamplesPerPixel(*displayBuffer, renderOutputBuffer.getFloatBuffer(), parallel); 360 | break; 361 | 362 | default: 363 | MNRY_ASSERT(0); 364 | } 365 | } 366 | 367 | } // end moonray_gui namespace 368 | 369 | -------------------------------------------------------------------------------- /cmd/moonray_gui/OrbitCam.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023-2024 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include "OrbitCam.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace scene_rdl2::math; 12 | 13 | namespace { 14 | 15 | // Print out matrix in lua format so it can be pasted into an rdla file. 16 | void printMatrix(const char *comment, const Mat4f &m) 17 | { 18 | std::cout << "-- " << comment << "\n" 19 | << "[\"node xform\"] = Mat4(" 20 | << m.vx.x << ", " << m.vx.y << ", " << m.vx.z << ", " << m.vx.w << ", " 21 | << m.vy.x << ", " << m.vy.y << ", " << m.vy.z << ", " << m.vy.w << ", " 22 | << m.vz.x << ", " << m.vz.y << ", " << m.vz.z << ", " << m.vz.w << ", " 23 | << m.vw.x << ", " << m.vw.y << ", " << m.vw.z << ", " << m.vw.w << "),\n" 24 | << std::endl; 25 | } 26 | 27 | } // end of anon namespace 28 | 29 | namespace moonray_gui { 30 | 31 | enum 32 | { 33 | ORBIT_FORWARD = 0x0001, 34 | ORBIT_BACKWARD = 0x0002, 35 | ORBIT_LEFT = 0x0004, 36 | ORBIT_RIGHT = 0x0008, 37 | ORBIT_UP = 0x0010, 38 | ORBIT_DOWN = 0x0020, 39 | ORBIT_SLOW_DOWN = 0x0040, 40 | ORBIT_SPEED_UP = 0x0080 41 | }; 42 | 43 | // orbit camera (taken from embree sample code) 44 | // This camera is in world space 45 | struct Camera { 46 | 47 | Camera () : 48 | position(0.0f, 0.0f, -3.0f), 49 | viewDir(normalize(-position)), 50 | up(0.0f, 1.0f, 0.0f), 51 | focusDistance(1.0f) {} 52 | 53 | Xform3f camera2world () const { 54 | // Warning: this needs to be double precision. If we use single then 55 | // there is slight imprecision introduced when computing the cross products 56 | // when orthonormalizing the vectors. 57 | // This normally wouldn't be a problem, but this camera2world matrix 58 | // gets fed into OrbitCam::resetTransform() when the scene is reloaded. 59 | // OrbitCam::resetTransform() then sets the vectors used for camera2world, 60 | // but those came from camera2world. Thus camera2world is used to set 61 | // itself, and the old value might be identical to the new if the user 62 | // hasn't manipulated the camera. 63 | // The imprecision from the single-precision cross products causes 64 | // a slight difference in camera2world when there should be no change 65 | // at all when camera2world hasn't changed. This causes nondeterminism 66 | // between successive renders in moonray_gui as this has a slight effect 67 | // on the ray directions each time. 68 | Vec3d vz = -viewDir; 69 | Vec3d vx = normalize(cross(Vec3d(up), vz)); 70 | Vec3d vy = normalize(cross(vz, vx)); 71 | return Xform3f( 72 | static_cast(vx.x), static_cast(vx.y), 73 | static_cast(vx.z), static_cast(vy.x), 74 | static_cast(vy.y), static_cast(vy.z), 75 | static_cast(vz.x), static_cast(vz.y), 76 | static_cast(vz.z), position.x, position.y, position.z); 77 | } 78 | 79 | Xform3f world2camera () const { return camera2world().inverse();} 80 | 81 | Vec3f world2camera(const Vec3f& p) const { return transformPoint(world2camera(),p);} 82 | Vec3f camera2world(const Vec3f& p) const { return transformPoint(camera2world(),p);} 83 | 84 | void move (float dx, float dy, float dz) 85 | { 86 | float moveSpeed = 0.03f; 87 | dx *= -moveSpeed; 88 | dy *= moveSpeed; 89 | dz *= moveSpeed; 90 | Xform3f xfm = camera2world(); 91 | Vec3f ds = transformVector(xfm, Vec3f(dx,dy,dz)); 92 | position += ds; 93 | } 94 | 95 | void rotate (float dtheta, float dphi) 96 | { 97 | float rotateSpeed = 0.005f; 98 | // in camera local space, viewDir is always (0, 0, -1) 99 | // and its spherical coordinate is always (PI, 0) 100 | float theta = sPi - dtheta * rotateSpeed; 101 | float phi = -dphi * rotateSpeed; 102 | 103 | float cosPhi, sinPhi; 104 | sincos(phi, &sinPhi, &cosPhi); 105 | float cosTheta, sinTheta; 106 | sincos(theta, &sinTheta, &cosTheta); 107 | 108 | float x = cosPhi*sinTheta; 109 | float y = sinPhi; 110 | float z = cosPhi*cosTheta; 111 | 112 | viewDir = transformVector(camera2world(), Vec3f(x, y, z)); 113 | } 114 | 115 | void rotateOrbit (float dtheta, float dphi) 116 | { 117 | bool currentlyValid = false; 118 | if (scene_rdl2::math::abs(dot(up, viewDir)) < 0.999f) { 119 | currentlyValid = true; 120 | } 121 | 122 | float rotateSpeed = 0.005f; 123 | // in camera local space, viewDir is always (0, 0, -1) 124 | // and its spherical coordinate is always (PI, 0) 125 | float theta = sPi - dtheta * rotateSpeed; 126 | float phi = -dphi * rotateSpeed; 127 | 128 | float cosPhi, sinPhi; 129 | sincos(phi, &sinPhi, &cosPhi); 130 | float cosTheta, sinTheta; 131 | sincos(theta, &sinTheta, &cosTheta); 132 | 133 | float x = cosPhi*sinTheta; 134 | float y = sinPhi; 135 | float z = cosPhi*cosTheta; 136 | 137 | Vec3f newViewDir = transformVector(camera2world(),Vec3f(x,y,z)); 138 | Vec3f newPosition = position + focusDistance * (viewDir - newViewDir); 139 | 140 | // Don't update 'position' if dir is near parallel with the up vector 141 | // unless the current state of 'position' is already invalid. 142 | if (scene_rdl2::math::abs(dot(up, newViewDir)) < 0.999f || !currentlyValid) { 143 | position = newPosition; 144 | viewDir = newViewDir; 145 | } 146 | } 147 | 148 | void dolly (float ds) 149 | { 150 | float dollySpeed = 0.005f; 151 | float k = scene_rdl2::math::pow((1.0f-dollySpeed), ds); 152 | Vec3f focusPoint = position + viewDir * focusDistance; 153 | position += focusDistance * (1-k) * viewDir; 154 | focusDistance = length(focusPoint - position); 155 | } 156 | 157 | void roll (float ds) 158 | { 159 | float rollSpeed = 0.005f; 160 | const Vec3f& axis = viewDir; 161 | up = transform3x3(Mat4f::rotate(Vec4f(axis[0], axis[1], axis[2], 0.0f), 162 | -ds * rollSpeed), up); 163 | } 164 | 165 | public: 166 | Vec3f position; //!< position of camera 167 | Vec3f viewDir; //!< lookat direction 168 | Vec3f up; //!< up vector 169 | float focusDistance; 170 | }; 171 | 172 | //---------------------------------------------------------------------------- 173 | 174 | OrbitCam::OrbitCam() : 175 | mRenderContext(nullptr), 176 | mCamera(new Camera), 177 | mSpeed(50.0f), 178 | mInputState(0), 179 | mMouseMode(NONE), 180 | mMouseX(-1), 181 | mMouseY(-1), 182 | mInitialTransformSet(false), 183 | mInitialFocusSet(false), 184 | mInitialFocusDistance(1.0f) 185 | { 186 | } 187 | 188 | OrbitCam::~OrbitCam() 189 | { 190 | delete mCamera; 191 | } 192 | 193 | void 194 | OrbitCam::setRenderContext(const moonray::rndr::RenderContext &context) 195 | { 196 | mRenderContext = &context; 197 | } 198 | 199 | Mat4f 200 | OrbitCam::resetTransform(const Mat4f &xform, bool makeDefault) 201 | { 202 | MNRY_ASSERT(mCamera); 203 | 204 | mCamera->position = asVec3(xform.vw); 205 | mCamera->viewDir = normalize(asVec3(-xform.vz)); 206 | mCamera->up = asVec3(xform.vy); 207 | mCamera->focusDistance = 1.0f; 208 | 209 | if (!mInitialTransformSet || makeDefault) { 210 | mInitialTransformSet = true; 211 | mInitialFocusSet = false; 212 | mInitialPosition = mCamera->position; 213 | mInitialViewDir = mCamera->viewDir; 214 | mInitialUp = mCamera->up; 215 | mInitialFocusDistance = mCamera->focusDistance; 216 | } 217 | 218 | return xform; 219 | } 220 | 221 | void 222 | OrbitCam::pickFocusPoint() 223 | { 224 | MNRY_ASSERT(mCamera); 225 | 226 | // Ensure the render context exists. 227 | // Key bindings can call this function before everything is fully ready. 228 | if (!mRenderContext) return; 229 | 230 | // Do this function only once every time we reset the default transform 231 | // Note: We can't do picking during resetTransform() because picking uses 232 | // the pbr Scene, which hasn't been initialized at that time. 233 | if (mInitialFocusSet) { 234 | return; 235 | } 236 | mInitialFocusSet = true; 237 | 238 | const scene_rdl2::math::HalfOpenViewport vp = mRenderContext->getRezedRegionWindow(); 239 | int width = int(vp.width()); 240 | int height = int(vp.height()); 241 | Vec3f focusPoint; 242 | if (pick(width / 2, height / 2, &focusPoint)) { 243 | Vec3f hitVec = focusPoint - mCamera->position; 244 | mCamera->viewDir = normalize(hitVec); 245 | mCamera->focusDistance = length(hitVec); 246 | } 247 | mInitialViewDir = mCamera->viewDir; 248 | mInitialFocusDistance = mCamera->focusDistance; 249 | } 250 | 251 | Mat4f 252 | OrbitCam::update(float dt) 253 | { 254 | float movement = mSpeed * dt; 255 | 256 | // Process keyboard input. 257 | if (mInputState & ORBIT_FORWARD) { 258 | mCamera->move(0.0f, 0.0f, -movement); 259 | } 260 | if (mInputState & ORBIT_BACKWARD) { 261 | mCamera->move(0.0f, 0.0f, movement); 262 | } 263 | if (mInputState & ORBIT_LEFT) { 264 | mCamera->move(movement, 0.0f, 0.0f); 265 | } 266 | if (mInputState & ORBIT_RIGHT) { 267 | mCamera->move(-movement, 0.0f, 0.0f); 268 | } 269 | if (mInputState & ORBIT_UP) { 270 | mCamera->move(0.0f, movement, 0.0f); 271 | } 272 | if (mInputState & ORBIT_DOWN) { 273 | mCamera->move(0.0f, -movement, 0.0f); 274 | } 275 | if (mInputState & ORBIT_SLOW_DOWN) { 276 | mSpeed += -mSpeed * dt; 277 | } 278 | if (mInputState & ORBIT_SPEED_UP) { 279 | mSpeed += mSpeed * dt; 280 | } 281 | 282 | return makeMatrix(*mCamera); 283 | } 284 | 285 | bool 286 | OrbitCam::processKeyboardEvent(QKeyEvent *event, bool pressed) 287 | { 288 | bool used = false; 289 | 290 | if (event->modifiers() == Qt::NoModifier) { 291 | 292 | used = true; 293 | 294 | if (pressed) { 295 | pickFocusPoint(); 296 | 297 | // Check for pressed keys. 298 | switch (event->key()) { 299 | case Qt::Key_W: mInputState |= ORBIT_FORWARD; break; 300 | case Qt::Key_S: mInputState |= ORBIT_BACKWARD; break; 301 | case Qt::Key_A: mInputState |= ORBIT_LEFT; break; 302 | case Qt::Key_D: mInputState |= ORBIT_RIGHT; break; 303 | case Qt::Key_Space: mInputState |= ORBIT_UP; break; 304 | case Qt::Key_C: mInputState |= ORBIT_DOWN; break; 305 | case Qt::Key_Q: mInputState |= ORBIT_SLOW_DOWN; break; 306 | case Qt::Key_E: mInputState |= ORBIT_SPEED_UP; break; 307 | case Qt::Key_F: recenterCamera(); break; 308 | case Qt::Key_T: printCameraMatrices(); break; 309 | case Qt::Key_U: mCamera->up = Vec3f(0.0f, 1.0f, 0.0f); break; 310 | case Qt::Key_R: 311 | if(mInitialTransformSet) { 312 | clearMovementState(); 313 | mCamera->position = mInitialPosition; 314 | mCamera->viewDir = mInitialViewDir; 315 | mCamera->up = mInitialUp; 316 | mCamera->focusDistance = mInitialFocusDistance; 317 | } 318 | break; 319 | default: used = false; 320 | } 321 | } else { 322 | // Check for released keys. 323 | switch (event->key()) { 324 | case Qt::Key_W: mInputState &= ~ORBIT_FORWARD; break; 325 | case Qt::Key_S: mInputState &= ~ORBIT_BACKWARD; break; 326 | case Qt::Key_A: mInputState &= ~ORBIT_LEFT; break; 327 | case Qt::Key_D: mInputState &= ~ORBIT_RIGHT; break; 328 | case Qt::Key_Space: mInputState &= ~ORBIT_UP; break; 329 | case Qt::Key_C: mInputState &= ~ORBIT_DOWN; break; 330 | case Qt::Key_Q: mInputState &= ~ORBIT_SLOW_DOWN; break; 331 | case Qt::Key_E: mInputState &= ~ORBIT_SPEED_UP; break; 332 | default: used = false; 333 | } 334 | } 335 | } 336 | 337 | return used; 338 | } 339 | 340 | bool 341 | OrbitCam::processMousePressEvent(QMouseEvent *event, int key) 342 | { 343 | pickFocusPoint(); 344 | 345 | mMouseMode = NONE; 346 | auto buttons = event->buttons(); 347 | auto modifiers = event->modifiers(); 348 | 349 | mMouseX = event->x(); 350 | mMouseY = event->y(); 351 | 352 | bool used = false; 353 | 354 | if (modifiers == Qt::AltModifier) { 355 | if (buttons == Qt::LeftButton) { 356 | mMouseMode = ORBIT; 357 | used = true; 358 | } else if (buttons == Qt::MiddleButton) { 359 | mMouseMode = PAN; 360 | used = true; 361 | } else if (buttons == Qt::RightButton) { 362 | mMouseMode = DOLLY; 363 | used = true; 364 | } else if (buttons == (Qt::LeftButton | Qt::RightButton)) { 365 | mMouseMode = ROLL; 366 | used = true; 367 | } 368 | } else if (modifiers == Qt::ControlModifier) { 369 | if (buttons == Qt::LeftButton) { 370 | mMouseMode = NONE; 371 | recenterCamera(); 372 | used = true; 373 | } 374 | } 375 | 376 | return used; 377 | } 378 | 379 | bool 380 | OrbitCam::processMouseMoveEvent(QMouseEvent *event) 381 | { 382 | if (mMouseX == -1 || mMouseY == -1) { 383 | return false; 384 | } 385 | 386 | int x = event->x(); 387 | int y = event->y(); 388 | float dClickX = float(x - mMouseX); 389 | float dClickY = float(y - mMouseY); 390 | mMouseX = x; 391 | mMouseY = y; 392 | 393 | switch (mMouseMode) { 394 | case ORBIT: mCamera->rotateOrbit(dClickX, dClickY); break; 395 | case PAN: mCamera->move(dClickX, dClickY, 0.0f); break; 396 | case DOLLY: mCamera->dolly(dClickX + dClickY); break; 397 | case ROLL: mCamera->roll(dClickX); break; 398 | case ROTATE_CAMERA: mCamera->rotate(dClickX, dClickY); break; 399 | default: return false; 400 | } 401 | 402 | return true; 403 | } 404 | 405 | void 406 | OrbitCam::clearMovementState() 407 | { 408 | mInputState = 0; 409 | mMouseMode = NONE; 410 | mMouseX = -1; 411 | mMouseY = -1; 412 | } 413 | 414 | void 415 | OrbitCam::recenterCamera() 416 | { 417 | if (mMouseX == -1 || mMouseY == -1) { 418 | return; 419 | } 420 | 421 | Vec3f newFocus; 422 | if (pick(mMouseX, mMouseY, &newFocus)) { 423 | Vec3f delta = newFocus - 424 | (mCamera->position + mCamera->viewDir * mCamera->focusDistance); 425 | mCamera->position += delta; 426 | mCamera->focusDistance = length(newFocus - mCamera->position); 427 | } 428 | 429 | // reset mouse positions so repeatedly pressing F does not result in 430 | // repeated recentering. 431 | mMouseX = mMouseY = -1; 432 | } 433 | 434 | bool 435 | OrbitCam::pick(int x, int y, Vec3f *hitPoint) const 436 | { 437 | MNRY_ASSERT(mRenderContext); 438 | 439 | // must use offset between center point of aperture window and center point 440 | // of region window so that the region window is centered on the pick point. 441 | const scene_rdl2::math::HalfOpenViewport avp = mRenderContext->getRezedApertureWindow(); 442 | const scene_rdl2::math::HalfOpenViewport rvp = mRenderContext->getRezedRegionWindow(); 443 | const int offsetX = (avp.max().x + avp.min().x) / 2 - (rvp.max().x + rvp.min().x) / 2; 444 | const int offsetY = (avp.max().y + avp.min().y) / 2 - (rvp.max().y + rvp.min().y) / 2; 445 | 446 | return mRenderContext->handlePickLocation(x + offsetX, y - offsetY, hitPoint); 447 | } 448 | 449 | Mat4f 450 | OrbitCam::makeMatrix(const Camera &camera) const 451 | { 452 | Xform3f c2w = camera.camera2world(); 453 | return Mat4f( Vec4f(c2w.l.vx.x, c2w.l.vx.y, c2w.l.vx.z, 0.0f), 454 | Vec4f(c2w.l.vy.x, c2w.l.vy.y, c2w.l.vy.z, 0.0f), 455 | Vec4f(c2w.l.vz.x, c2w.l.vz.y, c2w.l.vz.z, 0.0f), 456 | Vec4f(c2w.p.x, c2w.p.y, c2w.p.z, 1.0f) ); 457 | } 458 | 459 | void 460 | OrbitCam::printCameraMatrices() const 461 | { 462 | Mat4f fullMat = makeMatrix(*mCamera); 463 | printMatrix("Full matrix containing rotation and position.", fullMat); 464 | } 465 | 466 | //---------------------------------------------------------------------------- 467 | 468 | } // namespace moonray_gui 469 | 470 | -------------------------------------------------------------------------------- /cmd/moonray_gui/GlslBuffer.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023-2024 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | /// @file GlslBuffer.cc 5 | 6 | #include "GlslBuffer.h" 7 | 8 | #include 9 | 10 | #include 11 | 12 | // It's outside the scope of moonray to do the conversion into the binary format 13 | // we use, plus we want to avoid a run-time dependency on legacy folios. 14 | // 15 | // Alternate LUTs can however be passed into via the lutOverride parameter. 16 | // The LUTs are assumed to contain 64*64*64 * RGB float OpenGL compatible 17 | // volume texture data. 18 | // 19 | // The following program generates such data. For the default LUT, the .bin file 20 | // is compiled and linked into the lib via objcopy. It relies on the legacy 21 | // color render transform library which is part of the dwaimagebase folio. 22 | // 23 | // #include 24 | // const char *file = "/rel/folio/cs_legacy/cs_legacy-1.2.0-5/aux/ani/color_render_transform.cdf"; 25 | // const char *indx = "25"; 26 | // auto crt = color_rt_base::GlslColorRenderTransform::create(file, indx); 27 | // // write the tables, there should be 3: a 1D pre, 1D post, and 3D lut 28 | // for (int texId = 0; texId < crt->texCount(); ++texId) { 29 | // std::string filename = "moonray_rndr_gui_" + crt->samplerName(texId) + ".bin"; 30 | // std::ofstream fstr(filename); 31 | // size_t nFloats = 0; 32 | // switch(crt->texDims(texId)) { 33 | // case 1: 34 | // nFloats = crt->texSize(texId, 0); 35 | // break; 36 | // case 2: 37 | // MNRY_ASSERT(0 && "unexpected tex dims"); 38 | // break; 39 | // case 3: 40 | // nFloats = crt->texSize(texId, 0) * crt->texSize(texId, 1) * crt->texSize(texId, 2) * 3; 41 | // break; 42 | // default: 43 | // MNRY_ASSERT(0 && "unexpected tex dims"); 44 | // break; 45 | // } 46 | // size_t nBytes = nFloats * sizeof(float); 47 | // fstr.write(reinterpret_cast(crt->texData(texId).get()), nBytes); 48 | // } 49 | 50 | // objcopy generates these symbols 51 | #if defined(__APPLE__) 52 | #define _3dlut_3d _binary_moonray_rndr_gui_tex_3dlut_3d 53 | #define _3dlut_post1d _binary_moonray_rndr_gui_tex_3dlut_post1d 54 | #define _3dlut_pre1d _binary_moonray_rndr_gui_tex_3dlut_pre1d 55 | #else 56 | #define _3dlut_3d _binary_cmd_moonray_gui_data_moonray_rndr_gui_tex_3dlut_3d_bin_start 57 | #define _3dlut_post1d _binary_cmd_moonray_gui_data_moonray_rndr_gui_tex_3dlut_post1d_bin_start 58 | #define _3dlut_pre1d _binary_cmd_moonray_gui_data_moonray_rndr_gui_tex_3dlut_pre1d_bin_start 59 | #endif 60 | 61 | extern "C" float _3dlut_3d; 62 | extern "C" float _3dlut_post1d; 63 | extern "C" float _3dlut_pre1d; 64 | 65 | namespace { 66 | // LINEAR RGB32F -> Color render transform -> gamma 67 | // const char *file = "/rel/folio/cs_legacy/cs_legacy-1.2.0-5/aux/ani/color_render_transform.cdf"; 68 | // const char *indx = "25"; 69 | // auto crt = color_rt_base::GlslColorRenderTransform::create(file, indx); 70 | // crt->setEntryPoint("apply_transform"); 71 | // crt->setSrcSpace(color_rt_base::GlslColorRenderTransform::LINEAR); 72 | // crt->setDstSpace(color_rt_base::GlslColorRenderTransform::GAMMA_2_2); 73 | // crt->header() 74 | const char *sCrtGammaProgram = R"( 75 | #version 330 core 76 | uniform sampler1D tex_3dlut_pre1d; 77 | uniform sampler1D tex_3dlut_post1d; 78 | uniform sampler3D tex_3dlut_3d; 79 | uniform float exposure; 80 | uniform float gamma; 81 | 82 | vec4 oddPow_3dlut(const in vec4 x, const in vec4 y) 83 | { 84 | return vec4(pow(abs(x), y) * sign(x)); 85 | } 86 | vec3 oddPow_3dlut(const in vec3 x, const in vec3 y) 87 | { 88 | return vec3(pow(abs(x), y) * sign(x)); 89 | } 90 | 91 | vec2 frac(const in vec2 v) 92 | { 93 | return vec2(v.x - floor(v.x), v.y - floor(v.y)); 94 | } 95 | 96 | vec3 apply_dither(const in vec3 srcColor, const in vec2 pos) 97 | { 98 | float dither_matrix_8x8[64] = float[]( 99 | 1.f/65.f, 49.f/65.f, 13.f/65.f, 61.f/65.f, 4.f/65.f, 52.f/65.f, 16.f/65.f, 64.f/65.f, 100 | 33.f/65.f, 17.f/65.f, 45.f/65.f, 29.f/65.f, 36.f/65.f, 20.f/65.f, 48.f/65.f, 32.f/65.f, 101 | 9.f/65.f, 57.f/65.f, 5.f/65.f, 53.f/65.f, 12.f/65.f, 60.f/65.f, 8.f/65.f, 56.f/65.f, 102 | 41.f/65.f, 25.f/65.f, 37.f/65.f, 21.f/65.f, 44.f/65.f, 28.f/65.f, 40.f/65.f, 24.f/65.f, 103 | 3.f/65.f, 51.f/65.f, 15.f/65.f, 63.f/65.f, 2.f/65.f, 50.f/65.f, 14.f/65.f, 62.f/65.f, 104 | 35.f/65.f, 19.f/65.f, 47.f/65.f, 31.f/65.f, 34.f/65.f, 18.f/65.f, 46.f/65.f, 30.f/65.f, 105 | 11.f/65.f, 59.f/65.f, 7.f/65.f, 55.f/65.f, 10.f/65.f, 58.f/65.f, 6.f/65.f, 54.f/65.f, 106 | 43.f/65.f, 27.f/65.f, 39.f/65.f, 23.f/65.f, 42.f/65.f, 26.f/65.f, 38.f/65.f, 22.f/65.f); 107 | 108 | vec2 idx = frac(pos.xy * 0.125f) * 8.f; 109 | int y = int(floor(idx.y)); 110 | int x = int(floor(idx.x)); 111 | float dither_val = dither_matrix_8x8[y * 8 + x]; 112 | return floor(srcColor * 255.f + vec3(dither_val)) * (1.f / 255.f); 113 | } 114 | 115 | vec4 apply_transform(const in vec4 srcColor, const in vec2 pos) 116 | { 117 | // Application of film lut: transforms linear color values into a space visible in theaters 118 | // Transform is implemented with 1-D pre-lookup array, followed by a 64x64x64 lookup, followed by a 1-D post-lookup 119 | 120 | // Setup scale + offset terms for the texture lookups 121 | vec4 scalePre = vec4(0.311342); 122 | vec4 offsetPre = vec4(0.000488281); 123 | vec4 scale3d = vec4(0.984375); 124 | vec4 offset3d = vec4(0.0078125); 125 | vec4 scalePost = vec4(0.999023); 126 | vec4 offsetPost = vec4(0.000488281); 127 | 128 | // Setup to sample the preLUT in gamma 2.2 space 129 | // srcColor is assumed to be in linear space. 130 | vec4 fragColor = oddPow_3dlut(srcColor, vec4(.454545454545)); 131 | 132 | // Scale and offset for the preLUT 133 | vec4 newTexCoord3d = fragColor * scalePre + offsetPre; 134 | newTexCoord3d = clamp(newTexCoord3d, 0.0, 1.0); 135 | 136 | // Apply preLUT 137 | fragColor.r = texture( tex_3dlut_pre1d, newTexCoord3d.r).r; 138 | fragColor.g = texture( tex_3dlut_pre1d, newTexCoord3d.g).r; 139 | fragColor.b = texture( tex_3dlut_pre1d, newTexCoord3d.b).r; 140 | 141 | // Scale and offset for the 3d LUT 142 | newTexCoord3d = fragColor * scale3d + offset3d; 143 | newTexCoord3d = clamp(newTexCoord3d, 0.0, 1.0); 144 | 145 | // Apply 3d LUT 146 | fragColor.rgb = texture( tex_3dlut_3d, newTexCoord3d.rgb).rgb; 147 | 148 | // Scale and offset for the postLUT 149 | newTexCoord3d = fragColor * scalePost + offsetPost; 150 | newTexCoord3d = clamp(newTexCoord3d, 0.0, 1.0); 151 | 152 | // Apply postLUT 153 | fragColor.r = texture( tex_3dlut_post1d, newTexCoord3d.r).r; 154 | fragColor.g = texture( tex_3dlut_post1d, newTexCoord3d.g).r; 155 | fragColor.b = texture( tex_3dlut_post1d, newTexCoord3d.b).r; 156 | 157 | // Apply exposure 158 | float gain = pow(2.0, exposure); 159 | fragColor.r *= gain; 160 | fragColor.g *= gain; 161 | fragColor.b *= gain; 162 | 163 | // Output in gamma 2.2 space 164 | // Conversion to gamma2.2 space is necessary for the monitor response to a linear 165 | // increase to result in a linear increase in perceived brightness. 166 | fragColor.rgb = oddPow_3dlut(fragColor.rgb, vec3(.454545454545)); 167 | 168 | // Apply user gamma 169 | fragColor.r = pow(fragColor.r, 1.0 / gamma); 170 | fragColor.g = pow(fragColor.g, 1.0 / gamma); 171 | fragColor.b = pow(fragColor.b, 1.0 / gamma); 172 | 173 | // Apply dithering: palletize the results into 8-bit values 174 | fragColor.rgb = apply_dither(fragColor.rgb, pos); 175 | 176 | return fragColor; 177 | } 178 | in vec2 uv; 179 | out vec3 color; 180 | 181 | uniform sampler2D textureSampler; 182 | uniform int channel; 183 | uniform int width; 184 | uniform int height; 185 | 186 | void main() { 187 | vec4 t = texture(textureSampler, uv); 188 | vec2 pos; 189 | pos.x = uv.x * (width - 1); 190 | pos.y = uv.y * (height - 1); 191 | vec4 res = apply_transform(t, pos); 192 | if (channel == 0) { 193 | color.rgb = res.rgb; 194 | } else if (channel == 1) { 195 | color.r = res.r; 196 | color.g = res.r; 197 | color.b = res.r; 198 | } else if (channel == 2) { 199 | color.r = res.g; 200 | color.g = res.g; 201 | color.b = res.g; 202 | } else if (channel == 3) { 203 | color.r = res.b; 204 | color.g = res.b; 205 | color.b = res.b; 206 | } 207 | } 208 | )"; 209 | 210 | static const GLuint INVALID_HANDLE = 0xFFFFFFFF; 211 | 212 | } // anonymous namespace 213 | 214 | namespace moonray_gui { 215 | 216 | GlslBuffer::GlslBuffer(int width, int height, const float *lutOverride): 217 | mWidth(width), 218 | mHeight(height), 219 | mPixelBuffer(width, height), 220 | mVertexBuffer(INVALID_HANDLE), 221 | mUvBuffer(INVALID_HANDLE), 222 | mTexture(-1), 223 | mVertexShaderID(INVALID_HANDLE), 224 | mProgram(INVALID_HANDLE), 225 | mChannel(INVALID_HANDLE), 226 | mExposure(0.f), 227 | mGamma(1.f), 228 | mLutOverride(lutOverride) 229 | { 230 | // all our programs require the same vertex shader, 231 | // so go ahead and define that now 232 | mPixelBuffer.makeCurrent(); 233 | 234 | // define our full screen quad in screen space 235 | // 4 verts, 3 floats per vert 236 | const GLfloat quad[12] = { -1.f, -1.f, 0.f, 237 | 1.f, -1.f, 0.f, 238 | 1.f, 1.f, 0.f, 239 | -1.f, 1.f, 0.f }; 240 | 241 | GLuint vertexArrayID; 242 | glGenVertexArrays(1, &vertexArrayID); 243 | glBindVertexArray(vertexArrayID); 244 | glGenBuffers(1, &mVertexBuffer); 245 | glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); 246 | glBufferData(GL_ARRAY_BUFFER, sizeof(quad), quad, GL_STATIC_DRAW); 247 | 248 | // assign uvs to our quad 249 | const GLfloat quadUV[8] = { 0.f, 0.f, 250 | 1.f, 0.f, 251 | 1.f, 1.f, 252 | 0.f, 1.f }; 253 | GLuint uvArrayID; 254 | glGenVertexArrays(1, &uvArrayID); 255 | glBindVertexArray(uvArrayID); 256 | glGenBuffers(1, &mUvBuffer); 257 | glBindBuffer(GL_ARRAY_BUFFER, mUvBuffer); 258 | glBufferData(GL_ARRAY_BUFFER, sizeof(quadUV), quadUV, GL_STATIC_DRAW); 259 | 260 | // compile the vertex shader 261 | const char *vCode = R"( 262 | #version 330 core 263 | layout(location = 0) in vec3 vertexPos; 264 | layout(location = 1) in vec2 vertexUV; 265 | out vec2 uv; 266 | void main() { 267 | gl_Position.xyz = vertexPos; 268 | gl_Position.w = 1.0; 269 | uv = vertexUV; 270 | } 271 | )"; 272 | 273 | mVertexShaderID = glCreateShader(GL_VERTEX_SHADER); 274 | glShaderSource(mVertexShaderID, 1, &vCode, nullptr); 275 | glCompileShader(mVertexShaderID); 276 | GLint vResult; 277 | glGetShaderiv(mVertexShaderID, GL_COMPILE_STATUS, &vResult); 278 | MNRY_ASSERT(vResult); 279 | 280 | glDisable(GL_DEPTH_TEST); 281 | 282 | mPixelBuffer.doneCurrent(); 283 | } 284 | 285 | GlslBuffer::~GlslBuffer() 286 | { 287 | // cleanup vertex shader 288 | // TODO: does this happen automatically when the context is destroyed? 289 | glDeleteShader(mVertexShaderID); 290 | } 291 | 292 | // LINEAR RGBA -> CRT -> GAMMA -> RGB 293 | void 294 | GlslBuffer::makeCrtGammaProgram() 295 | { 296 | mPixelBuffer.makeCurrent(); 297 | 298 | // cleanup any existing program 299 | if (mProgram != INVALID_HANDLE) { 300 | glDeleteProgram(mProgram); 301 | mProgram = INVALID_HANDLE; 302 | } 303 | 304 | // compile the fragment shader 305 | GLuint fShaderID = glCreateShader(GL_FRAGMENT_SHADER); 306 | glShaderSource(fShaderID, 1, &sCrtGammaProgram, nullptr); 307 | glCompileShader(fShaderID); 308 | GLint fResult; 309 | glGetShaderiv(fShaderID, GL_COMPILE_STATUS, &fResult); 310 | 311 | if (!fResult) { 312 | int infoLength; 313 | glGetShaderiv(fShaderID, GL_INFO_LOG_LENGTH, &infoLength); 314 | std::vector message(infoLength + 1); 315 | glGetShaderInfoLog(fShaderID, infoLength, nullptr, &message[0]); 316 | std::cerr << &message[0] << '\n'; 317 | } 318 | 319 | // link the program 320 | mProgram = glCreateProgram(); 321 | glAttachShader(mProgram, mVertexShaderID); 322 | glAttachShader(mProgram, fShaderID); 323 | glLinkProgram(mProgram); 324 | GLint pResult; 325 | glGetProgramiv(mProgram, GL_LINK_STATUS, &pResult); 326 | MNRY_ASSERT(pResult); 327 | 328 | // cleanup - a little 329 | glDetachShader(mProgram, mVertexShaderID); 330 | glDetachShader(mProgram, fShaderID); 331 | // we no longer need the fragment shader, we'll reuse the 332 | // vertex shader if we run a different program 333 | glDeleteShader(fShaderID); 334 | 335 | // assign texture maps 336 | // need to use the program for the remainder of our setup 337 | glUseProgram(mProgram); 338 | 339 | // texture mapping 340 | // define the luts as textures used by the crt program 341 | // pre 1d table 342 | { 343 | int textureUnit = 1; // 0 = main image, 1 = pre1d, 2 = post1d, 3 = 3dlut 344 | glActiveTexture(GL_TEXTURE0 + textureUnit); 345 | GLint textureID = glGetUniformLocation(mProgram, "tex_3dlut_pre1d"); 346 | MNRY_ASSERT(textureID != -1); 347 | glUniform1i(textureID, textureUnit); 348 | glBindTexture(GL_TEXTURE_1D, textureID); 349 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 350 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 351 | const float *data = &_3dlut_pre1d; 352 | const size_t size = 1024; 353 | glTexImage1D(GL_TEXTURE_1D, 0, GL_R32F, size, 0, GL_RED, GL_FLOAT, data); 354 | } 355 | 356 | // post 1d table 357 | { 358 | int textureUnit = 2; // 0 = main image, 1 = pre1d, 2 = post1d, 3 = 3dlut 359 | glActiveTexture(GL_TEXTURE0 + textureUnit); 360 | GLint textureID = glGetUniformLocation(mProgram, "tex_3dlut_post1d"); 361 | MNRY_ASSERT(textureID != -1); 362 | glUniform1i(textureID, textureUnit); 363 | glBindTexture(GL_TEXTURE_1D, textureID); 364 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 365 | glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 366 | const float *data = &_3dlut_post1d; 367 | const size_t size = 1024; 368 | glTexImage1D(GL_TEXTURE_1D, 0, GL_R32F, size, 0, GL_RED, GL_FLOAT, data); 369 | } 370 | 371 | // 3d lut 372 | { 373 | int textureUnit = 3; // 0 = main image, 1 = pre1d, 2 = post1d, 3 = 3dlut 374 | glActiveTexture(GL_TEXTURE0 + textureUnit); 375 | GLint textureID = glGetUniformLocation(mProgram, "tex_3dlut_3d"); 376 | MNRY_ASSERT(textureID != -1); 377 | glUniform1i(textureID, textureUnit); 378 | glBindTexture(GL_TEXTURE_3D, textureID); 379 | glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 380 | glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 381 | const float *data = mLutOverride ? mLutOverride : 382 | &_3dlut_3d; 383 | const size_t size = 64; 384 | glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB32F, size, size, size, 0, GL_RGB, 385 | GL_FLOAT, data); 386 | } 387 | 388 | // define our main texture - its the render buffer 389 | glActiveTexture(GL_TEXTURE0); 390 | mTexture = glGetUniformLocation(mProgram, "textureSampler"); 391 | glUniform1i(mTexture, 0); // 0 = main image, 1 = pre1d, 2 = post1d, 3 = 3dlut 392 | glBindTexture(GL_TEXTURE_2D, mTexture); 393 | // since the texture aligns perfectly with the window dimensions, 394 | // we can use GL_NEAREST for the filter 395 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 396 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 397 | 398 | // bind the display channel 399 | mChannel = glGetUniformLocation(mProgram, "channel"); 400 | glUniform1i(mChannel, 0); // rgb display is the default 401 | 402 | // bind the exposure factor 403 | mExposure = glGetUniformLocation(mProgram, "exposure"); 404 | glUniform1f(mExposure, 0.f); // 0 is the default 405 | 406 | // bind gamma correction 407 | mGamma = glGetUniformLocation(mProgram, "gamma"); 408 | glUniform1f(mGamma, 1.f); // no gamma correction is the default 409 | 410 | // provide width and height for dithering 411 | // note: that these values are constant since we do not support resizing 412 | GLint var = glGetUniformLocation(mProgram, "width"); 413 | glUniform1i(var, mWidth); 414 | var = glGetUniformLocation(mProgram, "height"); 415 | glUniform1i(var, mHeight); 416 | 417 | 418 | mPixelBuffer.doneCurrent(); 419 | } 420 | 421 | void 422 | GlslBuffer::render(const FrameBuffer &frame, FrameType frameType, DebugMode mode, 423 | float exposure, float gamma) 424 | { 425 | mPixelBuffer.makeCurrent(); 426 | 427 | glEnableVertexAttribArray(0); 428 | glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer); 429 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); 430 | glEnableVertexAttribArray(1); 431 | glBindBuffer(GL_ARRAY_BUFFER, mUvBuffer); 432 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0); 433 | 434 | // set debug mode 435 | MNRY_ASSERT(mode == RGB || mode == RED || mode == GREEN || mode == BLUE); 436 | glUniform1i(mChannel, mode); 437 | 438 | // set exposure 439 | glUniform1f(mExposure, exposure); 440 | 441 | // set gamma 442 | glUniform1f(mGamma, gamma); 443 | 444 | // send image to gpu 445 | glActiveTexture(GL_TEXTURE0); 446 | glBindTexture(GL_TEXTURE_2D, mTexture); 447 | switch (frameType) { 448 | case FRAME_TYPE_IS_RGB8: 449 | MNRY_ASSERT(0 && "8 bit texture unsupported"); 450 | break; 451 | case FRAME_TYPE_IS_XYZ32: 452 | { 453 | const fb_util::Float3Buffer *buf = frame.xyz32; 454 | MNRY_ASSERT(mWidth == int(buf->getWidth()) && 455 | mHeight == int(buf->getHeight())); 456 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, mWidth, mHeight, 0, GL_RGB, GL_FLOAT, 457 | buf->getData()); 458 | } 459 | break; 460 | case FRAME_TYPE_IS_XYZW32: 461 | { 462 | const fb_util::Float4Buffer *buf = frame.xyzw32; 463 | MNRY_ASSERT(mWidth == int(buf->getWidth()) && 464 | mHeight == int(buf->getHeight())); 465 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, mWidth, mHeight, 0, GL_RGBA, GL_FLOAT, 466 | buf->getData()); 467 | } 468 | break; 469 | } 470 | 471 | glUseProgram(mProgram); 472 | glDrawArrays(GL_QUADS, 0, 4); 473 | 474 | glDisableVertexAttribArray(0); 475 | glDisableVertexAttribArray(1); 476 | 477 | mPixelBuffer.doneCurrent(); 478 | } 479 | 480 | QImage 481 | GlslBuffer::asImage() const 482 | { 483 | return mPixelBuffer.toImage(); 484 | } 485 | 486 | } // namespace moonray_gui 487 | 488 | -------------------------------------------------------------------------------- /cmd/moonray_gui/PathVisualizerGui.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2025 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include "ColorPicker.h" 5 | #include "PathVisualizerGui.h" 6 | #include "RenderViewport.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | namespace moonray_gui { 21 | 22 | /// ----------------------- Defaults ------------------------------ 23 | 24 | #define PIXEL_X_MIN 0 25 | #define PIXEL_X_MAX 10000 26 | 27 | #define PIXEL_Y_MIN 0 28 | #define PIXEL_Y_MAX 10000 29 | 30 | #define PIXEL_SAMPLES_MIN 0 31 | #define PIXEL_SAMPLES_MAX 12 32 | 33 | #define BSDF_SAMPLES_MIN 0 34 | #define BSDF_SAMPLES_MAX 12 35 | 36 | #define LIGHT_SAMPLES_MIN 0 37 | #define LIGHT_SAMPLES_MAX 12 38 | 39 | #define MAX_DEPTH_MIN 0 40 | #define MAX_DEPTH_MAX 50 41 | 42 | #define LINE_WIDTH_INTERVAL 1 43 | #define LINE_WIDTH_MIN 1 44 | #define LINE_WIDTH_MAX 8 45 | 46 | /// --------------------------- Helpers ------------------------------ 47 | 48 | QColor convertToQColor(const scene_rdl2::math::Color& color) 49 | { 50 | return QColor(color.r * 255, color.g * 255, color.b * 255); 51 | } 52 | 53 | scene_rdl2::math::Color convertFromQColor(const QColor& color) 54 | { 55 | return scene_rdl2::math::Color(color.red() / 255.f, color.green() / 255.f, color.blue() / 255.f); 56 | } 57 | 58 | /// ------------------------------------------------------------------ 59 | 60 | PathVisualizerGui::PathVisualizerGui(QWidget* parent, moonray::rndr::PathVisualizerManager* manager) : 61 | QWidget(parent), 62 | mPathVisualizerManager(manager), 63 | mCurrentRow(0) 64 | { 65 | // Set up the layout 66 | QGridLayout* layout = new QGridLayout; 67 | layout->setContentsMargins(5, 5, 5, 5); 68 | 69 | // Set up the title 70 | QLabel* title = new QLabel("Path Visualizer", this); 71 | title->setStyleSheet("QLabel { font-size: 25px; border-bottom: 1px solid white; margin-bottom:5px; }"); 72 | layout->addWidget(title, mCurrentRow++, 0, 1, 2); 73 | 74 | // Setup the rest of the path visualizer GUI 75 | setupOnBtn(layout); 76 | setupPixelUI(layout); 77 | setupSamplingUI(layout); 78 | setupDepthUI(layout); 79 | setupVisibilityUI(layout); 80 | setupStyleUI(layout); 81 | 82 | // Setup stylesheet 83 | QFile styleFile(":/PathVisualizerGui.qss"); 84 | 85 | if (styleFile.open(QFile::ReadOnly | QFile::Text)) { 86 | QTextStream ts(&styleFile); 87 | QString styleSheet = ts.readAll(); 88 | this->setStyleSheet(styleSheet); 89 | } 90 | 91 | // Set the layout for this widget 92 | setLayout(layout); 93 | } 94 | 95 | PathVisualizerGui::~PathVisualizerGui() {} 96 | 97 | void PathVisualizerGui::setupOnBtn(QGridLayout* layout) 98 | { 99 | mOnBtn = new QPushButton("Turn On", this); 100 | mOnBtn->setProperty("class", "button"); 101 | connect(mOnBtn, SIGNAL(clicked()), this, SLOT(slot_togglePathVisualizer())); 102 | 103 | mOnBtn->setCursor(Qt::PointingHandCursor); 104 | 105 | layout->addWidget(mOnBtn, mCurrentRow++, 0, 1, 3); 106 | } 107 | 108 | void PathVisualizerGui::setupPixelUI(QGridLayout* layout) 109 | { 110 | QLabel* pixelTitle = new QLabel("Pixel: ", this); 111 | mPixelXSpinBox = new QSpinBox(this); 112 | mPixelYSpinBox = new QSpinBox(this); 113 | mPixelXSpinBox->setRange(PIXEL_X_MIN, PIXEL_X_MAX); 114 | mPixelYSpinBox->setRange(PIXEL_Y_MIN, PIXEL_Y_MAX); 115 | mPixelXSpinBox->setSingleStep(1); 116 | mPixelYSpinBox->setSingleStep(1); 117 | mPixelXSpinBox->setSuffix(" px"); 118 | mPixelYSpinBox->setSuffix(" py"); 119 | 120 | const uint32_t pixelXDefault = mPathVisualizerManager->getPixelX(); 121 | const uint32_t pixelYDefault = mPathVisualizerManager->getPixelY(); 122 | mPixelXSpinBox->setValue(pixelXDefault); 123 | mPixelYSpinBox->setValue(pixelYDefault); 124 | // only process the input once the user is done typing and presses enter 125 | mPixelXSpinBox->setKeyboardTracking(false); 126 | mPixelYSpinBox->setKeyboardTracking(false); 127 | connect(mPixelXSpinBox, SIGNAL(valueChanged(int)), this, SLOT(slot_processPixelXValue(int))); 128 | connect(mPixelYSpinBox, SIGNAL(valueChanged(int)), this, SLOT(slot_processPixelYValue(int))); 129 | 130 | layout->addWidget(pixelTitle, mCurrentRow, 0, 1, 1); 131 | layout->addWidget(mPixelXSpinBox, mCurrentRow, 1, 1, 1); 132 | layout->addWidget(mPixelYSpinBox, mCurrentRow++, 2, 1, 1); 133 | } 134 | 135 | void PathVisualizerGui::setupSamplingUI(QGridLayout* layout) 136 | { 137 | QLabel* samplingTitle = new QLabel("Sampling Settings", this); 138 | samplingTitle->setProperty("class", "header"); 139 | 140 | /// ---------------------------------- Use Scene Samples Checkbox -------------------------------------------------- 141 | 142 | QCheckBox* useSceneSamples = new QCheckBox("Use Scene Sampling Settings", this); 143 | useSceneSamples->setCheckState(mPathVisualizerManager->getUseSceneSamples() ? Qt::Checked : Qt::Unchecked); 144 | useSceneSamples->setCursor(Qt::PointingHandCursor); 145 | 146 | connect(useSceneSamples, SIGNAL(stateChanged(int)), this, SLOT(slot_processUseSceneSamples(int))); 147 | 148 | /// ---------------------------------- Sampling Spin Boxes --------------------------------------------------------- 149 | 150 | QLabel* pixelSamplesTitle = new QLabel("Pixel Samples:", this); 151 | QLabel* lightSamplesTitle = new QLabel("Light Samples:", this); 152 | QLabel* bsdfSamplesTitle = new QLabel("Bsdf Samples:", this); 153 | 154 | mPixelSamples = new QSpinBox(this); 155 | mLightSamples = new QSpinBox(this); 156 | mBsdfSamples = new QSpinBox(this); 157 | 158 | mPixelSamples->setRange(PIXEL_SAMPLES_MIN, PIXEL_SAMPLES_MAX); 159 | mLightSamples->setRange(LIGHT_SAMPLES_MIN, LIGHT_SAMPLES_MAX); 160 | mBsdfSamples->setRange(BSDF_SAMPLES_MIN, BSDF_SAMPLES_MAX); 161 | 162 | mPixelSamples->setValue(mPathVisualizerManager->getPixelSamples()); 163 | mLightSamples->setValue(mPathVisualizerManager->getLightSamples()); 164 | mBsdfSamples->setValue(mPathVisualizerManager->getBsdfSamples()); 165 | 166 | connect(mPixelSamples, SIGNAL(valueChanged(int)), this, SLOT(slot_processPixelSamples(int))); 167 | connect(mLightSamples, SIGNAL(valueChanged(int)), this, SLOT(slot_processLightSamples(int))); 168 | connect(mBsdfSamples, SIGNAL(valueChanged(int)), this, SLOT(slot_processBsdfSamples(int))); 169 | 170 | /// ---------------------------------------------------------------------------------------------------------------- 171 | 172 | layout->addWidget(samplingTitle, mCurrentRow++, 0, 1, 3); 173 | layout->addWidget(useSceneSamples, mCurrentRow++, 0, 1, 3); 174 | 175 | layout->addWidget(pixelSamplesTitle, mCurrentRow, 0, 1, 2); 176 | layout->addWidget(mPixelSamples, mCurrentRow++, 2, 1, 1); 177 | 178 | layout->addWidget(lightSamplesTitle, mCurrentRow, 0, 1, 2); 179 | layout->addWidget(mLightSamples, mCurrentRow++, 2, 1, 1); 180 | 181 | layout->addWidget(bsdfSamplesTitle, mCurrentRow, 0, 1, 2); 182 | layout->addWidget(mBsdfSamples, mCurrentRow++, 2, 1, 1); 183 | } 184 | 185 | void PathVisualizerGui::setupDepthUI(QGridLayout* layout) 186 | { 187 | QLabel* depthTitle = new QLabel("Max Depth", this); 188 | depthTitle->setProperty("class", "header"); 189 | 190 | QSpinBox* maxDepth = new QSpinBox(this); 191 | maxDepth->setValue(mPathVisualizerManager->getMaxDepth()); 192 | maxDepth->setRange(MAX_DEPTH_MIN, MAX_DEPTH_MAX); 193 | maxDepth->setKeyboardTracking(false); 194 | connect(maxDepth, SIGNAL(valueChanged(int)), this, SLOT(slot_processMaxDepth(int))); 195 | 196 | layout->addWidget(depthTitle, mCurrentRow++, 0, 1, 3); 197 | layout->addWidget(maxDepth, mCurrentRow++, 0, 1, 1); 198 | } 199 | 200 | void PathVisualizerGui::setupVisibilityUI(QGridLayout* layout) 201 | { 202 | QLabel* visibilityTitle = new QLabel("Visibility Toggles", this); 203 | visibilityTitle->setProperty("class", "header"); 204 | 205 | QCheckBox* specularRaysOn = new QCheckBox("Specular rays", this); 206 | QCheckBox* diffuseRaysOn = new QCheckBox("Diffuse rays", this); 207 | QCheckBox* bsdfSamplesOn = new QCheckBox("Bsdf samples", this); 208 | QCheckBox* lightSamplesOn = new QCheckBox("Light samples", this); 209 | 210 | specularRaysOn->setCheckState(mPathVisualizerManager->getSpecularRaysFlag() ? Qt::Checked : Qt::Unchecked); 211 | diffuseRaysOn->setCheckState(mPathVisualizerManager->getDiffuseRaysFlag() ? Qt::Checked : Qt::Unchecked); 212 | bsdfSamplesOn->setCheckState(mPathVisualizerManager->getBsdfSamplesFlag() ? Qt::Checked : Qt::Unchecked); 213 | lightSamplesOn->setCheckState(mPathVisualizerManager->getLightSamplesFlag() ? Qt::Checked : Qt::Unchecked); 214 | 215 | specularRaysOn->setCursor(Qt::PointingHandCursor); 216 | diffuseRaysOn->setCursor(Qt::PointingHandCursor); 217 | bsdfSamplesOn->setCursor(Qt::PointingHandCursor); 218 | lightSamplesOn->setCursor(Qt::PointingHandCursor); 219 | 220 | /// NOTE: stateChanged will be deprecated starting with Qt v6.9 221 | connect(specularRaysOn, SIGNAL(stateChanged(int)), this, SLOT(slot_processSpecularRayFlag(int))); 222 | connect(diffuseRaysOn, SIGNAL(stateChanged(int)), this, SLOT(slot_processDiffuseRayFlag(int))); 223 | connect(bsdfSamplesOn, SIGNAL(stateChanged(int)), this, SLOT(slot_processBsdfSampleFlag(int))); 224 | connect(lightSamplesOn, SIGNAL(stateChanged(int)), this, SLOT(slot_processLightSampleFlag(int))); 225 | 226 | layout->addWidget(visibilityTitle, mCurrentRow++, 0, 1, 3); 227 | layout->addWidget(specularRaysOn, mCurrentRow++, 0, 1, 3); 228 | layout->addWidget(diffuseRaysOn, mCurrentRow++, 0, 1, 3); 229 | layout->addWidget(bsdfSamplesOn, mCurrentRow++, 0, 1, 3); 230 | layout->addWidget(lightSamplesOn, mCurrentRow++, 0, 1, 3); 231 | } 232 | 233 | void PathVisualizerGui::setupStyleUI(QGridLayout* layout) 234 | { 235 | // ----------------------------- Section title ------------------------------------------------- 236 | 237 | QLabel* styleTitle = new QLabel("Style Options", this); 238 | styleTitle->setProperty("class", "header"); 239 | 240 | // ----------------------------- Line Width Slider --------------------------------------------- 241 | 242 | QLabel* lineWidthLabel = new QLabel("Line Width: ", this); 243 | QSlider* lineWidthSlider = new QSlider(this); 244 | const uint32_t lineWidthDefault = mPathVisualizerManager->getLineWidth(); 245 | 246 | lineWidthSlider->setValue(lineWidthDefault); 247 | lineWidthSlider->setTickInterval(LINE_WIDTH_INTERVAL); 248 | lineWidthSlider->setMinimum(LINE_WIDTH_MIN); 249 | lineWidthSlider->setMaximum(LINE_WIDTH_MAX); 250 | lineWidthSlider->setOrientation(Qt::Horizontal); 251 | 252 | mLineWidthValue = new QLabel(QString::number(lineWidthDefault), this); 253 | mLineWidthValue->setAlignment(Qt::AlignHCenter); 254 | 255 | connect(lineWidthSlider, SIGNAL(sliderMoved(int)), this, SLOT(slot_setLineWidth(int))); 256 | 257 | // ----------------------------- Ray Color Pickers --------------------------------------------- 258 | 259 | const scene_rdl2::math::Color& diffuseRayColorDefault = mPathVisualizerManager->getDiffuseRayColor(); 260 | const scene_rdl2::math::Color& specularRayColorDefault = mPathVisualizerManager->getSpecularRayColor(); 261 | const scene_rdl2::math::Color& bsdfSampleColorDefault = mPathVisualizerManager->getBsdfSampleColor(); 262 | const scene_rdl2::math::Color& lightSampleColorDefault = mPathVisualizerManager->getLightSampleColor(); 263 | const scene_rdl2::math::Color& cameraRayColorDefault = mPathVisualizerManager->getCameraRayColor(); 264 | 265 | mDiffuseRayColorPicker = new ColorPicker(this, "Diffuse Ray Color: ", convertToQColor(diffuseRayColorDefault)); 266 | mSpecularRayColorPicker = new ColorPicker(this, "Specular Ray Color: ", convertToQColor(specularRayColorDefault)); 267 | mBsdfSampleColorPicker = new ColorPicker(this, "Bsdf Sample Ray Color: ", convertToQColor(bsdfSampleColorDefault)); 268 | mLightSampleColorPicker = new ColorPicker(this, "Light Sample Ray Color: ", convertToQColor(lightSampleColorDefault)); 269 | mCameraRayColorPicker = new ColorPicker(this, "Camera Ray Color: ", convertToQColor(cameraRayColorDefault)); 270 | 271 | connect(mDiffuseRayColorPicker, SIGNAL(sig_colorChanged(const QColor&)), 272 | this, SLOT(slot_setDiffuseRayColor(const QColor&))); 273 | connect(mSpecularRayColorPicker, SIGNAL(sig_colorChanged(const QColor&)), 274 | this, SLOT(slot_setSpecularRayColor(const QColor&))); 275 | connect(mBsdfSampleColorPicker, SIGNAL(sig_colorChanged(const QColor&)), 276 | this, SLOT(slot_setBsdfSampleColor(const QColor&))); 277 | connect(mLightSampleColorPicker, SIGNAL(sig_colorChanged(const QColor&)), 278 | this, SLOT(slot_setLightSampleColor(const QColor&))); 279 | connect(mCameraRayColorPicker, SIGNAL(sig_colorChanged(const QColor&)), 280 | this, SLOT(slot_setCameraRayColor(const QColor&))); 281 | 282 | // --------------------------------------------------------------------------------------------- 283 | 284 | layout->addWidget(styleTitle, mCurrentRow++, 0, 1, 3); 285 | layout->addWidget(lineWidthLabel, mCurrentRow++, 0, 1, 3); 286 | layout->addWidget(lineWidthSlider, mCurrentRow, 0, 1, 2); 287 | layout->addWidget(mLineWidthValue, mCurrentRow++, 2, 1, 1); 288 | layout->addWidget(mDiffuseRayColorPicker, mCurrentRow++, 0, 1, 3); 289 | layout->addWidget(mSpecularRayColorPicker, mCurrentRow++, 0, 1, 3); 290 | layout->addWidget(mBsdfSampleColorPicker, mCurrentRow++, 0, 1, 3); 291 | layout->addWidget(mLightSampleColorPicker, mCurrentRow++, 0, 1, 3); 292 | layout->addWidget(mCameraRayColorPicker, mCurrentRow++, 0, 1, 3); 293 | } 294 | 295 | /// -------------------------------------------- SLOTS ----------------------------------------------------------------- 296 | 297 | void 298 | PathVisualizerGui::slot_togglePathVisualizer() 299 | { 300 | if (mPathVisualizerManager->isOn()) { 301 | // If the visualizer is on, turn it off, set the button text 302 | // to "Turn on", and refresh the frame 303 | mPathVisualizerManager->turnOff(); 304 | mOnBtn->setText("Turn On"); 305 | emit sig_styleParamChanged(); 306 | } else { 307 | // If the visualizer is off, turn it on, start the visualization 308 | // process, and set the button text to "Turn off" 309 | mPathVisualizerManager->turnOn(); 310 | mOnBtn->setText("Turn Off"); 311 | } 312 | } 313 | 314 | void 315 | PathVisualizerGui::slot_processPixelXValue(const int x) 316 | { 317 | mPathVisualizerManager->setPixelX(static_cast(x)); 318 | mPathVisualizerManager->startSimulation(); 319 | } 320 | 321 | void 322 | PathVisualizerGui::slot_processPixelYValue(const int y) 323 | { 324 | mPathVisualizerManager->setPixelY(static_cast(y)); 325 | mPathVisualizerManager->startSimulation(); 326 | } 327 | 328 | void 329 | PathVisualizerGui::slot_processPixel(const int x, const int y) 330 | { 331 | mPathVisualizerManager->setPixelX(static_cast(x)); 332 | mPathVisualizerManager->setPixelY(static_cast(y)); 333 | 334 | // We must temporarily block signals before making changes to 335 | // avoid processing the pixel values twice 336 | mPixelXSpinBox->blockSignals(true); 337 | mPixelXSpinBox->setValue(x); 338 | mPixelXSpinBox->blockSignals(false); 339 | 340 | mPixelYSpinBox->blockSignals(true); 341 | mPixelYSpinBox->setValue(y); 342 | mPixelYSpinBox->blockSignals(false); 343 | 344 | mPathVisualizerManager->startSimulation(); 345 | } 346 | 347 | void 348 | PathVisualizerGui::slot_processUseSceneSamples(const int useSceneSamples) 349 | { 350 | mPathVisualizerManager->setUseSceneSamples(static_cast(useSceneSamples)); 351 | mPathVisualizerManager->startSimulation(); 352 | 353 | // Toggle on/off the other sampling settings 354 | const bool enableSamplingSettings = !static_cast(useSceneSamples); 355 | 356 | mPixelSamples->setEnabled(enableSamplingSettings); 357 | mLightSamples->setEnabled(enableSamplingSettings); 358 | mBsdfSamples->setEnabled(enableSamplingSettings); 359 | } 360 | 361 | void 362 | PathVisualizerGui::slot_processPixelSamples(const int samples) 363 | { 364 | mPathVisualizerManager->setPixelSamples(samples); 365 | mPathVisualizerManager->startSimulation(); 366 | } 367 | 368 | void 369 | PathVisualizerGui::slot_processLightSamples(const int samples) 370 | { 371 | mPathVisualizerManager->setLightSamples(samples); 372 | mPathVisualizerManager->startSimulation(); 373 | } 374 | 375 | void 376 | PathVisualizerGui::slot_processBsdfSamples(const int samples) 377 | { 378 | mPathVisualizerManager->setBsdfSamples(samples); 379 | mPathVisualizerManager->startSimulation(); 380 | } 381 | 382 | void 383 | PathVisualizerGui::slot_processMaxDepth(const int depth) 384 | { 385 | mPathVisualizerManager->setMaxDepth(depth); 386 | mPathVisualizerManager->startSimulation(); 387 | } 388 | 389 | void 390 | PathVisualizerGui::slot_processDiffuseRayFlag(const int flag) 391 | { 392 | mPathVisualizerManager->setDiffuseRaysFlag(static_cast(flag)); 393 | mPathVisualizerManager->startSimulation(); 394 | } 395 | 396 | void 397 | PathVisualizerGui::slot_processSpecularRayFlag(const int flag) 398 | { 399 | mPathVisualizerManager->setSpecularRaysFlag(static_cast(flag)); 400 | mPathVisualizerManager->startSimulation(); 401 | } 402 | 403 | void 404 | PathVisualizerGui::slot_processBsdfSampleFlag(const int flag) 405 | { 406 | mPathVisualizerManager->setBsdfSamplesFlag(static_cast(flag)); 407 | mPathVisualizerManager->startSimulation(); 408 | } 409 | 410 | void 411 | PathVisualizerGui::slot_processLightSampleFlag(const int flag) 412 | { 413 | mPathVisualizerManager->setLightSamplesFlag(static_cast(flag)); 414 | mPathVisualizerManager->startSimulation(); 415 | } 416 | 417 | void 418 | PathVisualizerGui::slot_setLineWidth(const int value) 419 | { 420 | mPathVisualizerManager->setLineWidth(value); 421 | mLineWidthValue->setText(QString::number(value)); 422 | emit sig_styleParamChanged(); 423 | } 424 | 425 | void 426 | PathVisualizerGui::slot_setDiffuseRayColor(const QColor& color) 427 | { 428 | mPathVisualizerManager->setDiffuseRayColor(convertFromQColor(color)); 429 | emit sig_styleParamChanged(); 430 | } 431 | 432 | void 433 | PathVisualizerGui::slot_setSpecularRayColor(const QColor& color) 434 | { 435 | mPathVisualizerManager->setSpecularRayColor(convertFromQColor(color)); 436 | emit sig_styleParamChanged(); 437 | } 438 | 439 | void 440 | PathVisualizerGui::slot_setBsdfSampleColor(const QColor& color) 441 | { 442 | mPathVisualizerManager->setBsdfSampleColor(convertFromQColor(color)); 443 | emit sig_styleParamChanged(); 444 | } 445 | 446 | void 447 | PathVisualizerGui::slot_setLightSampleColor(const QColor& color) 448 | { 449 | mPathVisualizerManager->setLightSampleColor(convertFromQColor(color)); 450 | emit sig_styleParamChanged(); 451 | } 452 | 453 | void 454 | PathVisualizerGui::slot_setCameraRayColor(const QColor& color) 455 | { 456 | mPathVisualizerManager->setCameraRayColor(convertFromQColor(color)); 457 | emit sig_styleParamChanged(); 458 | } 459 | 460 | } 461 | -------------------------------------------------------------------------------- /cmd/moonray_gui/moonray_gui.cc: -------------------------------------------------------------------------------- 1 | // Copyright 2023-2025 DreamWorks Animation LLC 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "RenderGui.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | namespace moonray_gui { 26 | 27 | class RaasGuiApplication : public moonray::RaasApplication 28 | { 29 | public: 30 | RaasGuiApplication(); 31 | ~RaasGuiApplication(); 32 | 33 | protected: 34 | void parseOptions(); 35 | void run(); 36 | 37 | private: 38 | 39 | static void* startRenderThread(void* me); 40 | // Parses an rdla file and adds references to other 41 | // rdla files (via the lua language) to the 42 | // referencedRdlaFiles set. Recursive. 43 | static void parseRdlaFileForReferences( 44 | const std::string &sceneFile, 45 | std::set &referencedRdlaFiles, 46 | 47 | // Holds values of rdla file lua variables which are 48 | // possibly referenced in dofile() or other such type 49 | // file inclusion mechanisms 50 | std::map &luaVariables 51 | ); 52 | 53 | CameraType mInitialCamType; 54 | pthread_t mRenderThread; 55 | RenderGui* mRenderGui; 56 | std::exception_ptr mException; 57 | }; 58 | 59 | RaasGuiApplication::RaasGuiApplication() 60 | : RaasApplication() 61 | , mInitialCamType(ORBIT_CAM) 62 | , mRenderThread(0) 63 | , mRenderGui(nullptr) 64 | , mException(nullptr) 65 | 66 | { 67 | } 68 | 69 | RaasGuiApplication::~RaasGuiApplication() 70 | { 71 | } 72 | 73 | void 74 | RaasGuiApplication::parseOptions() 75 | { 76 | using scene_rdl2::util::Args; 77 | Args args(mArgc, mArgv); 78 | Args::StringArray values; 79 | if (args.getFlagValues("-free_cam", 0, values) >= 0) { 80 | mInitialCamType = FREE_CAM; 81 | auto newLast = std::remove_if(mArgv, mArgv + mArgc, [](char *str) { 82 | return strcmp(str, "-free_cam") == 0; 83 | }); 84 | mArgc = static_cast(newLast - mArgv); 85 | } 86 | 87 | RaasApplication::parseOptions(true); 88 | } 89 | 90 | void RaasGuiApplication::parseRdlaFileForReferences ( 91 | const std::string &sceneFile, 92 | std::set &referencedRdlaFiles, 93 | std::map &luaVariables 94 | ){ 95 | // Keep the newly found rdla files separate for recursion 96 | std::set newReferencedRdlaFiles; 97 | 98 | // Open sceneFile 99 | std::ifstream fin; 100 | fin.open(sceneFile); 101 | if (!fin.good()) { 102 | std::cerr << "Failed to load scenefile: " << sceneFile 103 | << std::endl; 104 | return; 105 | } 106 | 107 | // Find rdla references with varialbe concatenation like the following: 108 | // dofile(asset_lib_dir .. "char/astrid/skin/rdla/astrid_skin.rdla") 109 | // TODO: Make this work with more than one variable preceding the path 110 | //boost::regex rdlaWithVariableRegex (R"(.*\((\w+)\s+\.\.\s+\"+(.*rdla)\"+.*)"); 111 | boost::regex rdlaWithVariableRegex (R"([^-]*\((\w+)\s+\.\.\s+\"+(.*rdla)\"+.*)"); 112 | 113 | // Find straigh rdla references with no variables lines like the following: 114 | // dofile("/work/gshad/moonshine/lib/char/astrid/skin/rdla/astrid_skin.rdla") 115 | boost::regex rdlaWithoutVariableRegex (R"([^-]*\"+(.*rdla)\"+.*)"); 116 | 117 | // Find lua variable assignment lines like the following: 118 | // asset_lib_dir = "/work/gshad/moonshine/lib/" 119 | boost::regex variableAssignmentRegex (R"(^\s*(\w+)\s*=\s*\"+(.*)\"+)"); 120 | 121 | std::string buf; 122 | while (std::getline(fin, buf)) { 123 | // Really long lines can cause issues for the regex parser. 124 | // Long lines are usually layer entries, or rdl mesh attributes, and 125 | // certainly do not contain rdla file references. 126 | const unsigned int maxLineSize = 1024; 127 | if (buf.size() > maxLineSize) continue; 128 | 129 | boost::cmatch cm; 130 | if (boost::regex_match(buf.c_str(), cm, rdlaWithVariableRegex)) { 131 | // Found line with .rdla file with variable used. 132 | // Try to find the lua variable in the map and then 133 | // add it to the found rdla path. 134 | std::string luaVariable = cm[1]; 135 | std::string rdlaPath = cm[2]; 136 | if (luaVariables.find(luaVariable) != luaVariables.end()) { 137 | std::string &luaVariableValue = luaVariables[luaVariable]; 138 | if (luaVariableValue.back() == '/') 139 | newReferencedRdlaFiles.insert(luaVariableValue + rdlaPath); 140 | else 141 | newReferencedRdlaFiles.insert(luaVariableValue + '/' + rdlaPath); 142 | } 143 | } else if (boost::regex_match(buf.c_str(), cm, rdlaWithoutVariableRegex)) { 144 | // Found line with .rdla file and no variable used. 145 | // Just add the rdla file to the set. 146 | newReferencedRdlaFiles.insert(cm[1]); 147 | } else if (boost::regex_match(buf.c_str(), cm, variableAssignmentRegex)) { 148 | // Found line with variable assignment. 149 | // Add it to the map. 150 | luaVariables[cm[1]] = cm[2]; 151 | } 152 | // Add other regex situations here 153 | } 154 | fin.close(); 155 | 156 | // Parse refernced rdla files recusively 157 | for (auto &rdlaFile : newReferencedRdlaFiles) { 158 | parseRdlaFileForReferences (rdlaFile, referencedRdlaFiles, luaVariables); 159 | } 160 | 161 | // Concatenate the new set onto the set passed in 162 | referencedRdlaFiles.insert(newReferencedRdlaFiles.begin(), newReferencedRdlaFiles.end()); 163 | } 164 | 165 | static bool 166 | isRdla(const std::string &sceneFile) 167 | { 168 | const std::string rdlaExt = ".rdla"; 169 | if (rdlaExt.size() > sceneFile.size()) return false; 170 | return std::equal(rdlaExt.rbegin(), rdlaExt.rend(), sceneFile.rbegin()); 171 | } 172 | 173 | // static method for a thread starting point // 174 | void* 175 | RaasGuiApplication::startRenderThread(void* me) 176 | { 177 | RaasGuiApplication* self = static_cast(me); 178 | 179 | // Run global init (creates a RenderDriver) This *must* be called on the same thread we 180 | // intend to call RenderContext::startFrame from. 181 | moonray::rndr::initGlobalDriver(self->mOptions); 182 | 183 | self->logInitMessages(); 184 | 185 | scene_rdl2::fb_util::RenderBuffer outputBuffer; 186 | scene_rdl2::fb_util::HeatMapBuffer heatMapBuffer; 187 | scene_rdl2::fb_util::FloatBuffer weightBuffer; 188 | scene_rdl2::fb_util::RenderBuffer renderBufferOdd; 189 | scene_rdl2::fb_util::VariablePixelBuffer renderOutputBuffer; 190 | 191 | try { 192 | // Create the change watchers if applicable 193 | std::unique_ptr changeWatcher(moonray::ChangeWatcher::CreateChangeWatcher()); 194 | std::unique_ptr deltasWatcher(moonray::ChangeWatcher::CreateChangeWatcher()); 195 | 196 | const auto& sceneFiles = self->mOptions.getSceneFiles(); 197 | std::set referencedRdlaFiles; 198 | std::map luaVariables; 199 | for (const auto& sceneFile : sceneFiles) { 200 | changeWatcher->watchFile(sceneFile); 201 | 202 | // avoid parsing rdlb files 203 | if (!isRdla(sceneFile)) continue; 204 | 205 | // Parse referenced rdla files 206 | parseRdlaFileForReferences( 207 | sceneFile, 208 | referencedRdlaFiles, 209 | luaVariables 210 | ); 211 | 212 | // Add referenced rdla files to watch list 213 | for (const auto& rdlaFile : referencedRdlaFiles) { 214 | changeWatcher->watchFile(rdlaFile); 215 | std::cout << "Watching file: " << rdlaFile << std::endl; 216 | } 217 | 218 | referencedRdlaFiles.clear(); 219 | luaVariables.clear(); 220 | } 221 | 222 | for (const std::string & deltasFile : self->mOptions.getDeltasFiles()) { 223 | deltasWatcher->watchFile(deltasFile); 224 | 225 | // Parse deltas file's referenced rdla files 226 | parseRdlaFileForReferences( 227 | deltasFile, 228 | referencedRdlaFiles, 229 | luaVariables 230 | ); 231 | 232 | // Add delta file's referenced rdla files to watch list 233 | for (const auto& rdlaFile : referencedRdlaFiles) { 234 | changeWatcher->watchFile(rdlaFile); 235 | } 236 | 237 | referencedRdlaFiles.clear(); 238 | luaVariables.clear(); 239 | } 240 | 241 | bool hasCameraXform = false; 242 | scene_rdl2::math::Mat4f origCameraXform; 243 | scene_rdl2::math::Mat4f currCameraXform; 244 | 245 | do { 246 | std::unique_ptr renderContext; 247 | 248 | // Loop until we have a successful load of main scene. 249 | do { 250 | try { 251 | // Scene load happens in here. 252 | renderContext.reset(new moonray::rndr::RenderContext(self->mOptions, 253 | &self->mInitMessages)); 254 | constexpr auto loggingConfig = moonray::rndr::RenderContext::LoggingConfiguration::ATHENA_DISABLED; 255 | renderContext->initialize(self->mInitMessages, loggingConfig); 256 | 257 | // Ensure we are either in progressive or fast progressive mode 258 | if (self->mRenderGui->isFastProgressive()) { 259 | renderContext->setRenderMode(moonray::rndr::RenderMode::PROGRESSIVE_FAST); 260 | renderContext->setFastRenderMode(self->mRenderGui->getFastRenderMode()); 261 | } else { 262 | renderContext->setRenderMode(moonray::rndr::RenderMode::PROGRESSIVE); 263 | // Set to default fast render mode - doesn't matter in regular progressive 264 | renderContext->setFastRenderMode(moonray::rndr::FastRenderMode::NORMALS); 265 | } 266 | } catch(const std::exception& e) { 267 | std::cerr << "Load failed! Fix the file and resave!\n" 268 | << "ERROR: " << e.what() << std::endl; 269 | renderContext.reset(); 270 | 271 | changeWatcher->waitForChange(); 272 | } 273 | } while(!renderContext); 274 | 275 | self->mRenderGui->setContext(renderContext.get()); 276 | 277 | // Set up file watchers for all the shader DSOs. 278 | 279 | watchShaderDsos(*changeWatcher, *renderContext); 280 | 281 | // Record camera location the first time around so that we can maintain 282 | // positioning between dso/shader changes. 283 | const rdl2::Camera *camera = renderContext->getCamera(); 284 | MNRY_ASSERT(camera); 285 | 286 | // Tolerate a double to float precision loss in the gui 287 | scene_rdl2::math::Mat4f rdlaCameraXform = toFloat(camera->get(rdl2::Node::sNodeXformKey)); 288 | 289 | // Typically we want to preserve the current camera location on reload. 290 | // The exception is if it has been manually changed in the rdla file. 291 | bool makeDefaultXform = false; 292 | if (!hasCameraXform || rdlaCameraXform != origCameraXform) { 293 | origCameraXform = currCameraXform = rdlaCameraXform; 294 | hasCameraXform = true; 295 | makeDefaultXform = true; 296 | } 297 | 298 | self->mRenderGui->beginInteractiveRendering(currCameraXform, makeDefaultXform); 299 | 300 | uint32_t prevFrameTimestamp = 0; 301 | uint32_t frameSavedTimestamp = 0; 302 | 303 | std::set changedDeltaFiles; 304 | 305 | moonray::rndr::PathVisualizerManager* visualizer = renderContext->getPathVisualizerManager().get(); 306 | 307 | while (self->mRenderGui->isActive()) { 308 | 309 | // Execute startFrame() if renderContext has forceCallStartFrame condition 310 | renderContext->forceGuiCallStartFrameIfNeed(); 311 | 312 | if (deltasWatcher->hasChanged(&changedDeltaFiles)) { 313 | currCameraXform = self->mRenderGui->endInteractiveRendering(); 314 | 315 | // Apply the deltas to the scene objects 316 | for (const std::string & filename : changedDeltaFiles) { 317 | renderContext->updateScene(filename); 318 | } 319 | changedDeltaFiles.clear(); 320 | // Tolerate a double to float precision loss in the gui 321 | rdlaCameraXform = toFloat(camera->get(rdl2::Node::sNodeXformKey)); 322 | 323 | // The same logic we do for full reloads applies for camera xforms 324 | // applies to delta updates also. 325 | makeDefaultXform = false; 326 | if (rdlaCameraXform != origCameraXform) { 327 | origCameraXform = currCameraXform = rdlaCameraXform; 328 | makeDefaultXform = true; 329 | } 330 | 331 | self->mRenderGui->beginInteractiveRendering(currCameraXform, makeDefaultXform); 332 | } 333 | 334 | // This is the timestamp of the last frame we kicked off. 335 | uint32_t currFrameTimestamp = self->mRenderGui->updateInteractiveRendering(); 336 | 337 | // Don't dump out text or save the file if rendering in real-time mode 338 | // since there will be many frames rendered per second. 339 | if (renderContext->getRenderMode() == moonray::rndr::RenderMode::REALTIME) { 340 | 341 | // This effectively caps the max framerate to 500fps. 342 | usleep(2000); 343 | 344 | } else { 345 | bool frameComplete = false; 346 | 347 | // when the visualizer is done recording ray info, we need to stop the frame, 348 | // if necessary, then request that drawing begin 349 | if (visualizer->isInStopRecordState()) { 350 | if (renderContext->isFrameRendering()) { 351 | renderContext->stopFrame(/*simulationMode*/ true); 352 | } 353 | /// TODO: By calling startFrame here, it ensures that, even 354 | /// if rendering is complete, it will force a full render 355 | /// restart. Ideally, if we are finished rendering, we would only 356 | /// run the simulation (see moonray::RenderContext::forceCameraUpdates()) 357 | /// TODO: Also, ideally we wouldn't do heavy renderPrep work inside the event loop 358 | renderContext->startFrame(); 359 | visualizer->generateLines(); 360 | 361 | /// TODO: This section could be improved for efficiency. This conditional section will rarely 362 | /// (if ever) be entered because the first snapshot likely will not be ready before 363 | /// generateLines is complete. However, we do want to take a snapshot as soon as possible. 364 | if (renderContext->isFrameReadyForDisplay()) { 365 | const bool parallel = renderContext->getRenderMode() == moonray::rndr::RenderMode::REALTIME; 366 | self->mRenderGui->snapshotFrame(&outputBuffer, &heatMapBuffer, &weightBuffer, 367 | &renderBufferOdd, &renderOutputBuffer, 368 | true, parallel); 369 | self->mRenderGui->updateFrame(&outputBuffer, &renderOutputBuffer, false, parallel); 370 | } 371 | } 372 | 373 | if (currFrameTimestamp > prevFrameTimestamp) { 374 | // We've hit a brand new frame, do any new frame logic here... 375 | self->mNextLogProgressTime = 0.0; 376 | self->mNextLogProgressPercentage = 0.0; 377 | 378 | } else if (currFrameTimestamp == prevFrameTimestamp && 379 | frameSavedTimestamp != currFrameTimestamp && 380 | renderContext->isFrameRendering() && 381 | renderContext->isFrameComplete()) { 382 | // We've finished rendering so get the latest version (there may have 383 | // been more samples rendered since the last snapshot), and save it. 384 | frameComplete = true; 385 | self->printStatusLine(*renderContext, renderContext->getLastFrameMcrtStartTime(), frameComplete); 386 | renderContext->stopFrame(); 387 | 388 | // If we're in realtime mode then all rendering should have stopped by this 389 | // point, so use all threads for the snapshot. 390 | bool parallel = renderContext->getRenderMode() == moonray::rndr::RenderMode::REALTIME; 391 | self->mRenderGui->snapshotFrame(&outputBuffer, &heatMapBuffer, &weightBuffer, &renderBufferOdd, 392 | &renderOutputBuffer, 393 | true, parallel); 394 | self->mRenderGui->updateFrame(&outputBuffer, &renderOutputBuffer, 395 | false, parallel); 396 | } 397 | 398 | // This effectively caps the max framerate to 200fps. 399 | usleep(5000); 400 | 401 | // Display progress bar if we're actively rendering. 402 | if (frameSavedTimestamp != currFrameTimestamp && renderContext->isFrameRendering()) { 403 | self->printStatusLine(*renderContext, renderContext->getLastFrameMcrtStartTime(), frameComplete); 404 | } 405 | 406 | // Save out file to disk (if not in real-time mode). 407 | if (frameComplete) { 408 | // need to snapshot all the required buffers, the 409 | // render buffer might not have been snapshot at all if 410 | // displaying alternate render outputs 411 | renderContext->snapshotRenderBuffer(&outputBuffer, true, true, true); 412 | if (visualizer->isInDrawState()) { 413 | visualizer->draw(&outputBuffer); 414 | } 415 | 416 | const std::string outputFilename = renderContext->getSceneContext().getSceneVariables().get(rdl2::SceneVariables::sOutputFile); 417 | const rdl2::SceneObject *metadata = renderContext->getSceneContext().getSceneVariables().getExrHeaderAttributes(); 418 | const scene_rdl2::math::HalfOpenViewport aperture = renderContext->getRezedApertureWindow(); 419 | const scene_rdl2::math::HalfOpenViewport region = renderContext->getRezedRegionWindow(); 420 | 421 | moonray::writeImageWithMessage(&outputBuffer, outputFilename, metadata, aperture, region); 422 | 423 | // write any arbitrary RenderOutput objects 424 | const moonray::pbr::DeepBuffer *deepBuffer = renderContext->getDeepBuffer(); 425 | moonray::pbr::CryptomatteBuffer *cryptomatteBuffer = renderContext->getCryptomatteBuffer(); 426 | renderContext->snapshotHeatMapBuffer(&heatMapBuffer, /*untile*/ true, /*parallel*/ true); 427 | renderContext->snapshotWeightBuffer(&weightBuffer, /*untile*/ true, /*parallel*/ true); 428 | std::vector aovBuffers; 429 | renderContext->snapshotAovBuffers(aovBuffers, /*untile*/ true, /*parallel*/ true); 430 | renderContext->snapshotRenderBufferOdd(&renderBufferOdd, /*untile*/ true, /*parallel*/ true); 431 | std::vector displayFilterBuffers; 432 | renderContext->snapshotDisplayFilterBuffers(displayFilterBuffers, 433 | /*untile*/ true, /*parallel*/ true); 434 | moonray::writeRenderOutputsWithMessages(renderContext->getRenderOutputDriver(), 435 | deepBuffer, cryptomatteBuffer, &heatMapBuffer, 436 | &weightBuffer, &renderBufferOdd, aovBuffers, 437 | displayFilterBuffers); 438 | frameSavedTimestamp = currFrameTimestamp; 439 | } 440 | } 441 | 442 | // We're done, exit function. 443 | if (changeWatcher->hasChanged()) { 444 | 445 | // Grab most recent camera transform. 446 | currCameraXform = self->mRenderGui->endInteractiveRendering(); 447 | 448 | // Get out of this loop to pick up changes. 449 | std::cout << "Scene change detected." << std::endl; 450 | break; 451 | } 452 | 453 | prevFrameTimestamp = currFrameTimestamp; 454 | } 455 | 456 | // not strictly necessary, but just to be thorough: 457 | self->mRenderGui->setContext(nullptr); 458 | 459 | } while (self->mRenderGui->isActive()); 460 | } catch (...) { 461 | self->mException = std::current_exception(); 462 | if (self->mRenderGui) { 463 | self->mRenderGui->close(); 464 | } 465 | } 466 | 467 | moonray::rndr::cleanUpGlobalDriver(); 468 | 469 | return nullptr; 470 | } 471 | 472 | void 473 | RaasGuiApplication::run() 474 | { 475 | // Fire up the Qt app and display the main window. 476 | QApplication app(mArgc, mArgv); 477 | 478 | std::string lut = mOptions.getColorRenderTransformOverrideLut(); 479 | std::string snapPath = mOptions.getSnapshotPath(); 480 | RenderGui renderGui(mInitialCamType, mOptions.getTileProgress(), 481 | mOptions.getApplyColorRenderTransform(), 482 | lut.empty() ? nullptr : lut.c_str(), snapPath); 483 | mRenderGui = &renderGui; 484 | 485 | // Spin off a thread for rendering. 486 | int retVal = pthread_create(&mRenderThread, nullptr, RaasGuiApplication::startRenderThread, this); 487 | MNRY_ASSERT_REQUIRE(retVal == 0, "Failed to create render thread."); 488 | 489 | app.exec(); 490 | 491 | // Clean up thread 492 | retVal = pthread_join(mRenderThread, nullptr); 493 | MNRY_ASSERT_REQUIRE(retVal == 0, "Failed to join render thread."); 494 | 495 | if (mException) { 496 | std::rethrow_exception(mException); 497 | } 498 | } 499 | 500 | } // namespace moonray_gui 501 | 502 | 503 | int main(int argc, char* argv[]) 504 | { 505 | moonray_gui::RaasGuiApplication app; 506 | try { 507 | return app.main(argc, argv); 508 | } catch (const std::exception& e) { 509 | std::cerr << "ERROR: " << e.what() << std::endl; 510 | std::exit(EXIT_FAILURE); 511 | } 512 | } 513 | 514 | --------------------------------------------------------------------------------