├── 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