diff --git a/.github/actions/coverage-ci/action.yml b/.github/actions/coverage-ci/action.yml index 0c97d245ef..9494823328 100644 --- a/.github/actions/coverage-ci/action.yml +++ b/.github/actions/coverage-ci/action.yml @@ -76,7 +76,12 @@ runs: -DF3D_PLUGIN_BUILD_USD=ON -DF3D_PLUGIN_BUILD_VDB=ON -DF3D_STRICT_BUILD=ON + -DF3D_TESTING_ENABLE_EXTERNAL_EGL=ON + -DF3D_TESTING_ENABLE_EXTERNAL_GLFW=ON + -DF3D_TESTING_ENABLE_EXTERNAL_OSMESA=ON + -DF3D_TESTING_ENABLE_EXTERNAL_QT=ON -DF3D_TESTING_ENABLE_LONG_TIMEOUT_TESTS=ON + -DF3D_TESTING_ENABLE_GLX_TESTS=ON - name: Build shell: bash diff --git a/.github/actions/generic-ci/action.yml b/.github/actions/generic-ci/action.yml index 51bd08989e..0733cc5280 100644 --- a/.github/actions/generic-ci/action.yml +++ b/.github/actions/generic-ci/action.yml @@ -25,10 +25,10 @@ inputs: description: 'Label to control bundle' required: false default: 'no-bundle' - egl_label: - description: 'Label to control egl' + rendering_backend: + description: 'Label to control the rendering backend' required: false - default: 'no-egl' + default: 'auto' static_label: description: 'Label to control static build' required: false @@ -82,7 +82,6 @@ runs: vtk_version: ${{inputs.vtk_version}} vtk_sha_file: ./source/.github/actions/vtk_commit_sha raytracing_label: ${{inputs.raytracing_label}} - egl_label: ${{inputs.egl_label}} cpu: ${{inputs.cpu}} - name: Install F3D dependencies @@ -112,15 +111,6 @@ runs: mkdir install mkdir install/output - # There is a RPATH issue with VTK < 9.2.0 - # This is a simple workaround for it - # See https://gitlab.kitware.com/vtk/vtk/-/merge_requests/8210 - - name: Set LD_LIBRARY_PATH ubuntu VTK - if: runner.os == 'Linux' - shell: bash - working-directory: ${{github.workspace}}/dependencies/ - run: echo "LD_LIBRARY_PATH=$(pwd)/install/lib:$LD_LIBRARY_PATH" >> $GITHUB_ENV - - name: Set PATH windows if: runner.os == 'Windows' shell: powershell @@ -140,6 +130,10 @@ runs: # https://github.com/f3d-app/f3d/issues/1322 # MacOS x86_64 occasionally segfault with Java # https://github.com/f3d-app/f3d/issues/1410 + # F3D_TESTING_ENABLE_EXTERNAL_(EGL|QT|GLFW) are disabled if no X server is running + # EGL is crashing in the CI but not locally + # QT/GLFW external tests should be adapted to run offscreen + # https://github.com/f3d-app/f3d/issues/1670 - name: Configure shell: bash working-directory: ${{github.workspace}}/build @@ -174,6 +168,12 @@ runs: -DF3D_PLUGIN_BUILD_USD=${{ inputs.optional_deps_label == 'optional-deps' && 'ON' || 'OFF' }} -DF3D_PLUGIN_BUILD_VDB=${{ matrix.vtk_version != 'v9.2.6' && (runner.os != 'Windows' || matrix.vtk_version != 'v9.3.1') && inputs.optional_deps_label == 'optional-deps' && 'ON' || 'OFF' }} -DF3D_STRICT_BUILD=ON + -DF3D_TESTING_ENABLE_EXTERNAL_EGL=${{ (runner.os == 'Linux' && inputs.rendering_backend == 'auto') && 'ON' || 'OFF' }} + -DF3D_TESTING_ENABLE_EXTERNAL_GLFW=${{ (runner.os == 'Linux' && inputs.rendering_backend == 'auto') && 'ON' || 'OFF' }} + -DF3D_TESTING_ENABLE_EXTERNAL_OSMESA=${{ runner.os == 'Linux' && 'ON' || 'OFF' }} + -DF3D_TESTING_ENABLE_EXTERNAL_QT=${{ (runner.os == 'Linux' && inputs.rendering_backend == 'auto') && 'ON' || 'OFF' }} + -DF3D_TESTING_ENABLE_GLX_TESTS=${{ inputs.rendering_backend == 'auto' && 'ON' || 'OFF' }} + -DF3D_TESTING_FORCE_RENDERING_BACKEND=${{ inputs.rendering_backend }} -DF3D_WINDOWS_GUI=ON ${{ runner.os == 'Windows' && '-Ax64 -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL' || null }} ${{ runner.os == 'macOS' && '-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15' || null }} @@ -187,7 +187,7 @@ runs: - name: Run Xvfb ubuntu if: | runner.os == 'Linux' && - inputs.egl_label != 'egl' + inputs.rendering_backend == 'auto' shell: bash run: Xvfb $DISPLAY -screen 0 1280x1024x24 & @@ -214,7 +214,7 @@ runs: runner.os == 'macOS' && inputs.cpu == 'arm64' shell: bash - run: echo "F3D_CTEST_EXCEPTIONS=(TestDepthPeelingToneMapping)|(TestDepthPeeling)|(TestTextureColor)|(TestDXF)|(TestScalarsCell)|(TestXCAFColors)|(TestGrid)|(TestConfig)|(TestGLTFDracoImporter)" >> $GITHUB_ENV + run: echo "F3D_CTEST_EXCEPTIONS=(TestDepthPeelingToneMapping)|(TestDepthPeeling)|(TestTextureColor)|(TestDXF)|(TestScalarsCell)|(TestXCAFColors)|(TestGrid)|(TestConfig)|(TestGLTFDracoImporter)|(TestSDKInteractorCallBack)" >> $GITHUB_ENV - name: Test shell: bash diff --git a/.github/actions/sanitizer-ci/action.yml b/.github/actions/sanitizer-ci/action.yml index 044aa97614..84179f8b62 100644 --- a/.github/actions/sanitizer-ci/action.yml +++ b/.github/actions/sanitizer-ci/action.yml @@ -1,4 +1,4 @@ -name: 'Sanitzier CI' +name: 'Sanitizer CI' description: 'Sanitizer CI' inputs: sanitizer_type: @@ -78,6 +78,11 @@ runs: -DF3D_PLUGIN_BUILD_VDB=OFF -DF3D_SANITIZER=${{inputs.sanitizer_type}} -DF3D_STRICT_BUILD=ON + -DF3D_TESTING_ENABLE_EXTERNAL_EGL=ON + -DF3D_TESTING_ENABLE_EXTERNAL_GLFW=ON + -DF3D_TESTING_ENABLE_EXTERNAL_OSMESA=ON + -DF3D_TESTING_ENABLE_EXTERNAL_QT=ON + -DF3D_TESTING_ENABLE_GLX_TESTS=ON -DF3D_TESTING_ENABLE_LONG_TIMEOUT_TESTS=ON - name: Build diff --git a/.github/actions/vtk-install-dep/action.yml b/.github/actions/vtk-install-dep/action.yml index 7707876a49..9178da5198 100644 --- a/.github/actions/vtk-install-dep/action.yml +++ b/.github/actions/vtk-install-dep/action.yml @@ -13,10 +13,6 @@ inputs: description: 'Label to control raytracing' required: false default: 'no-raytracing' - egl_label: - description: 'Label to control egl' - required: false - default: 'no-egl' cpu: description: 'CPU architecture to build for' required: false @@ -47,7 +43,7 @@ runs: uses: actions/cache@v4 with: path: dependencies/vtk_install - key: vtk-${{env.VTK_SHA_OR_TAG}}-${{runner.os}}-${{inputs.raytracing_label}}-${{inputs.egl_label}}-${{inputs.cpu}}-8 + key: vtk-${{env.VTK_SHA_OR_TAG}}-${{runner.os}}-${{inputs.raytracing_label}}-${{inputs.cpu}}-4 - name: Setup VTK if: steps.cache-vtk.outputs.cache-hit != 'true' @@ -68,6 +64,16 @@ runs: path: './dependencies/vtk' ref: ${{env.VTK_SHA_OR_TAG}} + # CMake 3.31 introduced a new policy producing a warning when using vtkModule.cmake + # See https://gitlab.kitware.com/vtk/vtk/-/issues/19526 + - name: Patch VTK + if: steps.cache-vtk.outputs.cache-hit != 'true' + working-directory: ${{github.workspace}}/dependencies/vtk + shell: bash + run: | + cat $GITHUB_ACTION_PATH/set_cmp0177_policy.cmake CMake/vtkModule.cmake > patched.cmake + mv patched.cmake CMake/vtkModule.cmake + # OpenVDB_CMAKE_PATH is required because of # https://github.com/AcademySoftwareFoundation/openvdb/issues/1160 and # https://gitlab.kitware.com/vtk/vtk/-/merge_requests/10393 @@ -86,7 +92,6 @@ runs: -DVTKOSPRAY_ENABLE_DENOISER=ON -DVTK_BUILD_TESTING=OFF -DVTK_DEBUG_LEAKS=ON - -DVTK_DEFAULT_RENDER_WINDOW_HEADLESS=${{ inputs.egl_label == 'egl' && 'ON' || 'OFF' }} -DVTK_ENABLE_LOGGING=OFF -DVTK_ENABLE_REMOTE_MODULES=OFF -DVTK_ENABLE_WRAPPING=OFF @@ -116,7 +121,7 @@ runs: -DVTK_MODULE_ENABLE_VTK_RenderingRayTracing=${{ inputs.raytracing_label == 'raytracing' && 'YES' || 'DEFAULT' }} -DVTK_MODULE_ENABLE_VTK_RenderingVolumeOpenGL2=YES -DVTK_MODULE_ENABLE_VTK_TestingCore=YES - -DVTK_OPENGL_HAS_EGL=${{ inputs.egl_label == 'egl' && 'ON' || 'OFF' }} + -DVTK_OPENGL_HAS_EGL=${{ inputs.vtk_version != 'v9.2.6' && inputs.vtk_version != 'v9.3.1' && 'ON' || 'OFF' }} -DVTK_SMP_IMPLEMENTATION_TYPE=TBB -DVTK_VERSIONED_INSTALL=OFF ${{ runner.os == 'macOS' && '-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15' || null }} diff --git a/.github/actions/vtk-install-dep/set_cmp0177_policy.cmake b/.github/actions/vtk-install-dep/set_cmp0177_policy.cmake new file mode 100644 index 0000000000..163cc9ccc8 --- /dev/null +++ b/.github/actions/vtk-install-dep/set_cmp0177_policy.cmake @@ -0,0 +1,4 @@ +# CMake 3.31 trigger a warning without this policy set +if(POLICY CMP0174) + cmake_policy(SET CMP0174 OLD) +endif() diff --git a/.github/actions/vtk_commit_sha b/.github/actions/vtk_commit_sha index 518ad1fd17..51130f5eae 100644 --- a/.github/actions/vtk_commit_sha +++ b/.github/actions/vtk_commit_sha @@ -1 +1 @@ -6a898fd91da30d3ff903416bf856f0d1cea82cdf +aab695b6405fef4a7472039766ced36bd162221f diff --git a/.github/baselines/install_example_plugin_output.png b/.github/baselines/install_example_plugin_output.png index f8f9e5fc55..8003a55eae 100644 --- a/.github/baselines/install_example_plugin_output.png +++ b/.github/baselines/install_example_plugin_output.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e149dec1786da57f43b0f766e8953abc7c3d8416784b43088d8e3cd477090d88 -size 42168 +oid sha256:3acbfcc9b26a338bbb3b59ff53ad08b9759bb7780528c8bc9b432a2cd16f582d +size 43420 diff --git a/.github/baselines/install_output.png b/.github/baselines/install_output.png index 1f91620a09..db5da15bd9 100644 --- a/.github/baselines/install_output.png +++ b/.github/baselines/install_output.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:988e45812a83c8657307e05803b2781f2a7ca7b63004e7679fc2c52f6e6f9b94 -size 67397 +oid sha256:39d97abfaca8b994022074d63d311765dd51f278bbb557b4003b1facd1293398 +size 65217 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 746aef6b1c..a2d16fbc18 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,35 +86,42 @@ jobs: - raytracing_label: raytracing - exclude_deprecated_label: no-exclude-deprecated - optional_deps_label: optional-deps - - egl_label: no-egl + - rendering_backend: auto - static_label: no-static - - build_type: headless + - build_type: egl vtk_version: commit raytracing_label: raytracing optional_deps_label: optional-deps exclude_deprecated_label: no-exclude-deprecated - egl_label: egl + rendering_backend: egl + static_label: no-static + - build_type: osmesa + vtk_version: commit + raytracing_label: raytracing + optional_deps_label: optional-deps + exclude_deprecated_label: no-exclude-deprecated + rendering_backend: osmesa static_label: no-static - build_type: exclude_deprecated vtk_version: commit raytracing_label: raytracing optional_deps_label: optional-deps exclude_deprecated_label: exclude-deprecated - egl_label: no-egl + rendering_backend: auto static_label: no-static - build_type: no_optional_deps vtk_version: commit raytracing_label: no-raytracing optional_deps_label: no-optional-deps exclude_deprecated_label: no-exclude-deprecated - egl_label: no-egl + rendering_backend: auto static_label: no-static - build_type: static_libs vtk_version: commit raytracing_label: no-raytracing optional_deps_label: optional-deps exclude_deprecated_label: no-exclude-deprecated - egl_label: no-egl + rendering_backend: auto static_label: static runs-on: ubuntu-latest @@ -137,9 +144,9 @@ jobs: build_type: ${{matrix.build_type}} vtk_version: ${{matrix.vtk_version}} raytracing_label: ${{matrix.raytracing_label}} + rendering_backend: ${{matrix.rendering_backend}} optional_deps_label: ${{matrix.optional_deps_label}} exclude_deprecated_label: ${{matrix.exclude_deprecated_label}} - egl_label: ${{matrix.egl_label}} static_label: ${{matrix.static_label}} lfs_sha: ${{ needs.cache_lfs.outputs.lfs_sha}} @@ -182,7 +189,7 @@ jobs: strategy: fail-fast: false matrix: - vtk_version: [commit, v9.3.1, v9.2.6] + vtk_version: [commit, v9.3.1] # VTK 9.2.6 not compatible with recent clang version on macOS bundle_label: [no-bundle] static_label: [no-static] include: diff --git a/.github/workflows/nightly_vtk_master.yml b/.github/workflows/nightly_vtk_master.yml index ec5820386e..56a8ceeb30 100644 --- a/.github/workflows/nightly_vtk_master.yml +++ b/.github/workflows/nightly_vtk_master.yml @@ -98,10 +98,11 @@ jobs: matrix: build_type: [standard] include: - - egl_label: no-egl - - build_type: headless - egl_label: egl - + - rendering_backend: auto + - build_type: egl + rendering_backend: egl + - build_type: osmesa + rendering_backend: osmesa runs-on: ubuntu-latest container: ghcr.io/f3d-app/f3d-ci @@ -122,8 +123,7 @@ jobs: build_type: ${{matrix.build_type}} vtk_version: ${{needs.check_nightly.outputs.vtk_sha}} raytracing_label: 'raytracing' - exclude_deprecated_label: ${{matrix.exclude_deprecated_label}} - egl_label: ${{matrix.egl_label}} + rendering_backend: ${{matrix.rendering_backend}} lfs_sha: ${{ needs.cache_lfs.outputs.lfs_sha}} #---------------------------------------------------------------------------- diff --git a/.github/workflows/public-api-warn.yml b/.github/workflows/public-api-warn.yml index c45ae9d518..d03a6a6c06 100644 --- a/.github/workflows/public-api-warn.yml +++ b/.github/workflows/public-api-warn.yml @@ -3,6 +3,7 @@ on: pull_request_target: paths: - 'library/public/*.h' + - 'library/public/*.h.in' jobs: public-api-warn: diff --git a/.gitignore b/.gitignore index 899b667589..bb8f4ac65c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,19 @@ -.vscode +## This file should contain only F3D process related files and folder +## For any files specific to user process, use either ~/.gitignore or .git/info/exclude instead +# doc process related _site -.vs -CMakeUserPresets.json -build/ + +# scikit build related _skbuild dist *.egg-info + +# python usage related __pycache__ *.pyc + +# python wheel process related _version.py -.DS_Store + +# wasm process related node_modules diff --git a/.lsan.supp b/.lsan.supp index 6ef6ca8fff..0171b8c180 100644 --- a/.lsan.supp +++ b/.lsan.supp @@ -18,6 +18,9 @@ leak:TKernel # TBB leak:libtbb +# OSMesa +leak:libOSMesa + # Potential mesa/VTK leak with incomplete callstack # forces us to hide all leaks from the tests using a render window # https://gitlab.kitware.com/vtk/vtk/-/issues/18504 diff --git a/.tsan.supp b/.tsan.supp index 33baf05278..4f9e24ba73 100644 --- a/.tsan.supp +++ b/.tsan.supp @@ -14,3 +14,6 @@ race:libOpenEXR # dmon # https://github.com/septag/dmon/issues/33 race:dmon.h + +# OSMesa +race:libOSMesa diff --git a/CMakeLists.txt b/CMakeLists.txt index 9544a84d9e..0fa43084c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,7 +75,7 @@ endif () # VTK dependency # Optional components should list VTK modules # needed by plugins and optional modules -find_package(VTK 9.0 REQUIRED +find_package(VTK 9.2.6 REQUIRED COMPONENTS CommonCore CommonDataModel @@ -99,14 +99,19 @@ find_package(VTK 9.0 REQUIRED RenderingVolumeOpenGL2 TestingCore jsoncpp - opengl OPTIONAL_COMPONENTS + opengl IOExodus IOOpenVDB RenderingExternal RenderingRayTracing) message(STATUS "VTK ${VTK_VERSION} found") +if(VTK_VERSION VERSION_LESS 9.3.20240914) + # Not optional before + find_package(VTK REQUIRED COMPONENTS opengl) +endif() + # Shared options between application and library include(GNUInstallDirs) cmake_dependent_option(F3D_WINDOWS_GUI "Build a non-console Win32 application" ON "WIN32" OFF) @@ -184,10 +189,18 @@ list(APPEND f3d_link_options_public ${f3d_sanitizer_link_options}) option(BUILD_TESTING "Build the tests" OFF) cmake_dependent_option(F3D_TESTING_ENABLE_RENDERING_TESTS "Enable rendering tests" ON "BUILD_TESTING" OFF) cmake_dependent_option(F3D_TESTING_ENABLE_LONG_TIMEOUT_TESTS "Enable long timeout tests" OFF "BUILD_TESTING" OFF) +cmake_dependent_option(F3D_TESTING_ENABLE_GLX_TESTS "Enable test that require a X server running on Linux" ON "F3D_TESTING_ENABLE_RENDERING_TESTS AND UNIX AND NOT APPLE" OFF) if(BUILD_TESTING) enable_testing() endif() +# Testing offscreen backend +if(NOT F3D_TESTING_FORCE_RENDERING_BACKEND) + set(F3D_TESTING_FORCE_RENDERING_BACKEND "auto" CACHE STRING "Force testing offscreen backend" FORCE) + set_property(CACHE F3D_TESTING_FORCE_RENDERING_BACKEND PROPERTY STRINGS "auto" "egl" "osmesa") +endif() +mark_as_advanced(F3D_TESTING_FORCE_RENDERING_BACKEND) + # Figure out F3D configuration directory if(UNIX AND NOT APPLE) option(F3D_LINUX_INSTALL_DEFAULT_CONFIGURATION_FILE_IN_PREFIX "Install the default configuration at the prefix root instead of system wide" OFF) diff --git a/README.md b/README.md index c3ea227fb1..d52ba7023c 100644 --- a/README.md +++ b/README.md @@ -31,24 +31,14 @@ If you need any help or want to discuss with other F3D users and developers, hea # Quickstart -Open a file and visualize it interactively: - +Open a file directly in F3D or from the command line by running: ``` f3d /path/to/file.ext -``` - -Open a file and save the rendering into an image file: - -``` -f3d /path/to/file.ext --output=/path/to/img.png -``` +``` -Get help: +Optionally, append `--output=/path/to/img.png` to save the rendering into an image file. -``` -f3d --help -man f3d # Linux only -``` +See the [Quickstart Guide](doc/user/QUICKSTART.md) for more information about getting started with F3D. # Documentation diff --git a/_config.yml b/_config.yml index 81ed0f84e9..a340fb5b2b 100644 --- a/_config.yml +++ b/_config.yml @@ -76,8 +76,9 @@ defaults: - scope: - path: "doc/user/USAGE.md" + path: "doc/user/QUICKSTART.md" values: + title: Quickstart parent: User Documentation nav_order: 0 @@ -90,53 +91,74 @@ defaults: - scope: - path: "doc/user/OPTIONS.md" + path: "doc/user/SUPPORTED_FORMATS.md" values: - title: Options + title: Supported File Formats parent: User Documentation nav_order: 2 - scope: - path: "doc/user/INTERACTIONS.md" + path: "doc/user/OPTIONS.md" values: + title: Options parent: User Documentation nav_order: 3 + + - + scope: + path: "doc/user/INTERACTIONS.md" + values: + parent: User Documentation + nav_order: 4 - scope: path: "doc/user/ANIMATIONS.md" values: parent: User Documentation - nav_order: 4 + nav_order: 5 - scope: path: "doc/user/CONFIGURATION_FILE.md" values: parent: User Documentation - nav_order: 5 + nav_order: 6 + - + scope: + path: "doc/user/COMMANDS.md" + values: + parent: User Documentation + nav_order: 7 - scope: path: "doc/user/COLOR_MAPS.md" values: parent: User Documentation - nav_order: 6 + nav_order: 8 - scope: path: "doc/user/FINAL_SHADER.md" values: parent: User Documentation - nav_order: 7 + nav_order: 9 - scope: path: "doc/user/DESKTOP_INTEGRATION.md" values: parent: User Documentation - nav_order: 8 + nav_order: 10 + + - + scope: + path: "doc/user/PLUGINS.md" + values: + parent: User Documentation + nav_order: 11 - scope: @@ -144,7 +166,7 @@ defaults: values: title: Limitations and Troubleshooting parent: User Documentation - nav_order: 9 + nav_order: 12 # libf3d doc - diff --git a/application/CMakeLists.txt b/application/CMakeLists.txt index be09595465..b6116ccee7 100644 --- a/application/CMakeLists.txt +++ b/application/CMakeLists.txt @@ -106,9 +106,11 @@ if (F3D_MODULE_EXR) target_compile_definitions(f3d PRIVATE F3D_MODULE_EXR) endif () -# Headless EGL build -if (VTK_OPENGL_HAS_EGL) - target_compile_definitions(f3d PRIVATE F3D_HEADLESS_BUILD) +if(VTK_VERSION VERSION_LESS 9.3.20240914) + # Headless EGL build + if (VTK_OPENGL_HAS_EGL) + target_compile_definitions(f3d PRIVATE F3D_HEADLESS_BUILD) + endif () endif () # F3D_STRICT_BUILD diff --git a/application/F3DNSDelegate.mm b/application/F3DNSDelegate.mm index 856f971198..3802d04d81 100644 --- a/application/F3DNSDelegate.mm +++ b/application/F3DNSDelegate.mm @@ -24,7 +24,7 @@ - (BOOL)application:(NSApplication*)theApplication openFile:(NSString*)filename { (void)theApplication; Starter->AddFile([filename UTF8String]); - Starter->LoadFile(); + Starter->LoadFileGroup(); Starter->Render(); return YES; } diff --git a/application/F3DOptionsTools.cxx b/application/F3DOptionsTools.cxx index 9acccb8643..31a70bbfb8 100644 --- a/application/F3DOptionsTools.cxx +++ b/application/F3DOptionsTools.cxx @@ -68,6 +68,7 @@ static inline const std::array CLIOptions = {{ { "config", "", "Specify the configuration file to use. absolute/relative path or filename/filestem to search in configuration file locations", "", "" }, { "dry-run", "", "Do not read the configuration file", "", "1" }, { "no-render", "", "Do not read the configuration file", "", "1" }, + { "rendering-backend", "", "Backend to use when rendering (auto|glx|wgl|egl|osmesa)", "", "" }, { "max-size", "", "Maximum size in Mib of a file to load, negative value means unlimited", "", "" }, { "watch", "", "Watch current file and automatically reload it whenever it is modified on disk", "", "1" }, { "load-plugins", "", "List of plugins to load separated with a comma", "", "" }, @@ -77,8 +78,7 @@ static inline const std::array CLIOptions = {{ { { "verbose", "", "Set verbose level, providing more information about the loaded data in the console output", "{debug, info, warning, error, quiet}", "debug" }, { "progress", "", "Show loading progress bar", "", "1" }, { "animation-progress", "", "Show animation progress bar", "", "1" }, - { "geometry-only", "", "Do not read materials, cameras and lights from file", "", "1" }, - { "group-geometries", "", "When opening multiple files, show them all in the same scene. Force geometry-only. The configuration file for the first file will be loaded.", "", "1" }, + { "multi-file-mode", "", R"(Choose the behavior when opening multiple files. "single" will show one file at a time, "all" will show all files in a single scene.)", "", "" }, { "up", "", "Up direction", "{-X, +X, -Y, +Y, -Z, +Z}", "" }, { "axis", "x", "Show axes", "", "1" }, { "grid", "g", "Show grid", "", "1" }, { "grid-absolute", "", "Position grid at the absolute origin instead of below the model", "", "1" }, @@ -96,7 +96,7 @@ static inline const std::array CLIOptions = {{ {"animation-frame-rate", "", "Set animation frame rate when playing animation interactively", "", ""}, {"font-file", "", "Path to a FreeType compatible font file", "", ""} } }, { "Material", - { {"point-sprites", "o", "Show sphere sprites instead of geometry", "", "1" }, + { {"point-sprites", "o", "Show sphere sprites instead of surfaces", "", "1" }, {"point-sprites-type", "", "Point sprites type", "", ""}, {"point-sprites-size", "", "Point sprites size", "", ""}, {"point-size", "", "Point size when showing vertices, model specified by default", "", ""}, diff --git a/application/F3DOptionsTools.h b/application/F3DOptionsTools.h index 2c53a227c5..3905621bee 100644 --- a/application/F3DOptionsTools.h +++ b/application/F3DOptionsTools.h @@ -31,13 +31,13 @@ static inline const OptionsDict DefaultAppOptions = { { "config", "" }, { "dry-run", "false" }, { "no-render", "false" }, + { "rendering-backend", "auto" }, { "max-size", "-1.0" }, { "watch", "false" }, { "load-plugins", "" }, { "screenshot-filename", "{app}/{model}_{n}.png" }, { "verbose", "info" }, - { "geometry-only", "false" }, - { "group-geometries", "false" }, + { "multi-file-mode", "single" }, { "resolution", "1000, 600" }, { "position", "" }, { "colormap-file", "" }, diff --git a/application/F3DStarter.cxx b/application/F3DStarter.cxx index 3afb2b5eba..e1cc563a83 100644 --- a/application/F3DStarter.cxx +++ b/application/F3DStarter.cxx @@ -69,13 +69,13 @@ class F3DStarter::F3DInternals std::string Output; bool NoBackground; bool NoRender; + std::string RenderingBackend; double MaxSize; bool Watch; std::vector Plugins; std::string ScreenshotFilename; std::string VerboseLevel; - bool GeometryOnly; - bool GroupGeometries; + std::string MultiFileMode; std::vector Resolution; std::vector Position; std::string ColorMapFile; @@ -204,9 +204,10 @@ class F3DStarter::F3DInternals dmon_watch_id, dmon_action, const char*, const char* filename, const char*, void* userData) { F3DStarter* self = reinterpret_cast(userData); - const std::lock_guard lock(self->Internals->FilesListMutex); - fs::path filePath = self->Internals->FilesList[self->Internals->CurrentFileIndex]; - if (filePath.filename().string() == std::string(filename)) + const std::lock_guard lock(self->Internals->LoadedFilesMutex); + if (std::find_if(self->Internals->LoadedFiles.begin(), self->Internals->LoadedFiles.end(), + [&](const auto& path) + { return path.filename() == filename; }) != self->Internals->LoadedFiles.end()) { self->Internals->ReloadFileRequested = true; } @@ -254,6 +255,24 @@ class F3DStarter::F3DInternals const std::regex numberingRe("\\{(n:?([0-9]*))\\}"); const std::regex dateRe("date:?([A-Za-z%]*)"); + /* Return a file related string depending on the currently loaded files, or the empty string if + * a single file is loaded */ + const auto fileCheck = [&]() + { + if (this->LoadedFiles.empty()) + { + return "no_file"; + } + else if (this->LoadedFiles.size() > 1) + { + return "multi_file"; + } + else + { + return ""; + } + }; + /* return value for template variable name (eg. `app` -> `F3D`) */ const auto variableLookup = [&](const std::string& var) { @@ -271,15 +290,30 @@ class F3DStarter::F3DInternals } else if (var == "model") { - return FilesList[CurrentFileIndex].stem().string(); + std::string output = fileCheck(); + if (output.empty()) + { + output = this->LoadedFiles[0].stem().string(); + } + return output; } else if (var == "model.ext") { - return FilesList[CurrentFileIndex].filename().string(); + std::string output = fileCheck(); + if (output.empty()) + { + output = this->LoadedFiles[0].filename().string(); + } + return output; } else if (var == "model_ext") { - return FilesList[CurrentFileIndex].extension().string().substr(1); + std::string output = fileCheck(); + if (output.empty()) + { + output = this->LoadedFiles[0].extension().string().substr(1); + } + return output; } else if (std::regex_match(var, dateRe)) { @@ -404,9 +438,11 @@ class F3DStarter::F3DInternals std::to_string(maxNumberingAttempts) + " attempts"); } - void UpdateOptions( - const std::vector& entriesVector, const std::string& inputFile) + void UpdateOptions(const std::vector& entriesVector, + const std::vector& paths) { + assert(!paths.empty()); + f3d::log::debug("Updating Options:"); // Initialize libf3dOptions f3d::options libOptions; @@ -419,67 +455,73 @@ class F3DStarter::F3DInternals bool logOptions = this->AppOptions.VerboseLevel == "debug"; std::map> loggingMap; - // For each config entries, ordered by priority - for (const auto& entries : entriesVector) + // For each input file, order matter + for (const auto& tmpPath : paths) { - // For each entry (eg: difference config files) - for (auto const& [conf, source, pattern] : entries) + std::string inputFile = tmpPath.string(); + // For each config entries, ordered by priority + for (const auto& entries : entriesVector) { - std::regex re(pattern, std::regex_constants::icase); - std::smatch matches; - // If the source is empty, there is no pattern, all options applies - // Note: An empty inputFile matches with ".*" - if (source.empty() || std::regex_match(inputFile, matches, re)) + // For each entry (eg: difference config files) + for (auto const& [conf, source, pattern] : entries) { - // For each option key/value - for (auto const& [key, value] : conf) + std::regex re(pattern, std::regex_constants::icase); + std::smatch matches; + // If the source is empty, there is no pattern, all options applies + // Note: An empty inputFile matches with ".*" + if (source.empty() || std::regex_match(inputFile, matches, re)) { - // Check in appOptions first - auto appIter = appOptions.find(key); - if (appIter != appOptions.end()) + // For each option key/value + for (auto const& [key, value] : conf) { - appOptions[key] = value; - if (logOptions) + // Check in appOptions first + auto appIter = appOptions.find(key); + if (appIter != appOptions.end()) { - loggingMap.emplace(key, std::tuple(key, source, pattern, value)); + appOptions[key] = value; + if (logOptions) + { + loggingMap.emplace(key, std::tuple(key, source, pattern, value)); + } + continue; } - continue; - } - std::string libf3dOptionName = key; + std::string libf3dOptionName = key; - // Convert key into a libf3d option name if possible - auto libf3dIter = F3DOptionsTools::LibOptionsNames.find(key); - if (libf3dIter != F3DOptionsTools::LibOptionsNames.end()) - { - libf3dOptionName = std::string(libf3dIter->second); - } + // Convert key into a libf3d option name if possible + auto libf3dIter = F3DOptionsTools::LibOptionsNames.find(key); + if (libf3dIter != F3DOptionsTools::LibOptionsNames.end()) + { + libf3dOptionName = std::string(libf3dIter->second); + } - try - { - // Assume this is a libf3d option and set the value - libOptions.setAsString(libf3dOptionName, value); - } - catch (const f3d::options::parsing_exception& ex) - { - std::string origin = - source.empty() ? pattern : source.string() + ":`" + pattern + "`"; - f3d::log::warn("Could not set '", key, "' to '", value, "' from ", origin, - " because: ", ex.what()); - continue; - } - catch (const f3d::options::inexistent_exception&) - { - std::string origin = - source.empty() ? pattern : source.string() + ":`" + pattern + "`"; - auto [closestName, dist] = F3DOptionsTools::GetClosestOption(libf3dOptionName, true); - f3d::log::warn("'", key, "' option from ", origin, - " does not exists , did you mean '", closestName, "'?"); - continue; - } - if (logOptions) - { - loggingMap.emplace(libf3dOptionName, std::tuple(key, source, pattern, value)); + try + { + // Assume this is a libf3d option and set the value + libOptions.setAsString(libf3dOptionName, value); + + // Log the option if needed + if (logOptions) + { + loggingMap.emplace(libf3dOptionName, std::tuple(key, source, pattern, value)); + } + } + catch (const f3d::options::parsing_exception& ex) + { + std::string origin = + source.empty() ? pattern : source.string() + ":`" + pattern + "`"; + f3d::log::warn("Could not set '", key, "' to '", value, "' from ", origin, + " because: ", ex.what()); + } + catch (const f3d::options::inexistent_exception&) + { + std::string origin = + source.empty() ? pattern : source.string() + ":`" + pattern + "`"; + auto [closestName, dist] = + F3DOptionsTools::GetClosestOption(libf3dOptionName, true); + f3d::log::warn("'", key, "' option from ", origin, + " does not exists , did you mean '", closestName, "'?"); + } } } } @@ -492,6 +534,7 @@ class F3DStarter::F3DInternals std::string origin = source.empty() ? pattern : source.string() + ":`" + pattern + "`"; f3d::log::debug(" '", name, "' = '", value, "' from ", origin); } + f3d::log::debug(""); // Update typed app options from the string version this->UpdateTypedAppOptions(appOptions); @@ -515,6 +558,8 @@ class F3DStarter::F3DInternals this->AppOptions.Output = f3d::options::parse(appOptions.at("output")); this->AppOptions.NoBackground = f3d::options::parse(appOptions.at("no-background")); this->AppOptions.NoRender = f3d::options::parse(appOptions.at("no-render")); + this->AppOptions.RenderingBackend = + f3d::options::parse(appOptions.at("rendering-backend")); this->AppOptions.MaxSize = f3d::options::parse(appOptions.at("max-size")); this->AppOptions.Watch = f3d::options::parse(appOptions.at("watch")); this->AppOptions.Plugins = { f3d::options::parse>( @@ -522,8 +567,8 @@ class F3DStarter::F3DInternals this->AppOptions.ScreenshotFilename = f3d::options::parse(appOptions.at("screenshot-filename")); this->AppOptions.VerboseLevel = f3d::options::parse(appOptions.at("verbose")); - this->AppOptions.GeometryOnly = f3d::options::parse(appOptions.at("geometry-only")); - this->AppOptions.GroupGeometries = f3d::options::parse(appOptions.at("group-geometries")); + this->AppOptions.MultiFileMode = + f3d::options::parse(appOptions.at("multi-file-mode")); this->AppOptions.Resolution = f3d::options::parse>(appOptions.at("resolution")); this->AppOptions.Position = f3d::options::parse>(appOptions.at("position")); @@ -577,7 +622,7 @@ class F3DStarter::F3DInternals { window.setSize(this->AppOptions.Resolution[0], this->AppOptions.Resolution[1]); } - else if (this->AppOptions.Resolution.size() != 0) + else if (!this->AppOptions.Resolution.empty()) { f3d::log::warn("Provided resolution could not be applied"); } @@ -588,7 +633,7 @@ class F3DStarter::F3DInternals } else { - if (this->AppOptions.Position.size() != 0) + if (!this->AppOptions.Position.empty()) { f3d::log::warn("Provided position could not be applied"); } @@ -603,19 +648,34 @@ class F3DStarter::F3DInternals } } + // Recover a vector of unique parent paths from a vector of paths + static std::vector ParentPaths(const std::vector& paths) + { + std::vector parents; + for (const auto& tmpPath : paths) + { + fs::path parentPath = tmpPath.parent_path(); + if (std::find(parents.begin(), parents.end(), tmpPath) == parents.end()) + { + parents.emplace_back(parentPath); + } + } + return parents; + } + F3DAppOptions AppOptions; f3d::options LibOptions; F3DOptionsTools::OptionsEntries ConfigOptionsEntries; F3DOptionsTools::OptionsEntries CLIOptionsEntries; F3DOptionsTools::OptionsEntries DynamicOptionsEntries; std::unique_ptr Engine; - std::vector FilesList; - dmon_watch_id FolderWatchId; - bool LoadedFile = false; + std::vector> FilesGroups; + std::vector LoadedFiles; + std::vector FolderWatchIds; + int CurrentFilesGroupIndex = -1; // dmon used atomic and mutex - std::atomic CurrentFileIndex = -1; - std::mutex FilesListMutex; + std::mutex LoadedFilesMutex; // Event loop atomics std::atomic RenderRequested = false; @@ -688,7 +748,7 @@ int F3DStarter::Start(int argc, char** argv) // Update app and libf3d options based on config entries, with an empty input file // config < cli this->Internals->UpdateOptions( - { this->Internals->ConfigOptionsEntries, this->Internals->CLIOptionsEntries }, ""); + { this->Internals->ConfigOptionsEntries, this->Internals->CLIOptionsEntries }, { "" }); #if __APPLE__ // Initialize MacOS delegate @@ -702,66 +762,127 @@ int F3DStarter::Start(int argc, char** argv) if (this->Internals->AppOptions.NoRender) { - this->Internals->Engine = std::make_unique(f3d::window::Type::NONE); + this->Internals->Engine = std::make_unique(f3d::engine::createNone()); } else { bool offscreen = !reference.empty() || !output.empty(); - this->Internals->Engine = std::make_unique( - offscreen ? f3d::window::Type::NATIVE_OFFSCREEN : f3d::window::Type::NATIVE); + + if (this->Internals->AppOptions.RenderingBackend == "egl") + { + this->Internals->Engine = std::make_unique(f3d::engine::createEGL(offscreen)); + } + else if (this->Internals->AppOptions.RenderingBackend == "osmesa") + { + this->Internals->Engine = std::make_unique(f3d::engine::createOSMesa()); + } + else if (this->Internals->AppOptions.RenderingBackend == "glx") + { + this->Internals->Engine = std::make_unique(f3d::engine::createGLX(offscreen)); + } + else if (this->Internals->AppOptions.RenderingBackend == "wgl") + { + this->Internals->Engine = std::make_unique(f3d::engine::createWGL(offscreen)); + } + else + { + if (this->Internals->AppOptions.RenderingBackend != "auto") + { + f3d::log::warn("--rendering-backend value is invalid, falling back to \"auto\""); + } + this->Internals->Engine = std::make_unique(f3d::engine::create(offscreen)); + } f3d::window& window = this->Internals->Engine->getWindow(); window.setWindowName(F3D::AppTitle).setIcon(F3DIcon, sizeof(F3DIcon)); this->Internals->ApplyPositionAndResolution(); f3d::interactor& interactor = this->Internals->Engine->getInteractor(); - interactor.setKeyPressCallBack( - [this](int, const std::string& keySym) -> bool + + interactor.addCommandCallback("load_previous_file_group", + [this](const std::vector&) { return this->LoadRelativeFileGroup(-1); }); + + interactor.addCommandCallback("load_next_file_group", + [this](const std::vector&) { return this->LoadRelativeFileGroup(+1); }); + + interactor.addCommandCallback("reload_current_file_group", + [this](const std::vector&) + { return this->LoadRelativeFileGroup(0, true, true); }); + + interactor.addCommandCallback("add_current_directories", + [this](const std::vector&) -> bool { - if (keySym == "Left") - { - return this->LoadRelativeFile(-1); - } - if (keySym == "Right") - { - return this->LoadRelativeFile(+1); - } - if (keySym == "Up") - { - return this->LoadRelativeFile(0, true); - } - if (keySym == "Down") + if (this->Internals->LoadedFiles.size() > 0) { - if (this->Internals->LoadedFile) + for (const auto& parentPath : F3DInternals::ParentPaths(this->Internals->LoadedFiles)) { - this->AddFile( - this->Internals->FilesList[static_cast(this->Internals->CurrentFileIndex)] - .parent_path(), - true); - return this->LoadRelativeFile(0); + this->AddFile(parentPath, true); } - return true; + return this->LoadRelativeFileGroup(0); } + return true; + }); - if (keySym == "F12") + interactor.addCommandCallback("take_screenshot", + [this](const std::vector& args) -> bool + { + // XXX: Add a test for this one this can be reached with a non empty filename + std::string filename = + args.empty() ? this->Internals->AppOptions.ScreenshotFilename : args[0]; + this->SaveScreenshot(filename); + return true; + }); + + interactor.addCommandCallback("take_minimal_screenshot", + [this](const std::vector& args) -> bool + { + // XXX: Add a test for this one this can be reached with a non empty filename + std::string filename = + args.empty() ? this->Internals->AppOptions.ScreenshotFilename : args[0]; + this->SaveScreenshot(filename, true); + return true; + }); + + // This replace an existing command in libf3d + interactor.addCommandCallback("add_files", + [this](const std::vector& files) -> bool + { + int index = -1; + for (const std::string& file : files) { - this->SaveScreenshot(this->Internals->AppOptions.ScreenshotFilename); - return true; + index = this->AddFile(fs::path(file)); } - if (keySym == "F11") + if (index > -1) { - this->SaveScreenshot(this->Internals->AppOptions.ScreenshotFilename, true); - return true; + this->LoadFileGroup(index); } + return true; + }); - return false; + interactor.addCommandCallback("set_hdri", + [this](const std::vector& files) -> bool + { + if (files.empty() || files.size() > 1) + { + return false; + } + + // Set the first file has an HDRI + f3d::options& options = this->Internals->Engine->getOptions(); + options.render.hdri.file = files[0]; + options.render.hdri.ambient = true; + options.render.background.skybox = true; + + // Rendering now is needed for correct lighting + this->Render(); + return true; }); - interactor.setDropFilesCallBack( - [this](const std::vector& filesVec) -> bool + interactor.addCommandCallback("add_files_or_set_hdri", + [this](const std::vector& files) -> bool { int index = -1; - for (const std::string& file : filesVec) + for (const std::string& file : files) { if (F3DInternals::HasHDRIExtension(file)) { @@ -783,11 +904,28 @@ int F3DStarter::Start(int argc, char** argv) } if (index > -1) { - this->LoadFile(index); + this->LoadFileGroup(index); } - this->RequestRender(); return true; }); + + interactor.addInteractionCommand( + "Left", f3d::interactor::ModifierKeys::NONE, "load_previous_file_group"); + interactor.addInteractionCommand( + "Right", f3d::interactor::ModifierKeys::NONE, "load_next_file_group"); + interactor.addInteractionCommand( + "Up", f3d::interactor::ModifierKeys::NONE, "reload_current_file_group"); + interactor.addInteractionCommand( + "Down", f3d::interactor::ModifierKeys::NONE, "add_current_directories"); + interactor.addInteractionCommand( + "F11", f3d::interactor::ModifierKeys::NONE, "take_minimal_screenshot"); + interactor.addInteractionCommand("F12", f3d::interactor::ModifierKeys::NONE, "take_screenshot"); + + // This replace an existing default interaction command in the libf3d + interactor.addInteractionCommand( + "Drop", f3d::interactor::ModifierKeys::NONE, "add_files_or_set_hdri"); + interactor.addInteractionCommand("Drop", f3d::interactor::ModifierKeys::CTRL, "add_files"); + interactor.addInteractionCommand("Drop", f3d::interactor::ModifierKeys::SHIFT, "set_hdri"); } this->Internals->Engine->setOptions(this->Internals->LibOptions); @@ -800,7 +938,7 @@ int F3DStarter::Start(int argc, char** argv) } // Load a file - this->LoadFile(); + this->LoadFileGroup(); if (!this->Internals->AppOptions.NoRender) { @@ -836,7 +974,7 @@ int F3DStarter::Start(int argc, char** argv) // Render and compare with file if needed if (!reference.empty()) { - if (!this->Internals->LoadedFile && !noDataForceRender) + if (this->Internals->LoadedFiles.empty() && !noDataForceRender) { f3d::log::error("No file loaded, no rendering performed"); return EXIT_FAILURE; @@ -886,7 +1024,7 @@ int F3DStarter::Start(int argc, char** argv) f3d::log::info("Image comparison success with an error difference of: ", error); } - if (this->Internals->FilesList.size() > 1 && !this->Internals->AppOptions.GroupGeometries) + if (this->Internals->FilesGroups.size() > 1) { f3d::log::warn("Image comparison was performed using a single 3D file, other provided " "3D files were ignored."); @@ -895,9 +1033,9 @@ int F3DStarter::Start(int argc, char** argv) // Render to file if needed else if (!output.empty()) { - if (!this->Internals->LoadedFile && !noDataForceRender) + if (this->Internals->LoadedFiles.empty() && !noDataForceRender) { - f3d::log::error("No file loaded, no rendering performed"); + f3d::log::error("No files loaded, no rendering performed"); return EXIT_FAILURE; } @@ -917,7 +1055,7 @@ int F3DStarter::Start(int argc, char** argv) f3d::log::debug("Output image saved to ", path); } - if (this->Internals->FilesList.size() > 1 && !this->Internals->AppOptions.GroupGeometries) + if (this->Internals->FilesGroups.size() > 1) { f3d::log::warn("An output image was saved using a single 3D file, other provided 3D " "files were ignored."); @@ -941,7 +1079,52 @@ int F3DStarter::Start(int argc, char** argv) } //---------------------------------------------------------------------------- -void F3DStarter::LoadFile(int index, bool relativeIndex) +void F3DStarter::LoadFileGroup(int index, bool relativeIndex, bool forceClear) +{ + int groupIndex = this->Internals->CurrentFilesGroupIndex; + if (relativeIndex) + { + groupIndex += index; + } + else + { + groupIndex = index; + } + + // Compute a modulo to ensure 0 < groupIndex < size + // XXX Do not work if groupIndex + size < 0 + int size = static_cast(this->Internals->FilesGroups.size()); + if (size != 0) + { + groupIndex = (groupIndex + size) % size; + } + else + { + groupIndex = -1; + } + + if (groupIndex >= 0) + { + // Clear only if we change the group to load + bool clear = forceClear ? true : this->Internals->CurrentFilesGroupIndex != groupIndex; + this->Internals->CurrentFilesGroupIndex = groupIndex; + + // Create a nice looking group index eg: "(1/5)" + // XXX: Each group contains at least one path + std::string groupIdx = "(" + std::to_string(groupIndex + 1) + "/" + + std::to_string(this->Internals->FilesGroups.size()) + ")"; + this->LoadFileGroup(this->Internals->FilesGroups[groupIndex], clear, groupIdx); + } + else + { + this->Internals->CurrentFilesGroupIndex = groupIndex; + this->LoadFileGroup(std::vector{}, true, ""); + } +} + +//---------------------------------------------------------------------------- +void F3DStarter::LoadFileGroup( + const std::vector& paths, bool clear, const std::string& groupIdx) { // Make sure the animation is stopped before trying to load any file if (!this->Internals->AppOptions.NoRender) @@ -949,7 +1132,7 @@ void F3DStarter::LoadFile(int index, bool relativeIndex) this->Internals->Engine->getInteractor().stopAnimation(); } - f3d::log::debug("========== Loading 3D file =========="); + f3d::log::debug("========== Loading 3D files =========="); // Recover current options from the engine const f3d::options& dynamicOptions = this->Internals->Engine->getOptions(); @@ -975,160 +1158,164 @@ void F3DStarter::LoadFile(int index, bool relativeIndex) dynamicOptionsDict, fs::path(), "dynamic options"); // Recover file information - f3d::loader& loader = this->Internals->Engine->getLoader(); - fs::path filePath; - std::string filenameInfo; - size_t size = this->Internals->FilesList.size(); - if (size != 0) - { - if (relativeIndex) - { - assert(this->Internals->CurrentFileIndex >= 0); - this->Internals->CurrentFileIndex += index; - } - else - { - this->Internals->CurrentFileIndex = index; - } - // Create a nice looking filename info eg: "cow.vtp (1/5)" - // XXX Do not work if CurrentFileIndex + size < 0 - size_t fileIndex = (this->Internals->CurrentFileIndex + size) % size; - filePath = this->Internals->FilesList[fileIndex]; - filenameInfo = "(" + std::to_string(fileIndex + 1) + "/" + - std::to_string(this->Internals->FilesList.size()) + ") " + filePath.filename().string(); - - this->Internals->CurrentFileIndex = static_cast(fileIndex); - } - else - { - f3d::log::debug("No file to load provided."); - this->Internals->CurrentFileIndex = -1; - } + f3d::scene& scene = this->Internals->Engine->getScene(); + bool unsupported = false; - if (this->Internals->CurrentFileIndex >= 0) + std::vector localPaths; + try { - if (this->Internals->AppOptions.GroupGeometries) + // In the main thread, we only need to guard writing + const std::lock_guard lock(this->Internals->LoadedFilesMutex); + + if (clear) { - // Group geometries mode, consider the first file configuration file only - this->Internals->CurrentFileIndex = 0; - filePath = this->Internals->FilesList[static_cast(this->Internals->CurrentFileIndex)]; + scene.clear(); + this->Internals->LoadedFiles.clear(); } - // Update app and libf3d options based on config entries, selecting block using the input file - // config < cli < dynamic - this->Internals->UpdateOptions( - { this->Internals->ConfigOptionsEntries, this->Internals->CLIOptionsEntries, - this->Internals->DynamicOptionsEntries }, - filePath.string()); - this->Internals->Engine->setOptions(this->Internals->LibOptions); - this->Internals->ApplyPositionAndResolution(); - - // Load any new plugins the updated app options - F3DPluginsTools::LoadPlugins(this->Internals->AppOptions.Plugins); - - // Position the loaded file flag before we start loading - this->Internals->LoadedFile = false; - - // Check the size of the file before loading it - // XXX: Not considered in the context of GroupGeometries - static constexpr int BYTES_IN_MIB = 1048576; - if (this->Internals->AppOptions.MaxSize >= 0.0 && - fs::file_size(filePath) > - static_cast(this->Internals->AppOptions.MaxSize * BYTES_IN_MIB)) + if (paths.empty()) { - f3d::log::info("No file loaded, file is bigger than max size"); + f3d::log::debug("No files to load provided"); } else { - try + // Update app and libf3d options based on config entries, selecting block using the input file + // config < cli < dynamic + // Options must be updated before checking the supported files in order to load plugins + std::vector configPaths = this->Internals->LoadedFiles; + std::copy(paths.begin(), paths.end(), std::back_inserter(configPaths)); + this->Internals->UpdateOptions( + { this->Internals->ConfigOptionsEntries, this->Internals->CLIOptionsEntries, + this->Internals->DynamicOptionsEntries }, + configPaths); + + this->Internals->Engine->setOptions(this->Internals->LibOptions); + this->Internals->ApplyPositionAndResolution(); + + f3d::log::debug("Checking files:"); + for (const fs::path& tmpPath : paths) { - if (loader.hasSceneReader(filePath.string()) && !this->Internals->AppOptions.GeometryOnly && - !this->Internals->AppOptions.GroupGeometries) + if (std::find(this->Internals->LoadedFiles.begin(), this->Internals->LoadedFiles.end(), + tmpPath) == this->Internals->LoadedFiles.end()) { - loader.loadScene(filePath.string()); - this->Internals->LoadedFile = true; - } - else if (loader.hasGeometryReader(filePath.string())) - { - // In GroupGeometries, just load all the files from the list - if (this->Internals->AppOptions.GroupGeometries) + if (scene.supports(tmpPath)) { - int nGeom = 0; - for (size_t i = 0; i < size; i++) + // Check the size of the file before loading it + static constexpr int BYTES_IN_MIB = 1048576; + if (this->Internals->AppOptions.MaxSize >= 0.0 && + fs::file_size(tmpPath) > + static_cast(this->Internals->AppOptions.MaxSize * BYTES_IN_MIB)) { - auto geomPath = this->Internals->FilesList[i]; - if (loader.hasGeometryReader(geomPath.string())) - { - // Reset for the first file, then add geometries without resetting - // XXX this means the scene is reset and loaded from scratch every time a file is - // dropped This could be improved - loader.loadGeometry(this->Internals->FilesList[i].string(), i == 0 ? true : false); - nGeom++; - } - else - { - f3d::log::warn(geomPath, " is not a geometry of a supported file format\n"); - } + f3d::log::info(tmpPath.string(), " skipped, file is bigger than max size"); } - if (nGeom > 1) + else { - filenameInfo = std::to_string(nGeom) + " geometries loaded"; + localPaths.emplace_back(tmpPath); } } else { - // Standard loadGeometry code - loader.loadGeometry(filePath.string(), true); + f3d::log::warn(tmpPath.string(), " is not a file of a supported file format"); + unsupported = true; } - this->Internals->LoadedFile = true; - } - else - { - f3d::log::debug("No reader found for \"" + filePath.string() + "\""); - f3d::log::warn(filePath.string(), " is not a file of a supported file format\n"); - filenameInfo += " [UNSUPPORTED]"; } } - catch (const f3d::loader::load_failure_exception& ex) - { - // XXX Not reachable until vtkImporter is improved to support returning a failure - f3d::log::error("Could not load file: ", ex.what()); - } - if (!dynamicOptions.scene.camera.index.has_value() && !this->Internals->AppOptions.NoRender) + if (!localPaths.empty()) { - // Setup the camera according to options - this->Internals->SetupCamera(this->Internals->AppOptions.CamConf); + // Add files to the scene + scene.add(localPaths); - this->Internals->Engine->getWindow().setWindowName( - filePath.filename().string() + " - " + F3D::AppName); + // Update loaded files + std::copy( + localPaths.begin(), localPaths.end(), std::back_inserter(this->Internals->LoadedFiles)); } } } + catch (const f3d::scene::load_failure_exception& ex) + { + f3d::log::error("Some of these files could not be loaded: ", ex.what()); + for (const fs::path& tmpPath : localPaths) + { + f3d::log::error(" ", tmpPath.string()); + } + unsupported = true; + } - if (this->Internals->LoadedFile) + std::string filenameInfo; + if (this->Internals->LoadedFiles.size() > 0) { + // Loaded files, create a filename info like this: + // "(1/5) cow.vtp + N [+UNSUPPORTED]" + filenameInfo = groupIdx + " " + this->Internals->LoadedFiles.at(0).filename().string(); + if (this->Internals->LoadedFiles.size() > 1) + { + filenameInfo += " +" + std::to_string(this->Internals->LoadedFiles.size() - 1); + } + if (unsupported) + { + filenameInfo += " [+UNSUPPORTED]"; + } + + // Update dmon watch logic if (this->Internals->AppOptions.Watch) { // Always unwatch and watch current folder, even on reload - if (this->Internals->FolderWatchId.id > 0) + for (const auto& dmonId : this->Internals->FolderWatchIds) + { + if (dmonId.id > 0) + { + dmon_unwatch(dmonId); + } + } + this->Internals->FolderWatchIds.clear(); + + for (const auto& parentPath : F3DInternals::ParentPaths(this->Internals->LoadedFiles)) { - dmon_unwatch(this->Internals->FolderWatchId); + this->Internals->FolderWatchIds.emplace_back( + dmon_watch(parentPath.string().c_str(), &F3DInternals::dmonFolderChanged, 0, this)); } - this->Internals->FolderWatchId = dmon_watch( - filePath.parent_path().string().c_str(), &F3DInternals::dmonFolderChanged, 0, this); } } else { - // No file loaded, remove any previously loaded file - loader.loadGeometry("", true); + // No files loaded, create a simple filename info like this: + // (1/5) cow.vtt [UNSUPPORTED] + // (1/1) cow.vtt [+UNSUPPORTED] + if (unsupported) + { + filenameInfo = groupIdx + " " + paths.at(0).filename().string() + " ["; + if (paths.size() > 1) + { + filenameInfo += "+"; + } + filenameInfo += "UNSUPPORTED]"; + } + } + + if (!this->Internals->AppOptions.NoRender) + { + if (!filenameInfo.empty()) + { + this->Internals->Engine->getWindow().setWindowName(filenameInfo + " - " + F3D::AppName); + } + + if (dynamicOptions.scene.camera.index.has_value()) + { + // Camera is setup by importer, save its configuration as default + this->Internals->Engine->getWindow().getCamera().setCurrentAsDefault(); + } + else + { + // Setup the camera according to options + this->Internals->SetupCamera(this->Internals->AppOptions.CamConf); + } } // XXX: We can force dropzone and filename_info because they cannot be set // manually by the user for now f3d::options& options = this->Internals->Engine->getOptions(); - options.ui.dropzone = !this->Internals->LoadedFile; + options.ui.dropzone = this->Internals->LoadedFiles.empty(); options.ui.filename_info = filenameInfo; } @@ -1208,6 +1395,7 @@ void F3DStarter::SaveScreenshot(const std::string& filenameTemplate, bool minima //---------------------------------------------------------------------------- int F3DStarter::AddFile(const fs::path& path, bool quiet) { + // Check file exists auto tmpPath = fs::absolute(path); if (!fs::exists(tmpPath)) { @@ -1217,6 +1405,7 @@ int F3DStarter::AddFile(const fs::path& path, bool quiet) } return -1; } + // If file is a folder, add files recursively else if (fs::is_directory(tmpPath)) { std::set sortedPaths; @@ -1229,45 +1418,73 @@ int F3DStarter::AddFile(const fs::path& path, bool quiet) // Recursively add all files this->AddFile(entryPath, quiet); } - return static_cast(this->Internals->FilesList.size()) - 1; + return static_cast(this->Internals->FilesGroups.size()) - 1; } else { - auto it = - std::find(this->Internals->FilesList.begin(), this->Internals->FilesList.end(), tmpPath); + // Check if file has already been added + bool found = false; + std::vector>::iterator it; + for (it = this->Internals->FilesGroups.begin(); it != this->Internals->FilesGroups.end(); it++) + { + auto localIt = std::find(it->begin(), it->end(), tmpPath); + found |= localIt != it->end(); + if (found) + { + break; + } + } - if (it == this->Internals->FilesList.end()) + if (!found) { - // In the main thread, we only need to guard writing - const std::lock_guard lock(this->Internals->FilesListMutex); - this->Internals->FilesList.push_back(tmpPath); - return static_cast(this->Internals->FilesList.size()) - 1; + // Add to the right file group + // XXX more multi-file mode may be added in the future + if (this->Internals->AppOptions.MultiFileMode == "all") + { + if (this->Internals->FilesGroups.empty()) + { + this->Internals->FilesGroups.resize(1); + } + assert(this->Internals->FilesGroups.size() == 1); + this->Internals->FilesGroups[0].emplace_back(tmpPath); + } + else + { + if (this->Internals->AppOptions.MultiFileMode != "single") + { + f3d::log::warn("Unrecognized multi-file-mode: ", + this->Internals->AppOptions.MultiFileMode, ". Assuming \"single\" mode."); + } + this->Internals->FilesGroups.emplace_back(std::vector{ tmpPath }); + } + return static_cast(this->Internals->FilesGroups.size()) - 1; } else { + // If already added, just return the index of the group containing the file if (!quiet) { f3d::log::warn("File ", tmpPath.string(), " has already been added"); } - return static_cast(std::distance(this->Internals->FilesList.begin(), it)); + return static_cast(std::distance(this->Internals->FilesGroups.begin(), it)); } } } //---------------------------------------------------------------------------- -bool F3DStarter::LoadRelativeFile(int index, bool restoreCamera) +bool F3DStarter::LoadRelativeFileGroup(int index, bool restoreCamera, bool forceClear) { if (restoreCamera) { f3d::camera& cam = this->Internals->Engine->getWindow().getCamera(); const auto camState = cam.getState(); - this->LoadFile(index, true); + this->LoadFileGroup(index, true, forceClear); cam.setState(camState); } else { - this->LoadFile(index, true); + this->LoadFileGroup(index, true, forceClear); } this->RequestRender(); @@ -1280,7 +1497,7 @@ void F3DStarter::EventLoop() { if (this->Internals->ReloadFileRequested) { - this->LoadRelativeFile(0, true); + this->LoadRelativeFileGroup(0, true, true); this->Internals->ReloadFileRequested = false; } if (this->Internals->RenderRequested) diff --git a/application/F3DStarter.h b/application/F3DStarter.h index 578f3946dd..6f1bd17de0 100644 --- a/application/F3DStarter.h +++ b/application/F3DStarter.h @@ -7,31 +7,30 @@ #ifndef F3DStarter_h #define F3DStarter_h -#include "loader.h" - #include #include +#include class F3DStarter { public: /** - * Parse the options and configure a f3d::loader accordingly + * Parse the options and configure a f3d::scene accordingly */ int Start(int argc, char** argv); /** - * Add a file or directory to the list of paths - * Returns the index of the added file + * Add a file or directory to a file group + * Returns the index of the group where the file was added */ int AddFile(const std::filesystem::path& path, bool quiet = false); /** - * Load a file if any have been added - * Set the index to select the index of the file to load + * Load a file group if any have been added + * Set the index to select the index of the file group to load * Set relativeIndex to true to use the index as a relative index with the current index */ - void LoadFile(int index = 0, bool relativeIndex = false); + void LoadFileGroup(int index = 0, bool relativeIndex = false, bool forceClear = false); /** * Trigger a render on the next event loop @@ -63,9 +62,18 @@ class F3DStarter /** * Internal method triggered when interacting with the application - * that load a file using relative index and handle camera restore + * that load a file group using relative index and handle camera restore + */ + bool LoadRelativeFileGroup( + int relativeIndex = 0, bool restoreCamera = false, bool forceClear = false); + + /** + * Internal method used to load a provided file group into the scene. + * Set clear to true to clear the scene first + * GroupIdx is only used for display purposes of the filename */ - bool LoadRelativeFile(int relativeIndex = 0, bool restoreCamera = false); + void LoadFileGroup( + const std::vector& paths, bool clear, const std::string& groupIdx); /** * Internal event loop that is triggered repeatedly to handle specific events: diff --git a/application/testing/CMakeLists.txt b/application/testing/CMakeLists.txt index f8550621ce..16b7723c56 100644 --- a/application/testing/CMakeLists.txt +++ b/application/testing/CMakeLists.txt @@ -1,7 +1,7 @@ # F3D Testing function(f3d_test) - cmake_parse_arguments(F3D_TEST "TONE_MAPPING;LONG_TIMEOUT;INTERACTION;INTERACTION_CONFIGURE;NO_BASELINE;NO_RENDER;NO_OUTPUT;WILL_FAIL;NO_DATA_FORCE_RENDER" "NAME;CONFIG;DATA;RESOLUTION;THRESHOLD;REGEXP;REGEXP_FAIL;HDRI" "DEPENDS;ARGS" ${ARGN}) + cmake_parse_arguments(F3D_TEST "TONE_MAPPING;LONG_TIMEOUT;INTERACTION;INTERACTION_CONFIGURE;NO_BASELINE;NO_RENDER;NO_OUTPUT;WILL_FAIL;NO_DATA_FORCE_RENDER" "NAME;CONFIG;RESOLUTION;THRESHOLD;REGEXP;REGEXP_FAIL;HDRI;RENDERING_BACKEND" "DATA;DEPENDS;ARGS" ${ARGN}) if(F3D_TEST_CONFIG) list(APPEND F3D_TEST_ARGS "--config=${F3D_TEST_CONFIG}") @@ -10,7 +10,9 @@ function(f3d_test) endif() if (F3D_TEST_DATA) - list(APPEND F3D_TEST_ARGS "${F3D_SOURCE_DIR}/testing/data/${F3D_TEST_DATA}") + foreach(_single_data ${F3D_TEST_DATA}) + list(APPEND F3D_TEST_ARGS "${F3D_SOURCE_DIR}/testing/data/${_single_data}") + endforeach() endif() if(F3D_TEST_INTERACTION) @@ -49,6 +51,16 @@ function(f3d_test) endif() endif() + if(F3D_TEST_RENDERING_BACKEND) + list(APPEND F3D_TEST_ARGS "--rendering-backend=${F3D_TEST_RENDERING_BACKEND}") + else() + # If no rendering backend is specified by the test, "auto" is used. + # However, F3D_TESTING_FORCE_RENDERING_BACKEND can be used to force the rendering backend for these tests + if(F3D_TESTING_FORCE_RENDERING_BACKEND) + list(APPEND F3D_TEST_ARGS "--rendering-backend=${F3D_TESTING_FORCE_RENDERING_BACKEND}") + endif() + endif() + add_test(NAME "f3d::${F3D_TEST_NAME}" COMMAND $ ${F3D_TEST_ARGS} COMMAND_EXPAND_LISTS) set(_timeout "30") @@ -117,7 +129,7 @@ function(f3d_test) endfunction() f3d_test(NAME TestPLY DATA suzanne.ply) -f3d_test(NAME TestOBJ DATA suzanne.obj ARGS --geometry-only) +f3d_test(NAME TestOBJ DATA world.obj) f3d_test(NAME TestSTL DATA suzanne.stl) f3d_test(NAME TestVTU DATA dragon.vtu) f3d_test(NAME TestVTP DATA cow.vtp) @@ -150,7 +162,6 @@ f3d_test(NAME TestScalarsWithBar DATA suzanne.ply ARGS -b -s --coloring-array=No f3d_test(NAME TestGLTFImporter DATA f3d.glb) f3d_test(NAME TestGLTFImporterWithAnimation DATA BoxAnimated.gltf ARGS --animation-time=2 --animation-progress) f3d_test(NAME TestGLTFSkin DATA SimpleSkin.gltf) -f3d_test(NAME TestGLTFReaderWithAnimation DATA BoxAnimated.gltf ARGS --geometry-only --animation-time=2 --animation-progress) f3d_test(NAME TestDicom DATA IM-0001-1983.dcm ARGS --scalar-coloring --roughness=1) f3d_test(NAME TestMHD DATA HeadMRVolume.mhd ARGS --scalar-coloring --roughness=1) f3d_test(NAME TestVTICell DATA waveletMaterial.vti ARGS -s --coloring-array=Material -c --roughness=1) @@ -175,14 +186,15 @@ f3d_test(NAME TestVolumeMag DATA vase_4comp.vti ARGS -vb LONG_TIMEOUT) f3d_test(NAME TestVolumeComp DATA vase_4comp.vti ARGS -vb --comp=3 LONG_TIMEOUT) f3d_test(NAME TestVolumeDirect DATA vase_4comp.vti ARGS -vb --comp=-2 LONG_TIMEOUT) f3d_test(NAME TestVolumeCells DATA waveletArrays.vti ARGS -vb --cells LONG_TIMEOUT) -f3d_test(NAME TestVolumeNonScalars DATA waveletArrays.vti ARGS -vb -s --coloring-array=RandomPointScalars LONG_TIMEOUT) -f3d_test(NAME TestTextureNormal DATA WaterBottle.glb ARGS --geometry-only --texture-normal=${F3D_SOURCE_DIR}/testing/data/normal.png --normal-scale=0.1) -f3d_test(NAME TestTextureMaterial DATA WaterBottle.glb ARGS --geometry-only --texture-material=${F3D_SOURCE_DIR}/testing/data/red_mod.jpg --roughness=1 --metallic=1) -f3d_test(NAME TestTextureMaterialWithOptions DATA WaterBottle.glb ARGS --geometry-only --texture-material=${F3D_SOURCE_DIR}/testing/data/red_mod.jpg --roughness=0.5 --metallic=0.5) -f3d_test(NAME TestTextureEmissive DATA WaterBottle.glb ARGS --geometry-only --texture-emissive=${F3D_SOURCE_DIR}/testing/data/red.jpg --emissive-factor=0.1,0.1,0.1) -f3d_test(NAME TestTextures DATA WaterBottle.glb ARGS --geometry-only --texture-material=${F3D_SOURCE_DIR}/testing/data/red.jpg --roughness=1 --metallic=1 --texture-base-color=${F3D_SOURCE_DIR}/testing/data/albedo.png --texture-normal=${F3D_SOURCE_DIR}/testing/data/normal.png --texture-emissive=${F3D_SOURCE_DIR}/testing/data/red.jpg --emissive-factor=0.1,0.1,0.1) +f3d_test(NAME TestVolumeColoringArray DATA waveletArrays.vti ARGS -vb --coloring-array=Result LONG_TIMEOUT) +f3d_test(NAME TestTextureNormal DATA WaterBottle.glb ARGS --texture-normal=${F3D_SOURCE_DIR}/testing/data/normal.png --normal-scale=0.1) +f3d_test(NAME TestTextureMaterial DATA WaterBottle.glb ARGS --texture-material=${F3D_SOURCE_DIR}/testing/data/red_mod.jpg --roughness=1 --metallic=1) +f3d_test(NAME TestTextureMaterialWithOptions DATA WaterBottle.glb ARGS --texture-material=${F3D_SOURCE_DIR}/testing/data/red_mod.jpg --roughness=0.5 --metallic=0.5) +f3d_test(NAME TestTextureEmissive DATA WaterBottle.glb ARGS --texture-emissive=${F3D_SOURCE_DIR}/testing/data/red.jpg --emissive-factor=0.1,0.1,0.1) +f3d_test(NAME TestTextures DATA WaterBottle.glb ARGS --texture-material=${F3D_SOURCE_DIR}/testing/data/red.jpg --roughness=1 --metallic=1 --texture-base-color=${F3D_SOURCE_DIR}/testing/data/albedo.png --texture-normal=${F3D_SOURCE_DIR}/testing/data/normal.png --texture-emissive=${F3D_SOURCE_DIR}/testing/data/red.jpg --emissive-factor=0.1,0.1,0.1) f3d_test(NAME TestMetaDataImporter DATA BoxAnimated.gltf ARGS -m) f3d_test(NAME TestMultiblockMetaData DATA mb.vtm ARGS -m) +f3d_test(NAME TestMultiFileMetaData DATA mb/recursive ARGS -m --multi-file-mode=all) f3d_test(NAME TestTIFF DATA f3d.tif ARGS -sy --up=-Y) f3d_test(NAME TestLightIntensityBrighter DATA cow.vtp ARGS --light-intensity=5.0) f3d_test(NAME TestLightIntensityDarker DATA cow.vtp ARGS --light-intensity=0.2) @@ -192,25 +204,30 @@ f3d_test(NAME TestUTF8 DATA "(ノಠ益ಠ )ノ.vtp") f3d_test(NAME TestFilenameCommasSpaces DATA "tetrahedron, with commas & spaces.stl") f3d_test(NAME TestFont DATA suzanne.ply ARGS -n --font-file=${F3D_SOURCE_DIR}/testing/data/Crosterian.ttf) f3d_test(NAME TestAnimationIndex DATA InterpolationTest.glb ARGS --animation-index=7 --animation-time=0.5 --animation-progress) +f3d_test(NAME TestMultiFileAnimationIndex DATA InterpolationTest.glb BoxAnimated.gltf ARGS --animation-index=9 --animation-time=0.85 --animation-progress --multi-file-mode=all) f3d_test(NAME TestAnimationAutoplay DATA InterpolationTest.glb ARGS --animation-autoplay) f3d_test(NAME TestMaxSizeBelow DATA suzanne.stl ARGS --max-size=1) -f3d_test(NAME TestMaxSizeAbove DATA WaterBottle.glb ARGS --max-size=0.2 REGEXP "No file loaded, file is bigger than max size" NO_BASELINE) -f3d_test(NAME TestAlternativeOptionSyntax DATA WaterBottle.glb ARGS --max-size 0.2 REGEXP "No file loaded, file is bigger than max size" NO_BASELINE) +f3d_test(NAME TestMaxSizeAbove DATA WaterBottle.glb ARGS --max-size=0.2 REGEXP "file is bigger than max size" NO_BASELINE) +f3d_test(NAME TestMaxSizeAboveMultiFile DATA suzanne.obj WaterBottle.glb ARGS --multi-file-mode=all --max-size=0.6 --translucency-support --opacity=0.5) +f3d_test(NAME TestAlternativeOptionSyntax DATA WaterBottle.glb ARGS --max-size 0.2 REGEXP "file is bigger than max size" NO_BASELINE) f3d_test(NAME TestNonExistentFile DATA nonExistentFile.vtp ARGS --filename WILL_FAIL) f3d_test(NAME TestUnsupportedFile DATA unsupportedFile.dummy ARGS --filename WILL_FAIL) f3d_test(NAME TestComponentName DATA from_abq.vtu ARGS --scalar-coloring --bar --comp=2) f3d_test(NAME TestNoRender DATA dragon.vtu NO_RENDER) f3d_test(NAME TestNoRenderWithOptions DATA dragon.vtu ARGS --hdri-ambient --axis NO_RENDER) # These options causes issues if not handled correctly f3d_test(NAME TestNoFile NO_DATA_FORCE_RENDER) -f3d_test(NAME TestGroupGeometries DATA mb/recursive ARGS --group-geometries) -f3d_test(NAME TestGroupGeometriesColoring DATA mb/recursive ARGS --group-geometries -s --coloring-array=Polynomial -b) -f3d_test(NAME TestMultiInputPositionals ARGS ${F3D_SOURCE_DIR}/testing/data/mb/recursive/mb_0_0.vtu ${F3D_SOURCE_DIR}/testing/data/mb/recursive/mb_1_0.vtp --group-geometries -s --coloring-array=Polynomial -b) -f3d_test(NAME TestMultiInputArg ARGS --input ${F3D_SOURCE_DIR}/testing/data/mb/recursive/mb_0_0.vtu ${F3D_SOURCE_DIR}/testing/data/mb/recursive/mb_1_0.vtp --group-geometries -s --coloring-array=Polynomial -b) -f3d_test(NAME TestMultiInputMultiArgs ARGS --input ${F3D_SOURCE_DIR}/testing/data/mb/recursive/mb_0_0.vtu --input ${F3D_SOURCE_DIR}/testing/data/mb/recursive/mb_1_0.vtp --group-geometries -s --coloring-array=Polynomial -b) +f3d_test(NAME TestMultiFile DATA mb/recursive ARGS --multi-file-mode=all) +f3d_test(NAME TestMultiFileColoring DATA mb/recursive ARGS --multi-file-mode=all -s --coloring-array=Polynomial -b) +f3d_test(NAME TestMultiFileVolume DATA multi ARGS --multi-file-mode=all -vsb --coloring-array=Scalars_) +f3d_test(NAME TestMultiFileColoringTexture DATA mb/recursive/mb_1_0.vtp mb/recursive/mb_2_0.vtp world.obj ARGS --multi-file-mode=all -sb --coloring-array=Normals --comp=1) +f3d_test(NAME TestMultiFilePositionals DATA mb/recursive/mb_0_0.vtu mb/recursive/mb_1_0.vtp ARGS --multi-file-mode=all -s --coloring-array=Polynomial -b) +f3d_test(NAME TestMultiFileNonCoherentComponentNames DATA bluntfin.vts bluntfin_t.vtu ARGS --multi-file-mode=all --scalar-coloring --coloring-array=Momentum --comp=2 --bar) +f3d_test(NAME TestMultiInputArg ARGS --input ${F3D_SOURCE_DIR}/testing/data/mb/recursive/mb_0_0.vtu ${F3D_SOURCE_DIR}/testing/data/mb/recursive/mb_1_0.vtp --multi-file-mode=all -s --coloring-array=Polynomial -b) +f3d_test(NAME TestMultiInputMultiArgs ARGS --input ${F3D_SOURCE_DIR}/testing/data/mb/recursive/mb_0_0.vtu --input ${F3D_SOURCE_DIR}/testing/data/mb/recursive/mb_1_0.vtp --multi-file-mode=all -s --coloring-array=Polynomial -b) f3d_test(NAME TestInvalidUpDirection DATA suzanne.ply ARGS -g --up=W REGEXP "W is not a valid up direction" NO_BASELINE) f3d_test(NAME TestUpDirectionNoSign DATA suzanne.ply ARGS --up=X) f3d_test(NAME TestTextureMatCap DATA suzanne.ply ARGS --texture-matcap=${F3D_SOURCE_DIR}/testing/data/skin.png) -f3d_test(NAME TestTextureMatCapWithTCoords DATA WaterBottle.glb ARGS --geometry-only --texture-matcap=${F3D_SOURCE_DIR}/testing/data/skin.png) +f3d_test(NAME TestTextureMatCapWithTCoords DATA WaterBottle.glb ARGS --texture-matcap=${F3D_SOURCE_DIR}/testing/data/skin.png) f3d_test(NAME TestConfigOrder DATA suzanne.ply ARGS CONFIG ${F3D_SOURCE_DIR}/testing/configs/config_order.json) # `.+` > `.*` alphabetically but overridden by the order f3d_test(NAME TestOutputStream DATA suzanne.ply ARGS --verbose=quiet --output=- REGEXP "^.PNG" NO_BASELINE NO_OUTPUT) f3d_test(NAME TestOutputStreamInfo DATA suzanne.ply ARGS --verbose=info --output=- REGEXP "redirected to stderr" NO_BASELINE NO_OUTPUT) @@ -268,20 +285,20 @@ f3d_ss_test(NAME UserModelN TEMPLATE {model}_{n}.png EXPECTED ${_screenshot_user set_tests_properties(f3d::TestScreenshotUserModelN PROPERTIES ENVIRONMENT "XDG_PICTURES_DIR=${_screenshot_user_dir};HOME=${_screenshot_user_dir};USERPROFILE=${_screenshot_user_dir}") if(NOT APPLE OR VTK_VERSION VERSION_GREATER_EQUAL 9.3.0) - f3d_test(NAME TestTextureColor DATA WaterBottle.glb ARGS --geometry-only --texture-base-color=${F3D_SOURCE_DIR}/testing/data/albedo_mod.png --translucency-support) + f3d_test(NAME TestTextureColor DATA WaterBottle.glb ARGS --texture-base-color=${F3D_SOURCE_DIR}/testing/data/albedo_mod.png --translucency-support) endif() # Needs SSBO: https://gitlab.kitware.com/vtk/vtk/-/merge_requests/10675 if(VTK_VERSION VERSION_GREATER_EQUAL 9.3.20231108) if(APPLE) # MacOS does not support OpenGL 4.3 - f3d_test(NAME TestSkinningManyBonesFailure DATA tube_254bones.glb ARGS -v REGEXP "which requires OpenGL" NO_BASELINE) + f3d_test(NAME TestSkinningManyBonesFailure DATA tube_254bones.glb ARGS --verbose REGEXP "which requires OpenGL" NO_BASELINE) else() # Strictly speaking, this test can also fail if ran without OpenGL 4.3 support on Windows and Linux # Instead of checking MacOS only, we should try to get OpenGL capabilities from CMake later instead f3d_test(NAME TestSkinningManyBones DATA tube_254bones.glb) endif() else() - f3d_test(NAME TestSkinningManyBonesWarning DATA tube_254bones.glb ARGS -v REGEXP "with more than 250 bones \\\(254\\\)" NO_BASELINE) + f3d_test(NAME TestSkinningManyBonesWarning DATA tube_254bones.glb ARGS --verbose REGEXP "with more than 250 bones \\\(254\\\)" NO_BASELINE) endif() # Needs splat sorting with compute shaders @@ -313,6 +330,8 @@ if(NOT F3D_MACOS_BUNDLE) f3d_test(NAME TestConfigFileBuild DATA dragon.vtu CONFIG complex_build.json) f3d_test(NAME TestConfigStemBuild DATA dragon.vtu CONFIG complex_build) f3d_test(NAME TestConfigFileUpperCase DATA suzanne_upper.STL CONFIG complex_build) + f3d_test(NAME TestConfigFileMultiFileSTL DATA mb/recursive/mb_1_0.vtp suzanne.stl ARGS --multi-file-mode=all CONFIG complex_build) + f3d_test(NAME TestConfigFileMultiFileVTP DATA mb/recursive/mb_1_0.vtp suzanne.stl mb/recursive/mb_2_0.vtp ARGS --multi-file-mode=all CONFIG complex_build) file(COPY "${F3D_SOURCE_DIR}/resources/configs/config.d/" "${F3D_SOURCE_DIR}/plugins/native/configs/config.d/" DESTINATION "${CMAKE_BINARY_DIR}/share/f3d/configs/config_build.d") f3d_test(NAME TestDefaultConfigFileVTU DATA dragon.vtu CONFIG config_build LONG_TIMEOUT TONE_MAPPING) @@ -332,12 +351,12 @@ endif() # color texture with opacity needs https://gitlab.kitware.com/vtk/vtk/-/merge_requests/9467 if(VTK_VERSION VERSION_GREATER_EQUAL 9.2.20220811) - f3d_test(NAME TestTextureColorWithOptions DATA WaterBottle.glb ARGS --geometry-only --texture-base-color=${F3D_SOURCE_DIR}/testing/data/albedo_mod.png --color=1,1,0 --opacity=0.4 --translucency-support) + f3d_test(NAME TestTextureColorWithOptions DATA WaterBottle.glb ARGS --texture-base-color=${F3D_SOURCE_DIR}/testing/data/albedo_mod.png --color=1,1,0 --opacity=0.4 --translucency-support) endif() -f3d_test(NAME TestOBJImporter DATA world.obj) f3d_test(NAME TestGLTFImporterUnlit DATA UnlitTest.glb) f3d_test(NAME TestMaterial DATA suzanne.ply ARGS --color=0.72,0.45,0.2 --metallic=0.7 --roughness=0.2) +f3d_test(NAME TestMaterialFullScene DATA WaterBottle.glb ARGS --color=0.9,0.1,0.1 --metallic=0.7 --roughness=0.2) f3d_test(NAME TestMetaData DATA pdiag.vtu ARGS -m) f3d_test(NAME TestEdges DATA suzanne.ply ARGS -e) f3d_test(NAME TestLineWidth DATA cow.vtk ARGS -e --line-width=5) @@ -387,6 +406,16 @@ endif() f3d_test(NAME TestCameraPersp DATA Cameras.gltf ARGS --camera-index=0) f3d_test(NAME TestCameraOrtho DATA Cameras.gltf ARGS --camera-index=1) f3d_test(NAME TestCameraIndexConfiguration DATA Cameras.gltf ARGS --camera-index=0 --camera-azimuth-angle=15 --camera-position=0.7,0.5,3) +f3d_test(NAME TestCameraIndexInvalid DATA Cameras.gltf ARGS --camera-index=3 REGEXP "is higher than the number of available camera" NO_BASELINE) +f3d_test(NAME TestCameraIndexNegative DATA Cameras.gltf ARGS --camera-index=-1 REGEXP "Invalid camera index" NO_BASELINE) + +# Require improved importer support https://gitlab.kitware.com/vtk/vtk/-/merge_requests/11303 +if(VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240910) + f3d_test(NAME TestInvalidFileFileName DATA invalid.vtp ARGS --filename NO_DATA_FORCE_RENDER) + f3d_test(NAME TestMultiFileInvalidFilesFileName DATA mb/mb_3_0.vtt invalid.vtp ARGS --multi-file-mode=all --filename NO_DATA_FORCE_RENDER) + f3d_test(NAME TestMultiFileCameraIndex DATA Cameras.gltf CameraAnimated.glb ARGS --multi-file-mode=all --camera-index=2 --opacity=0.5 --translucency-support) +endif() + # Test Verbose camera f3d_test(NAME TestVerboseCamera DATA Cameras.gltf ARGS --camera-index=1 --verbose NO_RENDER REGEXP "0:.*1:") @@ -574,8 +603,7 @@ if(F3D_PLUGIN_BUILD_DRACO) # Needs https://gitlab.kitware.com/vtk/vtk/-/merge_requests/10884 if(VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240214) f3d_test(NAME TestGLTFDracoImporter DATA Box_draco.gltf ARGS --load-plugins=draco --camera-position=-1.6,1.3,2.7) - f3d_test(NAME TestGLTFDracoReader DATA Box_draco.gltf ARGS --load-plugins=draco --geometry-only --camera-position=-1.6,1.3,2.7) - f3d_test(NAME TestGLTFDracoReaderWithoutCompression DATA BoxAnimated.gltf ARGS --load-plugins=draco --geometry-only --animation-time=2 --animation-progress) + f3d_test(NAME TestGLTFDracoImporterWithoutCompression DATA BoxAnimated.gltf ARGS --load-plugins=draco --animation-time=2 --animation-progress) endif() if(NOT F3D_MACOS_BUNDLE) @@ -647,13 +675,15 @@ if(F3D_PLUGIN_BUILD_OCCT) f3d_test(NAME TestBinaryBREP DATA f3d.bin.brep ARGS --load-plugins=occt --up=+Z) if(VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240707) - f3d_test(NAME TestInvalidBREP DATA invalid.brep ARGS --verbose --load-plugins=occt REGEXP "failed to load geometry" NO_BASELINE) + f3d_test(NAME TestInvalidBREP DATA invalid.brep ARGS --verbose --load-plugins=occt REGEXP "failed to load scene" NO_BASELINE) endif() if(F3D_PLUGIN_OCCT_COLORING_SUPPORT) f3d_test(NAME TestXCAFColors DATA xcaf-colors.stp ARGS --load-plugins=occt -csy --up=+Z --line-width=3 --camera-direction=-1,-1,-1) f3d_test(NAME TestXCAFColorsXBF DATA xcaf-colors.xbf ARGS --load-plugins=occt -csy --up=+Z --line-width=3 --camera-direction=-1,-1,-1) - f3d_test(NAME TestInvalidXBF DATA invalid.xbf ARGS --verbose --load-plugins=occt REGEXP "A reader failed to update" NO_BASELINE) + if(VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240707) + f3d_test(NAME TestInvalidXBF DATA invalid.xbf ARGS --verbose --load-plugins=occt REGEXP "failed to load scene" NO_BASELINE) + endif() if (NOT F3D_MACOS_BUNDLE) file(COPY "${F3D_SOURCE_DIR}/plugins/occt/configs/config.d/" DESTINATION "${CMAKE_BINARY_DIR}/share/f3d/configs/config_build.d") @@ -723,14 +753,19 @@ f3d_test(NAME TestInteractionMisc DATA cow.vtp NO_BASELINE INTERACTION) #KK f3d_test(NAME TestInteractionCycleCell DATA waveletArrays.vti INTERACTION) #VCCC f3d_test(NAME TestInteractionCycleComp DATA dragon.vtu INTERACTION) #SYYYY f3d_test(NAME TestInteractionCycleScalars DATA dragon.vtu INTERACTION) #BSSSS +f3d_test(NAME TestInteractionCycleCellInvalidIndex DATA waveletArrays.vti INTERACTION) #SSC +f3d_test(NAME TestInteractionVolumeCycle DATA waveletArrays.vti ARGS INTERACTION) #VSS +f3d_test(NAME TestInteractionVolumeAfterColoring DATA waveletArrays.vti ARGS INTERACTION) #SSSV f3d_test(NAME TestInteractionVolumeInverse DATA HeadMRVolume.mhd ARGS --camera-position=127.5,-400,127.5 --camera-view-up=0,0,1 INTERACTION) #VI +f3d_test(NAME TestInteractionMultiFileVolume DATA multi ARGS --multi-file-mode=all INTERACTION) #SSVB f3d_test(NAME TestInteractionPointCloud DATA pointsCloud.vtp ARGS --point-sprites-size=20 INTERACTION) #O f3d_test(NAME TestInteractionDirectory DATA mb INTERACTION ARGS --scalar-coloring) #Right;Right;Right;Left;Up; -f3d_test(NAME TestInteractionDirectoryLoop DATA mb INTERACTION ARGS --scalar-coloring) #Left;Left;Left; +f3d_test(NAME TestInteractionDirectoryLoop DATA mb/recursive INTERACTION ARGS --scalar-coloring --filename) #Left;Left;Left;Left;Left; f3d_test(NAME TestInteractionDirectoryEmpty DATA mb INTERACTION NO_DATA_FORCE_RENDER) #Right;Right;Right; f3d_test(NAME TestInteractionDirectoryEmptyVerbose DATA mb ARGS --verbose NO_BASELINE INTERACTION REGEXP "is not a file of a supported file format") #Right;Right;Right;HMCSY f3d_test(NAME TestInteractionAnimationNotStopped DATA InterpolationTest.glb NO_BASELINE INTERACTION)#Space; f3d_test(NAME TestInteractionResetCamera DATA dragon.vtu INTERACTION)#MouseMovements;Return; +f3d_test(NAME TestInteractionResetCameraWithCameraIndex DATA CameraAnimated.glb ARGS --camera-index=0 INTERACTION)#MouseMovements;Return; f3d_test(NAME TestInteractionTensorsCycleComp DATA tensors.vti ARGS --scalar-coloring --comp=-2 INTERACTION) #SYYYYYYYYYY f3d_test(NAME TestInteractionCycleScalarsCompCheck DATA dragon.vtu ARGS -b --scalar-coloring --comp=2 INTERACTION) #S f3d_test(NAME TestInteractionConfigFileNoColorBar DATA multi CONFIG ${F3D_SOURCE_DIR}/testing/configs/complex.json INTERACTION) #Right;Right;Left @@ -744,11 +779,11 @@ f3d_test(NAME TestInteractionFocalPointPickingDefault DATA dragon.vtu INTERACTIO f3d_test(NAME TestInteractionFocalPointPickingShift DATA dragon.vtu INTERACTION) f3d_test(NAME TestInteractionFocalPointPickingPoints DATA pointsCloud.vtp INTERACTION) f3d_test(NAME TestInteractionLightIntensity DATA dragon.vtu INTERACTION) -f3d_test(NAME TestInteractionGroupGeometriesColoring DATA mb/recursive ARGS --group-geometries INTERACTION) #SSB +f3d_test(NAME TestInteractionMultiFileColoring DATA mb/recursive ARGS --multi-file-mode=all INTERACTION) #SSSB f3d_test(NAME TestInteractionReload DATA dragon.vtu ARGS -e INTERACTION) #Up; f3d_test(NAME TestInteractionLoadParentDirectory DATA multi/dragon.vtu ARGS --filename INTERACTION) #Down; -f3d_test(NAME TestInteractionEmptyLoadParentDirectory INTERACTION NO_BASELINE REGEXP "No file loaded, no rendering performed") #Down; -f3d_test(NAME TestInteractionGroupGeometriesLoadParentDirectory DATA mb/mb_0_0.vtu ARGS --group-geometries --filename INTERACTION) #Down; +f3d_test(NAME TestInteractionEmptyLoadParentDirectory INTERACTION NO_BASELINE REGEXP "No files loaded, no rendering performed") #Down; +f3d_test(NAME TestInteractionMultiFileLoadParentDirectory DATA mb/mb_0_0.vtu ARGS --multi-file-mode=all --filename INTERACTION) #Down; f3d_test(NAME TestInteractionInvertZoom DATA suzanne.ply ARGS --invert-zoom INTERACTION) f3d_test(NAME TestInteractionCameraHotkeys DATA cow.vtp INTERACTION) f3d_test(NAME TestInteractionZoomToMouse DATA cow.vtp INTERACTION) @@ -768,8 +803,8 @@ if(F3D_SANITIZER STREQUAL "none") endif() f3d_test(NAME TestInteractionCycleAnimationNoAnimation DATA cow.vtp INTERACTION NO_BASELINE) #W -f3d_test(NAME TestInteractionDropFiles INTERACTION_CONFIGURE)#X;DropEvent cow.vtp;DropEvent dragon.vtu suzanne.stl; -f3d_test(NAME TestInteractionGroupGeometriesDrop DATA mb/mb_1_0.vtp ARGS --group-geometries -e INTERACTION_CONFIGURE) #DropEvent mb_2_0.vtp +f3d_test(NAME TestInteractionDropFiles ARGS -n INTERACTION_CONFIGURE)#X;DropEvent cow.vtp;DropEvent dragon.vtu suzanne.stl; +f3d_test(NAME TestInteractionMultiFileDrop ARGS --multi-file-mode=all -e INTERACTION_CONFIGURE) #DropEvent mb_1_0.vtp mb_2_0.vtp f3d_test(NAME TestInteractionDropSameFiles ARGS -x INTERACTION_CONFIGURE)#DropEvent cow.vtp;#DropEvent dragon.vtu;#DropEvent cow.vtp#DropEvent cow.vtp; # HDRI test needs https://gitlab.kitware.com/vtk/vtk/-/merge_requests/9767 @@ -778,6 +813,10 @@ if(VTK_VERSION VERSION_GREATER_EQUAL 9.2.20221220) f3d_test(NAME TestInteractionDropHDRIInvert INTERACTION_CONFIGURE LONG_TIMEOUT)#X;DropEvent palermo.hdr;DropEvent dragon.vtu; f3d_test(NAME TestInteractionDropHDRIMulti INTERACTION_CONFIGURE LONG_TIMEOUT)#X;DropEvent dragon.vtu palermo.hdr; + # Test modified drops, this test rendering is impacted by https://github.com/f3d-app/f3d/issues/1558 + # Empty drop is for coverage + f3d_test(NAME TestInteractionDropHDRIModifiers INTERACTION_CONFIGURE LONG_TIMEOUT)#CTRL+DropEvent f3d.tif;SHIFT+DropEvent;SHIFT+DropEven palermo.tif;SYYYY + # Test interactive animation and dropping HDRI if(F3D_SANITIZER STREQUAL "none") f3d_test(NAME TestInteractionAnimationDropHDRI DATA InterpolationTest.glb ARGS --animation-index=-1 --animation-progress INTERACTION_CONFIGURE LONG_TIMEOUT)#Space;DropEvent palermo.hdr;Space; @@ -792,7 +831,7 @@ endif() # Output option test f3d_test(NAME TestOutput DATA cow.vtp NO_BASELINE) f3d_test(NAME TestOutputOutput DATA cow.vtp ARGS --ref=${CMAKE_BINARY_DIR}/Testing/Temporary/TestOutput.png DEPENDS TestOutput NO_BASELINE) -f3d_test(NAME TestUnsupportedInputOutput DATA unsupportedFile.dummy REGEXP "No file loaded, no rendering performed" NO_BASELINE) +f3d_test(NAME TestUnsupportedInputOutput DATA unsupportedFile.dummy REGEXP "No files loaded, no rendering performed" NO_BASELINE) f3d_test(NAME TestOutputNoBackground DATA cow.vtp ARGS --no-background NO_BASELINE) # Test Non existent interaction record directory @@ -831,14 +870,20 @@ f3d_test(NAME TestVerboseWrongArray DATA dragon.vtu ARGS -s --coloring-array=dum # Default scalar array verbosity test f3d_test(NAME TestVerboseDefaultScalar DATA HeadMRVolume.mhd ARGS -s --verbose REGEXP "Coloring using point array named MetaImage, Magnitude" NO_BASELINE) +# Volume array verbosity test +f3d_test(NAME TestVerboseVolume DATA HeadMRVolume.mhd ARGS -v --verbose REGEXP "Coloring using point array named MetaImage .forced., Magnitude" NO_BASELINE) + # Incorrect component test f3d_test(NAME TestIncorrectComponent DATA dragon.vtu ARGS -s --comp=4 REGEXP "Invalid component index: 4" NO_BASELINE) +# Incorrect volume coloring with multi file +f3d_test(NAME TestIncorrectMultiFileVolume DATA multi ARGS -sv --coloring-array=Normals --multi-file-mode=all REGEXP "Cannot find the array \"Normals\" to display volume with" NO_BASELINE) + # Incorrect color map f3d_test(NAME TestIncorrectColormap DATA IM-0001-1983.dcm ARGS --scalar-coloring --roughness=1 --colormap=0,1,0,0,1,0,1 REGEXP "Specified color map list count is not a multiple of 4, ignoring it." NO_BASELINE) # Test opening a directory -f3d_test(NAME TestVerboseDirectory DATA mb REGEXP "Loading 3D geometry: .*mb_._0.vt." NO_RENDER) +f3d_test(NAME TestVerboseDirectory DATA mb REGEXP "mb_0_0.vtu" NO_RENDER) # Test opening multiple file and rendering only one f3d_test(NAME TestVerboseMultiFileRender DATA mb REGEXP "An output image was saved using a single 3D file, other provided 3D files were ignored." NO_BASELINE) @@ -864,7 +909,7 @@ f3d_test(NAME TestTensorsDirect DATA tensors.vti ARGS -s --coloring-array=tensor f3d_test(NAME TestTensorsVolumeDirect DATA tensors.vti ARGS -v -s --coloring-array=tensors1 --comp=-2 REGEXP "Direct scalars rendering not supported by array with more than 4 components" NO_BASELINE) # Test volume rendering without any array -f3d_test(NAME TestVerboseVolumeNoArray DATA cow.vtp ARGS -v REGEXP "Cannot use volume with this dataset" NO_BASELINE) +f3d_test(NAME TestVerboseVolumeNoArray DATA cow.vtp ARGS -v REGEXP "Cannot use volume with this data" NO_BASELINE) # Test scalar rendering without any array f3d_test(NAME TestVerboseNoArray DATA cow.vtp ARGS -s --verbose=debug REGEXP "No array to color with" NO_BASELINE) @@ -895,19 +940,22 @@ f3d_test(NAME TestUnsupportedFileText DATA unsupportedFile.dummy ARGS --filename f3d_test(NAME TestNonExistentTexture DATA cow.vtp ARGS --texture-material=${F3D_SOURCE_DIR}/testing/data/dummy.png REGEXP "Texture file does not exist" NO_BASELINE) if(VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240707) - # Test invalid geometry - f3d_test(NAME TestInvalidGeometry DATA world_invalid.obj ARGS --geometry-only REGEXP "failed to load geometry" NO_BASELINE) - - # Test invalid full scene file - f3d_test(NAME TestInvalidFullScene DATA duck_invalid.gltf REGEXP "failed to load scene" NO_BASELINE) + # Test invalid file + f3d_test(NAME TestInvalidFile DATA duck_invalid.gltf REGEXP "failed to load scene" NO_BASELINE) - # Test invalid geometry with animation - f3d_test(NAME TestInteractionAnimationInvalid DATA BoxAnimated_invalid_animation.gltf ARGS --geometry-only REGEXP "A reader failed to update at a timeValue" INTERACTION NO_BASELINE) + # Test invalid animation + f3d_test(NAME TestAnimationInvalid DATA BoxAnimated_invalid_animation.gltf ARGS --animation-time 1 REGEXP "Could not load time value: 1" NO_BASELINE) endif () # Test invalid texture f3d_test(NAME TestInvalidTexture DATA cow.vtp ARGS --texture-material=${F3D_SOURCE_DIR}/testing/data/invalid.png REGEXP "Cannot open texture file" NO_BASELINE) +# Test invalid color +f3d_test(NAME TestInvalidColor DATA cow.vtp ARGS --color=0,0,0,1 REGEXP "Invalid surface color provided, not applying" NO_BASELINE) + +# Test invalid emmisive factor +f3d_test(NAME TestInvalidEmissiveFactor DATA cow.vtp ARGS --emissive-factor=0,0,0,1 REGEXP "Invalid emissive factor provided, not applying" NO_BASELINE) + # Test non existent interaction file, do not add a TestNonExistentInteraction f3d_test(NAME TestNonExistentInteraction DATA cow.vtp INTERACTION REGEXP "Interaction record file to play does not exist" NO_BASELINE) @@ -927,6 +975,13 @@ f3d_test(NAME TestNonExistentConfigFileStem DATA cow.vtp CONFIG "dummy" REGEXP " # Test invalid config file f3d_test(NAME TestInvalidConfigFile DATA cow.vtp CONFIG ${F3D_SOURCE_DIR}/testing/configs/invalid.json REGEXP "Unable to parse the configuration file" NO_BASELINE) +# Test invalid multifile mode +f3d_test(NAME TestInvalidMultiFileMode DATA mb/recursive ARGS --multi-file-mode=add REGEXP "Unrecognized multi-file-mode: add. Assuming \"single\" mode." NO_BASELINE) + +# Test unnamed cameras/animation +f3d_test(NAME TestVerboseUnnamedCamera DATA Cameras.gltf ARGS --verbose REGEXP "1: unnamed_1" NO_BASELINE) +f3d_test(NAME TestVerboseUnnamedAnimation DATA BoxAnimated.gltf ARGS --verbose REGEXP "0: unnamed_0" NO_BASELINE) + # Test invalid value in config file f3d_test(NAME TestConfigFileInvalidValue DATA cow.vtp CONFIG ${F3D_SOURCE_DIR}/testing/configs/invalid_value.json REGEXP "must be a string, a boolean or a number" NO_BASELINE) @@ -940,7 +995,7 @@ f3d_test(NAME TestConfigFileInexistentKey DATA cow.vtp CONFIG ${F3D_SOURCE_DIR}/ f3d_test(NAME TestConfigFileQuiet DATA nonExistentFile.vtp CONFIG ${F3D_SOURCE_DIR}/testing/configs/quiet.json REGEXP_FAIL "File .*/testing/data/nonExistentFile.vtp does not exist" NO_BASELINE) # Test no file with config file -f3d_test(NAME TestNoFileConfigFile CONFIG ${F3D_SOURCE_DIR}/testing/configs/verbose.json ARGS --verbose REGEXP "No file to load provided." NO_BASELINE) +f3d_test(NAME TestNoFileConfigFile CONFIG ${F3D_SOURCE_DIR}/testing/configs/verbose.json ARGS --verbose REGEXP "No files to load provided" NO_BASELINE) # Test help display f3d_test(NAME TestHelp ARGS --help REGEXP "Usage:") @@ -952,11 +1007,35 @@ f3d_test(NAME TestVersion ARGS --version REGEXP "Version:") # Test readers-list display f3d_test(NAME TestReadersList ARGS --readers-list REGEXP_FAIL "No registered reader found") +# Test invalid component string coverage +f3d_test(NAME TestInteractionInvalidComponent INTERACTION DATA cow.vtp ARGS --comp=1 NO_BASELINE) #H + # Test multi plugin readers-lists if(F3D_PLUGIN_BUILD_ALEMBIC AND F3D_PLUGIN_BUILD_ASSIMP) f3d_test(NAME TestReadersListMultiplePlugins ARGS --readers-list --load-plugins=assimp,alembic NO_BASELINE REGEXP_FAIL "Plugin failed to load") endif() +# Test rendering backends +# For some reason the sanitizer detects leaks because of EGL and OSMesa +f3d_test(NAME TestRenderingBackendAuto DATA cow.vtp RENDERING_BACKEND auto) +if(UNIX AND NOT APPLE AND VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240914 AND F3D_SANITIZER STREQUAL "none") + f3d_test(NAME TestRenderingBackendEGL DATA cow.vtp RENDERING_BACKEND egl) + f3d_test(NAME TestRenderingBackendEGLCheckClass DATA cow.vtp RENDERING_BACKEND egl ARGS --verbose REGEXP "vtkEGLRenderWindow" NO_BASELINE) + f3d_test(NAME TestRenderingBackendOSMesa DATA cow.vtp RENDERING_BACKEND osmesa) + f3d_test(NAME TestRenderingBackendOSMesaCheckClass DATA cow.vtp RENDERING_BACKEND osmesa ARGS --verbose REGEXP "vtkOSOpenGLRenderWindow" NO_BASELINE) + f3d_test(NAME TestRenderingBackendWGLFailure DATA cow.vtp RENDERING_BACKEND wgl REGEXP "Cannot use a WGL context on this platform" NO_BASELINE) + if(F3D_TESTING_ENABLE_GLX_TESTS) + f3d_test(NAME TestRenderingBackendGLX DATA cow.vtp RENDERING_BACKEND glx) + f3d_test(NAME TestRenderingBackendGLXCheckClass DATA cow.vtp RENDERING_BACKEND glx ARGS --verbose REGEXP "vtkXOpenGLRenderWindow" NO_BASELINE) + endif() +endif() +if(WIN32) + f3d_test(NAME TestRenderingBackendWGL DATA cow.vtp RENDERING_BACKEND wgl) + f3d_test(NAME TestRenderingBackendWGLCheckClass DATA cow.vtp RENDERING_BACKEND wgl ARGS --verbose REGEXP "vtkWin32OpenGLRenderWindow" NO_BASELINE) + f3d_test(NAME TestRenderingBackendGLXFailure DATA cow.vtp RENDERING_BACKEND glx REGEXP "Cannot use a GLX context on this platform" NO_BASELINE) +endif() +f3d_test(NAME TestRenderingBackendInvalid DATA cow.vtp RENDERING_BACKEND invalid ARGS --verbose REGEXP "rendering-backend value is invalid, falling back to" NO_BASELINE) + # Test that f3d can try to read a system config file add_test(NAME f3d::TestNoDryRun COMMAND $ --no-render) set_tests_properties(f3d::TestNoDryRun PROPERTIES TIMEOUT 4) @@ -968,6 +1047,15 @@ set_tests_properties(f3d::TestInvalidCLIArgs PROPERTIES PASS_REGULAR_EXPRESSION # Test that f3d resolution can be controlled from config file add_test(NAME f3d::TestConfigResolution COMMAND $ --config=${F3D_SOURCE_DIR}/testing/configs/resolution.json ${F3D_SOURCE_DIR}/testing/data/suzanne.stl --output=${CMAKE_BINARY_DIR}/Testing/Temporary/TestConfigResolution.png --ref=${F3D_SOURCE_DIR}/testing/baselines/TestConfigResolution.png) +# Test filename template with multiple files +add_test(NAME f3d::TestMultiFileFileNameTemplate COMMAND $ ${F3D_SOURCE_DIR}/testing/data/suzanne.stl ${F3D_SOURCE_DIR}/testing/data/dragon.vtu --output=${CMAKE_BINARY_DIR}/Testing/Temporary/{model.ext}.png --multi-file-mode=all --verbose) +set_tests_properties(f3d::TestMultiFileFileNameTemplate PROPERTIES PASS_REGULAR_EXPRESSION "Output image saved to \".*/Testing/Temporary/multi_file.png\"") + +# Test filename template with no files +add_test(NAME f3d::TestNoFileFileNameTemplate COMMAND $ --output=${CMAKE_BINARY_DIR}/Testing/Temporary/{model.ext}.png --verbose) +set_tests_properties(f3d::TestNoFileFileNameTemplate PROPERTIES PASS_REGULAR_EXPRESSION "Output image saved to \".*/Testing/Temporary/no_file.png\"") +set_tests_properties(f3d::TestNoFileFileNameTemplate PROPERTIES ENVIRONMENT "CTEST_F3D_NO_DATA_FORCE_RENDER=1") + # Test failure without a reference, please do not create a TestNoRef.png file f3d_test(NAME TestNoRef DATA cow.vtp WILL_FAIL) @@ -998,8 +1086,7 @@ if(NOT F3D_MACOS_BUNDLE) endif() endif() -# Watch testing require onscreen rendering -if(NOT VTK_OPENGL_HAS_EGL) +if(WIN32 OR APPLE OR F3D_TESTING_ENABLE_GLX_TESTS) # Watch testing require GLX rendering on Linux if(UNIX) set(_f3d_os_script_ext "sh") set(_f3d_os_script_exec "") diff --git a/cmake/f3dOptions.cmake b/cmake/f3dOptions.cmake index e7c5e5924c..c5ce696e05 100644 --- a/cmake/f3dOptions.cmake +++ b/cmake/f3dOptions.cmake @@ -129,6 +129,11 @@ function(_parse_json_option _top_json) elseif(_option_type STREQUAL "ratio") set(_option_actual_type "f3d::ratio_t") set(_option_variant_type "double") + elseif(_option_type STREQUAL "vector") + set(_option_actual_type "f3d::vector3_t") + set(_option_variant_type "std::vector") + set(_option_default_value_start "{") + set(_option_default_value_end "}") endif() # Add option to struct and methods diff --git a/cmake/f3dPlugin.cmake b/cmake/f3dPlugin.cmake index 41021a01a6..414da288b3 100644 --- a/cmake/f3dPlugin.cmake +++ b/cmake/f3dPlugin.cmake @@ -129,6 +129,10 @@ macro(f3d_plugin_declare_reader) set(F3D_READER_HAS_GEOMETRY_READER 0) endif() + if (NOT F3D_READER_HAS_SCENE_READER AND NOT F3D_READER_HAS_GEOMETRY_READER) + message(FATAL_ERROR "Please provide either a VTK_IMPORTER or a VTK_READER") + endif () + string(JSON F3D_PLUGIN_JSON SET "${F3D_PLUGIN_JSON}" "readers" ${F3D_PLUGIN_CURRENT_READER_INDEX} "${F3D_READER_JSON}") @@ -196,7 +200,7 @@ The `NAME` argument is required. The arguments are as follows: macro(f3d_plugin_build) cmake_parse_arguments(F3D_PLUGIN "FREEDESKTOP;FORCE_STATIC" "NAME;DESCRIPTION;VERSION" "VTK_MODULES;ADDITIONAL_RPATHS;MIMETYPE_XML_FILES;CONFIGURATION_DIRS" ${ARGN}) - find_package(VTK 9.0 REQUIRED COMPONENTS + find_package(VTK 9.2.6 REQUIRED COMPONENTS CommonCore CommonExecutionModel IOImport ${F3D_PLUGIN_VTK_MODULES}) diff --git a/cmake/pluginsdk-config.cmake b/cmake/pluginsdk-config.cmake index 122d17c660..09e94ca667 100644 --- a/cmake/pluginsdk-config.cmake +++ b/cmake/pluginsdk-config.cmake @@ -3,7 +3,7 @@ if (UNIX AND NOT APPLE) find_package(X11) endif() find_package(OpenGL) -find_package(VTK 9.0 REQUIRED COMPONENTS CommonCore CommonExecutionModel IOImport) +find_package(VTK 9.2.6 REQUIRED COMPONENTS CommonCore CommonExecutionModel IOImport) include("${CMAKE_CURRENT_LIST_DIR}/f3dPlugin.cmake") include("${CMAKE_CURRENT_LIST_DIR}/../f3d_vtkext/f3d_vtkext-targets.cmake") diff --git a/doc/dev/TESTING.md b/doc/dev/TESTING.md index 433bc2d389..8ae0acfa18 100644 --- a/doc/dev/TESTING.md +++ b/doc/dev/TESTING.md @@ -8,6 +8,12 @@ There are a few CMake options to configure the F3D testing framework: * `BUILD_TESTING`: Enable the test framework, off by default. Requires [git LFS](https://git-lfs.com/) if repository is cloned. * `F3D_TESTING_ENABLE_RENDERING_TESTS`: An option to enable/disable test that require rendering capabilities, on by default. * `F3D_TESTING_ENABLE_LONG_TIMEOUT_TESTS`: Certain tests can take some time to run, off by default, requires rendering tests. +* `F3D_TESTING_FORCE_RENDERING_BACKEND`: Configure the rendering backend to use. Can be `auto` (default), `glx`, `wgl`, `egl` or `osmesa`. +* `F3D_TESTING_ENABLE_GLX_TESTS`: Enable tests requiring a X11 server running on Linux. +* `F3D_TESTING_ENABLE_EXTERNAL_GLFW`: Enable test requiring GLFW dependency. +* `F3D_TESTING_ENABLE_EXTERNAL_QT`: Enable test requiring QT dependency. +* `F3D_TESTING_ENABLE_EXTERNAL_OSMESA`: Enable test requiring OSMesa dependency. +* `F3D_TESTING_ENABLE_EXTERNAL_EGL`: Enable test requiring EGL dependency. ## Running the tests diff --git a/doc/libf3d/BINDINGS.md b/doc/libf3d/BINDINGS.md index a695d16eab..b0432cf2f6 100644 --- a/doc/libf3d/BINDINGS.md +++ b/doc/libf3d/BINDINGS.md @@ -17,7 +17,7 @@ eng.options.update({ "render.grid.enable": True, }) -eng.loader.load_geometry("f3d/testing/data/dragon.vtu") +eng.scene.add("f3d/testing/data/dragon.vtu") eng.interactor.start() ``` @@ -40,8 +40,8 @@ public class F3DExample { // Always use try-with-resources idiom to ensure the native engine is released try (Engine engine = new Engine(Window.Type.NATIVE)) { - Loader loader = engine.getLoader(); - loader.loadGeometry("f3d/testing/data/dragon.vtu"); + Scene scene = engine.getScene(); + scene.add("f3d/testing/data/dragon.vtu"); engine.getWindow().render(); } diff --git a/doc/libf3d/CLASSES.md b/doc/libf3d/CLASSES.md index 10b5b7f404..40934b4de3 100644 --- a/doc/libf3d/CLASSES.md +++ b/doc/libf3d/CLASSES.md @@ -5,30 +5,35 @@ For the complete documentation, please consult the [libf3d doxygen documentation ## Engine class -The engine class is the main class that needs to be instantiated. All other classes instance are provided by the engine using getters, `getLoader`, `getWindow`, `getInteractor`, `getOptions`. - -The engine constructor lets you choose the type of window in its constructor, `NONE`, `NATIVE`, `NATIVE_OFFSCREEN`, `EXTERNAL`. Default is `NATIVE`. See [Window class](#window-class) documentation for more info. Please note that the engine will not provide a interactor with `NONE` and `EXTERNAL`. +The engine class is the main class that needs to be instantiated. All other classes instance are provided by the engine using getters, `getScene`, `getWindow`, `getInteractor`, `getOptions`. + +The engine factory lets you choose between the different types of OpenGL rendering backend. +The generic `create()` is recommended in most cases and will use the best context possible available on your system. +However, it's possible to force the rendering backend in some specific use cases: +* `createGLX()`: force usage of GLX backend, works on Linux only and requires a X11 server to run. +* `createWGL()`: force usage of WGL native backend on Windows. +* `createEGL()`: force usage of EGL backend, recommended when doing offscreen rendering with a GPU available. Requires EGL library available. No interactor provided. +* `createOSMesa()`: force usage of OSMesa backend (software rendering), recommended when doing offscreen rendering without any GPU. Requires OSMesa library available. No interactor provided. +* `createNone()`: do not use any rendering. Useful to retrieve metadata only. +* `createExternal()`: the user is responsible of the rendering stack. It lets the user integrate libf3d in other frameworks like Qt or GLFW. No interactor provided. See [Context](#context-class) documentation for more info. +An additional boolean argument is available to specify if offscreen rendering is requested when relevant on the selected rendering backend. A static function `loadPlugin` can also be called to load reader plugins. It must be called before loading any file. An internal plugin containing VTK native readers can be loaded by calling `f3d::engine::loadPlugin("native");`. Other plugins maintained by F3D team are available if their build is enabled: `alembic`, `assimp`, `draco`, `exodus`, `occt` and `usd`. If CMake option `F3D_PLUGINS_STATIC_BUILD` is enabled, the plugins listed above are also static just like `native` plugin. All static plugins can be loaded using `f3d::engine::autoloadPlugins()`. -## Loader class - -The loader class is responsible to read and load the file from the disk. It supports reading full scene files as well as multiple geometries into a default scene. - -## Window class - -The window class is responsible for rendering the meshes. It supports multiple modes. +## Scene class -* `NONE`: A window that will not render anything, very practical when only trying to recover meta-information about the data. +The scene class is responsible to `add` file from the disk into the scene. It supports reading multiple files at the same time and even mesh from memory. +It is possible to `clear` the scene and to check if the scene `supports` a file. -* `NATIVE`: Default mode where a window is shown onscreen using native graphical capabilities. +## Context class -* `NATIVE_OFFSCREEN`: Use native graphical capabilities for rendering, but unto an offscreen window, which will not appear on screen, practical when generating screenshots. +Convenience class providing generic context API when using a external rendering backend (using `f3d::engine::createExternal()` factory). -* `EXTERNAL`: A window where the OpenGL context is not created but assumed to have been created externally. To be used with other frameworks like Qt or GLFW. +## Window class +The window class is responsible for rendering the data. Window lets you `render`, `renderToImage` and control other parameters of the window, like icon or windowName. ## Interactor class @@ -37,7 +42,13 @@ When provided by the engine, the interactor class lets you choose how to interac It contains the animation API to start and stop animation. -Interactor also lets you set your interaction callbacks in order to modify how the interaction with the data is done. +Interactor lets you add, remove and trigger your [command](../user/COMMANDS.md) callbacks. +Command lets you interact with the libf3d in a very flexible manner. + +Interactor also lets you add and remove interaction commands in order to modify how +the libf3d react to different interactions, eg. when a key is pressed or when a file is dropped. + +Use `log::setVerboseLevel(log::VerboseLevel::DEBUG)` to print debug information on interaction and command use. Of course, you can use `start` and `stop` to control the interactor behavior. @@ -55,7 +66,7 @@ A class to control logging in the libf3d. Simple using the different dedicated m ## Options class -This class lets you control the behavior of the libf3d. An option is basically a string used as a key associated with a value, see the exhaustive [list](OPTIONS.md). +This class lets you control the behavior of the libf3d. An option is basically a value that can be a optional or not. There is different API to access it, see the exhaustive [doc](OPTIONS.md). ## Reader class diff --git a/doc/libf3d/OPTIONS.md b/doc/libf3d/OPTIONS.md index 10aba01ae4..ead0a5c702 100644 --- a/doc/libf3d/OPTIONS.md +++ b/doc/libf3d/OPTIONS.md @@ -9,8 +9,8 @@ The possible option are listed below and are organized by categories and subcate * `render` options are related to the way the render is done * `render.effect` options are related to specific techniques used that modify the render * `ui` options are related to the screenspace UI element displayed - * `model` options are related to modifications on the model, they are only meaningful when using the default scene - * `interactor` options requires an interactor to be present to have any effect. + * `model` options are related to modifications on the model + * `interactor` options requires an interactor to be present to have any effect Please note certain options are taken into account when rendering, others when loading a file. See the exhaustive list below, but note that this may change in the future. @@ -27,9 +27,9 @@ scene.animation.index|int
0
load|Select the animation to load.
Any nega scene.animation.speed_factor|double
1
render|Set the animation speed factor to slow, speed up or even invert animation.|\-\-animation-speed-factor scene.animation.time|double
optional
load|Set the animation time to load.|\-\-animation-time scene.animation.frame_rate|double
60
render|Set the animation frame rate used to play the animation interactively.|\-\-animation-frame-rate -scene.camera.index|int
optional
load|Select the scene camera to use when available in the file.
Any negative value means automatic camera.
The default scene always uses automatic camera.|\-\-camera-index -scene.up_direction|string
+Y
load|Define the Up direction|\-\-up -scene.camera.orthographic|bool
false
load|Toggles between orthographic projection and parallel mode.|\-\-camera\-orthographic +scene.camera.index|int
optional
load|Select the scene camera to use when available in the file.
The default scene always uses automatic camera.|\-\-camera-index +scene.up_direction|string
+Y
load|Define the Up direction. It impacts the grid, the axis, the HDRI and the camera.|\-\-up +scene.camera.orthographic|bool
optional
load|Set to true to force orthographic projection. Model specified by default, which is false if not specified.|\-\-camera\-orthographic ## Interactor Options @@ -42,17 +42,17 @@ interactor.trackball|bool
false
render|Enable trackball interaction.|\-\-t Option|Type
Default
Trigger|Description|F3D option :---:|:---:|:---|:---: -model.matcap.texture|string
-
render|Path to a texture file containing a material capture. All other model options for surfaces are ignored if this is set.|\-\-texture-matcap -model.color.opacity|double
1.0
render|Set *opacity* on the geometry. Usually used with Depth Peeling option. Multiplied with the `model.color.texture` when present.|\-\-opacity -model.color.rgb|vector\
1.0,1.0,1.0
render|Set a *color* on the geometry. Multiplied with the `model.color.texture` when present.|\-\-color -model.color.texture|string
-
render|Path to a texture file that sets the color of the object. Will be multiplied with rgb and opacity.|\-\-texture-base-color -model.emissive.factor|vector\
1.0,1.0,1.0
render| Multiply the emissive color when an emissive texture is present.|\-\-emissive-factor -model.emissive.texture|string
-
render|Path to a texture file that sets the emitted light of the object. Multiplied with the `model.emissive.factor`.|\-\-texture-emissive -model.material.metallic|double
0.0
render|Set the *metallic coefficient* on the geometry (0.0-1.0). Multiplied with the `model.material.texture` when present.|\-\-metallic -model.material.roughness|double
0.3
render|Set the *roughness coefficient* on the geometry (0.0-1.0). Multiplied with the `model.material.texture` when present.|\-\-roughness -model.material.texture|string
-
render|Path to a texture file that sets the Occlusion, Roughness and Metallic values of the object. Multiplied with the `model.material.roughness` and `model.material.metallic`, set both of them to 1.0 to get a true result.|\-\-texture-material -model.normal.scale|double
1.0
render|Normal scale affects the strength of the normal deviation from the normal texture.|\-\-normal-scale -model.normal.texture|string
-
render|Path to a texture file that sets the normal map of the object.|\-\-texture-normal +model.matcap.texture|string
optional
render|Path to a texture file containing a material capture. All other model options for surfaces are ignored if this is set. Model specified by default.|\-\-texture-matcap +model.color.opacity|double
optional
render|Set *opacity* on the geometry. Usually used with Depth Peeling option. Multiplied with the `model.color.texture` when present. Model specified by default.|\-\-opacity +model.color.rgb|vector\
optional
render|Set a *color* on the geometry. Multiplied with the `model.color.texture` when present. Model specified by default.|\-\-color +model.color.texture|string
optional
render|Path to a texture file that sets the color of the object. Will be multiplied with rgb and opacity. Model specified by default.|\-\-texture-base-color +model.emissive.factor|vector\
optional
render| Multiply the emissive color when an emissive texture is present. Model specified by default.|\-\-emissive-factor +model.emissive.texture|string

render|Path to a texture file that sets the emitted light of the object. Multiplied with the `model.emissive.factor`. Model specified by default.|\-\-texture-emissive +model.material.metallic|double
optional
render|Set the *metallic coefficient* on the geometry (0.0-1.0). Multiplied with the `model.material.texture` when present. Model specified by default.|\-\-metallic +model.material.roughness|double
optional
render|Set the *roughness coefficient* on the geometry (0.0-1.0). Multiplied with the `model.material.texture` when present. Model specified by default.|\-\-roughness +model.material.texture|string
optional
render|Path to a texture file that sets the Occlusion, Roughness and Metallic values of the object. Multiplied with the `model.material.roughness` and `model.material.metallic`, set both of them to 1.0 to get a true result. Model specified by default.|\-\-texture-material +model.normal.scale|double
optional
render|Normal scale affects the strength of the normal deviation from the normal texture. Model specified by default.|\-\-normal-scale +model.normal.texture|string
optional
render|Path to a texture file that sets the normal map of the object. Model specified by default.|\-\-texture-normal model.scivis.enable|bool
false
render|*Color by an array* present in on the data. If `model.scivis.array_name` is not set, the first available array will be used.|\-\-scalar-coloring model.scivis.cells|bool
false
render|Color the data with value found *on the cells* instead of points|\-\-cells model.scivis.colormap|vector\
\
render|Set a *custom colormap for the coloring*.
This is a list of colors in the format `val1,red1,green1,blue1,...,valN,redN,greenN,blueN`
where all values are in the range (0,1).|\-\-colormap @@ -62,7 +62,7 @@ model.scivis.range|vector\
optional
render|Set the *coloring rang model.point_sprites.enable|bool
false
render|Show sphere *points sprites* instead of the geometry.|\-\-point-sprites model.point_sprites.type|string
sphere
render|Set the sprites type when showing point sprites (can be `sphere` or `gaussian`).|\-\-point-stripes-type model.point_sprites.size|double
10.0
render|Set the *size* of point sprites.|\-\-point-stripes-size -model.volume.enable|bool
false
render|Enable *volume rendering*. It is only available for 3D image data (vti, dcm, nrrd, mhd files) and will display nothing with other default scene formats.|\-\-volume +model.volume.enable|bool
false
render|Enable *volume rendering*. It is only available for 3D image data (vti, dcm, nrrd, mhd files) and will display nothing with other formats. It forces coloring.|\-\-volume model.volume.inverse|bool
false
render|Inverse the linear opacity function.|\-\-inverse ## Render Options @@ -75,7 +75,7 @@ render.effect.ambient_occlusion|bool
false
render|Enable *ambient occlusio render.effect.tone_mapping|bool
false
render|Enable generic filmic *Tone Mapping Pass*. This technique is used to map colors properly to the monitor colors.|\-\-tone-mapping render.effect.final_shader|string
optional
render|Add a final shader to the output image|\-\-final-shader. See [user documentation](../user/FINAL_SHADER.md). render.line_width|double
optional
render|Set the *width* of lines when showing edges. Model specified by default.|\-\-line-width -render.show_edges|bool
false
render|Show the *cell edges*|\-\-edges +render.show_edges|bool
optional
render|Set to true to show the *cell edges*. Model specified by default.|\-\-edges render.point_size|double
optional
render|Set the *size* of points when showing vertices. Model specified by default.|\-\-point-size render.backface_type|string
optional
render|Set the Backface type, can be `visible` or `hidden`, model specified by default.|\-\-backface-type render.grid.enable|bool
false
render|Show *a grid* aligned with the horizontal (orthogonal to the Up direction) plane.|\-\-grid @@ -119,7 +119,7 @@ There are three APIs to access the options The most straightforward and easy to use API, just access it through the structs available in the options instance, eg: ```cpp - f3d::engine eng(f3d::window::Type::NATIVE); + f3d::engine eng = f3d::engine::create(); f3d::options& opt = eng.getOptions(); opt.render.show_edges = true; opt.render.grid.enable = true; @@ -130,7 +130,7 @@ The most straightforward and easy to use API, just access it through the structs Please note that when accessing optional options, special care must be used, eg: ```cpp - f3d::engine eng(f3d::window::Type::NATIVE); + f3d::engine eng = f3d::engine::create(); f3d::options& opt = eng.getOptions(); if (opt.render.line_width.has_value()) { @@ -141,13 +141,16 @@ Please note that when accessing optional options, special care must be used, eg: std::cout << "Line Width: unset" << std::endl; } ``` + +It's even more true with the few optional boolean options as std::optional has an implicit boolean cast operator. + ## String API The most generic and flexible API, as it rely on parsing and string generation. The documentation about option parsing is upcoming. ```cpp - f3d::engine eng(f3d::window::Type::NATIVE); + f3d::engine eng = f3d::engine::create(); f3d::options& opt = eng.getOptions(); opt.setAsString("render.show_edges", "true"); opt.setAsString("render.grid.enable", "true"); @@ -158,7 +161,7 @@ The documentation about option parsing is upcoming. When using this API make sure to catch exceptions has needed, eg: ```cpp - f3d::engine eng(f3d::window::Type::NATIVE); + f3d::engine eng = f3d::engine::create(); f3d::options& opt = eng.getOptions(); try @@ -180,7 +183,7 @@ When using this API make sure to catch exceptions has needed, eg: An API that is similar to the F3D 2.0 options API thanks to std::variant. ```cpp - f3d::engine eng(f3d::window::Type::NATIVE); + f3d::engine eng = f3d::engine::create(); f3d::options& opt = eng.getOptions(); opt.set("render.show_edges", true); opt.set("render.grid.enable", true); diff --git a/doc/libf3d/OVERVIEW.md b/doc/libf3d/OVERVIEW.md index ab17feb562..59da97ab2f 100644 --- a/doc/libf3d/OVERVIEW.md +++ b/doc/libf3d/OVERVIEW.md @@ -8,12 +8,12 @@ libf3d API is still in alpha version and may change drastically in the future. ## Getting Started -Rendering a full scene file and starting the interaction is very easy: +Rendering a file and starting the interaction is very easy: ```cpp #include #include -#include +#include // Load VTK native readers f3d::engine::autoloadPlugins(); @@ -21,19 +21,19 @@ f3d::engine::autoloadPlugins(); // Create a f3d::engine f3d::engine eng(); -// Load a scene -eng.getLoader().loadScene("path/to/file.ext"); +// Add a file into a scene +eng.getScene().add("path/to/file.ext"); // Start rendering and interacting eng.getInteractor().start(); ``` -As well as loading multiple geometries into a default scene: +As well as loading multiple files: ```cpp #include #include -#include +#include // Load VTK native readers f3d::engine::autoloadPlugins(); @@ -42,7 +42,7 @@ f3d::engine::autoloadPlugins(); f3d::engine eng(); // Load multiples geometries -eng.getLoader().loadGeometry("path/to/file.ext").loadGeometry("path/to/file2.ext"); +eng.getScene().add({"path/to/file.ext", "path/to/file2.ext"}); // Start rendering and interacting eng.getInteractor().start(); @@ -53,7 +53,7 @@ It's also possible to load a geometry from memory buffers: ```cpp #include #include -#include +#include // Create a f3d::engine f3d::engine eng(); @@ -63,7 +63,7 @@ f3d::mesh_t mesh = {}; mesh.points = { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f }; mesh.face_sides = { 3 }; mesh.face_indices = { 0, 1, 2 }; -eng.getLoader().loadGeometry(mesh); +eng.getScene().add(mesh); // Start rendering and interacting eng.getInteractor().start(); @@ -74,17 +74,17 @@ Manipulating the window directly can be done this way: ```cpp #include #include -#include +#include #include // Load VTK native readers f3d::engine::autoloadPlugins(); -// Create a f3d::engine -f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); +// Create a f3d::engine with a offscreen window +f3d::engine eng = f3d::engine::create(true); // Load a geometry -eng.getLoader().loadGeometry("path/to/file.ext"); +eng.getScene().add("path/to/file.ext"); // Set the window size and render to an image f3d::image img = eng.getWindow().setSize(300, 300).renderToImage(); @@ -99,7 +99,7 @@ Changing some options can be done this way: #include #include #include -#include +#include // Load VTK native readers f3d::engine::autoloadPlugins(); @@ -113,7 +113,7 @@ opt.render.effect.ambient_occlusion = true; opt.render.effect.anti_aliasing = true; // Standard libf3d usage -eng.getLoader().loadGeometry("path/to/file.ext"); +eng.getScene().add("path/to/file.ext"); eng.getInteractor().start(); ``` Most options are dynamic, some are only taken into account when loading a file. See the [options](OPTIONS.md) documentation. diff --git a/doc/user/COMMANDS.md b/doc/user/COMMANDS.md new file mode 100644 index 0000000000..f2d7ba9820 --- /dev/null +++ b/doc/user/COMMANDS.md @@ -0,0 +1,92 @@ +# Commands + +F3D will provide access to commands through interactions configuration and command line widget in the future. +Commands let you trigger specific behavior that may not be available otherwise. +Please note commands are currently experimental and the behaviors, names and arguments may change without deprecation. + +Commands have the following syntax: + +`action [args]` + +## libf3d provided commands + +The libf3d provides a few commands, many related to manipulating libf3d (options)[../libf3d/OPTIONS.md]. + +`set option.name values`: A command to set a libf3d option, eg: `set scene.up.direction +Z` or `set render.hdri.file "/path/to/file with spaces.png"` + +`toggle option.name`: A command to toggle a boolean libf3d option, eg: `toggle ui.scalar_bar`. + +`reset option.name`: A command to reset a libf3d option to its default values, eg: `reset render.background.blur_coc`. + +`print option.name`: A command to print the value of an libf3d option, eg: `print scene.up.direction`. + +`cycle_animation`: A specific command to cycle `scene.animation.index` option using model information, No argument. + +`cycle_coloring field/array/component`: A specific command to manipulate scivis options using model information. +Supports `field`, `array` or `component` arguments, see [documentation](INTERACTIONS.md#cycling-coloring). +eg: `cycle_coloring array`. + +`roll_camera value`: A specific command to roll the camera on its side, takes an angle in degrees as an argument. +eg: `roll_camera 120`. + +`increase_light_intensity`: A specific command to increase light intensity. No argument. + +`decrease_light_intensity`: A specific command to decrease light intensity. No argument. + +`print_scene_info`: A specific command to print information about the scene, No argument. + +`set_camera front/top/right/isometric`: A specific command to position the camera in the specified location relative to the model. +Supports `front`, `top`, `right`, `isometric` arguments. eg: `set_camera top`. + +`toggle_volume_rendering`: A specific command to toggle `model.volume.enable` and print coloring information. No argument. + +`toggle_ui_fps`: A specific command to toggle `ui.fps` and update it. No argument. + +`stop_interactor`: A specific command to stop the interactor hence quitting the application. No argument. + +`reset_camera`: A specific command to reset the camera to its original location. No argument. + +`toggle_animation`: A specific command to start/stop the animation. No argument. + +`add_files [path/to/file1] [path/to/file2]`: A specific command to add files to the scene (overridden by a F3D specific command, see below). Take one or more files as arguments. +eg: `add_files /path/to/dragon.vtu`. + +## F3D provided specific commands + +The F3D application provides a few more commands. + +`load_previous_file_group`: A specific command to load the previous file or file group. No argument. + +`load_next_file_group`: A specific command to load the next file or file group. No argument. + +`reload_current_file_group`: A specific command to reload the current file or file group. No argument. + +`add_current_directories`: A specific command to add all files from the current file or file group directories. No argument. + +`take_screenshot [filename]`: A specific command to [take a screenshot](INTERACTIONS.md#taking-screenshots). If filename is not specified, +rely on the `--screenshot-filename` CLI option. eg: `take_screenshot path/to/file.png`. + +`take_minimal_screenshot [filename]`: A specific command to [take a minimal screenshot](INTERACTIONS.md#taking-screenshots). If filename is not specified, +rely on the `--screenshot-filename` CLI option. eg: `take_screenshot path/to/file.png`. + +`add_files [path/to/file1] [path/to/file2]`: A specific command to add files to the scene according to the current grouping logic. Take one or more files as arguments. +eg: `add_files /path/to/dragon.vtu`. + +`set_hdri [path/to/hdri]`: A specific command to set and use an HDRI image. Take a HDRI file as an argument. +eg: `set_hdri /path/to/file.hdr`. + +`add_files_or_set_hdri [path/to/file1] [path/to/file2]`: A specific command that will process each files and either, `set_hdri` if the provided file uses a recognised HDR extension or `add_files` otherwise. Take one or more files as arguments. +eg: `add_files_or_set_hdri /path/to/dragon.vtu /path/to/file.hdr`. + +## Command syntax + +Command syntax is similar to bash, as in they will be split by "token" to be processed. +Tokens are spaces separated, eg: `set scene.up.direction +Z`. +Tokens can also be quoted to support spaces inside, eg: `set render.hdri.file "/path/to/file with spaces.png"`. +Supported quotes are `` `'" ``, eg: `set render.hdri.file '/path/to/file with spaces.png'`. +Quotes inside quotes are supported as well, eg: `set render.hdri.file "/path/to/file'with'quotes.png"`. +Quotes and spaces can be escaped, eg: `set render.hdri.file /path/to/file\ with\ spaces\ and\ \'quotes\".png`. +Escapes can be escaped too: eg: `set render.hdri.file C:\\path\\to\\windows\\file.png`. +Other escaped character will be processed as if the escape was not present, eg: `set scene.up.direction +\Z` +Unfinished quoted section is invalid, eg: `set scene.up.direction "+Z` +A escape at the end is also invalid, eg: `set scene.up.direction +Z\` diff --git a/doc/user/INTERACTIONS.md b/doc/user/INTERACTIONS.md index 18a5cd5fa2..2652dadf53 100644 --- a/doc/user/INTERACTIONS.md +++ b/doc/user/INTERACTIONS.md @@ -30,7 +30,7 @@ Other options can be toggled directly by pressing the following hotkeys: * W: cycle animations. * B: display of the scalar bar, only when coloring and not using direct scalars. -* V: volume rendering. +* V: volume rendering, forces coloring. * I: opacity function inversion during volume rendering. * O: point sprites rendering. * P: translucency support. @@ -72,8 +72,8 @@ Other hotkeys are available: * Space: play the animation if any. * : load the previous file if any and reset the camera. * : load the next file if any and reset the camera. -* : reload the current file. -* : add current file parent directory to the list of files, reload the current file and reset the camera. +* : reload the currently loaded files. +* : add all current files parent directories to the list of files, reload the currently loaded files and reset the camera. * F12: take a screenshot, ie. render the current view to an image file. * F11: take a "minimal" screenshot, ie. render the current view with no grid and no overlays to an image file with a transparent background. @@ -81,7 +81,7 @@ When loading another file or reloading, options that have been changed interacti ## Cycling Coloring -When using the default scene, the following hotkeys let you cycle the coloring of the data: +The following hotkeys let you cycle the coloring of the data: * C: cycle between point data and cell data - field data is not supported. * S: cycle the array available on the currently selected data in alphabetical order, diff --git a/doc/user/LIMITATIONS_AND_TROUBLESHOOTING.md b/doc/user/LIMITATIONS_AND_TROUBLESHOOTING.md index 7f2c0713bf..e2bdaccef6 100644 --- a/doc/user/LIMITATIONS_AND_TROUBLESHOOTING.md +++ b/doc/user/LIMITATIONS_AND_TROUBLESHOOTING.md @@ -4,11 +4,10 @@ Here is a non exhaustive list of F3D limitations: * No support for specifying manual lighting in the default scene apart from using `--light-intensity` option. * Multiblock (.vtm, .gml) support is partial, non-surfacic data will be converted into surfaces. -* Animation support with full scene data format require VTK >= 9.0.20201016. -* Full drag and drop support require VTK >= 9.0.20210620 * Drag and drop interaction cannot be recorded nor played back. * Volume rendering and HDRI support requires a decent GPU. * The `--camera-zoom-factor` option require VTK >= 9.3.0 +* Information about the failure to load a file is not provided before VTK >= 9.4.0 ## Assimp FBX, DAE, OFF, DXF, X and 3MF file formats rely on [Assimp](https://github.com/assimp/assimp) library. It comes with some known limitations: diff --git a/doc/user/OPTIONS.md b/doc/user/OPTIONS.md index 6df488c502..906256d082 100644 --- a/doc/user/OPTIONS.md +++ b/doc/user/OPTIONS.md @@ -19,8 +19,8 @@ Options|Default|Description \-\-no-render||Do not render anything and quit just after loading the first file, use with \-\-verbose to recover information about a file. \-\-max-size=\|-1|Prevent F3D to load a file bigger than the provided size in Mib, negative value means unlimited, useful for thumbnails. \-\-watch||Watch current file and automatically reload it whenever it is modified on disk. -\-\-load-plugins=\||List of plugins to load separated with a comma. Official plugins are `alembic`, `assimp`, `draco`, `exodus`, `occt`, `usd`, `vdb`. See [usage](USAGE.md) for more info. -\-\-scan-plugins||Scan standard directories for plugins and display their names, results may be incomplete. See [usage](USAGE.md) for more info. +\-\-load-plugins=\||List of plugins to load separated with a comma. Official plugins are `alembic`, `assimp`, `draco`, `exodus`, `occt`, `usd`, `vdb`. See [plugins](PLUGINS.md) for more info. +\-\-scan-plugins||Scan standard directories for plugins and display their names, results may be incomplete. See [plugins](PLUGINS.md) for more info. \-\-screenshot-filename=\|`{app}/{model}_{n}.png`|Filename to save [screenshots](INTERACTIONS.md#taking-screenshots) to. Can use [template variables](#filename-templating). ## General Options @@ -30,8 +30,7 @@ Options|Default|Description \-\-verbose=\<[debug\|info\|warning\|error\|quiet]\>|info| Set *verbose* level, in order to provide more information about the loaded data in the console output. If no level is provided, assume `debug`. Option parsing may ignore this flag. \-\-progress||Show a *progress bar* when loading the file. \-\-animation-progress||Show a *progress bar* when playing the animation. -\-\-geometry-only||For certain **full scene** file formats (gltf/glb and obj),
reads *only the geometry* from the file and use default scene construction instead. -\-\-group-geometries||When opening multiple files, show them all in the same scene.
Force geometry-only. The configuration file for the first file will be loaded. +\-\-multi-file-mode=\||When opening multiple files, select if they should be grouped (`all`) or alone (`single`). Configuration files for all loaded files will be used in the order they are provided. \-\-up=\<[+\|-][X\|Y\|Z]\>|+Y|Define the Up direction. -x, \-\-axis||Show *axes* as a trihedron in the scene. -g, \-\-grid||Show *a grid* aligned with the horizontal (orthogonal to the Up direction) plane. @@ -58,17 +57,17 @@ Options|Default|Description \-\-point-size=\||Set the *size* of points when showing vertices. Model specified by default. \-\-line-width=\||Set the *width* of lines when showing edges. Model specified by default. \-\-backface-type=\||Set the Backface type. Model specified by default. -\-\-color=\|1.0, 1.0, 1.0| Set a *color* on the geometry. Multiplied with the base color texture when present.
Requires a default scene. -\-\-opacity=\|1.0|Set *opacity* on the geometry. Multiplied with the base color texture when present.
Requires a default scene. Usually used with Depth Peeling option. -\-\-roughness=\|0.3|Set the *roughness coefficient* on the geometry (0.0-1.0). Multiplied with the material texture when present.
Requires a default scene. -\-\-metallic=\|0.0|Set the *metallic coefficient* on the geometry (0.0-1.0). Multiplied with the material texture when present.
Requires a default scene. +\-\-color=\|1.0, 1.0, 1.0| Set a *color* on the geometry. Multiplied with the base color texture when present.
Model specified by default. +\-\-opacity=\|1.0|Set *opacity* on the geometry. Multiplied with the base color texture when present.
Model specified by default. Usually used with Depth Peeling option. +\-\-roughness=\|0.3|Set the *roughness coefficient* on the geometry (0.0-1.0). Multiplied with the material texture when present.
Model specified by default. +\-\-metallic=\|0.0|Set the *metallic coefficient* on the geometry (0.0-1.0). Multiplied with the material texture when present.
Model specified by default. \-\-hdri-file=\||Set the *HDRI* image that can be used as ambient lighting and skybox.
Valid file format are hdr, exr, png, jpg, pnm, tiff, bmp.
If not set, a default is provided. \-\-hdri-ambient||Light the scene using the *HDRI* image as ambient lighting.
The environment act as a light source and is reflected on the material. -\-\-texture-matcap=\||Set the texture file to control the material capture of the object. All other model options for surfaces are ignored if this is set. Must be in linear color space. -\-\-texture-base-color=\||Set the texture file to control the color of the object. Please note this will be multiplied with the color and opacity options. Must be in sRGB color space. -\-\-texture-material=\||Set the texture file to control the occlusion, roughness and metallic values of the object. Please note this will be multiplied with the roughness and metallic options, which have impactful default values. To obtain true results, use \-\-roughness=1 \-\-metallic=1. Must be in linear color space. -\-\-texture-emissive=\||Set the texture file to control the emitted light of the object. Please note this will be multiplied with the emissive factor. Must be in sRGB color space. -\-\-emissive-factor=\|1.0, 1.0, 1.0|Set the emissive factor. This value is multiplied with the emissive color when an emissive texture is present. +\-\-texture-matcap=\||Set the texture file to control the material capture of the object. All other model options for surfaces are ignored if this is set. Must be in linear color space.
Model specified by default. +\-\-texture-base-color=\||Set the texture file to control the color of the object. Please note this will be multiplied with the color and opacity options. Must be in sRGB color space.
Model specified by default. +\-\-texture-material=\||Set the texture file to control the occlusion, roughness and metallic values of the object. Please note this will be multiplied with the roughness and metallic options, which have impactful default values. To obtain true results, use \-\-roughness=1 \-\-metallic=1. Must be in linear color space.
Model specified by default. +\-\-texture-emissive=\||Set the texture file to control the emitted light of the object. Please note this will be multiplied with the emissive factor. Must be in sRGB color space.
Model specified by default. +\-\-emissive-factor=\|1.0, 1.0, 1.0|Set the emissive factor. This value is multiplied with the emissive color when an emissive texture is present.
Model specified by default. ## Window options @@ -79,7 +78,7 @@ Options|Default|Description \-\-position=\||Set the *window position* (top left corner) , in pixels, starting from the top left of your screens. -z, \-\-fps||Display a *frame per second counter*. -n, \-\-filename||Display the *name of the file* on top of the window. --m, \-\-metadata||Display the *metadata*.
Empty without a default scene. +-m, \-\-metadata||Display the *metadata*. \-\-hdri-skybox||Show the HDRI as a skybox. Overrides \-\-bg-color and \-\-no-background. -u, \-\-blur-background||Blur background.
Useful with a HDRI skybox. \-\-blur-coc|20|Blur circle of confusion radius. @@ -89,7 +88,7 @@ Options|Default|Description Options|Default|Description ------|------|------ --s, \-\-scalar-coloring||Enable scalar coloring if present in the file. If `--coloring-array` is not set, the first in alphabetical order will be picked if any are available.
Requires a default scene. +-s, \-\-scalar-coloring||Enable scalar coloring if present in the file. If `--coloring-array` is not set, the first in alphabetical order will be picked if any are available. \-\-coloring-array=\||The coloring array name to use when coloring.
Use \-\-verbose to recover the usable array names. -y, \-\-comp=\|-1|Specify the *component from the scalar* array to color with.
Use with the scalar option. -1 means *magnitude*. -2 or the short option, -y, means *direct values*.
When using *direct values*, components are used as L, LA, RGB, RGBA values depending on the number of components. -c, \-\-cells||Specify that the scalar array is to be found *on the cells* instead of on the points.
Use with the scalar option. @@ -97,7 +96,7 @@ Options|Default|Description -b, \-\-bar||Show *scalar bar* of the coloring by array.
Use with the scalar option. \-\-colormap\-file=\||Set a *colormap file for the coloring*.
See [color maps](COLOR_MAPS.md).
Use with the scalar option. \-\-colormap=\||Set a *custom colormap for the coloring*.
This is a list of colors in the format `val1,red1,green1,blue1,...,valN,redN,greenN,blueN`
where all values are in the range (0,1).
Ignored if `--colormap-file` option is specified.
Use with the scalar option. --v, \-\-volume||Enable *volume rendering*. It is only available for 3D image data (vti, dcm, nrrd, mhd files) and will display nothing with other formats. +-v, \-\-volume||Enable *volume rendering*. It is only available for 3D image data (vti, dcm, nrrd, mhd files) and will display nothing with other formats. It forces coloring. -i, \-\-inverse||Inverse the linear opacity function used for volume rendering. ## Camera configuration options @@ -157,7 +156,7 @@ As documented, only the `--option=value` syntax is supported. The syntax `--opti ## Filename templating -The destination filename used by `--output` or to save screenshots can use the following template variables: +The destination filename used by `--output` or to save screenshots using `--screenshot-filename` can use the following template variables: - `{app}`: application name (ie. `F3D`) - `{version}`: application version (eg. `2.4.0`) @@ -173,3 +172,5 @@ The destination filename used by `--output` or to save screenshots can use the f For example the screenshot filename is configured as `{app}/{model}_{n}.png` by default, meaning that, assuming the model `hello.glb` is being viewed, consecutive screenshots are going to be saved as `F3D/hello_1.png`, `F3D/hello_2.png`, `F3D/hello_3.png`, ... + +Model related variables will be replaced by `no_file` if no file is loaded and `multi_file` if multiple files are loaded using the `multi-file-mode` option. diff --git a/doc/user/PLUGINS.md b/doc/user/PLUGINS.md new file mode 100644 index 0000000000..8e552a98c5 --- /dev/null +++ b/doc/user/PLUGINS.md @@ -0,0 +1,51 @@ +## Plugins + +F3D officially supports plugins for certain file formats. If you installed F3D using the binary +release, there's no need to manually load these plugins when opening your file. +F3D will load them automatically. + +However, if you installed F3D using a package manager, +it's possible that the packager chose to bundle the plugins into different packages or +to list plugin dependencies as optional in order to reduce the number of dependencies of the main package. + +In order to open a file that requires a plugin, make sure you've installed all necessary +dependencies. You can then specify the `--load-plugins=` [option](OPTIONS.md) +in the command line to load your plugin. + +Alternatively, you can add your plugin directly in the +[configuration file](CONFIGURATION_FILE.md) if it isn't there already. You can specify one or +multiple plugins in a single comma-separated list, like in the example below: + +``` +{ + ".*(file_extension)": { + "load-plugins": "plugin1", "plugin2" + } +} +``` + +### Supported plugins + +F3D supports the following plugins and their file formats: + +- **alembic**: `.abc` +- **assimp**: `.fbx`, `.dae`, `.off`, `.dxf`, `.x`, `.3mf` +- **draco**: `.drc` +- **exodus**: `.ex2` +- **occt**: `.step/.stp`, `.iges/.igs` +- **usd**: `.usd`, `.usda`, `.usdc`, `.usdz` +- **vdb**: `.vdb` (experimental) + +> Note: If you downloaded the binaries from the release page, it's not necessary to specify manually the plugins above. F3D loads them automatically. + +Here is how the plugins are searched (in preceding order): + +1. Search the static plugins. +2. Consider the `load-plugins` option given it is a full path. +3. Search in the paths specified in `F3D_PLUGINS_PATH` environment variable. +4. Search in a directory relative to the F3D application: `../lib`. +5. Rely on OS specific paths (e.g. `LD_LIBRARY_PATH` on Linux or `DYLD_LIBRARY_PATH` on macOS). + +You can also try plugins maintained by the community. If you have created a plugin and would like it to be listed here, please submit a pull request. + +- **Abaqus**: ODB support by @YangShen398 ([repository](https://github.com/YangShen398/F3D-ODB-Reader-Plugin)) diff --git a/doc/user/QUICKSTART.md b/doc/user/QUICKSTART.md new file mode 100644 index 0000000000..05f1fd3246 --- /dev/null +++ b/doc/user/QUICKSTART.md @@ -0,0 +1,97 @@ +# Quickstart Guide + +This guide will help you get started using F3D. + +As an overview, you'll learn how to run F3D and open files, configure basic scene constructions, interact with them, and play animations. + +## Prerequisites + +To use F3D, you'll need to install F3D. See the [Installation](INSTALLATION.md) page for the latest release package supported by your system. + +## Running F3D + +Once F3D has been installed, you should be able to open a file in any of the following ways: + +* Open a file directly from your file manager. +* Open the F3D application directly, then drag and drop a file into the application window. +* Run F3D from the terminal with a set of command-line [options](OPTIONS.md). +* Configure F3D as a [thumbnailer](DESKTOP_INTEGRATION.md) for supported file formats with certain file managers. + +If you choose to run F3D from the terminal, you can open your file by running the following: +``` +f3d /path/to/file.ext +``` + +Optionally, you can also save the rendering into an image file: + +``` +f3d /path/to/file.ext --output=/path/to/img.png +``` + +If you need help, specify the `--help` [option](OPTIONS.md): + +``` +f3d --help +man f3d # Linux only +``` + +Once you've opened your file in F3D, you're all set to start visualizing! Press H to open a list of shortcuts to help you interact with your scene. + +See the [supported file formats](SUPPORTED_FORMATS.md) page for the full list of file types that F3D can open. + +## Constructing scenes + +Certain [formats](SUPPORTED_FORMATS.md) are listed as **full scene** formats (.gltf/.glb, .3ds, .wrl, .obj, .fbx, .dae, .off, .x, .3mf, .usd) +which means these files contain not only *geometry* but also some scene information like *lights*, *cameras*, *actors* in the scene, +as well as *texture* properties. By default, all this information will be loaded from the file and displayed. +For file formats that are not **full scene**, **a default scene** is created. + +For **default scene** formats, certain default values are set automatically: + - texture-*: "" + - line-width: 1.0 + - point-size: 10.0 + - opacity: 1.0 + - color: 1.0, 1.0, 1.0 + - emissive-factor: 1.0, 1.0, 1.0 + - normal-scale: 1.0 + - metallic: 0.0 + - roughness: 0.3 + +They will be overridden when using corresponding [options](OPTIONS.md). + +## Interacting with your scene + +Once you've loaded your scene in F3D, you can interact with it by using your mouse and certain hotkeys. + +### Moving the camera + +* *Click and drag* with the *left* mouse button to **rotate** around the focal point of the camera. +* *Click and drag* vertically with the *right* mouse button OR *move the mouse wheel* to **zoom in/out**. +* *Click and drag* with the *middle* mouse button to **translate** the camera. + +### Other shortcuts +* Press Enter to reset the camera. +* Press Space to play animation, if any. +* Press G to toggle the horizontal grid. +* Press H to display a cheatsheet of hotkey interactions. + +For more information, see the [Interactions](INTERACTIONS.md) page. + +## Playing animations + +F3D can play [animations](ANIMATIONS.md) for any [supported files](SUPPORTED_FORMATS.md) that contain them. + +With your file loaded into F3D, press the W hotkey to cycle through available animations. Then, press Space to play your selected animation. + +Alternatively, you can use the command line to play animations. Use the `--animation-index` [option](OPTIONS.md) to specify which animation you want to play. To play all animations at once, use `--animation-index=-1` (`.gltf`/`.glb` only). + +For more information, see the [Animations](ANIMATIONS.md) page. + +## Further Reading + +* [A list of all F3D command-line options.](OPTIONS.md) +* [How to interact with scenes in F3D.](INTERACTIONS.MD) +* [How to play animations in F3D.](ANIMATIONS.md) +* [How to specify colormaps in F3D.](COLOR_MAPS.md) +* [How to configure plugins in F3D.](PLUGINS.md) +* [Limitations and how to troubleshoot F3D.](LIMITATIONS_AND_TROUBLESHOOTING.md) diff --git a/doc/user/README_USER.md b/doc/user/README_USER.md index 5503396864..7d5e5874c7 100644 --- a/doc/user/README_USER.md +++ b/doc/user/README_USER.md @@ -1,12 +1,15 @@ # User Documentation -- [How to use F3D.](USAGE.md) +- [How to use F3D.](QUICKSTART.md) - [How to install F3D.](INSTALLATION.md) +- [List of all supported file formats.](SUPPORTED_FORMATS.md) - [List of all F3D command line options.](OPTIONS.md) - [The different interactions in F3D.](INTERACTIONS.md) - [How to use animations in F3D.](ANIMATIONS.md) +- [How to configure F3D using a configuration file.](CONFIGURATION_FILE.md) +- [The different commands available in F3D.](COMMANDS.md) - [How to use colormaps in F3D.](COLOR_MAPS.md) - [How to a use custom final shader in F3D.](FINAL_SHADER.md) -- [How to configure F3D using a configuration file.](CONFIGURATION_FILE.md) - [How to integrate F3D in your desktop.](DESKTOP_INTEGRATION.md) +- [How to configure plugins in F3D.](PLUGINS.md) - [Limitations and troubleshooting of F3D.](LIMITATIONS_AND_TROUBLESHOOTING.md) diff --git a/doc/user/SUPPORTED_FORMATS.md b/doc/user/SUPPORTED_FORMATS.md new file mode 100644 index 0000000000..d306d58ff6 --- /dev/null +++ b/doc/user/SUPPORTED_FORMATS.md @@ -0,0 +1,35 @@ +# Supported File Formats + +F3D supports the following file formats: + +| Name | File Extension(s) | Type of Scene(s) Supported | Animations Supported? | Plugin Supported | +| -- | -- | -- | -- | -- | +| Legacy VTK | `.vtk` | Default | +| VTK XML | `.vtp`, `.vtu`, `.vtr`, `.vti`, `.vts`, `.vtm` | Default | +| Polygon File Format | `.ply` | Default | +| Standard Triangle Language | `.stl` | Default | +| DICOM | `.dcm` | Default | +| NRRD ("nearly raw raster data") | `.nrrd`, `.nhrd` | Default | +| MetaHeader MetaIO | `.mhd`, `.mha` | Default | +| Tag Image File Format 2D/3D | `.tif`, `.tiff` | Default | +| EXODUS II | `.e`, `.ex2`, `.exo`, `.g` | Default | Yes | `exodus` | +| CityGML | `.gml` | Default | +| Point Cloud | `.pts` | Default | +| Standard for the Exchange of Product Data | `.step`, `.stp` | Default | | `occt` | +| Initial Graphics Exchange Specification | `.iges`, `.igs` | Default | | `occt` | +| Open CASCADE Technology BRep format | `.brep` | Default | +| Alembic | `.abc` | Default | | `alembic` | +| Wavefront OBJ | `.obj` | Default, Full | +| GL Transmission Format | `.gltf`, `.glb` | Default, Full | Yes | +| Autodesk 3D Studio | `.3ds` | Full | +| Virtual Reality Modeling Language | `.wrl` | Full | +| Autodesk Filmbox | `.fbx` | Full | Yes | `assimp` | +| COLLADA | `.dae` | Full | Yes | `assimp` | +| Object File Format | `.off` | Full | | `assimp` | +| Drawing Exchange Format | `.dxf` | Full | | `assimp` | +| DirectX | `.x` | Full | Yes | `assimp` | +| 3D Manufacturing Format | `.3mf` | Full | | `assimp` | +| Universal Scene Description | `.usd`, `.usda`, `.usdc`, `.usdz` | Full | Yes | `usd` | +| VDB | `.vdb` | Default | | `vdb` | + +\***Note:** As of version 2.5.0, F3D support for VDB is still experimental. diff --git a/doc/user/USAGE.md b/doc/user/USAGE.md deleted file mode 100644 index 5bffe6aebf..0000000000 --- a/doc/user/USAGE.md +++ /dev/null @@ -1,88 +0,0 @@ -# Usage - -Once F3D has been [installed](INSTALLATION.md), you should be able to open any [supported file](#supported-file-formats), -by either: -* Using F3D automatically, from your file manager, by directly opening a file. -* Running F3D and then dragging and dropping files into it to open them. -* By running F3D from the terminal with a set of command-line [options](OPTIONS.md). -* As a [thumbnailer](DESKTOP_INTEGRATION.md) for all supported file formats with certain file managers. - -## Supported file formats - -Here is the list of supported file formats: - -* **.vtk** : the legacy VTK format -* **.vt[p\|u\|r\|i\|s\|m]** : XML based VTK formats -* **.ply** : Polygon File format -* **.stl** : Standard Triangle Language format -* **.dcm** : DICOM file format -* **.nrrd/.nhrd** : "nearly raw raster data" file format -* **.mhd/.mha** : MetaHeader MetaIO file format -* **.tif/.tiff** : TIFF 2D/3D file format -* **.ex2/.e/.exo/.g** : Exodus 2 file format -* **.gml** : CityGML file format -* **.pts** : Point Cloud file format -* **.step/.stp** : CAD STEP exchange ISO format -* **.iges/.igs** : CAD Initial Graphics Exchange Specification format -* **.brep** : Open CASCADE BRep format -* **.xbf** : Open CASCADE XBF format -* **.abc** : Alembic format -* **.vdb** : VDB format (experimental) -* **.obj** : Wavefront OBJ file format (full scene and default scene) -* **.gltf/.glb** : GL Transmission Format (full scene and default scene) -* **.3ds** : Autodesk 3D Studio file format (full scene) -* **.wrl** : VRML file format (full scene) -* **.fbx** : Autodesk Filmbox (full scene) -* **.dae** : COLLADA (full scene) -* **.off** : Object File Format (full scene) -* **.dxf** : Drawing Exchange Format (full scene) -* **.x** : DirectX Format (full scene) -* **.3mf** : 3D Manufacturing Format (full scene) -* **.usd** : Universal Scene Description (full scene) - -## Scene construction - -The **full scene** formats (.gltf/.glb, .3ds, .wrl, .obj, .fbx, .dae, .off, .x, .3mf, .usd) contain not only *geometry*, -but also some scene information like *lights*, *cameras*, *actors* in the scene, as well as *texture* properties. -By default, all this information will be loaded from the file and displayed. Use the `--geometry-only` [options](OPTIONS.md) -to modify this behavior. For file formats that do not support it, **a default scene** is created. - -For **default scene** formats, certain default values are set automatically: - - line_width: 1.0 - - point_size: 10.0 - -They will be overridden when using corresponding [options](OPTIONS.md). - -## Animations - -F3D can play animations for a number of file formats (.ex2/.e/.exo/.g, .gltf/.glb, .fbx, .dae, .x, .usd) if the file contains an animation. -It is possible to select the animation to play using `--animation-index`, or to play all animations at once using `--animation-index=-1` (.gltf/.glb only). -When F3D plays an animation, it assumes the time unit is in seconds to show accurate speed of animation. Use `--animation-speed-factor` if -an adjustment is needed. By default, F3D will try update the scene 60 times per seconds, use `--animation-frame-rate` to change that if needed. Press "W" hotkey to cycle through available animations. - -## Plugins - -If you installed F3D using a package manager, it's possible that the packager chose to bundle the plugins in different packages or to list plugin dependencies as optional dependencies to reduce the reduce the number of dependencies of the main package. -In this case, in order to open a file that requires a plugin, you will have to make sure all needed dependencies are installed and specify which plugin you want to load in order to be able to open this file. You can either use the `--load-plugins` option or add a line in the [configuration file](CONFIGURATION_FILE.md), if not already. Several plugins can be specified by giving a comma-separated list. -Here is the list of plugins provided officially by F3D: - -- **alembic**: ABC support -- **assimp**: FBX, DAE, OFF, DXF, X and 3MF support -- **draco**: DRC support -- **exodus**: EX2 support -- **occt**: STEP and IGES support -- **usd**: USD, USDA, UDSC and USDZ support -- **vdb**: VDB support (experimental) - -> Note: If you downloaded the binaries from the Release page, it's not necessary to specify manually the plugins above, all of them are loaded automatically. - -Here is how the plugins are searched (by precedence order): -1. Search the static plugins -2. Consider the option given is a full path -3. Search in the paths specified in `F3D_PLUGINS_PATH` environment variable -4. Search in a directory relative to the F3D application: `../lib` -5. Rely on OS specific paths (e.g. `LD_LIBRARY_PATH` on Linux or `DYLD_LIBRARY_PATH` on macOS) - -You can also try plugins maintained by the community. If you have created a plugin and would like it to be listed here, please submit a pull request. - -- **Abaqus**: ODB support by @YangShen398 ([repository](https://github.com/YangShen398/F3D-ODB-Reader-Plugin)) diff --git a/examples/libf3d/CMakeLists.txt b/examples/libf3d/CMakeLists.txt index a0975c1214..85d9e51aba 100644 --- a/examples/libf3d/CMakeLists.txt +++ b/examples/libf3d/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(libf3d-examples) diff --git a/examples/libf3d/cpp/CMakeLists.txt b/examples/libf3d/cpp/CMakeLists.txt index 611d0e12a7..73133952db 100644 --- a/examples/libf3d/cpp/CMakeLists.txt +++ b/examples/libf3d/cpp/CMakeLists.txt @@ -5,7 +5,7 @@ if(BUILD_TESTING) endif() add_subdirectory(check-engine) -add_subdirectory(multi-geom) +add_subdirectory(multi-files) add_subdirectory(render-image) add_subdirectory(render-interact) add_subdirectory(use-options-string) diff --git a/examples/libf3d/cpp/check-engine/CMakeLists.txt b/examples/libf3d/cpp/check-engine/CMakeLists.txt index 88c4ff1a9d..98dbcb4824 100644 --- a/examples/libf3d/cpp/check-engine/CMakeLists.txt +++ b/examples/libf3d/cpp/check-engine/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(check-engine) diff --git a/examples/libf3d/cpp/check-engine/main.cxx b/examples/libf3d/cpp/check-engine/main.cxx index e0dca3a6ff..fb4ef00abc 100644 --- a/examples/libf3d/cpp/check-engine/main.cxx +++ b/examples/libf3d/cpp/check-engine/main.cxx @@ -4,9 +4,9 @@ #include #include #include -#include #include #include +#include #include #include @@ -14,7 +14,7 @@ int main() { f3d::engine::autoloadPlugins(); - f3d::engine eng(f3d::window::Type::NONE); + f3d::engine eng = f3d::engine::createNone(); f3d::log::info("F3D engine is loaded"); diff --git a/examples/libf3d/cpp/multi-files/CMakeLists.txt b/examples/libf3d/cpp/multi-files/CMakeLists.txt new file mode 100644 index 0000000000..fbb6710ee9 --- /dev/null +++ b/examples/libf3d/cpp/multi-files/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.10) + +project(multi-files) + +find_package(f3d REQUIRED COMPONENTS library) + +add_executable(multi-files main.cxx) +target_link_libraries(multi-files f3d::libf3d) +set_target_properties(multi-files PROPERTIES CXX_STANDARD 17) + +if(UNIX AND NOT APPLE) + target_link_libraries(multi-files stdc++fs) +endif() + +# Simple testing +if(BUILD_TESTING) + enable_testing() + add_test(NAME test_multi-files COMMAND "$" "${CMAKE_CURRENT_SOURCE_DIR}/data/") + set_tests_properties(test_multi-files PROPERTIES + PASS_REGULAR_EXPRESSION "Scene bounding box: -0.487464,1,-0.487464,1,-0.5,1") +endif() diff --git a/examples/libf3d/cpp/multi-geom/data/mb_0_0.vtu b/examples/libf3d/cpp/multi-files/data/mb_0_0.vtu similarity index 100% rename from examples/libf3d/cpp/multi-geom/data/mb_0_0.vtu rename to examples/libf3d/cpp/multi-files/data/mb_0_0.vtu diff --git a/examples/libf3d/cpp/multi-geom/data/mb_1_0.vtp b/examples/libf3d/cpp/multi-files/data/mb_1_0.vtp similarity index 100% rename from examples/libf3d/cpp/multi-geom/data/mb_1_0.vtp rename to examples/libf3d/cpp/multi-files/data/mb_1_0.vtp diff --git a/examples/libf3d/cpp/multi-geom/data/mb_2_0.vtp b/examples/libf3d/cpp/multi-files/data/mb_2_0.vtp similarity index 100% rename from examples/libf3d/cpp/multi-geom/data/mb_2_0.vtp rename to examples/libf3d/cpp/multi-files/data/mb_2_0.vtp diff --git a/examples/libf3d/cpp/multi-geom/main.cxx b/examples/libf3d/cpp/multi-files/main.cxx similarity index 80% rename from examples/libf3d/cpp/multi-geom/main.cxx rename to examples/libf3d/cpp/multi-files/main.cxx index 9b9d589055..71a91e419d 100644 --- a/examples/libf3d/cpp/multi-geom/main.cxx +++ b/examples/libf3d/cpp/multi-files/main.cxx @@ -20,13 +20,13 @@ int main(int argc, char** argv) f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG); // Create a native window engine - f3d::engine eng(f3d::window::Type::NATIVE); + f3d::engine eng = f3d::engine::create(); - // Load all files from provided directory as geometries - f3d::loader& load = eng.getLoader(); + // Add all files from provided directory + f3d::scene& sce = eng.getScene(); for (auto& entry : std::filesystem::directory_iterator(argv[1])) { - load.loadGeometry(entry.path().string()); + sce.add(entry.path().string()); } // Render diff --git a/examples/libf3d/cpp/multi-geom/CMakeLists.txt b/examples/libf3d/cpp/multi-geom/CMakeLists.txt deleted file mode 100644 index 16920a15ee..0000000000 --- a/examples/libf3d/cpp/multi-geom/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -cmake_minimum_required(VERSION 3.5) - -project(multi-geom) - -find_package(f3d REQUIRED COMPONENTS library) - -add_executable(multi-geom main.cxx) -target_link_libraries(multi-geom f3d::libf3d) -set_target_properties(multi-geom PROPERTIES CXX_STANDARD 17) - -if(UNIX AND NOT APPLE) - target_link_libraries(multi-geom stdc++fs) -endif() - -# Simple testing -if(BUILD_TESTING) - enable_testing() - add_test(NAME test_multi-geom COMMAND "$" "${CMAKE_CURRENT_SOURCE_DIR}/data/") - set_tests_properties(test_multi-geom PROPERTIES - PASS_REGULAR_EXPRESSION "Scene bounding box: -0.487464,1,-0.487464,1,-0.5,1") -endif() diff --git a/examples/libf3d/cpp/render-image/CMakeLists.txt b/examples/libf3d/cpp/render-image/CMakeLists.txt index c6dfd39c25..a57cd599eb 100644 --- a/examples/libf3d/cpp/render-image/CMakeLists.txt +++ b/examples/libf3d/cpp/render-image/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(render-image) diff --git a/examples/libf3d/cpp/render-image/check.cxx b/examples/libf3d/cpp/render-image/check.cxx index 01e676c26c..1fe8284f81 100644 --- a/examples/libf3d/cpp/render-image/check.cxx +++ b/examples/libf3d/cpp/render-image/check.cxx @@ -14,5 +14,5 @@ int main(int argc, char** argv) // Compare them double error; - return img0.compare(img1, 50, error) ? EXIT_SUCCESS : EXIT_FAILURE; + return img0.compare(img1, 0.05, error) ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/examples/libf3d/cpp/render-image/main.cxx b/examples/libf3d/cpp/render-image/main.cxx index d5b0aa98c3..e510846714 100644 --- a/examples/libf3d/cpp/render-image/main.cxx +++ b/examples/libf3d/cpp/render-image/main.cxx @@ -19,10 +19,10 @@ int main(int argc, char** argv) f3d::engine::autoloadPlugins(); // Create a offscreen window engine - f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); + f3d::engine eng = f3d::engine::create(true); - // Load a model - eng.getLoader().loadGeometry(std::string(argv[1])); + // add a model + eng.getScene().add(std::string(argv[1])); // Set the window size and render to an image f3d::image img = eng.getWindow().setSize(300, 300).renderToImage(); diff --git a/examples/libf3d/cpp/render-interact/CMakeLists.txt b/examples/libf3d/cpp/render-interact/CMakeLists.txt index a3b4d188f5..c1fec51923 100644 --- a/examples/libf3d/cpp/render-interact/CMakeLists.txt +++ b/examples/libf3d/cpp/render-interact/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(render-interact) diff --git a/examples/libf3d/cpp/render-interact/main.cxx b/examples/libf3d/cpp/render-interact/main.cxx index 3e20259df1..7fc831b9e4 100644 --- a/examples/libf3d/cpp/render-interact/main.cxx +++ b/examples/libf3d/cpp/render-interact/main.cxx @@ -18,10 +18,10 @@ int main(int argc, char** argv) f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG); // Create a native window engine - f3d::engine eng(f3d::window::Type::NATIVE); + f3d::engine eng = f3d::engine::create(); - // Load a model - const f3d::loader& load = eng.getLoader().loadGeometry(std::string(argv[1])); + // add a model + eng.getScene().add(std::string(argv[1])); // Render f3d::window& win = eng.getWindow(); diff --git a/examples/libf3d/cpp/use-options-string/CMakeLists.txt b/examples/libf3d/cpp/use-options-string/CMakeLists.txt index 3b6f15f814..704599d8ab 100644 --- a/examples/libf3d/cpp/use-options-string/CMakeLists.txt +++ b/examples/libf3d/cpp/use-options-string/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(use-options-string) diff --git a/examples/libf3d/cpp/use-options-string/main.cxx b/examples/libf3d/cpp/use-options-string/main.cxx index 56a0ff242c..2cafb1e8e1 100644 --- a/examples/libf3d/cpp/use-options-string/main.cxx +++ b/examples/libf3d/cpp/use-options-string/main.cxx @@ -19,7 +19,7 @@ int main(int argc, char** argv) f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG); // Create a native window engine - f3d::engine eng(f3d::window::Type::NATIVE); + f3d::engine eng = f3d::engine::create(); // Modify options use struct API f3d::options& opt = eng.getOptions(); @@ -27,8 +27,8 @@ int main(int argc, char** argv) .setAsString("render.line_width", "10") .setAsString("render.grid.enable", "1"); - // Load a model - const f3d::loader& load = eng.getLoader().loadGeometry(std::string(argv[1])); + // add a model + const f3d::scene& sce = eng.getScene().add(std::string(argv[1])); // Render f3d::window& win = eng.getWindow(); diff --git a/examples/libf3d/cpp/use-options-struct/CMakeLists.txt b/examples/libf3d/cpp/use-options-struct/CMakeLists.txt index eda9e06a5a..ce1d6b49a9 100644 --- a/examples/libf3d/cpp/use-options-struct/CMakeLists.txt +++ b/examples/libf3d/cpp/use-options-struct/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(use-options-struct) diff --git a/examples/libf3d/cpp/use-options-struct/main.cxx b/examples/libf3d/cpp/use-options-struct/main.cxx index f78ac1cf31..59381ac0cc 100644 --- a/examples/libf3d/cpp/use-options-struct/main.cxx +++ b/examples/libf3d/cpp/use-options-struct/main.cxx @@ -19,7 +19,7 @@ int main(int argc, char** argv) f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG); // Create a native window engine - f3d::engine eng(f3d::window::Type::NATIVE); + f3d::engine eng = f3d::engine::create(); // Modify options use struct API f3d::options& opt = eng.getOptions(); @@ -27,8 +27,8 @@ int main(int argc, char** argv) opt.render.line_width = 10; opt.render.grid.enable = true; - // Load a model - const f3d::loader& load = eng.getLoader().loadGeometry(std::string(argv[1])); + // Add a model + eng.getScene().add(std::string(argv[1])); // Render f3d::window& win = eng.getWindow(); diff --git a/examples/libf3d/cpp/use-options-variant/CMakeLists.txt b/examples/libf3d/cpp/use-options-variant/CMakeLists.txt index 3a02711ef4..99bc91eb9f 100644 --- a/examples/libf3d/cpp/use-options-variant/CMakeLists.txt +++ b/examples/libf3d/cpp/use-options-variant/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5) +cmake_minimum_required(VERSION 3.10) project(use-options-variant) diff --git a/examples/libf3d/cpp/use-options-variant/main.cxx b/examples/libf3d/cpp/use-options-variant/main.cxx index 6118d256fa..b1e704ae1b 100644 --- a/examples/libf3d/cpp/use-options-variant/main.cxx +++ b/examples/libf3d/cpp/use-options-variant/main.cxx @@ -19,14 +19,14 @@ int main(int argc, char** argv) f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG); // Create a native window engine - f3d::engine eng(f3d::window::Type::NATIVE); + f3d::engine eng = f3d::engine::create(); // Modify options f3d::options& opt = eng.getOptions(); opt.set("render.show_edges", true).set("render.line_width", 10.0).set("render.grid.enable", true); - // Load a model - const f3d::loader& load = eng.getLoader().loadGeometry(std::string(argv[1])); + // ad a model + const f3d::scene& sce = eng.getScene().add(std::string(argv[1])); // Render f3d::window& win = eng.getWindow(); diff --git a/examples/libf3d/python/check-engine/check_engine.py b/examples/libf3d/python/check-engine/check_engine.py index bd9587dc30..e6af7ca64a 100644 --- a/examples/libf3d/python/check-engine/check_engine.py +++ b/examples/libf3d/python/check-engine/check_engine.py @@ -1,5 +1,5 @@ import f3d if __name__ == "__main__": - eng = f3d.Engine(f3d.Window.NONE) + eng = f3d.Engine.create_none() print("F3D engine is loaded") diff --git a/examples/libf3d/python/img-cmp/img-cmp.py b/examples/libf3d/python/img-cmp/img-cmp.py index 3b95e2acc8..093abdcb2a 100644 --- a/examples/libf3d/python/img-cmp/img-cmp.py +++ b/examples/libf3d/python/img-cmp/img-cmp.py @@ -11,7 +11,7 @@ diff = f3d.Image() error = 0.0 - result = img_0.compare(img_1, 50, diff, error) + result = img_0.compare(img_1, 0.05, diff, error) if result: print("Images are identical") diff --git a/examples/libf3d/python/multi-geom/multi_geom.py b/examples/libf3d/python/multi-files/multi_files.py similarity index 86% rename from examples/libf3d/python/multi-geom/multi_geom.py rename to examples/libf3d/python/multi-files/multi_files.py index ba66e8305b..cc00ba2f36 100644 --- a/examples/libf3d/python/multi-geom/multi_geom.py +++ b/examples/libf3d/python/multi-files/multi_files.py @@ -10,13 +10,13 @@ f3d.Engine.autoload_plugins() # Create a native window engine - eng = f3d.Engine(f3d.Window.NATIVE) + eng = f3d.Engine.create() # Load all files from provided directory as geometries files = [f for f in Path(sys.argv[1]).iterdir() if f.is_file()] for file in files: try: - eng.loader.load_geometry(str(file)) + eng.scene.add(str(file)) except RuntimeError as e: print(e) diff --git a/examples/libf3d/python/render-image/render_image.py b/examples/libf3d/python/render-image/render_image.py index c37abbd61a..c567e8ed66 100644 --- a/examples/libf3d/python/render-image/render_image.py +++ b/examples/libf3d/python/render-image/render_image.py @@ -8,8 +8,8 @@ try: f3d.Engine.autoload_plugins() - eng = f3d.Engine(f3d.Window.NATIVE_OFFSCREEN) - eng.loader.load_geometry(sys.argv[1]) + eng = f3d.Engine.create(True) + eng.scene.add(sys.argv[1]) eng.window.size = 300, 300 img = eng.window.render_to_image() diff --git a/examples/libf3d/python/render-interact/render_interact.py b/examples/libf3d/python/render-interact/render_interact.py index b5fcf1d908..3a5b566145 100644 --- a/examples/libf3d/python/render-interact/render_interact.py +++ b/examples/libf3d/python/render-interact/render_interact.py @@ -9,11 +9,11 @@ f3d.Engine.autoload_plugins() # Create a native window engine - eng = f3d.Engine(f3d.Window.NATIVE) + eng = f3d.Engine.create() - # Load a model + # Add a model try: - eng.loader.load_geometry(sys.argv[1]) + eng.scene.add(sys.argv[1]) except RuntimeError as e: print(e) diff --git a/examples/libf3d/python/render-terminal/render_terminal.py b/examples/libf3d/python/render-terminal/render_terminal.py index f420b3af63..96120a31c8 100644 --- a/examples/libf3d/python/render-terminal/render_terminal.py +++ b/examples/libf3d/python/render-terminal/render_terminal.py @@ -28,9 +28,9 @@ def main(): rows, cols = 40, 20 # setup engine - engine = f3d.Engine(f3d.Window.NATIVE_OFFSCREEN) + engine = f3d.Engine.create(True) engine.options.update(options) - engine.loader.load_geometry(str(model_path)) + engine.scene.add(str(model_path)) engine.window.size = rows, cols * 2 # fit view to loaded model and grab computed camera position diff --git a/examples/libf3d/python/tkinter/minimal_tkinter.py b/examples/libf3d/python/tkinter/minimal_tkinter.py index 6bfa534320..8f1abea41f 100644 --- a/examples/libf3d/python/tkinter/minimal_tkinter.py +++ b/examples/libf3d/python/tkinter/minimal_tkinter.py @@ -12,8 +12,8 @@ def __init__(self): # Initialize F3D def initgl(self): - self.engine = f3d.Engine(f3d.Window.Type.EXTERNAL) - self.engine.loader.load_geometry( + self.engine = f3d.Engine.create_external_glx() # use create_external_egl for Wayland + self.engine.scene.add( f3d.Mesh( points=[0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0], face_sides=[3], diff --git a/java/CMakeLists.txt b/java/CMakeLists.txt index db4d47805d..918e02c546 100644 --- a/java/CMakeLists.txt +++ b/java/CMakeLists.txt @@ -11,8 +11,8 @@ endif() set(F3D_JAVA_SOURCES Camera.java Engine.java - Loader.java Options.java + Scene.java Window.java) # Generate JNI headers, and builds a JAR package diff --git a/java/Engine.java b/java/Engine.java index d634ca7334..318786d9cb 100644 --- a/java/Engine.java +++ b/java/Engine.java @@ -8,7 +8,7 @@ public class Engine implements AutoCloseable { public Engine() { mNativeAddress = construct(); // instantiate the native engine - mLoader = new Loader(mNativeAddress); + mScene = new Scene(mNativeAddress); mOptions = new Options(mNativeAddress); mWindow = new Window(mNativeAddress); } @@ -26,14 +26,14 @@ public void close() { static public native void loadPlugin(String plugin); static public native void autoloadPlugins(); - public Loader getLoader() { return mLoader; } + public Scene getScene() { return mScene; } public Options getOptions() { return mOptions; } public Window getWindow() { return mWindow; } private native long construct(); private native void destroy(long nativeAddress); - private Loader mLoader; + private Scene mScene; private Options mOptions; private Window mWindow; diff --git a/java/F3DJavaBindings.cxx b/java/F3DJavaBindings.cxx index dc56f2f714..9f9391949b 100644 --- a/java/F3DJavaBindings.cxx +++ b/java/F3DJavaBindings.cxx @@ -1,7 +1,7 @@ // Automatically generated headers #include #include -#include +#include #include #include @@ -46,7 +46,7 @@ extern "C" JNIEXPORT jlong JAVA_BIND(Engine, construct)(JNIEnv*, jobject) { f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG); - return reinterpret_cast(new f3d::engine()); + return reinterpret_cast(new f3d::engine(f3d::engine::create())); } JNIEXPORT void JAVA_BIND(Engine, destroy)(JNIEnv*, jobject, jlong ptr) @@ -54,18 +54,16 @@ extern "C" delete reinterpret_cast(ptr); } - // Loader - JNIEXPORT void JAVA_BIND(Loader, loadScene)(JNIEnv* env, jobject self, jstring path) + // Scene + JNIEXPORT void JAVA_BIND(Scene, add)(JNIEnv* env, jobject self, jstring path) { const char* str = env->GetStringUTFChars(path, nullptr); - GetEngine(env, self)->getLoader().loadScene(str); + GetEngine(env, self)->getScene().add(str); env->ReleaseStringUTFChars(path, str); } - JNIEXPORT void JAVA_BIND(Loader, loadGeometry)(JNIEnv* env, jobject self, jstring path) + JNIEXPORT void JAVA_BIND(Scene, clear)(JNIEnv* env, jobject self) { - const char* str = env->GetStringUTFChars(path, nullptr); - GetEngine(env, self)->getLoader().loadGeometry(str); - env->ReleaseStringUTFChars(path, str); + GetEngine(env, self)->getScene().clear(); } // Window diff --git a/java/Loader.java b/java/Loader.java deleted file mode 100644 index 6f63704002..0000000000 --- a/java/Loader.java +++ /dev/null @@ -1,13 +0,0 @@ -package app.f3d.F3D; - -public class Loader { - - public Loader(long nativeAddress) { - mNativeAddress = nativeAddress; - } - - public native void loadScene(String file); - public native void loadGeometry(String file); - - private long mNativeAddress; -} diff --git a/java/Scene.java b/java/Scene.java new file mode 100644 index 0000000000..c04fb02de9 --- /dev/null +++ b/java/Scene.java @@ -0,0 +1,13 @@ +package app.f3d.F3D; + +public class Scene { + + public Scene(long nativeAddress) { + mNativeAddress = nativeAddress; + } + + public native void add(String file); + public native void clear(); + + private long mNativeAddress; +} diff --git a/java/testing/TestJavaBindings.java b/java/testing/TestJavaBindings.java index 384315e70c..57493f5eb2 100644 --- a/java/testing/TestJavaBindings.java +++ b/java/testing/TestJavaBindings.java @@ -28,8 +28,8 @@ public static void main(String[] args) { assert pos[1] == 1.0 : "Position Y is not valid"; assert pos[2] == 2.0 : "Position Z is not valid"; - Loader loader = engine.getLoader(); - loader.loadGeometry(args[0] + "data/cow.vtp"); + Scene scene = engine.getScene(); + scene.add(args[0] + "data/cow.vtp"); } } } diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index ff1dd1a93a..5cdc364839 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -47,6 +47,7 @@ set(F3D_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/animationManager.cxx ${CMAKE_CURRENT_SOURCE_DIR}/src/camera_impl.cxx ${CMAKE_CURRENT_BINARY_DIR}/src/config.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/src/context.cxx ${CMAKE_CURRENT_SOURCE_DIR}/src/engine.cxx ${CMAKE_CURRENT_BINARY_DIR}/src/factory.cxx ${CMAKE_CURRENT_SOURCE_DIR}/src/image.cxx @@ -54,14 +55,18 @@ set(F3D_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/interactor.cxx ${CMAKE_CURRENT_SOURCE_DIR}/src/interactor_impl.cxx ${CMAKE_CURRENT_SOURCE_DIR}/src/levenshtein.cxx - ${CMAKE_CURRENT_SOURCE_DIR}/src/loader_impl.cxx ${CMAKE_CURRENT_SOURCE_DIR}/src/log.cxx ${CMAKE_CURRENT_SOURCE_DIR}/src/options.cxx + ${CMAKE_CURRENT_SOURCE_DIR}/src/scene_impl.cxx ${CMAKE_CURRENT_SOURCE_DIR}/src/types.cxx ${CMAKE_CURRENT_SOURCE_DIR}/src/utils.cxx ${CMAKE_CURRENT_SOURCE_DIR}/src/window_impl.cxx ) +if(APPLE) + list(APPEND F3D_SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/context_cocoa.mm) +endif() + # List of headers that will be installed set(F3D_PUBLIC_HEADERS ${CMAKE_CURRENT_BINARY_DIR}/public/export.h @@ -69,12 +74,13 @@ set(F3D_PUBLIC_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/plugin/plugin.h ${CMAKE_CURRENT_SOURCE_DIR}/plugin/reader.h ${CMAKE_CURRENT_SOURCE_DIR}/public/camera.h + ${CMAKE_CURRENT_SOURCE_DIR}/public/context.h ${CMAKE_CURRENT_SOURCE_DIR}/public/engine.h ${CMAKE_CURRENT_SOURCE_DIR}/public/exception.h ${CMAKE_CURRENT_SOURCE_DIR}/public/image.h ${CMAKE_CURRENT_SOURCE_DIR}/public/interactor.h - ${CMAKE_CURRENT_SOURCE_DIR}/public/loader.h ${CMAKE_CURRENT_SOURCE_DIR}/public/log.h + ${CMAKE_CURRENT_SOURCE_DIR}/public/scene.h ${CMAKE_CURRENT_SOURCE_DIR}/public/types.h ${CMAKE_CURRENT_SOURCE_DIR}/public/utils.h ${CMAKE_CURRENT_SOURCE_DIR}/public/window.h diff --git a/library/options.json b/library/options.json index 4f843ddce8..12dcedb961 100644 --- a/library/options.json +++ b/library/options.json @@ -4,6 +4,10 @@ "type": "string", "default_value": "+Y" }, + "up_direction2": { + "type": "vector", + "default_value": "0, 1, 0" + }, "animation": { "autoplay": { "type": "bool", @@ -30,15 +34,13 @@ "type": "int" }, "orthographic": { - "type": "bool", - "default_value": "false" + "type": "bool" } } }, "render": { "show_edges": { - "type": "bool", - "default_value": "false" + "type": "bool" }, "line_width": { "type": "double" @@ -187,56 +189,45 @@ "model": { "matcap": { "texture": { - "type": "string", - "default_value": "" + "type": "string" } }, "color": { "opacity": { - "type": "double", - "default_value": "1.0" + "type": "double" }, "rgb": { - "type": "double_vector", - "default_value": "1.0, 1.0, 1.0" + "type": "double_vector" }, "texture": { - "type": "string", - "default_value": "" + "type": "string" } }, "emissive": { "factor": { - "type": "double_vector", - "default_value": "1.0, 1.0, 1.0" + "type": "double_vector" }, "texture": { - "type": "string", - "default_value": "" + "type": "string" } }, "normal": { "scale": { - "type": "double", - "default_value": "1.0" + "type": "double" }, "texture": { - "type": "string", - "default_value": "" + "type": "string" } }, "material": { "metallic": { - "type": "double", - "default_value": "0.0" + "type": "double" }, "roughness": { - "type": "double", - "default_value": "0.3" + "type": "double" }, "texture": { - "type": "string", - "default_value": "" + "type": "string" } }, "scivis": { diff --git a/library/private/animationManager.h b/library/private/animationManager.h index cf2c19d841..032946ddbd 100644 --- a/library/private/animationManager.h +++ b/library/private/animationManager.h @@ -29,16 +29,27 @@ class interactor_impl; class animationManager { public: - animationManager() = default; + animationManager(const options& options, window& window); ~animationManager() = default; + /** + * Set the interactor to use in the animation_manager, should be set before initializing if any + */ + void SetInteractor(interactor_impl* interactor); + + /** + * Set the importer to use in the animation_manager, must be set before initializing + */ + void SetImporter(vtkImporter* importer); + /** * Initialize the animation manager, required before playing the animation. - * Provided pointers are expected to be not null except interactor. + * Can be used to reset animation to the initial state. + * Importer must be set before use. + * Interactor should be set before use if any. * Return true if at least one animation is available, false otherwise. */ - bool Initialize( - const options* options, window* window, interactor_impl* interactor, vtkImporter* importer); + bool Initialize(); /** * Start/Stop playing the animation @@ -89,16 +100,16 @@ class animationManager */ void GetTimeRange(double timeRange[2]); -protected: +private: /** * Called by an internal timer to advance one animation tick */ void Tick(); + const options& Options; + window& Window; vtkImporter* Importer = nullptr; - window* Window = nullptr; interactor_impl* Interactor = nullptr; - const options* Options = nullptr; double TimeRange[2] = { 0.0, 0.0 }; bool Playing = false; diff --git a/library/private/context_cocoa.h b/library/private/context_cocoa.h new file mode 100644 index 0000000000..96a5f85496 --- /dev/null +++ b/library/private/context_cocoa.h @@ -0,0 +1,8 @@ +#ifndef f3d_context_cocoa_h +#define f3d_context_cocoa_h + +namespace f3d::detail +{ +void* getCocoaOpenGLSymbol(const char* name); +} +#endif diff --git a/library/private/interactor_impl.h b/library/private/interactor_impl.h index 21833578b7..349284c3b5 100644 --- a/library/private/interactor_impl.h +++ b/library/private/interactor_impl.h @@ -21,7 +21,7 @@ class options; namespace detail { -class loader_impl; +class scene_impl; class window_impl; class animationManager; @@ -32,11 +32,18 @@ class interactor_impl : public interactor /** * Documented public API */ - interactor_impl(options& options, window_impl& window, loader_impl& loader); + interactor_impl(options& options, window_impl& window, scene_impl& scene); ~interactor_impl() override; - interactor& setKeyPressCallBack(std::function callBack) override; - interactor& setDropFilesCallBack(std::function)> callBack) override; + interactor& addCommandCallback( + std::string action, std::function&)> callback) override; + interactor& removeCommandCallback(const std::string& action) override; + bool triggerCommand(std::string_view command) override; + interactor& addInteractionCommands( + std::string interaction, ModifierKeys modifiers, std::vector command) override; + interactor& addInteractionCommand( + std::string interaction, ModifierKeys modifiers, std::string command) override; + interactor& removeInteractionCommands(std::string interaction, ModifierKeys modifiers) override; unsigned long createTimerCallBack(double time, std::function callBack) override; void removeTimerCallBack(unsigned long id) override; @@ -71,7 +78,7 @@ class interactor_impl : public interactor /** * Implementation only API. * Initialize the animation manager using interactor objects. - * This is called by the loader after loading a file. + * This is called by the scene after add a file. */ void InitializeAnimation(vtkImporter* importer); diff --git a/library/private/loader_impl.h b/library/private/loader_impl.h deleted file mode 100644 index 690d86fd28..0000000000 --- a/library/private/loader_impl.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * @class loader_impl - * @brief A concrete implementation of loader - * - * A concrete implementation of loader that hides the private API - * See loader.h for the class documentation - */ - -#ifndef f3d_loader_impl_h -#define f3d_loader_impl_h - -#include "loader.h" - -#include - -namespace f3d -{ -class options; - -namespace detail -{ -class interactor_impl; -class window_impl; -class loader_impl : public loader -{ -public: - ///@{ - /** - * Documented public API - */ - loader_impl(const options& options, window_impl& window); - ~loader_impl(); - loader& loadGeometry(const std::string& filePath, bool reset) override; - loader& loadGeometry(const mesh_t& mesh, bool reset = false) override; - loader& loadScene(const std::string& filePath) override; - bool hasGeometryReader(const std::string& filePath) override; - bool hasSceneReader(const std::string& filePath) override; - ///@} - - /** - * Implementation only API. - * Set the interactor to use when interacting and set the AnimationManager on the interactor. - */ - void SetInteractor(interactor_impl* interactor); - -private: - class internals; - std::unique_ptr Internals; -}; -} -} - -#endif diff --git a/library/private/options_tools.h.in b/library/private/options_tools.h.in index 221dc674b9..db3ecdad3e 100644 --- a/library/private/options_tools.h.in +++ b/library/private/options_tools.h.in @@ -5,7 +5,11 @@ #include "types.h" #include +#include +#include +#include #include +#include namespace f3d { @@ -181,6 +185,60 @@ ratio_t parse(const std::string& str) } } +//---------------------------------------------------------------------------- +/** + * Parse provided string into a vector3_t. + */ +template<> +vector3_t parse(const std::string& str) +{ + auto parseCommaSeparated = [](const std::string& s) -> vector3_t + { + std::vector vec = parse>(s); + if (vec.size() == 2) + { + return f3d::vector3_t::fromSphericalCoordinates(vec[0], vec[1]); + } + if (vec.size() == 3) + { + return { vec[0], vec[1], vec[2] }; + } + else + { + throw options::parsing_exception("cannot parse " + s + " to a vector3_t"); + } + }; + auto parseYSyntax = [](const std::string& s) -> vector3_t + { + const std::regex re("([-+]?)([XYZ])", std::regex_constants::icase); + std::smatch match; + std::string ss = trim(s); + if (std::regex_match(ss, match, re)) + { + const double sign = match[1].str() == "-" ? -1.0 : 1.0; + const int index = std::toupper(match[2].str()[0]) - 'X'; + assert(index >= 0 && index < 3); + vector3_t vec = { 0, 0, 0 }; + vec[index] = 1.0 * sign; + return vec; + } + else + { + throw options::parsing_exception("cannot parse " + s + " to a vector3_t"); + } + }; + bool isNotCommaSeparated = std::find_if(str.begin(), str.end(), + [](unsigned char c) { return std::isalpha(c); }) != str.end(); + if (!isNotCommaSeparated) + { + return parseCommaSeparated(str); + } + else + { + return parseYSyntax(str); + } +} + //---------------------------------------------------------------------------- /** * Return provided string stripped of leading and trailing spaces. @@ -270,6 +328,17 @@ std::string format(const std::vector& var) return stream.str(); } +//---------------------------------------------------------------------------- +/** + * Format provided var into a string from provided vector3_t + * rely on format(double&) + */ +template<> +std::string format(const vector3_t& var) +{ + return format(static_cast>(var)); +} + //---------------------------------------------------------------------------- /** * Generated method, see `options::set` diff --git a/library/private/scene_impl.h b/library/private/scene_impl.h new file mode 100644 index 0000000000..496ab33193 --- /dev/null +++ b/library/private/scene_impl.h @@ -0,0 +1,54 @@ +/** + * @class scene_impl + * @brief A concrete implementation of scene + * + * A concrete implementation of scene that hides the private API + * See scene.h for the class documentation + */ + +#ifndef f3d_scene_impl_h +#define f3d_scene_impl_h + +#include "scene.h" + +#include + +namespace f3d +{ +class options; + +namespace detail +{ +class interactor_impl; +class window_impl; +class scene_impl : public scene +{ +public: + ///@{ + /** + * Documented public API + */ + scene_impl(const options& options, window_impl& window); + ~scene_impl(); + scene& add(const std::filesystem::path& filePath) override; + scene& add(const std::vector& filePath) override; + scene& add(const std::vector& filePathStrings) override; + scene& add(const mesh_t& mesh) override; + scene& clear() override; + bool supports(const std::filesystem::path& filePath) override; + ///@} + + /** + * Implementation only API. + * Set the interactor to use when interacting and set the AnimationManager on the interactor. + */ + void SetInteractor(interactor_impl* interactor); + +private: + class internals; + std::unique_ptr Internals; +}; +} +} + +#endif diff --git a/library/private/window_impl.h b/library/private/window_impl.h index 43da739a42..ca1bcd6b28 100644 --- a/library/private/window_impl.h +++ b/library/private/window_impl.h @@ -10,13 +10,15 @@ #ifndef f3d_window_impl_h #define f3d_window_impl_h +#include "context.h" #include "log.h" #include "window.h" #include +#include class vtkRenderWindow; -class vtkF3DGenericImporter; +class vtkF3DMetaImporter; namespace f3d { class options; @@ -30,8 +32,8 @@ class window_impl : public window * Create the internal vtkRenderWindow using the offscreen param * and store option ref for later usage */ - window_impl(const options& options, Type type); - + window_impl(const options& options, const std::optional& type, bool offscreen, + const context::function& getProcAddress); /** * Default destructor */ @@ -42,6 +44,7 @@ class window_impl : public window * Documented public API */ Type getType() override; + bool isOffscreen() override; camera& getCamera() override; bool render() override; image renderToImage(bool noBackground = false) override; @@ -58,27 +61,30 @@ class window_impl : public window /** * Implementation only API. - * Create and initialize the internal vtkF3DRenderer with the provided parameters - * Called by the loader right before reading a file + * Initialize the renderer by clearing it of all actors. + */ + void Initialize(); + + /** + * Implementation only API. + * Initialize the up vector on the renderer using the Up string option */ - virtual void Initialize(bool withColoring); + void InitializeUpVector(); /** * Implementation only API. - * Set the importer on an already created vtkF3DRendererWithColoring - * Called by the loader right after reading a file + * Set the importer on the internal renderer */ - virtual void SetImporterForColoring(vtkF3DGenericImporter* importer); + void SetImporter(vtkF3DMetaImporter* importer); /** * Implementation only API. * Use all the rendering related options to update the configuration of the window - * and the rendering stack below. This also initialize the rendering stack if needed. - * This will be called automatically when calling loader::loadFile but can also be called manually - * when needed. This must be called, either manually or automatically, before any render call. - * Return true on success, false otherwise. + * and the rendering stack below. + * This is called automatically when calling scene::add and window::render but can also be called + * manually when needed. Return true on success, false otherwise. */ - virtual void UpdateDynamicOptions(); + void UpdateDynamicOptions(); /** * Implementation only API. diff --git a/library/public/context.h b/library/public/context.h new file mode 100644 index 0000000000..b190740726 --- /dev/null +++ b/library/public/context.h @@ -0,0 +1,87 @@ +#ifndef f3d_context_h +#define f3d_context_h + +#include "exception.h" +#include "export.h" + +#include +#include + +namespace f3d +{ +/** + * @class context + * @brief Utility function that implements basic OpenGL context symbol resolvers + * + * These functions should be used only when the rendering context (the OpenGL window) isn't + * managed by libf3d, but by the user directly. + * Therefore, the engine is created using `f3d::engine::createExternal` and the functions below + * can be used as argument of this factory. + * One can also create a custom functor returning a `f3d::context::function`. + */ +class F3D_EXPORT context +{ +public: + using fptr = void (*)(); + using function = std::function; + + /** + * Create a GLX context function. + * Only supported on Linux. + */ + static function glx(); + + /** + * Create a WGL context function. + * Only supported on Windows. + */ + static function wgl(); + + /** + * Create a COCOA context function. + * This is usually required when using a headless context and a GPU device. + * Only supported on macOS. + */ + static function cocoa(); + + /** + * Create a EGL context function. + * This is usually required when using a headless context and a GPU device. + * Only supported on Linux and Windows. + */ + static function egl(); + + /** + * Create a OSMesa context function. + * This is usually required when using a headless context and no GPU device. + * Only supported on Linux and Windows. + */ + static function osmesa(); + + /** + * Create a context function from a library name and a function name. + * The library name must be specified without its prefix and extension. + * For example, `getSymbol("EGL", "eglGetProcAddress")` looks for the symbol + * `eglGetProcAddress` in the library `libEGL.so` on Linux. + */ + static function getSymbol(const std::string& lib, const std::string& func); + + /** + * An exception that can be thrown when the requested library cannot be loaded. + */ + struct loading_exception : public exception + { + explicit loading_exception(const std::string& what = ""); + }; + + /** + * An exception that can be thrown when the symbol resolver cannot be found in the library. + */ + struct symbol_exception : public exception + { + explicit symbol_exception(const std::string& what = ""); + }; +}; +} + +#endif diff --git a/library/public/engine.h b/library/public/engine.h index 9503e9bdb0..b6756fabce 100644 --- a/library/public/engine.h +++ b/library/public/engine.h @@ -1,11 +1,12 @@ #ifndef f3d_engine_h #define f3d_engine_h +#include "context.h" #include "exception.h" #include "export.h" #include "interactor.h" -#include "loader.h" #include "options.h" +#include "scene.h" #include "window.h" #include @@ -22,47 +23,136 @@ namespace f3d * Configured on creation using an enum, then all objects * can be accessed through their getter. * - * Example usage for a default scene file: + * Example usage for adding some files in the scene * * \code{.cpp} - * f3d::engine eng(f3d::engine::CREATE_WINDOW | f3d::engine::CREATE_INTERACTOR); - * f3d::loader& load = eng.getLoader(); - * load.loadGeometry("path/to/file").loadGeometry("path/to/another/file"); + * f3d::engine eng = f3d::engine::create(); + * f3d::scene& sce = eng.getscene(); + * sce.add({"path/to/file", "path/to/another/file"}); * f3d::interactor& inter = eng.getInteractor(); * inter.start(); * \endcode - * - * Example usage for a full scene file: - * - * \code{.cpp} - * f3d::engine eng(f3d::engine::CREATE_WINDOW | f3d::engine::CREATE_INTERACTOR); - * f3d::loader& load = eng.getLoader(); - * load.loadScene("path/to/file"); - * f3d::interactor& inter = eng.getInteractor(); - * inter.start(); - * \endcode - */ class F3D_EXPORT engine { public: /** - * Engine constructor, choose the window type using the enum. - * see window.h for details about the window. - * When using window::Type::NONE, window and interactor will not be provided by the engine. - * When using window::Type::EXTERNAL, interactor will not be provided by the engine. - * All objects instances will be created on construction. - * Default is window::Type::NATIVE. - * Throw a no_window_exception when using a Using window::Type::EXTERNAL without the right cmake - * option. + * Create an engine with an automatic window. + * Optionally, the window can be hidden by setting offscreen to true. + * For VTK < 9.3, the window type will depend on the VTK build options + * For VTK >= 9.4: + * Linux: Try GLX, then EGL, then OSMesa + * Windows: Try Win32, then EGL, then OSMesa + * macOS: Always use Cocoa + */ + static engine create(bool offscreen = false); + + /** + * Create an engine with no window. + */ + static engine createNone(); + + /** + * Create an engine with a GLX window. + * Works on Linux only. + * VTK >= 9.4 required. + * Optionally, the window can be hidden by setting offscreen to true. + * Throws engine::loading_exception in case of window creation failure. + */ + static engine createGLX(bool offscreen = false); + + /** + * Create an engine with a WGL window. + * Works on Windows only. + * VTK >= 9.4 required. + * Optionally, the window can be hidden by setting offscreen to true. + * Throws engine::loading_exception in case of window creation failure. + */ + static engine createWGL(bool offscreen = false); + + /** + * Create an engine with an EGL window. + * VTK >= 9.4 required. + * If several GPU are available, the environment variable + * `VTK_DEFAULT_EGL_DEVICE_INDEX` allows its selection. + * Optionally, the window can be hidden by setting offscreen to true. + * Throws engine::loading_exception in case of failure. + */ + static engine createEGL(bool offscreen = false); + + /** + * Create an engine with an OSMesa window. + * VTK >= 9.4 required. + * Throws engine::loading_exception in case of window creation failure. + */ + static engine createOSMesa(); + + /** + * Create an engine with an external window. + * A context to retrieve OpenGL symbols is required. + * The context can be nullptr for an external Cocoa window. + * Here's an example if a GLFW window is used: + * \code{.cpp} + * f3d::engine eng = f3d::engine::createExternal(glfwGetProcAddress); + * \endcode + */ + static engine createExternal(const context::function& getProcAddress); + + /** + * Create an engine with an external GLX context. + * Equivalent to createExternal(f3d::context::glx()); + * VTK >= 9.4 required. + * Throws context::loading_exception if GLX library is not found or if not running on Linux. + */ + static engine createExternalGLX(); + + /** + * Create an engine with an external WGL context. + * Equivalent to createExternal(f3d::context::wgl()); + * VTK >= 9.4 required. + * Throws context::loading_exception if WGL library is not found or if not running on Windows. + */ + static engine createExternalWGL(); + + /** + * Create an engine with an external COCOA context. + * Equivalent to createExternal(f3d::context::cocoa()); + * VTK >= 9.4 required. + * Throws context::loading_exception if WGL library is not found or if not running on Windows. */ - explicit engine(window::Type windowType = window::Type::NATIVE); + static engine createExternalCOCOA(); + + /** + * Create an engine with an external EGL context. + * Equivalent to createExternal(f3d::context::egl()); + * VTK >= 9.4 required. + * Throws context::loading_exception if EGL library is not found. + */ + static engine createExternalEGL(); + + /** + * Create an engine with an external OSMesa context. + * Equivalent to createExternal(f3d::context::osmesa()); + * VTK >= 9.4 required. + * Throws context::loading_exception if OSMesa library is not found. + */ + static engine createExternalOSMesa(); /** * Engine destructor, delete all object instances as well. */ ~engine(); + //@{ + /** + * Engine copy is not possible but move is allowed. + */ + engine(const engine& other) = delete; + engine(engine&& other) noexcept; + engine& operator=(const engine& other) = delete; + engine& operator=(engine&& other) noexcept; + //@} + /** * Set the cache path. Must be an absolute path. * Currently, it's only used to store HDRI baked textures. @@ -101,7 +191,7 @@ class F3D_EXPORT engine /** * Get the loaded provided by the engine. */ - loader& getLoader(); + scene& getScene(); /** * Get the interactor provided by the engine, if any. @@ -210,10 +300,13 @@ class F3D_EXPORT engine private: class internals; internals* Internals; - engine(const engine& opt) = delete; - engine(engine&& opt) = delete; - engine& operator=(const engine& opt) = delete; - engine& operator=(engine&& opt) = delete; + + /** + * Engine constructor. This is a private method. + * The user must rely on factories to create the engine instance. + */ + engine( + const std::optional& windowType, bool offscreen, const context::function& loader); }; } diff --git a/library/public/image.h b/library/public/image.h index 6ae6ba09c9..7f99c28456 100644 --- a/library/public/image.h +++ b/library/public/image.h @@ -144,6 +144,7 @@ class F3D_EXPORT image * Please note, due to possible arithmetic imprecision in the SSIM computation * using a threshold of zero may return false with identical images. * Depending on the VTK version, another comparison algorithm may be used. + * Threshold should be in range [0, 1[, this returns false otherwise. * 1e-14: Pixel perfect comparison. * 0.05: Visually indistinguishable. * 0.1: Small visible difference. diff --git a/library/public/interactor.h b/library/public/interactor.h index 5c8498bdd9..36d50641c9 100644 --- a/library/public/interactor.h +++ b/library/public/interactor.h @@ -2,7 +2,6 @@ #define f3d_interactor_h #include "export.h" -#include "loader.h" #include "options.h" #include "window.h" @@ -23,28 +22,73 @@ namespace f3d class F3D_EXPORT interactor { public: + ///@{ @name Command /** - * Use this method to specify your own keypress callback, with the expected API: - * \code - * bool callBack(int keyCode, std::string keySym) - * \endcode - * keyCode being the pressed key, eg: `C` and keySym the key symbol for keys which do not have - * codes, eg: Left, Right, Up, Down, Space, Enter. Your callBack should return true if the key was - * handled, false if you want standard interactor behavior instead. + * Use this method to add a callback into the command map + * to be called using triggerCommand. + * Adding a commandCallback with an existing action replaces it. */ - virtual interactor& setKeyPressCallBack(std::function callBack) = 0; + virtual interactor& addCommandCallback( + std::string action, std::function&)> callback) = 0; /** - * Use this method to specify your own drop files callback, with the expected API: - * \code - * bool callBack(std::vector files) - * \endcode - * files being a vector of string containing paths to dropped files. - * Your callBack should return true if the event was handled, false if you want standard - * interactor behavior instead. + * Remove a command callback for provided action */ - virtual interactor& setDropFilesCallBack( - std::function)> callBack) = 0; + virtual interactor& removeCommandCallback(const std::string& action) = 0; + + /** + * Trigger provided command, see COMMAND.md for more information + */ + virtual bool triggerCommand(std::string_view command) = 0; + ///@} + + ///@{ @name Interaction Commands + /** + * Enumeration of supported modifier combination, in binary. + */ + enum class ModifierKeys : unsigned char + { + ANY = 0x80, // 10000000 + NONE = 0x0, // 00000000 + CTRL = 0x1, // 00000001 + SHIFT = 0x2, // 00000010 + CTRL_SHIFT = 0x3 // 00000011 + }; + + /** + * Use this method to specify your own interaction commands for a specified interaction and + * modifiers flag. + * + * interaction can be a pressed key symbol, eg: "C", + * or a dedicated key symbol for special keys: + * "Left", "Right", "Up", "Down", "Space", "Enter", "Escape", "Question". + * + * modifiers is a binary flag from the dedicated enum that represent KeyModifiers. + * + * Adding commands for an existing combination of interaction and modifier will replace it. + * + * When the corresponding interaction and modifiers happens, the provided commands will be + * triggered using triggerCommand. + * ANY modifier interactions will only be triggered if no other interaction bind with modifier + * is found. + */ + virtual interactor& addInteractionCommands( + std::string interaction, ModifierKeys modifiers, std::vector commands) = 0; + + /** + * See addInteractionCommands + * Convenience method to add a single command for an interaction, similar as + * addInteractionCommands(interaction, modifiers, {command}) + */ + virtual interactor& addInteractionCommand( + std::string interaction, ModifierKeys modifiers, std::string command) = 0; + + /** + * Remove interaction commands corresponding to provided interaction and modifiers + */ + virtual interactor& removeInteractionCommands( + std::string interaction, ModifierKeys modifiers) = 0; + ///@} /** * Use this method to create your own timer callback. You callback will be called once every time diff --git a/library/public/loader.h b/library/public/loader.h deleted file mode 100644 index 3624aec6df..0000000000 --- a/library/public/loader.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef f3d_loader_h -#define f3d_loader_h - -#include "exception.h" -#include "export.h" -#include "types.h" - -#include -#include - -namespace f3d -{ -/** - * @class loader - * @brief Class to load files in F3D - * - * A class to load files in F3D. The loader can load a full scene or multiple geometries into a - * default scene. It also support checking if a scene or geometry reader is available for a given - * file. - * - * Example usage: - * \code{.cpp} - * std::string path = ... - * f3d::engine eng(f3d::window::Type::NATIVE); - * f3d::loader& load = eng.getLoader(); - * - * if (load.hasSceneReader(path) - * { - * load.loadScene(path); - * } - * else if (load.hasGeometryReader(path) - * { - * load.loadGeometry(path); - * } - * \endcode - * - */ -class F3D_EXPORT loader -{ -public: - /** - * An exception that can be thrown by the loader - * when it failed to load a file for some reason. - */ - struct load_failure_exception : public exception - { - explicit load_failure_exception(const std::string& what = "") - : exception(what){}; - }; - - /** - * Return true if the loader has a geometry reader for the providen file, false otherwise. - */ - virtual bool hasGeometryReader(const std::string& filePath) = 0; - - /** - * Load a geometry from a provided file to the scene. - * Calling this method will reset the scene before loading if a full scene was loaded previously - * or if the reset argument is set to true, It will not reset if only geometries were loaded - * previously. Geometries loaded using this method will be available in a default scene and use - * all default scene related options. Throw a load_failure_exception on failure. - */ - virtual loader& loadGeometry(const std::string& filePath, bool reset = false) = 0; - - /** - * Load a geometry from memory buffers. - * Calling this method will reset the scene before loading if a full scene was loaded previously - * or if the reset argument is set to true, It will not reset if only geometries were loaded - * previously. Geometries loaded using this method will be available in a default scene and use - * all default scene related options. - * Throw a load_failure_exception if the mesh is invalid. - */ - virtual loader& loadGeometry(const mesh_t& mesh, bool reset = false) = 0; - - /** - * Return true if the loader has a scene reader for the providen file, false otherwise. - */ - virtual bool hasSceneReader(const std::string& filePath) = 0; - - /** - * Reset scene and load a (full) scene from provided file. - * Please note default scene related options are not taken into account when loading a full scene. - * Throw a load_failure_exception on failure. - */ - virtual loader& loadScene(const std::string& filePath) = 0; - -protected: - //! @cond - loader() = default; - virtual ~loader() = default; - loader(const loader& opt) = delete; - loader(loader&& opt) = delete; - loader& operator=(const loader& opt) = delete; - loader& operator=(loader&& opt) = delete; - //! @endcond -}; -} - -#endif diff --git a/library/public/options.h.in b/library/public/options.h.in index ab600d749b..01dd6c5a4f 100644 --- a/library/public/options.h.in +++ b/library/public/options.h.in @@ -73,9 +73,9 @@ public: /** * A boolean option specific method to toggle it. + * If the option has not been set yet, set it to true. * Throw an options::inexistent_exception if option does not exist. * Throw an options::incompatible_exception if option is not boolean. - * Throw an options::no_value_exception if option has not been set. */ options& toggle(const std::string& name); diff --git a/library/public/scene.h b/library/public/scene.h new file mode 100644 index 0000000000..9eebb3af8a --- /dev/null +++ b/library/public/scene.h @@ -0,0 +1,97 @@ +#ifndef f3d_scene_h +#define f3d_scene_h + +#include "exception.h" +#include "export.h" +#include "types.h" + +#include +#include +#include + +namespace f3d +{ +/** + * @class scene + * @brief Class to load files into + * + * The scene where files and meshes can be added and loaded into. + * + * Example usage: + * \code{.cpp} + * std::string path = ... + * f3d::engine eng(f3d::window::Type::NATIVE); + * f3d::scene& load = eng.getScene(); + * + * if (load.supports(path) + * { + * load.add(path); + * } + * \endcode + * + */ +class F3D_EXPORT scene +{ +public: + /** + * An exception that can be thrown by the scene + * when it failed to load a file for some reason. + */ + struct load_failure_exception : public exception + { + explicit load_failure_exception(const std::string& what = "") + : exception(what){}; + }; + + ///@{ + /** + * Add and load provided files into the scene + * Already added file will NOT be reloaded + */ + virtual scene& add(const std::filesystem::path& filePath) = 0; + virtual scene& add(const std::vector& filePath) = 0; + virtual scene& add(const std::vector& filePathStrings) = 0; + ///@} + + /** + * Add and load provided mesh into the scene + */ + virtual scene& add(const mesh_t& mesh) = 0; + + ///@{ + /** + * Convenience initializer list signature for add method + */ + scene& add(std::initializer_list list) + { + return this->add(std::vector(list)); + } + scene& add(std::initializer_list list) + { + return this->add(std::vector(list)); + } + ///@} + + /** + * Clear the scene of all added files + */ + virtual scene& clear() = 0; + + /** + * Return true if provided file path is supported, false otherwise. + */ + virtual bool supports(const std::filesystem::path& filePath) = 0; + +protected: + //! @cond + scene() = default; + virtual ~scene() = default; + scene(const scene& opt) = delete; + scene(scene&& opt) = delete; + scene& operator=(const scene& opt) = delete; + scene& operator=(scene&& opt) = delete; + //! @endcond +}; +} + +#endif diff --git a/library/public/types.h b/library/public/types.h index 238a005184..60912d4221 100644 --- a/library/public/types.h +++ b/library/public/types.h @@ -1,33 +1,37 @@ #ifndef f3d_types_h #define f3d_types_h +#include "exception.h" #include "export.h" #include +#include +#include +#include #include #include namespace f3d { + /** - * Describe a 3D point. + * An exception that can be thrown when we fail to create a type */ -struct F3D_EXPORT point3_t : std::array +struct type_creation_exception : public exception { - template - point3_t(Args&&... args) - : array({ double(std::forward(args))... }) + explicit type_creation_exception(const std::string& what = "") + : exception(what) { } }; /** - * Describe a 3D vector. + * Describe a 3D point. */ -struct F3D_EXPORT vector3_t : std::array +struct F3D_EXPORT point3_t : std::array { template - vector3_t(Args&&... args) + point3_t(Args&&... args) : array({ double(std::forward(args))... }) { } @@ -82,6 +86,139 @@ struct mesh_t */ F3D_EXPORT std::pair isValid() const; }; + +/** + * Describe a 3D vector. + */ +struct F3D_EXPORT vector3_t +{ + vector3_t() = default; + vector3_t(double x, double y, double z) + : Value{ x, y, z } + { + } + vector3_t(const std::vector& vec) + { + if (vec.size() != 3) + { + throw type_creation_exception("cannot create a vector3_t"); + } + Value[0] = vec[0]; + Value[1] = vec[1]; + Value[2] = vec[2]; + } + vector3_t(const std::array& arr) + { + Value[0] = arr[0]; + Value[1] = arr[1]; + Value[2] = arr[2]; + } + vector3_t(const double* ptr) + { + Value[0] = ptr[0]; + Value[1] = ptr[1]; + Value[2] = ptr[2]; + } + vector3_t(std::initializer_list l) + { + if (l.size() != 3) + { + throw type_creation_exception("cannot create a vector3_t"); + } + std::copy(l.begin(), l.end(), std::begin(Value)); + } + + double* data() + { + return Value.data(); + } + const double* data() const + { + return Value.data(); + } + + double& operator[](int idx) + { + return Value[idx]; + } + double operator[](int idx) const + { + return Value[idx]; + } + operator std::vector() const + { + return { Value[0], Value[1], Value[2] }; + } + operator std::array() const + { + return { Value[0], Value[1], Value[2] }; + } + bool operator==(const vector3_t& vec) const + { + return Value[0] == vec.Value[0] && Value[1] == vec.Value[1] && Value[2] == vec.Value[2]; + } + bool operator!=(const vector3_t& vec) const + { + return !(*this == vec); + } + + auto begin() + { + return Value.begin(); + } + // auto begin() const + // { + // return Value.begin(); + // } + auto cbegin() const + { + return Value.cbegin(); + } + auto end() + { + return Value.end(); + } + // auto end() const + // { + // return Value.end(); + // } + auto cend() const + { + return Value.cend(); + } + + static vector3_t fromSphericalCoordinates(double theta, double phi) + { + auto sinPhi = std::sin(phi); + return { sinPhi * std::cos(theta), sinPhi * std::sin(theta), std::cos(phi) }; + } + static vector3_t x() + { + return { 1, 0, 0 }; + } + static vector3_t y() + { + return { 0, 1, 0 }; + } + static vector3_t z() + { + return { 0, 0, 1 }; + } + static vector3_t zero() + { + return { 0, 0, 0 }; + } + +private: + std::array Value; +}; + +inline std::ostream& operator<<(std::ostream& os, const f3d::vector3_t& vec) +{ + os << "{ " << vec[0] << ", " << vec[1] << ", " << vec[2] << " }"; + return os; +} + } #endif diff --git a/library/public/utils.h b/library/public/utils.h index 66b5a458eb..48fa7033c1 100644 --- a/library/public/utils.h +++ b/library/public/utils.h @@ -1,9 +1,11 @@ #ifndef f3d_utils_h #define f3d_utils_h +#include "exception.h" #include "export.h" #include +#include namespace f3d { @@ -22,6 +24,39 @@ class F3D_EXPORT utils * Can be useful for spell checking and typo detection. */ static unsigned int textDistance(const std::string& strA, const std::string& strB); + + // clang-format off + /** + * Tokenize provided string_view into a vector of strings, using the same logic as bash. + * - Split by spaces unless between quotes + * - Split by quoted section and remove the quotes + * - Supported quotes are: '"` + * - Use escaped \ quotes, spaces and escape to add them verbatim + * - Other escaped characters are also added verbatim + * Throw a tokenize_exception if a quoted section is not closed or if finishing with an escape + * + * Examples: + * `set scene.up.direction +Z` -> `set` `scene.up.direction` `+Z` + * `set render.hdri.file "/path/to/file with spaces.png"` -> `set`, `render.hdri.file`, + * `/path/to/file with spaces.png` `set render.hdri.file '/path/to/file with spaces.png'` -> + * `set`, `render.hdri.file`, `/path/to/file with spaces.png` `set render.hdri.file + * "/path/to/file'with'quotes.png"` -> `set`, `render.hdri.file`, `/path/to/file'with'quotes.png` + * `set render.hdri.file /path/to/file\ spaces\ \'quotes\".png` -> `set`, `render.hdri.file`, + * `/path/to/file spaces 'quotes".png` `set render.hdri.file C:\\path\\to\\windows\\file.png` -> + * `set`, `render.hdri.file`, `C:\path\to\windows\file.png` `set scene.up.direction +\Z` -> `set`, + * `scene.up.direction`, `+Z` `set scene.up.direction "+Z` -> tokenize_exception `set + * scene.up.direction +Z\` -> tokenize_exception + */ + static std::vector tokenize(std::string_view str); + // clang-format on + + /** + * An exception that can be thrown by tokenize + */ + struct tokenize_exception : public exception + { + explicit tokenize_exception(const std::string& what = ""); + }; }; } diff --git a/library/public/window.h b/library/public/window.h index de4cbbf748..37d2cf75e2 100644 --- a/library/public/window.h +++ b/library/public/window.h @@ -23,17 +23,27 @@ class F3D_EXPORT window * Enumeration of supported window types * ===================================== * NONE: A mock window without rendering capabilities. - * NATIVE: A window using the native graphical stack. - * NATIVE_OFFSCREEN: A native window rendering to an offscreen buffer, not visible on screen. - * EXTERNAL: An external window that assume the OpenGL context would have been created by. - * another framework + * EXTERNAL: An external window that assume the OpenGL context would have been created by another + * framework. + * GLX: A window using GLX. + * WGL: A window using WGL. + * COCOA: A window using COCOA. + * EGL: An offscreen window using hardware acceleration that can run headless. + * OSMESA: An offscreen window using software rendering that is always run headless. + * WASM: A webassembly window (when libf3d is built with emscripten). + * UNKNOWN: An unknown window (should not happen). */ enum class Type : unsigned char { NONE, - NATIVE, - NATIVE_OFFSCREEN, - EXTERNAL + EXTERNAL, + GLX, + WGL, + COCOA, + EGL, + OSMESA, + WASM, + UNKNOWN }; /** @@ -41,6 +51,11 @@ class F3D_EXPORT window */ virtual Type getType() = 0; + /** + * Is the window offscreen. + */ + virtual bool isOffscreen() = 0; + /** * Get the camera provided by the window. */ diff --git a/library/src/animationManager.cxx b/library/src/animationManager.cxx index e29c029626..439c4c89d7 100644 --- a/library/src/animationManager.cxx +++ b/library/src/animationManager.cxx @@ -16,26 +16,39 @@ namespace f3d::detail { //---------------------------------------------------------------------------- -bool animationManager::Initialize( - const options* options, window* window, interactor_impl* interactor, vtkImporter* importer) +animationManager::animationManager(const options& options, window& window) + : Options(options) + , Window(window) { - assert(importer); +} + +//---------------------------------------------------------------------------- +void animationManager::SetImporter(vtkImporter* importer) +{ + this->Importer = importer; +} + +//---------------------------------------------------------------------------- +void animationManager::SetInteractor(interactor_impl* interactor) +{ + this->Interactor = interactor; +} + +//---------------------------------------------------------------------------- +bool animationManager::Initialize() +{ + assert(this->Importer); this->HasAnimation = false; this->Playing = false; this->CurrentTime = 0; this->CurrentTimeSet = false; - this->Options = options; - this->Interactor = interactor; - this->Window = window; - this->Importer = importer; // This can be -1 if animation support is not implemented in the importer this->AvailAnimations = this->Importer->GetNumberOfAnimations(); - - if (this->AvailAnimations > 0 && interactor) + if (this->AvailAnimations > 0 && this->Interactor) { this->ProgressWidget = vtkSmartPointer::New(); - interactor->SetInteractorOn(this->ProgressWidget); + this->Interactor->SetInteractorOn(this->ProgressWidget); vtkProgressBarRepresentation* progressRep = vtkProgressBarRepresentation::SafeDownCast(this->ProgressWidget->GetRepresentation()); @@ -50,7 +63,7 @@ bool animationManager::Initialize( progressRep->SetShowBorderToOff(); progressRep->DrawFrameOff(); progressRep->SetPadding(0.0, 0.0); - progressRep->SetVisibility(options->ui.animation_progress); + progressRep->SetVisibility(this->Options.ui.animation_progress); this->ProgressWidget->On(); } else @@ -60,12 +73,12 @@ bool animationManager::Initialize( if (this->AvailAnimations <= 0) { - log::debug("No animation available in this file"); - if (options->scene.animation.index > 0) + log::debug("No animation available"); + if (this->Options.scene.animation.index > 0) { log::warn("An animation index has been specified but there are no animation available."); } - if (options->scene.animation.time.has_value()) + if (this->Options.scene.animation.time.has_value()) { log::warn("No animation available, cannot load a specific animation time"); } @@ -74,15 +87,14 @@ bool animationManager::Initialize( } else { - log::debug("Animation(s) available in this file are:"); + log::debug("Animation(s) available are:"); } for (int i = 0; i < this->AvailAnimations; i++) { log::debug(i, ": ", this->Importer->GetAnimationName(i)); } - log::debug(""); - this->AnimationIndex = options->scene.animation.index; + this->AnimationIndex = this->Options.scene.animation.index; if (this->AnimationIndex > 0 && this->AnimationIndex >= this->AvailAnimations) { log::warn( @@ -106,7 +118,7 @@ bool animationManager::Initialize( // Discard timesteps, F3D only cares about real elapsed time using time range // Specifying the frame rate in the next call is not needed after VTK 9.2.20230603 : // VTK_VERSION_CHECK(9, 2, 20230603) - double frameRate = options->scene.animation.frame_rate; + double frameRate = this->Options.scene.animation.frame_rate; this->Importer->GetTemporalInformation( animIndex, frameRate, nbTimeSteps, timeRange, timeSteps); @@ -127,8 +139,9 @@ bool animationManager::Initialize( { log::debug("Animation(s) time range is: [", this->TimeRange[0], ", ", this->TimeRange[1], "]."); } + log::debug(""); - bool autoplay = options->scene.animation.autoplay; + bool autoplay = this->Options.scene.animation.autoplay; if (autoplay) { this->StartAnimation(); @@ -177,12 +190,12 @@ void animationManager::ToggleAnimation() // Always reset previous tick when starting the animation this->PreviousTick = std::chrono::steady_clock::now(); - double frameRate = this->Options->scene.animation.frame_rate; + double frameRate = this->Options.scene.animation.frame_rate; this->CallBackId = this->Interactor->createTimerCallBack(1000.0 / frameRate, [this]() { this->Tick(); }); } - if (this->Playing && this->Options->scene.camera.index.has_value()) + if (this->Playing && this->Options.scene.camera.index.has_value()) { this->Interactor->disableCameraMovement(); } @@ -206,7 +219,7 @@ void animationManager::Tick() // Convert to a usable time in seconds double elapsedTime = static_cast(timeInMS) / 1000.0; - double animationSpeedFactor = this->Options->scene.animation.speed_factor; + double animationSpeedFactor = this->Options.scene.animation.speed_factor; // elapsedTime can be negative elapsedTime *= animationSpeedFactor; @@ -226,7 +239,7 @@ void animationManager::Tick() if (this->LoadAtTime(this->CurrentTime)) { - this->Window->render(); + this->Window.render(); } } @@ -306,7 +319,8 @@ int animationManager::GetAnimationIndex() // --------------------------------------------------------------------------------- std::string animationManager::GetAnimationName() { - if (!this->Importer || this->AvailAnimations <= 0) + assert(this->Importer); + if (this->AvailAnimations <= 0) { return "No animation"; } diff --git a/library/src/context.cxx b/library/src/context.cxx new file mode 100644 index 0000000000..4f5b61e321 --- /dev/null +++ b/library/src/context.cxx @@ -0,0 +1,127 @@ +#include "context.h" + +#ifdef __APPLE__ +#include "context_cocoa.h" +#endif + +#include +#include + +#if defined(VTK_USE_X) && VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240914) +#include +#endif + +#if defined(VTK_OPENGL_HAS_EGL) && VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240914) +#include +#endif + +#include + +namespace f3d +{ +//---------------------------------------------------------------------------- +context::function context::getSymbol(const std::string& lib, const std::string& func) +{ + std::string libName = vtksys::DynamicLoader::LibPrefix(); + libName += lib; + libName += vtksys::DynamicLoader::LibExtension(); + + vtksys::DynamicLoader::LibraryHandle handle = vtksys::DynamicLoader::OpenLibrary(libName); + + if (!handle) + { + throw context::loading_exception("Cannot find " + lib + " library"); + } + + using symbol = context::fptr (*)(const char*); + + symbol address = reinterpret_cast(vtksys::DynamicLoader::GetSymbolAddress(handle, func)); + + if (!address) + { + throw context::symbol_exception("Cannot find " + func + " symbol"); + } + + return address; +} + +//---------------------------------------------------------------------------- +context::function context::glx() +{ +#if defined(VTK_USE_X) && VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240914) + gladLoaderLoadGLX(nullptr, 0); // Load core glx functions. + return getSymbol("GLX", "glXGetProcAddress"); +#else + throw loading_exception("Cannot use a GLX context on this platform"); +#endif +} + +//---------------------------------------------------------------------------- +context::function context::wgl() +{ +#ifdef _WIN32 + return [](const char* name) + { + fptr p = reinterpret_cast(wglGetProcAddress(name)); + if (p == nullptr) + { + HMODULE module = LoadLibraryA("opengl32.dll"); + p = reinterpret_cast(GetProcAddress(module, name)); + } + + return p; + }; +#else + throw loading_exception("Cannot use a WGL context on this platform"); +#endif +} + +//---------------------------------------------------------------------------- +context::function context::cocoa() +{ +#ifdef __APPLE__ + return [](const char* name) + { + fptr p = reinterpret_cast(detail::getCocoaOpenGLSymbol(name)); + return p; + }; +#else + throw loading_exception("Cannot use a COCOA context on this platform"); +#endif +} + +//---------------------------------------------------------------------------- +context::function context::egl() +{ +#if defined(VTK_OPENGL_HAS_EGL) && VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240914) + gladLoaderLoadEGL(EGL_NO_DISPLAY); + return getSymbol("EGL", "eglGetProcAddress"); +#else + throw loading_exception("Cannot use a EGL context on this platform"); +#endif +} + +//---------------------------------------------------------------------------- +context::function context::osmesa() +{ +#ifdef __linux__ + return getSymbol("OSMesa", "OSMesaGetProcAddress"); +#elif _WIN32 + return getSymbol("osmesa", "OSMesaGetProcAddress"); +#else + throw loading_exception("Cannot use a OSMesa context on this platform"); +#endif +} + +//---------------------------------------------------------------------------- +context::loading_exception::loading_exception(const std::string& what) + : exception(what) +{ +} + +//---------------------------------------------------------------------------- +context::symbol_exception::symbol_exception(const std::string& what) + : exception(what) +{ +} +} diff --git a/library/src/context_cocoa.mm b/library/src/context_cocoa.mm new file mode 100644 index 0000000000..f2e27fec58 --- /dev/null +++ b/library/src/context_cocoa.mm @@ -0,0 +1,21 @@ +#include "context_cocoa.h" + +#import + +namespace f3d::detail +{ +//---------------------------------------------------------------------------- +void* getCocoaOpenGLSymbol(const char* name) +{ + CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, + name, + kCFStringEncodingASCII); + + void* symbol = CFBundleGetFunctionPointerForName(CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")), + symbolName); + + CFRelease(symbolName); + + return symbol; +} +} diff --git a/library/src/engine.cxx b/library/src/engine.cxx index da76189c82..60a9b999a7 100644 --- a/library/src/engine.cxx +++ b/library/src/engine.cxx @@ -3,8 +3,8 @@ #include "config.h" #include "init.h" #include "interactor_impl.h" -#include "loader_impl.h" #include "log.h" +#include "scene_impl.h" #include "window_impl.h" #include "factory.h" @@ -28,12 +28,13 @@ class engine::internals public: std::unique_ptr Options; std::unique_ptr Window; - std::unique_ptr Loader; + std::unique_ptr Scene; std::unique_ptr Interactor; }; //---------------------------------------------------------------------------- -engine::engine(window::Type windowType) +engine::engine( + const std::optional& windowType, bool offscreen, const context::function& loader) : Internals(new engine::internals) { // Ensure all lib initialization is done (once) @@ -58,26 +59,114 @@ engine::engine(window::Type windowType) this->Internals->Options = std::make_unique(); this->Internals->Window = - std::make_unique(*this->Internals->Options, windowType); + std::make_unique(*this->Internals->Options, windowType, offscreen, loader); this->Internals->Window->SetCachePath(cachePath); - this->Internals->Loader = - std::make_unique(*this->Internals->Options, *this->Internals->Window); + this->Internals->Scene = + std::make_unique(*this->Internals->Options, *this->Internals->Window); // Do not create an interactor for NONE or EXTERNAL if (windowType != window::Type::NONE && windowType != window::Type::EXTERNAL) { this->Internals->Interactor = std::make_unique( - *this->Internals->Options, *this->Internals->Window, *this->Internals->Loader); + *this->Internals->Options, *this->Internals->Window, *this->Internals->Scene); } } +//---------------------------------------------------------------------------- +engine engine::create(bool offscreen) +{ + return { std::nullopt, offscreen, nullptr }; +} + +//---------------------------------------------------------------------------- +engine engine::createNone() +{ + return { window::Type::NONE, true, nullptr }; +} + +//---------------------------------------------------------------------------- +engine engine::createGLX(bool offscreen) +{ + return { window::Type::GLX, offscreen, context::glx() }; +} + +//---------------------------------------------------------------------------- +engine engine::createWGL(bool offscreen) +{ + return { window::Type::WGL, offscreen, context::wgl() }; +} + +//---------------------------------------------------------------------------- +engine engine::createEGL(bool offscreen) +{ + return { window::Type::EGL, offscreen, context::egl() }; +} + +//---------------------------------------------------------------------------- +engine engine::createOSMesa() +{ + return { window::Type::OSMESA, true, context::osmesa() }; +} + +//---------------------------------------------------------------------------- +engine engine::createExternal(const context::function& getProcAddress) +{ + return { window::Type::EXTERNAL, false, getProcAddress }; +} + +//---------------------------------------------------------------------------- +engine engine::createExternalGLX() +{ + return { window::Type::EXTERNAL, false, context::glx() }; +} + +//---------------------------------------------------------------------------- +engine engine::createExternalWGL() +{ + return { window::Type::EXTERNAL, false, context::wgl() }; +} + +//---------------------------------------------------------------------------- +engine engine::createExternalCOCOA() +{ + return { window::Type::EXTERNAL, false, context::cocoa() }; +} + +//---------------------------------------------------------------------------- +engine engine::createExternalEGL() +{ + return { window::Type::EXTERNAL, false, context::egl() }; +} + +//---------------------------------------------------------------------------- +engine engine::createExternalOSMesa() +{ + return { window::Type::EXTERNAL, false, context::osmesa() }; +} + //---------------------------------------------------------------------------- engine::~engine() { delete this->Internals; } +//---------------------------------------------------------------------------- +engine::engine(engine&& other) noexcept +{ + this->Internals = other.Internals; + other.Internals = nullptr; +} + +//---------------------------------------------------------------------------- +engine& engine::operator=(engine&& other) noexcept +{ + delete this->Internals; + this->Internals = other.Internals; + other.Internals = nullptr; + return *this; +} + //---------------------------------------------------------------------------- engine& engine::setOptions(const options& opt) { @@ -109,9 +198,9 @@ window& engine::getWindow() } //---------------------------------------------------------------------------- -loader& engine::getLoader() +scene& engine::getScene() { - return *this->Internals->Loader; + return *this->Internals->Scene; } //---------------------------------------------------------------------------- diff --git a/library/src/image.cxx b/library/src/image.cxx index bd343834b0..a6ae3f457f 100644 --- a/library/src/image.cxx +++ b/library/src/image.cxx @@ -338,6 +338,13 @@ void* image::getContent() const //---------------------------------------------------------------------------- bool image::compare(const image& reference, double threshold, double& error) const { + // Sanity check for threshold + if (threshold < 0 || threshold >= 1) + { + error = 1; + return false; + } + ChannelType type = this->getChannelType(); if (type != reference.getChannelType()) { diff --git a/library/src/interactor_impl.cxx b/library/src/interactor_impl.cxx index 2157086605..de361cd820 100644 --- a/library/src/interactor_impl.cxx +++ b/library/src/interactor_impl.cxx @@ -1,17 +1,19 @@ #include "interactor_impl.h" #include "animationManager.h" -#include "loader_impl.h" #include "log.h" +#include "scene_impl.h" +#include "utils.h" #include "window_impl.h" #include "vtkF3DConfigure.h" #include "vtkF3DInteractorEventRecorder.h" #include "vtkF3DInteractorStyle.h" -#include "vtkF3DRendererWithColoring.h" +#include "vtkF3DRenderer.h" #include #include +#include #include #include #include @@ -24,9 +26,11 @@ #include #include +#include #include #include #include +#include #include "camera.h" @@ -35,11 +39,23 @@ namespace f3d::detail class interactor_impl::internals { public: - internals(options& options, window_impl& window, loader_impl& loader) + internals(options& options, window_impl& window, scene_impl& scene, interactor_impl& inter) : Options(options) , Window(window) - , Loader(loader) + , Scene(scene) + , Interactor(inter) { + window::Type type = window.getType(); + if (type == window::Type::GLX || type == window::Type::WGL || type == window::Type::COCOA || + type == window::Type::WASM) + { + this->VTKInteractor = vtkSmartPointer::New(); + } + else + { + this->VTKInteractor = vtkSmartPointer::New(); + } + #ifdef __EMSCRIPTEN__ vtkRenderWindowInteractor::InteractorManagesTheEventLoop = false; #endif @@ -103,11 +119,12 @@ class interactor_impl::internals VT_TOP, VT_ISOMETRIC }; - static void SetViewOrbit(ViewType view, internals* self) + + void SetViewOrbit(ViewType view) { vtkNew transform; - self->ToEnvironmentSpace(transform); - camera& cam = self->Window.getCamera(); + this->ToEnvironmentSpace(transform); + camera& cam = this->Window.getCamera(); vector3_t up = { 0, 0, 1 }; point3_t foc = cam.getFocalPoint(); point3_t axis, newPos; @@ -142,276 +159,71 @@ class interactor_impl::internals cam.resetToBounds(0.9); } - static void OnKeyPress(vtkObject*, unsigned long, void* clientData, void*) + //---------------------------------------------------------------------------- + // Increase/Decrease light intensity + void IncreaseLightIntensity(bool negative) { + const double intensity = this->Options.render.light.intensity; + + /* `ref < x` is equivalent to: + * - `intensity <= x` when going down + * - `intensity < x` when going up */ + const double ref = negative ? intensity - 1e-6 : intensity; + // clang-format off + /* offset in percentage points */ + const int offsetPp = ref < .5 ? 1 + : ref < 1 ? 2 + : ref < 5 ? 5 + : ref < 10 ? 10 + : 25; + // clang-format on + + /* new intensity in percents */ + const int newIntensityPct = std::lround(intensity * 100) + (negative ? -offsetPp : +offsetPp); + this->Options.render.light.intensity = std::max(newIntensityPct, 0) / 100.0; + } + //---------------------------------------------------------------------------- + // Synchronise options from the renderer properties + static void SynchronizeScivisOptions(f3d::options& opt, vtkF3DRenderer* ren) + { + // Synchronize renderer coloring status with scivis options + opt.model.scivis.enable = ren->GetEnableColoring(); + opt.model.scivis.cells = ren->GetUseCellColoring(); + opt.model.scivis.array_name = ren->GetArrayNameForColoring(); + opt.model.scivis.component = ren->GetComponentForColoring(); + } + + //---------------------------------------------------------------------------- + static void OnKeyPress(vtkObject*, unsigned long, void* clientData, void*) + { internals* self = static_cast(clientData); vtkRenderWindowInteractor* rwi = self->Style->GetInteractor(); - int keyCode = std::toupper(rwi->GetKeyCode()); - std::string keySym = rwi->GetKeySym(); - if (keySym.length() > 0) + std::string interaction = rwi->GetKeySym(); + if (!interaction.empty()) { - // Make sure key symbols starts with an upper char (e.g. "space") - keySym[0] = std::toupper(keySym[0]); + // Make sure key symbols starts with an upper char (e.g. "space" -> "Space") + interaction[0] = std::toupper(interaction[0]); } - if (self->KeyPressUserCallBack(keyCode, keySym)) - { - return; - } - - // No user defined behavior, use standard behavior - vtkRenderWindow* renWin = self->Window.GetRenderWindow(); - vtkF3DRenderer* ren = vtkF3DRenderer::SafeDownCast(renWin->GetRenderers()->GetFirstRenderer()); - vtkF3DRendererWithColoring* renWithColor = vtkF3DRendererWithColoring::SafeDownCast(ren); - bool checkColoring = false; - bool render = false; - - // Available keycodes: None - switch (keyCode) - { - case 'W': - self->AnimationManager->CycleAnimation(); - self->Options.scene.animation.index = self->AnimationManager->GetAnimationIndex(); - ren->SetAnimationnameInfo(self->AnimationManager->GetAnimationName()); - render = true; - break; - case 'C': - if (renWithColor) - { - renWithColor->CycleScalars(vtkF3DRendererWithColoring::CycleType::FIELD); - self->Window.PrintColoringDescription(log::VerboseLevel::DEBUG); - checkColoring = true; - render = true; - } - break; - case 'S': - if (renWithColor) - { - renWithColor->CycleScalars(vtkF3DRendererWithColoring::CycleType::ARRAY_INDEX); - self->Window.PrintColoringDescription(log::VerboseLevel::DEBUG); - checkColoring = true; - render = true; - } - break; - case 'Y': - if (renWithColor) - { - renWithColor->CycleScalars(vtkF3DRendererWithColoring::CycleType::COMPONENT); - self->Window.PrintColoringDescription(log::VerboseLevel::DEBUG); - checkColoring = true; - render = true; - } - break; - case 'B': - self->Options.ui.scalar_bar = !self->Options.ui.scalar_bar; - render = true; - break; - case 'P': - self->Options.render.effect.translucency_support = - !self->Options.render.effect.translucency_support; - render = true; - break; - case 'Q': - self->Options.render.effect.ambient_occlusion = - !self->Options.render.effect.ambient_occlusion; - render = true; - break; - case 'A': - self->Options.render.effect.anti_aliasing = !self->Options.render.effect.anti_aliasing; - render = true; - break; - case 'T': - self->Options.render.effect.tone_mapping = !self->Options.render.effect.tone_mapping; - render = true; - break; - case 'E': - self->Options.render.show_edges = !self->Options.render.show_edges; - render = true; - break; - case 'X': - self->Options.interactor.axis = !self->Options.interactor.axis; - render = true; - break; - case 'G': - self->Options.render.grid.enable = !self->Options.render.grid.enable; - render = true; - break; - case 'N': - self->Options.ui.filename = !self->Options.ui.filename; - render = true; - break; - case 'M': - self->Options.ui.metadata = !self->Options.ui.metadata; - render = true; - break; - case 'Z': - self->Options.ui.fps = !self->Options.ui.fps; - self->Window.render(); - self->Window.render(); - // XXX: Double render is needed here - break; - case 'R': - self->Options.render.raytracing.enable = !self->Options.render.raytracing.enable; - render = true; - break; - case 'D': - self->Options.render.raytracing.denoise = !self->Options.render.raytracing.denoise; - render = true; - break; - case 'V': - self->Options.model.volume.enable = !self->Options.model.volume.enable; - render = true; - break; - case 'I': - self->Options.model.volume.inverse = !self->Options.model.volume.inverse; - render = true; - break; - case 'O': - self->Options.model.point_sprites.enable = !self->Options.model.point_sprites.enable; - render = true; - break; - case 'U': - self->Options.render.background.blur = !self->Options.render.background.blur; - render = true; - break; - case 'K': - self->Options.interactor.trackball = !self->Options.interactor.trackball; - render = true; - break; - case 'F': - self->Options.render.hdri.ambient = !self->Options.render.hdri.ambient; - render = true; - break; - case 'J': - self->Options.render.background.skybox = !self->Options.render.background.skybox; - render = true; - break; - case 'L': - { - const double intensity = self->Options.render.light.intensity; - const bool down = rwi->GetShiftKey(); - - /* `ref < x` is equivalent to: - * - `intensity <= x` when going down - * - `intensity < x` when going up */ - const double ref = down ? intensity - 1e-6 : intensity; - // clang-format off - /* offset in percentage points */ - const int offsetPp = ref < .5 ? 1 - : ref < 1 ? 2 - : ref < 5 ? 5 - : ref < 10 ? 10 - : 25; - // clang-format on - /* new intensity in percents */ - const int newIntensityPct = std::lround(intensity * 100) + (down ? -offsetPp : +offsetPp); - - self->Options.render.light.intensity = std::max(newIntensityPct, 0) / 100.0; - render = true; - break; - } - case 'H': - self->Options.ui.cheatsheet = !self->Options.ui.cheatsheet; - render = true; - break; - case '?': - self->Window.PrintColoringDescription(log::VerboseLevel::INFO); - self->Window.PrintSceneDescription(log::VerboseLevel::INFO); - break; - case '1': - self->SetViewOrbit(ViewType::VT_FRONT, self); - render = true; - break; - case '3': - self->SetViewOrbit(ViewType::VT_RIGHT, self); - render = true; - break; - case '4': - self->Window.getCamera().roll(-90); - render = true; - break; - case '5': - self->Options.scene.camera.orthographic = !self->Options.scene.camera.orthographic; - render = true; - break; - case '6': - self->Window.getCamera().roll(90); - render = true; - break; - case '7': - self->SetViewOrbit(ViewType::VT_TOP, self); - render = true; - break; - case '9': - self->SetViewOrbit(ViewType::VT_ISOMETRIC, self); - render = true; - break; - default: - if (keySym == F3D_EXIT_HOTKEY_SYM) - { - self->StopInteractor(); - } - else if (keySym == "Return") - { - self->Window.getCamera().resetToDefault(); - render = true; - } - else if (keySym == "Space") - { - assert(self->AnimationManager); - self->AnimationManager->ToggleAnimation(); - } - break; - } - - if (checkColoring) - { - // Resynchronise renderer coloring status with options - self->Options.model.scivis.enable = renWithColor->GetColoringEnabled(); - self->Options.model.scivis.cells = renWithColor->GetColoringUseCell(); - self->Options.model.scivis.array_name = renWithColor->GetColoringArrayName(); - self->Options.model.scivis.component = renWithColor->GetColoringComponent(); - } - if (render) - { - self->Window.render(); - } + self->TriggerInteractionCommands(interaction, ""); } + //---------------------------------------------------------------------------- static void OnDropFiles(vtkObject*, unsigned long, void* clientData, void* callData) { internals* self = static_cast(clientData); vtkStringArray* filesArr = static_cast(callData); - std::vector filesVec; - filesVec.resize(filesArr->GetNumberOfTuples()); + std::string filesString; for (int i = 0; i < filesArr->GetNumberOfTuples(); i++) { - filesVec[i] = filesArr->GetValue(i); - } - - if (self->DropFilesUserCallBack(filesVec)) - { - return; + filesString.append(" \"" + filesArr->GetValue(i) + "\" "); } - // No user defined behavior, load the first file - if (!filesVec.empty()) - { - assert(self->AnimationManager); - self->AnimationManager->StopAnimation(); - if (self->Loader.hasSceneReader(filesVec[0])) - { - self->Loader.loadScene(filesVec[0]); - } - else if (self->Loader.hasGeometryReader(filesVec[0])) - { - self->Loader.loadGeometry(filesVec[0], true); - } - self->Window.render(); - } + self->TriggerInteractionCommands("Drop", filesString); } + //---------------------------------------------------------------------------- static void OnMiddleButtonPress(vtkObject*, unsigned long, void* clientData, void*) { internals* self = static_cast(clientData); @@ -421,6 +233,7 @@ class interactor_impl::internals self->Style->OnMiddleButtonDown(); } + //---------------------------------------------------------------------------- static void OnMiddleButtonRelease(vtkObject*, unsigned long, void* clientData, void*) { internals* self = static_cast(clientData); @@ -497,22 +310,20 @@ class interactor_impl::internals self->Style->OnMiddleButtonUp(); } - std::function KeyPressUserCallBack = [](int, const std::string&) - { return false; }; - std::function&)> DropFilesUserCallBack = - [](const std::vector&) { return false; }; - + //---------------------------------------------------------------------------- void StartInteractor() { this->VTKInteractor->Start(); } + //---------------------------------------------------------------------------- void StopInteractor() { this->VTKInteractor->RemoveObservers(vtkCommand::TimerEvent); this->VTKInteractor->ExitCallback(); } + //---------------------------------------------------------------------------- /** * Run a camera transition animation based on a camera state interpolation function. * The provided function will be called with an interpolation parameter @@ -548,16 +359,98 @@ class interactor_impl::internals this->Window.render(); } + //---------------------------------------------------------------------------- + void TriggerInteractionCommands(const std::string& interaction, const std::string& argsString) + { + ModifierKeys mod = ModifierKeys::NONE; + vtkRenderWindowInteractor* rwi = this->Style->GetInteractor(); + const bool shift = rwi->GetShiftKey() == 1; + const bool ctrl = rwi->GetControlKey() == 1; + if (shift && ctrl) + { + mod = ModifierKeys::CTRL_SHIFT; + } + else if (ctrl) + { + mod = ModifierKeys::CTRL; + } + else if (shift) + { + mod = ModifierKeys::SHIFT; + } + + std::string interactionLog; + switch (mod) + { + case f3d::interactor::ModifierKeys::CTRL_SHIFT: + interactionLog = "CTRL+SHIFT+" + interaction; + break; + case f3d::interactor::ModifierKeys::CTRL: + interactionLog = "CTRL+" + interaction; + break; + case f3d::interactor::ModifierKeys::SHIFT: + interactionLog = "SHIFT+" + interaction; + break; + default: + // No need to check for ANY (unreachable) or NONE (no log needed) + interactionLog = interaction; + break; + } + + // Check for an interaction command with modifiers + const InteractionBind bind = { interaction, mod }; + log::debug("Interaction: KeyPress ", interactionLog); + + auto commandsIt = this->InteractionCommands.find(bind); + if (commandsIt == this->InteractionCommands.end()) + { + // Modifiers version not found, try ANY instead + commandsIt = this->InteractionCommands.find({ interaction, ModifierKeys::ANY }); + } + + if (commandsIt != this->InteractionCommands.end()) + { + for (const std::string& command : commandsIt->second) + { + if (!this->Interactor.triggerCommand(command + argsString)) + { + log::error("Interaction: error running command:\"", command + argsString, "\", ignoring"); + } + } + } + + // Always render after interaction + this->Window.render(); + } + + //---------------------------------------------------------------------------- + options& Options; window_impl& Window; - loader_impl& Loader; + scene_impl& Scene; + interactor_impl& Interactor; animationManager* AnimationManager; - vtkNew VTKInteractor; + vtkSmartPointer VTKInteractor; vtkNew Style; vtkSmartPointer Recorder; std::map>> TimerCallBacks; + std::map&)>> CommandCallbacks; + + struct InteractionBind + { + std::string Interaction; + ModifierKeys Modifiers; + + bool operator<(const InteractionBind& bind) const + { + return this->Interaction < bind.Interaction || + (this->Interaction == bind.Interaction && this->Modifiers < bind.Modifiers); + } + }; + std::map> InteractionCommands; + vtkNew CellPicker; vtkNew PointPicker; @@ -568,28 +461,363 @@ class interactor_impl::internals }; //---------------------------------------------------------------------------- -interactor_impl::interactor_impl(options& options, window_impl& window, loader_impl& loader) - : Internals(std::make_unique(options, window, loader)) +interactor_impl::interactor_impl(options& options, window_impl& window, scene_impl& scene) + : Internals(std::make_unique(options, window, scene, *this)) { - // Loader need the interactor, loader will set the AnimationManager on the interactor - this->Internals->Loader.SetInteractor(this); + // scene need the interactor, scene will set the AnimationManager on the interactor + this->Internals->Scene.SetInteractor(this); + assert(this->Internals->AnimationManager); + + const auto check_args = [&](const std::vector& args, size_t expectedSize, + std::string_view actionName) -> bool + { + if (args.size() != expectedSize) + { + log::error("Command: ", actionName, " is expecting ", std::to_string(expectedSize), + " arguments, ignoring"); + return false; + } + return true; + }; + + // Add default callbacks + this->addCommandCallback("set", + [&](const std::vector& args) -> bool + { + if (!check_args(args, 2, "set")) + { + return false; + } + this->Internals->Options.setAsString(args[0], args[1]); + return true; + }); + + this->addCommandCallback("toggle", + [&](const std::vector& args) -> bool + { + if (!check_args(args, 1, "toggle")) + { + return false; + } + this->Internals->Options.toggle(args[0]); + return true; + }); + + this->addCommandCallback("reset", + [&](const std::vector& args) -> bool + { + if (!check_args(args, 1, "reset")) + { + return false; + } + this->Internals->Options.reset(args[0]); + return true; + }); + this->addCommandCallback("print", + [&](const std::vector& args) -> bool + { + if (!check_args(args, 1, "print")) + { + return false; + } + log::info(this->Internals->Options.getAsString(args[0])); + return true; + }); + + this->addCommandCallback("cycle_animation", + [&](const std::vector&) -> bool + { + vtkRenderWindow* renWin = this->Internals->Window.GetRenderWindow(); + vtkF3DRenderer* ren = + vtkF3DRenderer::SafeDownCast(renWin->GetRenderers()->GetFirstRenderer()); + this->Internals->AnimationManager->CycleAnimation(); + this->Internals->Options.scene.animation.index = + this->Internals->AnimationManager->GetAnimationIndex(); + ren->SetAnimationnameInfo(this->Internals->AnimationManager->GetAnimationName()); + return true; + }); + + this->addCommandCallback("cycle_coloring", + [&](const std::vector& args) -> bool + { + if (!check_args(args, 1, "cycle_coloring")) + { + return false; + } + + std::string_view type = args[0]; + vtkRenderWindow* renWin = this->Internals->Window.GetRenderWindow(); + vtkF3DRenderer* ren = + vtkF3DRenderer::SafeDownCast(renWin->GetRenderers()->GetFirstRenderer()); + if (type == "field") + { + ren->CycleFieldForColoring(); + } + else if (type == "array") + { + ren->CycleArrayForColoring(); + } + else if (type == "component") + { + ren->CycleComponentForColoring(); + } + else + { + log::error("Command: cycle_coloring arg:\"", type, "\" is not recognized, ignoring"); + return false; + } + this->Internals->SynchronizeScivisOptions(this->Internals->Options, ren); + this->Internals->Window.PrintColoringDescription(log::VerboseLevel::DEBUG); + return true; + }); + + this->addCommandCallback("roll_camera", + [&](const std::vector& args) -> bool + { + if (!check_args(args, 1, "roll_camera")) + { + return false; + } + this->Internals->Window.getCamera().roll(options::parse(args[0])); + return true; + }); + + this->addCommandCallback("increase_light_intensity", + [&](const std::vector&) -> bool + { + this->Internals->IncreaseLightIntensity(false); + return true; + }); + + this->addCommandCallback("decrease_light_intensity", + [&](const std::vector&) -> bool + { + this->Internals->IncreaseLightIntensity(true); + return true; + }); + + this->addCommandCallback("print_scene_info", + [&](const std::vector&) -> bool + { + this->Internals->Window.PrintColoringDescription(log::VerboseLevel::INFO); + this->Internals->Window.PrintSceneDescription(log::VerboseLevel::INFO); + return true; + }); + + this->addCommandCallback("set_camera", + [&](const std::vector& args) -> bool + { + if (!check_args(args, 1, "cycle_coloring")) + { + return false; + } + + std::string_view type = args[0]; + if (type == "front") + { + this->Internals->SetViewOrbit(internals::ViewType::VT_FRONT); + } + else if (type == "top") + { + this->Internals->SetViewOrbit(internals::ViewType::VT_TOP); + } + else if (type == "right") + { + this->Internals->SetViewOrbit(internals::ViewType::VT_RIGHT); + } + else if (type == "isometric") + { + this->Internals->SetViewOrbit(internals::ViewType::VT_ISOMETRIC); + } + else + { + log::error("Command: set_camera arg:\"", type, "\" is not recognized, ignoring"); + return false; + } + return true; + }); + + this->addCommandCallback("toggle_volume_rendering", + [&](const std::vector&) -> bool + { + this->Internals->Options.model.volume.enable = !this->Internals->Options.model.volume.enable; + this->Internals->Window.render(); + this->Internals->Window.PrintColoringDescription(log::VerboseLevel::DEBUG); + return true; + }); + + this->addCommandCallback("toggle_fps", + [&](const std::vector&) -> bool + { + this->Internals->Options.ui.fps = !this->Internals->Options.ui.fps; + this->Internals->Window.render(); + return true; + }); + + this->addCommandCallback("stop_interactor", + [&](const std::vector&) -> bool + { + this->Internals->StopInteractor(); + return true; + }); + + this->addCommandCallback("reset_camera", + [&](const std::vector&) -> bool + { + this->Internals->Window.getCamera().resetToDefault(); + return true; + }); + + this->addCommandCallback("toggle_animation", + [&](const std::vector&) -> bool + { + this->Internals->AnimationManager->ToggleAnimation(); + return true; + }); + + this->addCommandCallback("add_files", + [&](const std::vector& files) -> bool + { + this->Internals->AnimationManager->StopAnimation(); + this->Internals->Scene.add(files); + return true; + }); + + // Available standard keys: None + this->addInteractionCommand("W", ModifierKeys::NONE, "cycle_animation"); + this->addInteractionCommand("C", ModifierKeys::NONE, "cycle_coloring field"); + this->addInteractionCommand("S", ModifierKeys::NONE, "cycle_coloring array"); + this->addInteractionCommand("Y", ModifierKeys::NONE, "cycle_coloring component"); + this->addInteractionCommand("B", ModifierKeys::NONE, "toggle ui.scalar_bar"); + this->addInteractionCommand("P", ModifierKeys::NONE, "toggle render.effect.translucency_support"); + this->addInteractionCommand("Q", ModifierKeys::NONE, "toggle render.effect.ambient_occlusion"); + this->addInteractionCommand("A", ModifierKeys::NONE, "toggle render.effect.anti_aliasing"); + this->addInteractionCommand("T", ModifierKeys::NONE, "toggle render.effect.tone_mapping"); + this->addInteractionCommand("E", ModifierKeys::NONE, "toggle render.show_edges"); + this->addInteractionCommand("X", ModifierKeys::NONE, "toggle interactor.axis"); + this->addInteractionCommand("G", ModifierKeys::NONE, "toggle render.grid.enable"); + this->addInteractionCommand("N", ModifierKeys::NONE, "toggle ui.filename"); + this->addInteractionCommand("M", ModifierKeys::NONE, "toggle ui.metadata"); + this->addInteractionCommand("Z", ModifierKeys::NONE, "toggle_fps"); + this->addInteractionCommand("R", ModifierKeys::NONE, "toggle render.raytracing.enable"); + this->addInteractionCommand("D", ModifierKeys::NONE, "toggle render.raytracing.denoise"); + this->addInteractionCommand("V", ModifierKeys::NONE, "toggle_volume_rendering"); + this->addInteractionCommand("I", ModifierKeys::NONE, "toggle model.volume.inverse"); + this->addInteractionCommand("O", ModifierKeys::NONE, "toggle model.point_sprites.enable"); + this->addInteractionCommand("U", ModifierKeys::NONE, "toggle render.background.blur"); + this->addInteractionCommand("K", ModifierKeys::NONE, "toggle interactor.trackball"); + this->addInteractionCommand("F", ModifierKeys::NONE, "toggle render.hdri.ambient"); + this->addInteractionCommand("J", ModifierKeys::NONE, "toggle render.background.skybox"); + this->addInteractionCommand("L", ModifierKeys::NONE, "increase_light_intensity"); + this->addInteractionCommand("L", ModifierKeys::SHIFT, "decrease_light_intensity"); + this->addInteractionCommand("H", ModifierKeys::NONE, "toggle ui.cheatsheet"); + this->addInteractionCommand("Question", ModifierKeys::ANY, "print_scene_info"); + this->addInteractionCommand("1", ModifierKeys::ANY, "set_camera front"); + this->addInteractionCommand("3", ModifierKeys::ANY, "set_camera right"); + this->addInteractionCommand("4", ModifierKeys::ANY, "roll_camera -90"); + this->addInteractionCommand("5", ModifierKeys::ANY, "toggle scene.camera.orthographic"); + this->addInteractionCommand("6", ModifierKeys::ANY, "roll_camera 90"); + this->addInteractionCommand("7", ModifierKeys::ANY, "set_camera top"); + this->addInteractionCommand("9", ModifierKeys::ANY, "set_camera isometric"); + this->addInteractionCommand(F3D_EXIT_HOTKEY_SYM, ModifierKeys::NONE, "stop_interactor"); + this->addInteractionCommand("Return", ModifierKeys::NONE, "reset_camera"); + this->addInteractionCommand("Space", ModifierKeys::NONE, "toggle_animation"); + this->addInteractionCommand("Drop", ModifierKeys::NONE, "add_files"); } //---------------------------------------------------------------------------- interactor_impl::~interactor_impl() = default; //---------------------------------------------------------------------------- -interactor& interactor_impl::setKeyPressCallBack(std::function callBack) +interactor& interactor_impl::addCommandCallback( + std::string action, std::function&)> callback) +{ + this->Internals->CommandCallbacks[std::move(action)] = callback; + return *this; +} + +//---------------------------------------------------------------------------- +interactor& interactor_impl::removeCommandCallback(const std::string& action) +{ + this->Internals->CommandCallbacks.erase(action); + return *this; +} + +//---------------------------------------------------------------------------- +bool interactor_impl::triggerCommand(std::string_view command) +{ + log::debug("Command: ", command); + std::vector tokens; + try + { + tokens = utils::tokenize(command); + } + catch (const utils::tokenize_exception&) + { + log::error("Command: unable to tokenize command:\"", command, "\", ignoring"); + return false; + } + + const std::string& action = tokens[0]; + try + { + // Find the right command to call + auto callbackIt = this->Internals->CommandCallbacks.find(action); + if (callbackIt != this->Internals->CommandCallbacks.end()) + { + return callbackIt->second({ tokens.begin() + 1, tokens.end() }); + } + else + { + log::error("Command: \"", action, "\" is not recognized, ignoring"); + return false; + } + } + catch (const f3d::options::incompatible_exception&) + { + log::error("Command: provided args in command: \"", command, + "\" are not compatible with action:\"", action, "\", ignoring"); + } + catch (const f3d::options::inexistent_exception&) + { + log::error("Command: provided args in command: \"", command, + "\" point to an inexistent option, ignoring"); + } + catch (const f3d::options::no_value_exception&) + { + log::error("Command: provided args in command: \"", command, + "\" point to an option without a value, ignoring"); + } + catch (const f3d::options::parsing_exception&) + { + log::error("Command: provided args in command: \"", command, + "\" cannot be parsed into an option, ignoring"); + } + return false; +} + +//---------------------------------------------------------------------------- +interactor& interactor_impl::addInteractionCommands( + std::string interaction, ModifierKeys modifiers, std::vector commands) +{ + this->Internals->InteractionCommands[{ std::move(interaction), modifiers }] = std::move(commands); + return *this; +} + +//---------------------------------------------------------------------------- +interactor& interactor_impl::addInteractionCommand( + std::string interaction, ModifierKeys modifiers, std::string command) { - this->Internals->KeyPressUserCallBack = callBack; + this->Internals->InteractionCommands[{ std::move(interaction), modifiers }] = { std::move( + command) }; return *this; } //---------------------------------------------------------------------------- -interactor& interactor_impl::setDropFilesCallBack( - std::function)> callBack) +interactor& interactor_impl::removeInteractionCommands( + std::string interaction, ModifierKeys modifiers) { - this->Internals->DropFilesUserCallBack = callBack; + this->Internals->InteractionCommands.erase({ std::move(interaction), modifiers }); return *this; } diff --git a/library/src/loader_impl.cxx b/library/src/loader_impl.cxx deleted file mode 100644 index f2ecbaa91a..0000000000 --- a/library/src/loader_impl.cxx +++ /dev/null @@ -1,415 +0,0 @@ -#include "loader_impl.h" - -#include "animationManager.h" -#include "interactor_impl.h" -#include "log.h" -#include "options.h" -#include "window_impl.h" - -#include "factory.h" -#include "vtkF3DGenericImporter.h" -#include "vtkF3DMemoryMesh.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -namespace f3d::detail -{ -class loader_impl::internals -{ -public: - internals(const options& options, window_impl& window) - : Options(options) - , Window(window) - { - } - - struct ProgressDataStruct - { - vtkTimerLog* timer; - vtkProgressBarWidget* widget; - }; - - static void CreateProgressRepresentationAndCallback( - ProgressDataStruct* data, vtkImporter* importer, interactor_impl* interactor) - { - vtkNew progressCallback; - progressCallback->SetClientData(data); - progressCallback->SetCallback( - [](vtkObject*, unsigned long, void* clientData, void* callData) - { - auto progressData = static_cast(clientData); - progressData->timer->StopTimer(); - vtkProgressBarWidget* widget = progressData->widget; - // Only show and render the progress bar if loading takes more than 0.15 seconds - if (progressData->timer->GetElapsedTime() > 0.15 || - vtksys::SystemTools::HasEnv("CTEST_F3D_PROGRESS_BAR")) - { - widget->On(); - vtkProgressBarRepresentation* rep = - vtkProgressBarRepresentation::SafeDownCast(widget->GetRepresentation()); - rep->SetProgressRate(*static_cast(callData)); - widget->Render(); - } - }); - importer->AddObserver(vtkCommand::ProgressEvent, progressCallback); - - interactor->SetInteractorOn(data->widget); - - vtkProgressBarRepresentation* progressRep = - vtkProgressBarRepresentation::SafeDownCast(data->widget->GetRepresentation()); - progressRep->SetProgressRate(0.0); - progressRep->ProportionalResizeOff(); - progressRep->SetPosition(0.0, 0.0); - progressRep->SetPosition2(1.0, 0.0); - progressRep->SetMinimumSize(0, 5); - progressRep->SetProgressBarColor(1, 1, 1); - progressRep->DrawBackgroundOff(); - progressRep->DragableOff(); - progressRep->SetShowBorderToOff(); - progressRep->DrawFrameOff(); - progressRep->SetPadding(0.0, 0.0); - data->timer->StartTimer(); - } - - static void DisplayImporterDescription(vtkImporter* importer) - { - vtkIdType availCameras = importer->GetNumberOfCameras(); - if (availCameras <= 0) - { - log::debug("No camera available in this file"); - } - else - { - log::debug("Camera(s) available in this file are:"); - } - for (int i = 0; i < availCameras; i++) - { - log::debug(i, ": ", importer->GetCameraName(i)); - } - log::debug(""); - log::debug(importer->GetOutputsDescription(), "\n"); - } - - void Reset() - { - // Reset the generic importer - this->GenericImporter->RemoveInternalReaders(); - - // Remove the importer from the renderer - this->Window.SetImporterForColoring(nullptr); - - // Window initialization is needed - this->Window.Initialize(true); - } - - void LoadGeometry(const std::string& name, vtkAlgorithm* source, bool reset) - { - if (!this->DefaultScene || reset) - { - this->Reset(); - } - - // Manage progress bar - vtkNew progressWidget; - vtkNew timer; - loader_impl::internals::ProgressDataStruct callbackData; - callbackData.timer = timer; - callbackData.widget = progressWidget; - if (this->Options.ui.loader_progress && this->Interactor) - { - loader_impl::internals::CreateProgressRepresentationAndCallback( - &callbackData, this->GenericImporter, this->Interactor); - } - - // Add a single internal reader - this->GenericImporter->AddInternalReader(name, source); - - // Update the importer -#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240707) - if (!this->GenericImporter->Update()) - { - throw loader::load_failure_exception("failed to load geometry: " + name); - } -#else - this->GenericImporter->Update(); -#endif - - // Remove anything progress related if any - this->GenericImporter->RemoveObservers(vtkCommand::ProgressEvent); - progressWidget->Off(); - - // Initialize the animation using temporal information from the importer - if (this->AnimationManager.Initialize( - &this->Options, &this->Window, this->Interactor, this->GenericImporter)) - { - if (this->Options.scene.animation.time.has_value()) - { - double animationTime = this->Options.scene.animation.time.value(); - double timeRange[2]; - this->AnimationManager.GetTimeRange(timeRange); - - // We assume importers import data at timeRange[0] when not specified - if (animationTime != timeRange[0]) - { - this->AnimationManager.LoadAtTime(animationTime); - } - } - } - - // Set the name for animation - this->Window.setAnimationNameInfo(this->AnimationManager.GetAnimationName()); - - // Display the importer description - loader_impl::internals::DisplayImporterDescription(this->GenericImporter); - - // Set the importer to use for coloring and actors - this->Window.SetImporterForColoring(this->GenericImporter); - - // Initialize renderer and reset camera to bounds - this->Window.UpdateDynamicOptions(); - this->Window.getCamera().resetToBounds(); - - // Print info about scene and coloring - this->Window.PrintColoringDescription(log::VerboseLevel::DEBUG); - this->Window.PrintSceneDescription(log::VerboseLevel::DEBUG); - - this->DefaultScene = true; - } - - bool DefaultScene = false; - const options& Options; - window_impl& Window; - interactor_impl* Interactor = nullptr; - animationManager AnimationManager; - - vtkSmartPointer CurrentFullSceneImporter; - vtkNew GenericImporter; -}; - -//---------------------------------------------------------------------------- -loader_impl::loader_impl(const options& options, window_impl& window) - : Internals(std::make_unique(options, window)) -{ - // Set render window on generic importer - this->Internals->GenericImporter->SetRenderWindow(this->Internals->Window.GetRenderWindow()); -} - -//---------------------------------------------------------------------------- -loader_impl::~loader_impl() = default; - -//---------------------------------------------------------------------------- -loader& loader_impl::loadGeometry(const std::string& filePath, bool reset) -{ - // Check file validity - if (filePath.empty()) - { - // Calling with reset is an usual codepath to reset the window - if (!reset) - { - log::debug("Provided geometry file path is empty\n"); - } - this->Internals->Reset(); - return *this; - } - if (!vtksys::SystemTools::FileExists(filePath, true)) - { - throw loader::load_failure_exception(filePath + " does not exists"); - } - - f3d::reader* reader = f3d::factory::instance()->getReader(filePath); - if (reader) - { - log::debug("Found a reader for \"" + filePath + "\" : \"" + reader->getName() + "\""); - } - else - { - throw loader::load_failure_exception( - filePath + " is not a file of a supported 3D geometry file format"); - } - auto vtkReader = reader->createGeometryReader(filePath); - if (!vtkReader) - { - throw loader::load_failure_exception( - filePath + " is not a file of a supported 3D geometry file format for default scene"); - } - // Read the file - log::debug("Loading 3D geometry: ", filePath, "\n"); - - this->Internals->LoadGeometry(vtksys::SystemTools::GetFilenameName(filePath), vtkReader, reset); - - return *this; -} - -//---------------------------------------------------------------------------- -loader& loader_impl::loadScene(const std::string& filePath) -{ - if (filePath.empty()) - { - log::debug("No file to load a full scene provided\n"); - return *this; - } - if (!vtksys::SystemTools::FileExists(filePath, true)) - { - throw loader::load_failure_exception(filePath + " does not exists"); - } - - // Recover the importer for the provided file path - this->Internals->CurrentFullSceneImporter = nullptr; - f3d::reader* reader = f3d::factory::instance()->getReader(filePath); - if (reader) - { - log::debug("Found a reader for \"" + filePath + "\" : \"" + reader->getName() + "\""); - } - else - { - throw loader::load_failure_exception( - filePath + " is not a file of a supported 3D scene file format"); - } - this->Internals->CurrentFullSceneImporter = reader->createSceneReader(filePath); - if (!this->Internals->CurrentFullSceneImporter) - { - throw loader::load_failure_exception( - filePath + " is not a file of a supported 3D scene file format for full scene"); - } - - this->Internals->Window.Initialize(false); - this->Internals->DefaultScene = false; - - // Initialize importer for rendering - this->Internals->CurrentFullSceneImporter->SetRenderWindow( - this->Internals->Window.GetRenderWindow()); - - if (this->Internals->Options.scene.camera.index.has_value()) - { - this->Internals->CurrentFullSceneImporter->SetCamera( - this->Internals->Options.scene.camera.index.value()); - } - - log::debug("Loading 3D scene: ", filePath, "\n"); - - // Manage progress bar - vtkNew progressWidget; - vtkNew timer; - loader_impl::internals::ProgressDataStruct callbackData; - callbackData.timer = timer; - callbackData.widget = progressWidget; - if (this->Internals->Options.ui.loader_progress && this->Internals->Interactor) - { - loader_impl::internals::CreateProgressRepresentationAndCallback( - &callbackData, this->Internals->CurrentFullSceneImporter, this->Internals->Interactor); - } - - // Read the file -#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240707) - if (!this->Internals->CurrentFullSceneImporter->Update()) - { - throw loader::load_failure_exception("failed to load scene: " + filePath); - } -#else - this->Internals->CurrentFullSceneImporter->Update(); -#endif - - // Remove anything progress related if any - this->Internals->CurrentFullSceneImporter->RemoveObservers(vtkCommand::ProgressEvent); - progressWidget->Off(); - - // Initialize the animation using temporal information from the importer - if (this->Internals->AnimationManager.Initialize(&this->Internals->Options, - &this->Internals->Window, this->Internals->Interactor, - this->Internals->CurrentFullSceneImporter)) - { - if (this->Internals->Options.scene.animation.time.has_value()) - { - double animationTime = this->Internals->Options.scene.animation.time.value(); - double timeRange[2]; - this->Internals->AnimationManager.GetTimeRange(timeRange); - - // We assume importers import data at timeRange[0] when not specified - if (animationTime != timeRange[0]) - { - this->Internals->AnimationManager.LoadAtTime(animationTime); - } - } - } - - // Set the name for animation - this->Internals->Window.setAnimationNameInfo( - this->Internals->AnimationManager.GetAnimationName()); - - // Display output description - loader_impl::internals::DisplayImporterDescription(this->Internals->CurrentFullSceneImporter); - - // Initialize renderer and reset camera to bounds if needed - this->Internals->Window.UpdateDynamicOptions(); - if (!this->Internals->Options.scene.camera.index.has_value()) - { - this->Internals->Window.getCamera().resetToBounds(); - } - - // Print info about scene and coloring - this->Internals->Window.PrintColoringDescription(log::VerboseLevel::DEBUG); - this->Internals->Window.PrintSceneDescription(log::VerboseLevel::DEBUG); - - return *this; -} - -//---------------------------------------------------------------------------- -loader& loader_impl::loadGeometry(const mesh_t& mesh, bool reset) -{ - // sanity checks - auto [valid, err] = mesh.isValid(); - if (!valid) - { - throw loader::load_failure_exception(err); - } - - vtkNew vtkSource; - vtkSource->SetPoints(mesh.points); - vtkSource->SetNormals(mesh.normals); - vtkSource->SetTCoords(mesh.texture_coordinates); - vtkSource->SetFaces(mesh.face_sides, mesh.face_indices); - vtkSource->Update(); - - this->Internals->LoadGeometry("", vtkSource, reset); - - return *this; -} - -//---------------------------------------------------------------------------- -bool loader_impl::hasSceneReader(const std::string& filePath) -{ - f3d::reader* reader = f3d::factory::instance()->getReader(filePath); - if (!reader) - { - return false; - } - return reader->hasSceneReader(); -} - -//---------------------------------------------------------------------------- -bool loader_impl::hasGeometryReader(const std::string& filePath) -{ - f3d::reader* reader = f3d::factory::instance()->getReader(filePath); - if (!reader) - { - return false; - } - return reader->hasGeometryReader(); -} - -//---------------------------------------------------------------------------- -void loader_impl::SetInteractor(interactor_impl* interactor) -{ - this->Internals->Interactor = interactor; - this->Internals->Interactor->SetAnimationManager(&this->Internals->AnimationManager); -} -} diff --git a/library/src/options.cxx b/library/src/options.cxx index 23174d6799..62193be7ee 100644 --- a/library/src/options.cxx +++ b/library/src/options.cxx @@ -4,6 +4,7 @@ #include "export.h" #include "init.h" #include "log.h" +#include "types.h" #include "utils.h" #include "vtkF3DConfigure.h" @@ -56,6 +57,11 @@ options& options::toggle(const std::string& name) options_tools::set(*this, name, !std::get(val)); return *this; } + catch (const f3d::options::no_value_exception&) + { + options_tools::set(*this, name, true); + return *this; + } catch (const std::bad_variant_access&) { throw options::incompatible_exception( @@ -178,6 +184,7 @@ F3D_DECL_TYPE(bool); F3D_DECL_TYPE(int); F3D_DECL_TYPE(double); F3D_DECL_TYPE(f3d::ratio_t); +F3D_DECL_TYPE(f3d::vector3_t); F3D_DECL_TYPE(std::string); //---------------------------------------------------------------------------- diff --git a/library/src/scene_impl.cxx b/library/src/scene_impl.cxx new file mode 100644 index 0000000000..1a7e1f08c0 --- /dev/null +++ b/library/src/scene_impl.cxx @@ -0,0 +1,339 @@ +#include "scene_impl.h" + +#include "animationManager.h" +#include "interactor_impl.h" +#include "log.h" +#include "options.h" +#include "window_impl.h" + +#include "factory.h" +#include "vtkF3DGenericImporter.h" +#include "vtkF3DMemoryMesh.h" +#include "vtkF3DMetaImporter.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace fs = std::filesystem; + +namespace f3d::detail +{ +class scene_impl::internals +{ +public: + internals(const options& options, window_impl& window) + : Options(options) + , Window(window) + , AnimationManager(options, window) + { + this->MetaImporter->SetRenderWindow(this->Window.GetRenderWindow()); + this->Window.SetImporter(this->MetaImporter); + this->AnimationManager.SetImporter(this->MetaImporter); + } + + struct ProgressDataStruct + { + vtkTimerLog* timer; + vtkProgressBarWidget* widget; + }; + + static void CreateProgressRepresentationAndCallback( + ProgressDataStruct* data, vtkImporter* importer, interactor_impl* interactor) + { + vtkNew progressCallback; + progressCallback->SetClientData(data); + progressCallback->SetCallback( + [](vtkObject*, unsigned long, void* clientData, void* callData) + { + auto progressData = static_cast(clientData); + progressData->timer->StopTimer(); + vtkProgressBarWidget* widget = progressData->widget; + // Only show and render the progress bar if loading takes more than 0.15 seconds + if (progressData->timer->GetElapsedTime() > 0.15 || + vtksys::SystemTools::HasEnv("CTEST_F3D_PROGRESS_BAR")) + { + widget->On(); + vtkProgressBarRepresentation* rep = + vtkProgressBarRepresentation::SafeDownCast(widget->GetRepresentation()); + rep->SetProgressRate(*static_cast(callData)); + widget->Render(); + } + }); + importer->AddObserver(vtkCommand::ProgressEvent, progressCallback); + + interactor->SetInteractorOn(data->widget); + + vtkProgressBarRepresentation* progressRep = + vtkProgressBarRepresentation::SafeDownCast(data->widget->GetRepresentation()); + progressRep->SetProgressRate(0.0); + progressRep->ProportionalResizeOff(); + progressRep->SetPosition(0.0, 0.0); + progressRep->SetPosition2(1.0, 0.0); + progressRep->SetMinimumSize(0, 5); + progressRep->SetProgressBarColor(1, 1, 1); + progressRep->DrawBackgroundOff(); + progressRep->DragableOff(); + progressRep->SetShowBorderToOff(); + progressRep->DrawFrameOff(); + progressRep->SetPadding(0.0, 0.0); + data->timer->StartTimer(); + } + + void Load(const std::vector>& importers) + { + for (const vtkSmartPointer& importer : importers) + { + this->MetaImporter->AddImporter(importer); + } + + // Initialize the UpVector on load + this->Window.InitializeUpVector(); + + if (this->Options.scene.camera.index.has_value()) + { + this->MetaImporter->SetCameraIndex(this->Options.scene.camera.index.value()); + } + + // Manage progress bar + vtkNew progressWidget; + vtkNew timer; + scene_impl::internals::ProgressDataStruct callbackData; + callbackData.timer = timer; + callbackData.widget = progressWidget; + if (this->Options.ui.loader_progress && this->Interactor) + { + scene_impl::internals::CreateProgressRepresentationAndCallback( + &callbackData, this->MetaImporter, this->Interactor); + } + + // Update the meta importer, the will only update importers that have not been update before +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240707) + if (!this->MetaImporter->Update()) + { + throw scene::load_failure_exception("failed to load scene"); + } +#else + this->MetaImporter->Update(); +#endif + + // Remove anything progress related if any + this->MetaImporter->RemoveObservers(vtkCommand::ProgressEvent); + progressWidget->Off(); + + // Initialize the animation using temporal information from the importer + if (this->AnimationManager.Initialize()) + { + if (this->Options.scene.animation.time.has_value()) + { + double animationTime = this->Options.scene.animation.time.value(); + double timeRange[2]; + this->AnimationManager.GetTimeRange(timeRange); + + // We assume importers import data at timeRange[0] when not specified + if (animationTime != timeRange[0]) + { + this->AnimationManager.LoadAtTime(animationTime); + } + } + } + + // Set the name for animation + this->Window.setAnimationNameInfo(this->AnimationManager.GetAnimationName()); + + // Display output description + scene_impl::internals::DisplayImporterDescription(this->MetaImporter); + + // Update all window options and reset camera to bounds if needed + this->Window.UpdateDynamicOptions(); + if (!this->Options.scene.camera.index.has_value()) + { + this->Window.getCamera().resetToBounds(); + } + + // Display coloring information + this->Window.PrintColoringDescription(log::VerboseLevel::DEBUG); + log::debug(""); + + // Print scene description + this->Window.PrintSceneDescription(log::VerboseLevel::DEBUG); + } + + static void DisplayImporterDescription(vtkImporter* importer) + { + vtkIdType availCameras = importer->GetNumberOfCameras(); + if (availCameras <= 0) + { + log::debug("No camera available"); + } + else + { + log::debug("Camera(s) available are:"); + } + for (int i = 0; i < availCameras; i++) + { + log::debug(i, ": ", importer->GetCameraName(i)); + } + log::debug(""); + log::debug(importer->GetOutputsDescription(), "\n"); + } + + const options& Options; + window_impl& Window; + interactor_impl* Interactor = nullptr; + animationManager AnimationManager; + + vtkNew MetaImporter; +}; + +//---------------------------------------------------------------------------- +scene_impl::scene_impl(const options& options, window_impl& window) + : Internals(std::make_unique(options, window)) +{ +} + +//---------------------------------------------------------------------------- +scene_impl::~scene_impl() = default; + +//---------------------------------------------------------------------------- +scene& scene_impl::add(const fs::path& filePath) +{ + std::vector paths = { filePath }; + return this->add(paths); +} + +//---------------------------------------------------------------------------- +scene& scene_impl::add(const std::vector& filePathStrings) +{ + std::vector paths; + paths.reserve(filePathStrings.size()); + for (const std::string& str : filePathStrings) + { + paths.emplace_back(str); + } + return this->add(paths); +} + +//---------------------------------------------------------------------------- +scene& scene_impl::add(const std::vector& filePaths) +{ + if (filePaths.empty()) + { + log::debug("No file to load a full scene provided\n"); + return *this; + } + + std::vector> importers; + for (const fs::path& filePath : filePaths) + { + if (filePath.empty()) + { + log::debug("An empty file to load was provided\n"); + continue; + } + if (!vtksys::SystemTools::FileExists(filePath.string(), true)) + { + throw scene::load_failure_exception(filePath.string() + " does not exists"); + } + + // Recover the importer for the provided file path + f3d::reader* reader = f3d::factory::instance()->getReader(filePath.string()); + if (reader) + { + log::debug( + "Found a reader for \"" + filePath.string() + "\" : \"" + reader->getName() + "\""); + } + else + { + throw scene::load_failure_exception( + filePath.string() + " is not a file of a supported 3D scene file format"); + } + vtkSmartPointer importer = reader->createSceneReader(filePath.string()); + if (!importer) + { + // XXX: F3D Plugin CMake logic ensure there is either a scene reader or a geometry reader + auto vtkReader = reader->createGeometryReader(filePath.string()); + assert(vtkReader); + vtkSmartPointer genericImporter = + vtkSmartPointer::New(); + genericImporter->SetInternalReader(vtkReader); + importer = genericImporter; + } + importers.emplace_back(importer); + } + + log::debug("\nLoading files: "); + if (filePaths.size() == 1) + { + log::debug(filePaths[0].string()); + } + else + { + for (const fs::path& filePathStr : filePaths) + { + log::debug("- ", filePathStr.string()); + } + } + log::debug(""); + + this->Internals->Load(importers); + return *this; +} + +//---------------------------------------------------------------------------- +scene& scene_impl::add(const mesh_t& mesh) +{ + // sanity checks + auto [valid, err] = mesh.isValid(); + if (!valid) + { + throw scene::load_failure_exception(err); + } + + vtkNew vtkSource; + vtkSource->SetPoints(mesh.points); + vtkSource->SetNormals(mesh.normals); + vtkSource->SetTCoords(mesh.texture_coordinates); + vtkSource->SetFaces(mesh.face_sides, mesh.face_indices); + + vtkSmartPointer importer = vtkSmartPointer::New(); + importer->SetInternalReader(vtkSource); + + log::debug("Loading 3D scene from memory"); + this->Internals->Load({ importer }); + return *this; +} + +//---------------------------------------------------------------------------- +scene& scene_impl::clear() +{ + // Clear the meta importer from all importers + this->Internals->MetaImporter->Clear(); + + // Clear the window of all actors + this->Internals->Window.Initialize(); + + return *this; +} + +//---------------------------------------------------------------------------- +bool scene_impl::supports(const fs::path& filePath) +{ + return f3d::factory::instance()->getReader(filePath.string()) != nullptr; +} + +//---------------------------------------------------------------------------- +void scene_impl::SetInteractor(interactor_impl* interactor) +{ + this->Internals->Interactor = interactor; + this->Internals->AnimationManager.SetInteractor(interactor); + this->Internals->Interactor->SetAnimationManager(&this->Internals->AnimationManager); +} +} diff --git a/library/src/utils.cxx b/library/src/utils.cxx index fcbcbc8979..0f799f7178 100644 --- a/library/src/utils.cxx +++ b/library/src/utils.cxx @@ -9,4 +9,76 @@ unsigned int utils::textDistance(const std::string& strA, const std::string& str { return static_cast(detail::levenshtein(strA, strB)); } + +//---------------------------------------------------------------------------- +std::vector utils::tokenize(std::string_view str) +{ + std::vector tokens; + std::string token; + const auto accumulate = [&](const char& c) { token.push_back(c); }; + const auto emit = [&]() + { + if (!token.empty()) + { + tokens.push_back(token); + token.clear(); + } + }; + bool escaped = false; + char quoted = '\0'; + for (char c : str) + { + switch (c) + { + case '\\': + escaped = true; + break; + case ' ': + if (!escaped && !quoted) + { + emit(); + } + else + { + accumulate(c); + } + escaped = false; + break; + case '"': + case '\'': + case '`': + if (!escaped && quoted == c) + { + emit(); + quoted = '\0'; + } + else if (!escaped && !quoted) + { + quoted = c; + } + else + { + accumulate(c); + } + escaped = false; + break; + default: + accumulate(c); + escaped = false; + break; + } + } + if (quoted || escaped) + { + throw tokenize_exception(); + } + emit(); + return tokens; +} + +//---------------------------------------------------------------------------- +utils::tokenize_exception::tokenize_exception(const std::string& what) + : exception(what) +{ +} } diff --git a/library/src/window_impl.cxx b/library/src/window_impl.cxx index f70cf72c62..33ff621822 100644 --- a/library/src/window_impl.cxx +++ b/library/src/window_impl.cxx @@ -9,7 +9,7 @@ #include "vtkF3DGenericImporter.h" #include "vtkF3DNoRenderWindow.h" -#include "vtkF3DRendererWithColoring.h" +#include "vtkF3DRenderer.h" #include #include @@ -18,10 +18,27 @@ #include #include #include +#include #include #include #include +#ifdef VTK_USE_X +#include +#endif + +#ifdef _WIN32 +#include +#endif + +#ifdef VTK_OPENGL_HAS_EGL +#include +#endif + +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240914) +#include +#endif + #if F3D_MODULE_EXTERNAL_RENDERING #include #endif @@ -139,21 +156,28 @@ class window_impl::internals #endif } + static context::fptr SymbolLoader(void* userptr, const char* name) + { + assert(userptr != nullptr); + auto* fn = static_cast(userptr); + assert(fn != nullptr); + return (*fn)(name); + } + std::unique_ptr Camera; vtkSmartPointer RenWin; - vtkNew Renderer; - Type WindowType; + vtkNew Renderer; const options& Options; - bool Initialized = false; - bool WithColoring = false; std::string CachePath; + context::function GetProcAddress; }; //---------------------------------------------------------------------------- -window_impl::window_impl(const options& options, Type type) +window_impl::window_impl(const options& options, const std::optional& type, bool offscreen, + const context::function& getProcAddress) : Internals(std::make_unique(options)) { - this->Internals->WindowType = type; + this->Internals->GetProcAddress = getProcAddress; if (type == Type::NONE) { this->Internals->RenWin = vtkSmartPointer::New(); @@ -161,49 +185,164 @@ window_impl::window_impl(const options& options, Type type) else if (type == Type::EXTERNAL) { #if F3D_MODULE_EXTERNAL_RENDERING - this->Internals->RenWin = vtkSmartPointer::New(); + vtkNew extWin; + extWin->AutomaticWindowPositionAndResizeOff(); + this->Internals->RenWin = extWin; #else throw engine::no_window_exception( "Window type is external but F3D_MODULE_EXTERNAL_RENDERING is not enabled"); #endif } - else + else if (type == Type::EGL) { - this->Internals->RenWin = vtkSmartPointer::New(); - this->Internals->RenWin->SetOffScreenRendering(type == Type::NATIVE_OFFSCREEN); - this->Internals->RenWin->SetMultiSamples(0); // Disable hardware antialiasing - +#if defined(VTK_OPENGL_HAS_EGL) && VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240914) + this->Internals->RenWin = vtkSmartPointer::New(); #ifdef __ANDROID__ // Since F3D_MODULE_EXTERNAL_RENDERING is not supported on Android yet, we need to call // this workaround. It makes vtkEGLRenderWindow external if WindowInfo is not nullptr. this->Internals->RenWin->SetWindowInfo("jni"); +#endif +#else + throw engine::no_window_exception("Window type is EGL but VTK EGL support is not enabled"); #endif } + else if (type == Type::OSMESA) + { +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240914) + this->Internals->RenWin = vtkSmartPointer::New(); +#else + throw engine::no_window_exception( + "Window type is OSMesa but VTK OSMesa support is not enabled"); +#endif + } + else if (type == Type::GLX) + { +#if defined(VTK_USE_X) && VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240914) + this->Internals->RenWin = vtkSmartPointer::New(); +#else + throw engine::no_window_exception("Window type is GLX but VTK GLX support is not enabled"); +#endif + } + else if (type == Type::WGL) + { +#ifdef _WIN32 + this->Internals->RenWin = vtkSmartPointer::New(); +#endif + } + else if (!type.has_value()) + { + // rely on VTK logic + this->Internals->RenWin = vtkSmartPointer::New(); + } + + assert(this->Internals->RenWin != nullptr); + +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240914) + vtkOpenGLRenderWindow* oglRenWin = vtkOpenGLRenderWindow::SafeDownCast(this->Internals->RenWin); + if (oglRenWin) + { + if (this->Internals->GetProcAddress) + { + oglRenWin->SetOpenGLSymbolLoader(&internals::SymbolLoader, &this->Internals->GetProcAddress); + } +#if F3D_MODULE_EXTERNAL_RENDERING + if (oglRenWin->IsA("vtkExternalOpenGLRenderWindow")) + { + // We need to call vtkOpenGLRenderWindow function because vtkGenericOpenGLRenderWindow is + // reimplementing it incorrectly + oglRenWin->vtkOpenGLRenderWindow::OpenGLInit(); + } +#endif + } +#endif + #if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240606) this->Internals->RenWin->EnableTranslucentSurfaceOn(); #endif + this->Internals->RenWin->SetMultiSamples(0); // Disable hardware antialiasing + this->Internals->RenWin->SetOffScreenRendering(offscreen); this->Internals->RenWin->SetWindowName("f3d"); this->Internals->RenWin->AddRenderer(this->Internals->Renderer); this->Internals->Camera = std::make_unique(); this->Internals->Camera->SetVTKRenderer(this->Internals->Renderer); + + this->Initialize(); + this->Internals->UpdateTheme(); + + log::debug("VTK window class type is ", this->Internals->RenWin->GetClassName()); } //---------------------------------------------------------------------------- -window_impl::Type window_impl::getType() +void window_impl::Initialize() { - return this->Internals->WindowType; + this->Internals->Renderer->Initialize(); } //---------------------------------------------------------------------------- -camera& window_impl::getCamera() +void window_impl::InitializeUpVector() { - // Make sure the camera (and the whole rendering stack) - // is initialized before providing one. - if (!this->Internals->Initialized) + this->Internals->Renderer->InitializeUpVector(this->Internals->Options.scene.up_direction); +} + +//---------------------------------------------------------------------------- +window_impl::Type window_impl::getType() +{ + if (this->Internals->RenWin->IsA("vtkOSOpenGLRenderWindow")) + { + return Type::OSMESA; + } + +#ifdef VTK_USE_X + if (this->Internals->RenWin->IsA("vtkXOpenGLRenderWindow")) + { + return Type::GLX; + } +#endif + +#ifdef _WIN32 + if (this->Internals->RenWin->IsA("vtkWin32OpenGLRenderWindow")) + { + return Type::WGL; + } +#endif + +#ifdef __APPLE__ + if (this->Internals->RenWin->IsA("vtkCocoaRenderWindow")) + { + return Type::COCOA; + } +#endif + +#ifdef VTK_OPENGL_HAS_EGL + if (this->Internals->RenWin->IsA("vtkEGLRenderWindow")) + { + return Type::EGL; + } +#endif +#ifdef __EMSCRIPTEN__ + if (this->Internals->RenWin->IsA("vtkWebAssemblyOpenGLRenderWindow")) + { + return Type::WASM; + } +#endif + + if (this->Internals->RenWin->IsA("vtkF3DNoRenderWindow")) { - this->Initialize(false); + return Type::NONE; } + return Type::UNKNOWN; +} + +//---------------------------------------------------------------------------- +bool window_impl::isOffscreen() +{ + return !this->Internals->RenWin->GetShowWindow(); +} + +//---------------------------------------------------------------------------- +camera& window_impl::getCamera() +{ return *this->Internals->Camera; } @@ -307,27 +446,12 @@ window_impl::~window_impl() this->Internals->Renderer->ShowAxis(false); } -//---------------------------------------------------------------------------- -void window_impl::Initialize(bool withColoring) -{ - this->Internals->WithColoring = withColoring; - this->Internals->Renderer->Initialize(this->Internals->Options.scene.up_direction); - this->Internals->UpdateTheme(); - this->Internals->Initialized = true; -} - //---------------------------------------------------------------------------- void window_impl::UpdateDynamicOptions() { - if (!this->Internals->Initialized) - { - // Renderer is missing, create a default one - this->Initialize(false); - } - - vtkF3DRendererWithColoring* renderer = this->Internals->Renderer; + vtkF3DRenderer* renderer = this->Internals->Renderer; - if (this->Internals->WindowType == Type::NONE) + if (this->Internals->RenWin->IsA("vtkF3DNoRenderWindow")) { // With a NONE window type, only update the actors to get accurate bounding box information renderer->UpdateActors(); @@ -348,9 +472,9 @@ void window_impl::UpdateDynamicOptions() // XXX: model.point_sprites.type only has an effect on geometry scene // but we set it here for practical reasons const int pointSpritesSize = opt.model.point_sprites.size; - const vtkF3DRendererWithColoring::SplatType splatType = opt.model.point_sprites.type == "gaussian" - ? vtkF3DRendererWithColoring::SplatType::GAUSSIAN - : vtkF3DRendererWithColoring::SplatType::SPHERE; + const vtkF3DRenderer::SplatType splatType = opt.model.point_sprites.type == "gaussian" + ? vtkF3DRenderer::SplatType::GAUSSIAN + : vtkF3DRenderer::SplatType::SPHERE; renderer->SetPointSpritesProperties(splatType, pointSpritesSize); renderer->SetLineWidth(opt.render.line_width); @@ -397,37 +521,30 @@ void window_impl::UpdateDynamicOptions() renderer->SetUseOrthographicProjection(opt.scene.camera.orthographic); } - if (this->Internals->WithColoring) - { - std::vector rgb = opt.model.color.rgb; - renderer->SetSurfaceColor(rgb.data()); - renderer->SetOpacity(opt.model.color.opacity); - renderer->SetTextureBaseColor(opt.model.color.texture); - renderer->SetRoughness(opt.model.material.roughness); - renderer->SetMetallic(opt.model.material.metallic); - renderer->SetTextureMaterial(opt.model.material.texture); - renderer->SetTextureEmissive(opt.model.emissive.texture); - std::vector factor = opt.model.emissive.factor; - renderer->SetEmissiveFactor(factor.data()); - renderer->SetTextureNormal(opt.model.normal.texture); - renderer->SetNormalScale(opt.model.normal.scale); - renderer->SetTextureMatCap(opt.model.matcap.texture); - - renderer->SetColoring(opt.model.scivis.enable, opt.model.scivis.cells, - opt.model.scivis.array_name, opt.model.scivis.component); - renderer->SetScalarBarRange(opt.model.scivis.range); - renderer->SetColormap(opt.model.scivis.colormap); - renderer->ShowScalarBar(opt.ui.scalar_bar); - - renderer->SetUsePointSprites(opt.model.point_sprites.enable); - renderer->SetUseVolume(opt.model.volume.enable); - renderer->SetUseInverseOpacityFunction(opt.model.volume.inverse); - } - else - { - // make sure the scalar bar is hidden without coloring - renderer->ShowScalarBar(false); - } + renderer->SetSurfaceColor(opt.model.color.rgb); + renderer->SetOpacity(opt.model.color.opacity); + renderer->SetTextureBaseColor(opt.model.color.texture); + renderer->SetRoughness(opt.model.material.roughness); + renderer->SetMetallic(opt.model.material.metallic); + renderer->SetTextureMaterial(opt.model.material.texture); + renderer->SetTextureEmissive(opt.model.emissive.texture); + renderer->SetEmissiveFactor(opt.model.emissive.factor); + renderer->SetTextureNormal(opt.model.normal.texture); + renderer->SetNormalScale(opt.model.normal.scale); + renderer->SetTextureMatCap(opt.model.matcap.texture); + + renderer->SetEnableColoring(opt.model.scivis.enable); + renderer->SetUseCellColoring(opt.model.scivis.cells); + renderer->SetArrayNameForColoring(opt.model.scivis.array_name); + renderer->SetComponentForColoring(opt.model.scivis.component); + + renderer->SetScalarBarRange(opt.model.scivis.range); + renderer->SetColormap(opt.model.scivis.colormap); + renderer->ShowScalarBar(opt.ui.scalar_bar); + + renderer->SetUsePointSprites(opt.model.point_sprites.enable); + renderer->SetUseVolume(opt.model.volume.enable); + renderer->SetUseInverseOpacityFunction(opt.model.volume.inverse); renderer->UpdateActors(); } @@ -441,13 +558,10 @@ void window_impl::PrintSceneDescription(log::VerboseLevel level) //---------------------------------------------------------------------------- void window_impl::PrintColoringDescription(log::VerboseLevel level) { - if (this->Internals->WithColoring) + std::string descr = this->Internals->Renderer->GetColoringDescription(); + if (!descr.empty()) { - std::string descr = this->Internals->Renderer->GetColoringDescription(); - if (!descr.empty()) - { - log::print(level, descr); - } + log::print(level, descr); } } @@ -495,12 +609,9 @@ image window_impl::renderToImage(bool noBackground) } //---------------------------------------------------------------------------- -void window_impl::SetImporterForColoring(vtkF3DGenericImporter* importer) +void window_impl::SetImporter(vtkF3DMetaImporter* importer) { - if (this->Internals->WithColoring) - { - this->Internals->Renderer->SetImporter(importer); - } + this->Internals->Renderer->SetImporter(importer); } //---------------------------------------------------------------------------- diff --git a/library/testing/CMakeLists.txt b/library/testing/CMakeLists.txt index fc39047335..a6066437cb 100644 --- a/library/testing/CMakeLists.txt +++ b/library/testing/CMakeLists.txt @@ -12,8 +12,9 @@ list(APPEND libf3dSDKTests_list TestSDKImage.cxx TestSDKInteractorCallBack.cxx TestSDKInteractorDropFullScene.cxx - TestSDKLoadFromMemory.cxx - TestSDKLoader.cxx + TestSDKInteractorCommand.cxx + TestSDKSceneFromMemory.cxx + TestSDKScene.cxx TestSDKLog.cxx TestSDKMultiColoring.cxx TestSDKMultiOptions.cxx @@ -21,7 +22,8 @@ list(APPEND libf3dSDKTests_list TestSDKOptionsIO.cxx TestSDKRenderAndInteract.cxx TestSDKRenderFinalShader.cxx - TestSDKWindowNative.cxx + TestSDKUtils.cxx + TestSDKWindowAuto.cxx TestPseudoUnitTest.cxx ) @@ -32,11 +34,11 @@ if(VTK_VERSION VERSION_GREATER_EQUAL 9.2.20221220) ) endif() -# Invalid loader codepath needs https://gitlab.kitware.com/vtk/vtk/-/merge_requests/11287 -# Merge with TestSDKLoader.cxx when VTK 9.4.0 is required. +# Invalid scene codepath needs https://gitlab.kitware.com/vtk/vtk/-/merge_requests/11287 +# Merge with TestSDKScene.cxx when VTK 9.4.0 is required. if(VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240707) list(APPEND libf3dSDKTests_list - TestSDKLoaderInvalid.cxx + TestSDKSceneInvalid.cxx ) endif() @@ -47,18 +49,50 @@ configure_file("${F3D_SOURCE_DIR}/testing/recordings/TestSDKInteractorDropFullSc "${CMAKE_BINARY_DIR}/TestSDKInteractorDropFullScene.log") # world.obj; S # External window tests -if(F3D_MODULE_EXTERNAL_RENDERING AND NOT VTK_OPENGL_HAS_EGL) - find_package(glfw3 QUIET) - if(glfw3_FOUND) - list(APPEND libf3dSDKTests_list - TestSDKExternalWindowGLFW.cxx - ) +cmake_dependent_option(F3D_TESTING_ENABLE_EXTERNAL_GLFW "Test external GLFW" OFF "F3D_MODULE_EXTERNAL_RENDERING" OFF) +cmake_dependent_option(F3D_TESTING_ENABLE_EXTERNAL_QT "Test external QT" OFF "F3D_MODULE_EXTERNAL_RENDERING" OFF) +cmake_dependent_option(F3D_TESTING_ENABLE_EXTERNAL_OSMESA "Test external OSMesa" OFF "F3D_MODULE_EXTERNAL_RENDERING" OFF) +cmake_dependent_option(F3D_TESTING_ENABLE_EXTERNAL_EGL "Test external GLX" OFF "F3D_MODULE_EXTERNAL_RENDERING" OFF) + +set(libf3dSDKTests_link_libs "") + +if(F3D_MODULE_EXTERNAL_RENDERING) + if(F3D_TESTING_ENABLE_EXTERNAL_GLFW) + find_package(glfw3 REQUIRED) + list(APPEND libf3dSDKTests_list TestSDKExternalWindowGLFW.cxx) + list(APPEND libf3dSDKTests_link_libs glfw) endif() - find_package(Qt5 QUIET COMPONENTS OpenGL) - if(Qt5_FOUND) - list(APPEND libf3dSDKTests_list - TestSDKExternalWindowQT.cxx - ) + if(F3D_TESTING_ENABLE_EXTERNAL_QT) + find_package(Qt5 COMPONENTS OpenGL REQUIRED) + list(APPEND libf3dSDKTests_list TestSDKExternalWindowQT.cxx) + list(APPEND libf3dSDKTests_link_libs Qt5::OpenGL) + endif() + if(F3D_TESTING_ENABLE_EXTERNAL_OSMESA AND VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240914) + find_library(OSMesa_LIB OSMesa REQUIRED) + list(APPEND libf3dSDKTests_list TestSDKExternalWindowOSMesa.cxx) + list(APPEND libf3dSDKTests_link_libs "${OSMesa_LIB}") + endif() + if(F3D_TESTING_ENABLE_GLX_TESTS AND VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240914) + find_package(OpenGL COMPONENTS GLX REQUIRED) + find_library(X11_LIB X11 REQUIRED) + list(APPEND libf3dSDKTests_list TestSDKExternalWindowGLX.cxx) + list(APPEND libf3dSDKTests_link_libs OpenGL::GLX "${X11_LIB}") + endif() + if(F3D_TESTING_ENABLE_EXTERNAL_EGL AND VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240914) + find_package(OpenGL COMPONENTS EGL REQUIRED) + list(APPEND libf3dSDKTests_list TestSDKExternalWindowEGL.cxx) + list(APPEND libf3dSDKTests_link_libs OpenGL::EGL) + endif() + if(WIN32) + find_package(OpenGL COMPONENTS OpenGL REQUIRED) + list(APPEND libf3dSDKTests_list TestSDKExternalWindowWGL.cxx) + list(APPEND libf3dSDKTests_link_libs OpenGL::GL) + endif() + if(APPLE) + find_library(COCOA_LIBRARY Cocoa) + find_library(OpenGL_LIBRARY OpenGL) + list(APPEND libf3dSDKTests_list TestSDKExternalWindowCOCOA.mm) + list(APPEND libf3dSDKTests_link_libs "${COCOA_LIBRARY}" "${OpenGL_LIBRARY}") endif() endif() @@ -89,7 +123,7 @@ list(APPEND libf3dSDKTestsNoRender_list TestSDKOptions TestSDKOptionsIO TestSDKLog - TestSDKLoader) + TestSDKScene) # Add all the ADD_TEST for each test foreach (test ${libf3dSDKTests_list}) @@ -118,17 +152,7 @@ if(VTK_VERSION VERSION_GREATER_EQUAL 9.2.20221220) endif() endif() -target_link_libraries(libf3dSDKTests libf3d) - -if(glfw3_FOUND) - # external window test using glfw - target_link_libraries(libf3dSDKTests glfw) -endif() - -if(Qt5_FOUND) - # external window test using QT - target_link_libraries(libf3dSDKTests Qt5::OpenGL) -endif() +target_link_libraries(libf3dSDKTests libf3d ${libf3dSDKTests_link_libs}) # make sure the libf3d API is compatible with the right C++ standard set_target_properties(libf3dSDKTests PROPERTIES CXX_STANDARD 17) diff --git a/library/testing/PseudoUnitTest.h b/library/testing/PseudoUnitTest.h index 55ebbd7e79..31fe855f90 100644 --- a/library/testing/PseudoUnitTest.h +++ b/library/testing/PseudoUnitTest.h @@ -1,6 +1,7 @@ #ifndef PseudoUnitTest_h #define PseudoUnitTest_h +#include "types.h" #include #include #include diff --git a/library/testing/TestSDKAnimation.cxx b/library/testing/TestSDKAnimation.cxx index 3c1fe45bee..970d5dd81c 100644 --- a/library/testing/TestSDKAnimation.cxx +++ b/library/testing/TestSDKAnimation.cxx @@ -1,15 +1,15 @@ #include #include -#include +#include #include int TestSDKAnimation(int argc, char* argv[]) { - f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); - f3d::loader& load = eng.getLoader(); + f3d::engine eng = f3d::engine::create(true); + f3d::scene& sce = eng.getScene(); f3d::interactor& inter = eng.getInteractor(); - load.loadScene(std::string(argv[1]) + "/data/InterpolationTest.glb"); + sce.add(std::string(argv[1]) + "/data/InterpolationTest.glb"); inter.startAnimation(); if (!inter.isPlayingAnimation()) diff --git a/library/testing/TestSDKCamera.cxx b/library/testing/TestSDKCamera.cxx index 3ef6fa1d7d..3210f4f210 100644 --- a/library/testing/TestSDKCamera.cxx +++ b/library/testing/TestSDKCamera.cxx @@ -1,6 +1,6 @@ #include #include -#include +#include #include #include @@ -67,14 +67,14 @@ void checkDouble(const double actual, const double expected, const std::string& int TestSDKCamera(int argc, char* argv[]) { - f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); + f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG); + f3d::engine eng = f3d::engine::create(true); f3d::window& win = eng.getWindow(); f3d::camera& cam = win.getCamera(); // check coordinates conversion f3d::point3_t point = { 0.1, 0.1, 0.1 }; f3d::point3_t pointDC = win.getDisplayFromWorld(point); - if (!comparePoint(point, win.getWorldFromDisplay(pointDC))) { std::cerr << "coordinates conversion is not behaving as expected" << std::endl; diff --git a/library/testing/TestSDKCompareWithFile.cxx b/library/testing/TestSDKCompareWithFile.cxx index e44a4ecd8a..d27c489a8f 100644 --- a/library/testing/TestSDKCompareWithFile.cxx +++ b/library/testing/TestSDKCompareWithFile.cxx @@ -1,20 +1,20 @@ #include -#include +#include #include #include "TestSDKHelpers.h" int TestSDKCompareWithFile(int argc, char* argv[]) { - f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); - f3d::loader& load = eng.getLoader(); + f3d::engine eng = f3d::engine::create(true); + f3d::scene& sce = eng.getScene(); f3d::window& win = eng.getWindow(); win.setSize(300, 300); - load.loadGeometry(std::string(argv[1]) + "/data/cow.vtp"); + sce.add(std::string(argv[1]) + "/data/cow.vtp"); return TestSDKHelpers::RenderTest(eng.getWindow(), std::string(argv[1]) + "baselines/", - std::string(argv[2]), "TestSDKCompareWithFile", 50) + std::string(argv[2]), "TestSDKCompareWithFile") ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/library/testing/TestSDKDropZone.cxx b/library/testing/TestSDKDropZone.cxx index 4827131dc8..a487ddf60c 100644 --- a/library/testing/TestSDKDropZone.cxx +++ b/library/testing/TestSDKDropZone.cxx @@ -6,7 +6,7 @@ int TestSDKDropZone(int argc, char* argv[]) { - f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); + f3d::engine eng = f3d::engine::create(true); f3d::window& win = eng.getWindow(); f3d::options& opt = eng.getOptions(); win.setSize(300, 300); @@ -16,7 +16,7 @@ int TestSDKDropZone(int argc, char* argv[]) win.render(); return TestSDKHelpers::RenderTest(eng.getWindow(), std::string(argv[1]) + "baselines/", - std::string(argv[2]), "TestSDKDropZone", 50) + std::string(argv[2]), "TestSDKDropZone") ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/library/testing/TestSDKDynamicBackgroundColor.cxx b/library/testing/TestSDKDynamicBackgroundColor.cxx index efafd3f7f1..a288cecb37 100644 --- a/library/testing/TestSDKDynamicBackgroundColor.cxx +++ b/library/testing/TestSDKDynamicBackgroundColor.cxx @@ -1,21 +1,21 @@ #include -#include #include +#include #include #include "TestSDKHelpers.h" int TestSDKDynamicBackgroundColor(int argc, char* argv[]) { - f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); - f3d::loader& load = eng.getLoader(); + f3d::engine eng = f3d::engine::create(true); + f3d::scene& sce = eng.getScene(); f3d::window& win = eng.getWindow(); f3d::options& opt = eng.getOptions(); win.setSize(300, 300); opt.ui.filename = true; opt.ui.filename_info = "(1/1) cow.vtp"; - load.loadGeometry(std::string(argv[1]) + "/data/cow.vtp"); + sce.add(std::string(argv[1]) + "/data/cow.vtp"); win.render(); @@ -23,7 +23,7 @@ int TestSDKDynamicBackgroundColor(int argc, char* argv[]) opt.render.background.color = { 1.0, 1.0, 1.0 }; return TestSDKHelpers::RenderTest(eng.getWindow(), std::string(argv[1]) + "baselines/", - std::string(argv[2]), "TestSDKDynamicBackgrounColor", 50) + std::string(argv[2]), "TestSDKDynamicBackgrounColor") ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/library/testing/TestSDKDynamicFontFile.cxx b/library/testing/TestSDKDynamicFontFile.cxx index cb8c18dd6e..1817425999 100644 --- a/library/testing/TestSDKDynamicFontFile.cxx +++ b/library/testing/TestSDKDynamicFontFile.cxx @@ -1,21 +1,21 @@ #include -#include #include +#include #include #include "TestSDKHelpers.h" int TestSDKDynamicFontFile(int argc, char* argv[]) { - f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); - f3d::loader& load = eng.getLoader(); + f3d::engine eng = f3d::engine::create(true); + f3d::scene& sce = eng.getScene(); f3d::window& win = eng.getWindow(); f3d::options& opt = eng.getOptions(); win.setSize(300, 300); opt.ui.filename = true; opt.ui.filename_info = "(1/1) cow.vtp"; - load.loadGeometry(std::string(argv[1]) + "/data/cow.vtp"); + sce.add(std::string(argv[1]) + "/data/cow.vtp"); win.render(); @@ -23,7 +23,7 @@ int TestSDKDynamicFontFile(int argc, char* argv[]) opt.ui.font_file = std::string(argv[1]) + "data/Crosterian.ttf"; return TestSDKHelpers::RenderTest(eng.getWindow(), std::string(argv[1]) + "baselines/", - std::string(argv[2]), "TestSDKDynamicFontFile", 50) + std::string(argv[2]), "TestSDKDynamicFontFile") ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/library/testing/TestSDKDynamicHDRI.cxx b/library/testing/TestSDKDynamicHDRI.cxx index e75a43c490..0d8f530d74 100644 --- a/library/testing/TestSDKDynamicHDRI.cxx +++ b/library/testing/TestSDKDynamicHDRI.cxx @@ -1,8 +1,8 @@ #include #include -#include #include #include +#include #include #include "TestSDKHelpers.h" @@ -13,16 +13,16 @@ int TestSDKDynamicHDRI(int argc, char* argv[]) { f3d::log::setVerboseLevel(f3d::log::VerboseLevel::INFO); - f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); + f3d::engine eng = f3d::engine::create(true); - f3d::loader& load = eng.getLoader(); + f3d::scene& sce = eng.getScene(); f3d::window& win = eng.getWindow(); f3d::options& opt = eng.getOptions(); win.setSize(300, 300); opt.ui.filename = true; opt.ui.filename_info = "(1/1) cow.vtp"; - load.loadGeometry(std::string(argv[1]) + "/data/cow.vtp"); + sce.add(std::string(argv[1]) + "/data/cow.vtp"); bool ret = win.render(); if (!ret) @@ -42,7 +42,7 @@ int TestSDKDynamicHDRI(int argc, char* argv[]) opt.render.hdri.ambient = true; opt.render.background.skybox = true; ret = TestSDKHelpers::RenderTest(eng.getWindow(), std::string(argv[1]) + "baselines/", - std::string(argv[2]), "TestSDKDynamicHDRIDefault", 120); + std::string(argv[2]), "TestSDKDynamicHDRIDefault"); if (!ret) { std::cerr << "Render with Default HDRI failed" << std::endl; @@ -52,7 +52,7 @@ int TestSDKDynamicHDRI(int argc, char* argv[]) // Change the hdri and make sure it is taken into account opt.render.hdri.file = std::string(argv[1]) + "data/palermo_park_1k.hdr"; ret = TestSDKHelpers::RenderTest(eng.getWindow(), std::string(argv[1]) + "baselines/", - std::string(argv[2]), "TestSDKDynamicHDRI", 50); + std::string(argv[2]), "TestSDKDynamicHDRI"); if (!ret) { std::cerr << "Render with HDRI failed" << std::endl; @@ -70,7 +70,7 @@ int TestSDKDynamicHDRI(int argc, char* argv[]) // Force a cache path change to force a LUT reconfiguration and test dynamic cache path eng.setCachePath(std::string(argv[2]) + "/cache_" + std::to_string(dist(e1))); ret = TestSDKHelpers::RenderTest(eng.getWindow(), std::string(argv[1]) + "baselines/", - std::string(argv[2]), "TestSDKDynamicHDRI", 50); + std::string(argv[2]), "TestSDKDynamicHDRI"); if (!ret) { std::cerr << "Render with HDRI with another cache path failed" << std::endl; @@ -80,7 +80,7 @@ int TestSDKDynamicHDRI(int argc, char* argv[]) // Use an existing cache eng.setCachePath(cachePath); ret = TestSDKHelpers::RenderTest(eng.getWindow(), std::string(argv[1]) + "baselines/", - std::string(argv[2]), "TestSDKDynamicHDRI", 50); + std::string(argv[2]), "TestSDKDynamicHDRI"); if (!ret) { std::cerr << "Render with HDRI with existing cache path failed" << std::endl; @@ -91,7 +91,7 @@ int TestSDKDynamicHDRI(int argc, char* argv[]) // Change the hdri and make sure it is taken into account opt.render.hdri.file = std::string(argv[1]) + "/data/kloofendal_43d_clear_1k.exr"; ret = TestSDKHelpers::RenderTest(eng.getWindow(), std::string(argv[1]) + "baselines/", - std::string(argv[2]), "TestSDKDynamicHDRIExr", 50); + std::string(argv[2]), "TestSDKDynamicHDRIExr"); if (!ret) { diff --git a/library/testing/TestSDKDynamicLightIntensity.cxx b/library/testing/TestSDKDynamicLightIntensity.cxx index bd9ac7e872..0b20da7c33 100644 --- a/library/testing/TestSDKDynamicLightIntensity.cxx +++ b/library/testing/TestSDKDynamicLightIntensity.cxx @@ -1,25 +1,25 @@ #include -#include #include +#include #include #include "TestSDKHelpers.h" int TestSDKDynamicLightIntensity(int argc, char* argv[]) { - f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); - f3d::loader& load = eng.getLoader(); + f3d::engine eng = f3d::engine::create(true); + f3d::scene& Scene = eng.getScene(); f3d::window& win = eng.getWindow(); f3d::options& opt = eng.getOptions(); win.setSize(300, 300); - load.loadGeometry(std::string(argv[1]) + "/data/cow.vtp"); + Scene.add(std::string(argv[1]) + "/data/cow.vtp"); win.render(); // Check render with default light intensity if (!TestSDKHelpers::RenderTest(eng.getWindow(), std::string(argv[1]) + "baselines/", - std::string(argv[2]), "TestSDKDynamicLightIntensity-default", 50)) + std::string(argv[2]), "TestSDKDynamicLightIntensity-default")) { std::cerr << "failed for default light intensity" << std::endl; return EXIT_FAILURE; @@ -28,7 +28,7 @@ int TestSDKDynamicLightIntensity(int argc, char* argv[]) // set light intensity to 5x brighter opt.render.light.intensity = 5.; if (!TestSDKHelpers::RenderTest(eng.getWindow(), std::string(argv[1]) + "baselines/", - std::string(argv[2]), "TestSDKDynamicLightIntensity-5x-brighter", 50)) + std::string(argv[2]), "TestSDKDynamicLightIntensity-5x-brighter")) { std::cerr << "failed for light intensity = 5" << std::endl; return EXIT_FAILURE; @@ -37,7 +37,7 @@ int TestSDKDynamicLightIntensity(int argc, char* argv[]) // set light intensity to 5x darker opt.render.light.intensity = .2; if (!TestSDKHelpers::RenderTest(eng.getWindow(), std::string(argv[1]) + "baselines/", - std::string(argv[2]), "TestSDKDynamicLightIntensity-5x-darker", 50)) + std::string(argv[2]), "TestSDKDynamicLightIntensity-5x-darker")) { std::cerr << "failed for light intensity = .2" << std::endl; return EXIT_FAILURE; diff --git a/library/testing/TestSDKDynamicProperties.cxx b/library/testing/TestSDKDynamicProperties.cxx index 1759267d06..263ada0095 100644 --- a/library/testing/TestSDKDynamicProperties.cxx +++ b/library/testing/TestSDKDynamicProperties.cxx @@ -1,21 +1,21 @@ #include -#include #include +#include #include #include "TestSDKHelpers.h" int TestSDKDynamicProperties(int argc, char* argv[]) { - f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); - f3d::loader& load = eng.getLoader(); + f3d::engine eng = f3d::engine::create(true); + f3d::scene& sce = eng.getScene(); f3d::window& win = eng.getWindow(); f3d::options& opt = eng.getOptions(); win.setSize(300, 300); opt.ui.filename = true; opt.ui.filename_info = "(1/1) cow.vtp"; - load.loadGeometry(std::string(argv[1]) + "/data/cow.vtp"); + sce.add(std::string(argv[1]) + "/data/cow.vtp"); win.render(); @@ -26,7 +26,7 @@ int TestSDKDynamicProperties(int argc, char* argv[]) opt.model.color.opacity = 0.6; return TestSDKHelpers::RenderTest(eng.getWindow(), std::string(argv[1]) + "baselines/", - std::string(argv[2]), "TestSDKDynamicProperties", 50) + std::string(argv[2]), "TestSDKDynamicProperties") ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/library/testing/TestSDKEngine.cxx b/library/testing/TestSDKEngine.cxx index 0df0aadcf8..7aed0478b3 100644 --- a/library/testing/TestSDKEngine.cxx +++ b/library/testing/TestSDKEngine.cxx @@ -1,8 +1,8 @@ #include #include -#include #include #include +#include #include #include @@ -22,16 +22,16 @@ int TestSDKEngine(int argc, char* argv[]) // clang-format on // Test different flags combinations that makes sense - f3d::engine eng0(f3d::window::Type::NONE); - const f3d::loader& load = eng0.getLoader(); + f3d::engine eng0 = f3d::engine::createNone(); + const f3d::scene& sce = eng0.getScene(); - f3d::engine eng1(f3d::window::Type::NATIVE); - const f3d::loader& load1 = eng1.getLoader(); + f3d::engine eng1 = f3d::engine::create(); + const f3d::scene& sce1 = eng1.getScene(); const f3d::window& window1 = eng1.getWindow(); const f3d::interactor& inter1 = eng1.getInteractor(); - f3d::engine eng2(f3d::window::Type::NATIVE_OFFSCREEN); - const f3d::loader& load2 = eng2.getLoader(); + f3d::engine eng2 = f3d::engine::create(true); + const f3d::scene& sce2 = eng2.getScene(); const f3d::window& window2 = eng2.getWindow(); const f3d::interactor& inter2 = eng2.getInteractor(); diff --git a/library/testing/TestSDKEngineExceptions.cxx b/library/testing/TestSDKEngineExceptions.cxx index 6ed24b2edb..f5f4dc60fb 100644 --- a/library/testing/TestSDKEngineExceptions.cxx +++ b/library/testing/TestSDKEngineExceptions.cxx @@ -1,13 +1,12 @@ #include #include -#include #include #include int TestSDKEngineExceptions(int argc, char* argv[]) { - f3d::engine eng(f3d::window::Type::NONE); + f3d::engine eng = f3d::engine::createNone(); try { @@ -32,5 +31,69 @@ int TestSDKEngineExceptions(int argc, char* argv[]) std::cout << ex.what() << std::endl; } +// These tests are defined for coverage +#ifdef __linux__ + try + { + eng = f3d::engine::createWGL(); + std::cerr << "An exception has not been thrown when creating a WGL engine on Linux" + << std::endl; + return EXIT_FAILURE; + } + catch (const f3d::context::loading_exception& ex) + { + std::cout << ex.what() << std::endl; + } + + try + { + eng = f3d::engine::createExternalWGL(); + std::cerr << "An exception has not been thrown when creating an external WGL engine on Linux" + << std::endl; + return EXIT_FAILURE; + } + catch (const f3d::context::loading_exception& ex) + { + std::cout << ex.what() << std::endl; + } + + try + { + eng = f3d::engine::createExternalCOCOA(); + std::cerr << "An exception has not been thrown when creating an external COCOA engine on Linux" + << std::endl; + return EXIT_FAILURE; + } + catch (const f3d::context::loading_exception& ex) + { + std::cout << ex.what() << std::endl; + } + + try + { + eng = f3d::engine::createExternal(f3d::context::getSymbol("invalid", "invalid")); + std::cerr << "An exception has not been thrown when loading an invalid library" << std::endl; + return EXIT_FAILURE; + } + catch (const f3d::context::loading_exception& ex) + { + std::cout << ex.what() << std::endl; + } + + try + { + eng = f3d::engine::createExternal(f3d::context::getSymbol("GLX", "invalid")); + std::cerr << "An exception has not been thrown when loading an invalid symbol" << std::endl; + return EXIT_FAILURE; + } + catch (const f3d::context::symbol_exception& ex) + { + std::cout << ex.what() << std::endl; + } + + // cover operator=(engine&&) + eng = f3d::engine::create(false); +#endif + return EXIT_SUCCESS; } diff --git a/library/testing/TestSDKExternalWindowCOCOA.mm b/library/testing/TestSDKExternalWindowCOCOA.mm new file mode 100644 index 0000000000..3f49de6e48 --- /dev/null +++ b/library/testing/TestSDKExternalWindowCOCOA.mm @@ -0,0 +1,63 @@ +#include "engine.h" + +#include "TestSDKHelpers.h" + +#import +#import + +@interface AppDelegate : NSObject +@property (strong) NSWindow *window; +@property (strong) NSOpenGLContext *openGLContext; +@property std::string dataPath; +@property std::string outPath; +@end + +@implementation AppDelegate + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + NSRect frame = NSMakeRect(100, 100, 300, 300); + self.window = [[NSWindow alloc] initWithContentRect:frame + styleMask:(NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskResizable) + backing:NSBackingStoreBuffered + defer:NO]; + [self.window setTitle:@"F3D COCOA Window Test"]; + + NSOpenGLPixelFormatAttribute attrs[] = { + NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core, + NSOpenGLPFAColorSize, 24, + NSOpenGLPFAAlphaSize, 8, + NSOpenGLPFADepthSize, 32, + 0 + }; + + NSOpenGLPixelFormat *pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; + self.openGLContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil]; + [self.openGLContext makeCurrentContext]; + + f3d::engine engine = f3d::engine::createExternal(nullptr); + engine.getWindow().setSize(300, 300); + engine.getScene().add(_dataPath + "/data/cow.vtp"); + + if (!TestSDKHelpers::RenderTest(engine.getWindow(), _dataPath + "baselines/", _outPath, "TestSDKExternalWindowCOCOA")) + { + exit(1); + } + + [NSApp performSelector:@selector(terminate:) withObject:nil afterDelay:0.1]; +} + +@end + +int TestSDKExternalWindowCOCOA(int argc, char* argv[]) +{ + @autoreleasepool { + NSApplication *app = [NSApplication sharedApplication]; + AppDelegate *delegate = [[AppDelegate alloc] init]; + delegate.dataPath = argv[1]; + delegate.outPath = argv[2]; + app.delegate = delegate; + + [app run]; + } + return 0; +} diff --git a/library/testing/TestSDKExternalWindowEGL.cxx b/library/testing/TestSDKExternalWindowEGL.cxx new file mode 100644 index 0000000000..fbc3dfe05e --- /dev/null +++ b/library/testing/TestSDKExternalWindowEGL.cxx @@ -0,0 +1,49 @@ +#include "engine.h" + +#include "TestSDKHelpers.h" + +#include + +int TestSDKExternalWindowEGL(int argc, char* argv[]) +{ + EGLDisplay eglDpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); + + // initialize + EGLint major, minor; + eglInitialize(eglDpy, &major, &minor); + + // configure + EGLint numConfigs; + EGLConfig eglCfg; + + constexpr EGLint configAttribs[] = { EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_BLUE_SIZE, 8, + EGL_GREEN_SIZE, 8, EGL_RED_SIZE, 8, EGL_DEPTH_SIZE, 8, EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_NONE }; + eglChooseConfig(eglDpy, configAttribs, &eglCfg, 1, &numConfigs); + + // create a surface + constexpr int size[] = { 300, 300 }; + constexpr EGLint pbufferAttribs[] = { EGL_WIDTH, size[0], EGL_HEIGHT, size[1], EGL_NONE }; + EGLSurface eglSurf = eglCreatePbufferSurface(eglDpy, eglCfg, pbufferAttribs); + + // bind the API + eglBindAPI(EGL_OPENGL_API); + + // create a context + EGLContext eglCtx = eglCreateContext(eglDpy, eglCfg, EGL_NO_CONTEXT, nullptr); + eglMakeCurrent(eglDpy, eglSurf, eglSurf, eglCtx); + + f3d::engine eng = f3d::engine::createExternalEGL(); + eng.getWindow().setSize(size[0], size[1]); + eng.getScene().add(std::string(argv[1]) + "/data/cow.vtp"); + + if (!TestSDKHelpers::RenderTest( + eng.getWindow(), std::string(argv[1]) + "baselines/", argv[2], "TestSDKExternalWindowEGL")) + { + return EXIT_FAILURE; + } + + // terminate EGL when finished + eglTerminate(eglDpy); + return 0; +} diff --git a/library/testing/TestSDKExternalWindowGLFW.cxx b/library/testing/TestSDKExternalWindowGLFW.cxx index dafe686cde..8059bda05d 100644 --- a/library/testing/TestSDKExternalWindowGLFW.cxx +++ b/library/testing/TestSDKExternalWindowGLFW.cxx @@ -1,5 +1,5 @@ #include -#include +#include #include #include "TestSDKHelpers.h" @@ -10,10 +10,6 @@ int TestSDKExternalWindowGLFW(int argc, char* argv[]) { - // create engine and load file - f3d::engine eng(f3d::window::Type::EXTERNAL); - eng.getLoader().loadGeometry(std::string(argv[1]) + "/data/cow.vtp"); - // setup glfw window if (!glfwInit()) { @@ -37,7 +33,9 @@ int TestSDKExternalWindowGLFW(int argc, char* argv[]) } glfwMakeContextCurrent(window); - glfwSetWindowUserPointer(window, &eng); + + f3d::engine eng = f3d::engine::createExternal(glfwGetProcAddress); + eng.getWindow().setSize(300, 300); // key callback glfwSetKeyCallback(window, [](GLFWwindow* window, int key, int scancode, int action, int mods) { @@ -47,6 +45,8 @@ int TestSDKExternalWindowGLFW(int argc, char* argv[]) } }); + eng.getScene().add(std::string(argv[1]) + "/data/cow.vtp"); + while (!glfwWindowShouldClose(window) && glfwGetTime() < 1.0) { eng.getWindow().render(); diff --git a/library/testing/TestSDKExternalWindowGLX.cxx b/library/testing/TestSDKExternalWindowGLX.cxx new file mode 100644 index 0000000000..cc05727142 --- /dev/null +++ b/library/testing/TestSDKExternalWindowGLX.cxx @@ -0,0 +1,104 @@ +// Copyright 2008 Arne Reiners +#include "engine.h" + +#include "TestSDKHelpers.h" + +#include +#include + +#include +#include +#include +#include +#include + +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 +using glXCreateContextAttribsARBProc = GLXContext (*)( + Display*, GLXFBConfig, GLXContext, Bool, const int*); + +int TestSDKExternalWindowGLX(int argc, char* argv[]) +{ + Display* display = XOpenDisplay(nullptr); + + glXCreateContextAttribsARBProc glXCreateContextAttribsARB = nullptr; + + const char* extensions = glXQueryExtensionsString(display, DefaultScreen(display)); + std::cout << extensions << std::endl; + + static int visual_attribs[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + GLX_DOUBLEBUFFER, true, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, None }; + + std::cout << "Getting framebuffer config" << std::endl; + int fbcount; + GLXFBConfig* fbc = glXChooseFBConfig(display, DefaultScreen(display), visual_attribs, &fbcount); + if (!fbc) + { + std::cerr << "Failed to retrieve a framebuffer config" << std::endl; + return EXIT_FAILURE; + } + + std::cout << "Getting XVisualInfo" << std::endl; + XVisualInfo* vi = glXGetVisualFromFBConfig(display, fbc[0]); + + XSetWindowAttributes swa; + std::cout << "Creating colormap" << std::endl; + swa.colormap = XCreateColormap(display, RootWindow(display, vi->screen), vi->visual, AllocNone); + swa.border_pixel = 0; + swa.event_mask = StructureNotifyMask; + + std::cout << "Creating window" << std::endl; + Window win = XCreateWindow(display, RootWindow(display, vi->screen), 0, 0, 100, 100, 0, vi->depth, + InputOutput, vi->visual, CWBorderPixel | CWColormap | CWEventMask, &swa); + if (!win) + { + std::cerr << "Failed to create window." << std::endl; + return EXIT_FAILURE; + } + + std::cout << "Mapping window" << std::endl; + XMapWindow(display, win); + + // Create an oldstyle context first, to get the correct function pointer for + // glXCreateContextAttribsARB + GLXContext ctx_old = glXCreateContext(display, vi, nullptr, GL_TRUE); + glXCreateContextAttribsARB = + (glXCreateContextAttribsARBProc)glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB"); + glXMakeCurrent(display, 0, nullptr); + glXDestroyContext(display, ctx_old); + + if (glXCreateContextAttribsARB == nullptr) + { + std::cerr << "glXCreateContextAttribsARB entry point not found. Aborting." << std::endl; + return EXIT_FAILURE; + } + + static int context_attribs[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, + 2, None }; + + std::cout << "Creating context" << std::endl; + GLXContext ctx = glXCreateContextAttribsARB(display, fbc[0], nullptr, true, context_attribs); + if (!ctx) + { + std::cerr << "Failed to create GL3 context." << std::endl; + return EXIT_FAILURE; + } + + std::cout << "Making context current" << std::endl; + glXMakeCurrent(display, win, ctx); + + f3d::engine eng = f3d::engine::createExternalGLX(); + eng.getWindow().setSize(300, 300); + eng.getScene().add(std::string(argv[1]) + "/data/cow.vtp"); + + if (!TestSDKHelpers::RenderTest( + eng.getWindow(), std::string(argv[1]) + "baselines/", argv[2], "TestSDKExternalWindowGLX")) + { + return EXIT_FAILURE; + } + + ctx = glXGetCurrentContext(); + glXMakeCurrent(display, 0, nullptr); + glXDestroyContext(display, ctx); + return EXIT_SUCCESS; +} diff --git a/library/testing/TestSDKExternalWindowOSMesa.cxx b/library/testing/TestSDKExternalWindowOSMesa.cxx new file mode 100644 index 0000000000..e6b1d88f01 --- /dev/null +++ b/library/testing/TestSDKExternalWindowOSMesa.cxx @@ -0,0 +1,43 @@ +#include "engine.h" + +#include "TestSDKHelpers.h" + +#include + +int TestSDKExternalWindowOSMesa(int argc, char* argv[]) +{ + int size[] = { 300, 300 }; + + // Create an RGBA buffer to hold the rendered image + std::vector buffer(size[0] * size[1] * 4); + + // Create an OSMesa context + OSMesaContext ctx = OSMesaCreateContext(OSMESA_RGBA, nullptr); + if (!ctx) + { + std::cerr << "OSMesa context creation failed!\n"; + return EXIT_FAILURE; + } + + // Bind the buffer to the context + if (!OSMesaMakeCurrent(ctx, buffer.data(), GL_UNSIGNED_BYTE, size[0], size[1])) + { + std::cerr << "OSMesaMakeCurrent failed!\n"; + OSMesaDestroyContext(ctx); + return EXIT_FAILURE; + } + + f3d::engine eng = f3d::engine::createExternalOSMesa(); + eng.getWindow().setSize(size[0], size[1]); + eng.getScene().add(std::string(argv[1]) + "/data/cow.vtp"); + + if (!TestSDKHelpers::RenderTest(eng.getWindow(), std::string(argv[1]) + "baselines/", argv[2], + "TestSDKExternalWindowOSMesa")) + { + OSMesaDestroyContext(ctx); + return EXIT_FAILURE; + } + + OSMesaDestroyContext(ctx); + return EXIT_SUCCESS; +} diff --git a/library/testing/TestSDKExternalWindowQT.cxx b/library/testing/TestSDKExternalWindowQT.cxx index f32e9755e5..6dc08c8cba 100644 --- a/library/testing/TestSDKExternalWindowQT.cxx +++ b/library/testing/TestSDKExternalWindowQT.cxx @@ -1,5 +1,5 @@ #include -#include +#include #include #include @@ -13,14 +13,12 @@ class F3DWindow : public QOpenGLWindow { public: - F3DWindow(const std::string& filePath, std::string baselinePath, std::string outputPath) + F3DWindow(std::string filePath, std::string baselinePath, std::string outputPath) : QOpenGLWindow() - , mEngine(f3d::window::Type::EXTERNAL) + , mFilePath(std::move(filePath)) , mBaselinePath(std::move(baselinePath)) , mOutputPath(std::move(outputPath)) { - f3d::loader& load = mEngine.getLoader(); - load.loadGeometry(filePath); } protected: @@ -49,13 +47,26 @@ class F3DWindow : public QOpenGLWindow this->close(); } + void initializeGL() override + { + this->QOpenGLWindow::initializeGL(); + + f3d::context::function loadFunc = [this](const char* name) { + return this->context()->getProcAddress(name); + }; + + mEngine = std::make_unique(f3d::engine::createExternal(loadFunc)); + mEngine->getScene().add(mFilePath); + } + void paintGL() override { - mEngine.getWindow().render(); + mEngine->getWindow().render(); } private: - f3d::engine mEngine; + std::unique_ptr mEngine; + std::string mFilePath; std::string mBaselinePath; std::string mOutputPath; }; @@ -68,9 +79,9 @@ int TestSDKExternalWindowQT(int argc, char* argv[]) std::string(argv[1]) + "/data/cow.vtp", std::string(argv[1]) + "/baselines/", argv[2]); w.setTitle("F3D QT External Window"); w.resize(300, 300); - w.startTimer(1000); - w.show(); + w.startTimer(1000); + return a.exec(); } diff --git a/library/testing/TestSDKExternalWindowWGL.cxx b/library/testing/TestSDKExternalWindowWGL.cxx new file mode 100644 index 0000000000..1e2c24cacc --- /dev/null +++ b/library/testing/TestSDKExternalWindowWGL.cxx @@ -0,0 +1,88 @@ +#include "engine.h" + +#include "TestSDKHelpers.h" + +#include + +#include + +std::unique_ptr engine; + +LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_PAINT: + engine->getWindow().render(); + ValidateRect(hwnd, nullptr); + return 0; + case WM_DESTROY: + PostQuitMessage(0); + return 0; + } + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +int TestSDKExternalWindowWGL(int argc, char* argv[]) +{ + HINSTANCE hInstance = GetModuleHandle(NULL); + + // Define window class + const char* className = "F3DWin32Class"; + + WNDCLASS wc = {}; + wc.lpfnWndProc = WindowProc; + wc.hInstance = hInstance; + wc.lpszClassName = className; + + // Register window class + RegisterClass(&wc); + + // Create window + constexpr int size[] = { 300, 300 }; + HWND hwnd = CreateWindowEx(0, className, "F3D Win32 Example", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, + CW_USEDEFAULT, size[0], size[1], nullptr, nullptr, hInstance, nullptr); + + if (hwnd == nullptr) + { + return 0; + } + + ShowWindow(hwnd, SW_HIDE); + + // Initialize OpenGL + HDC hDC = GetDC(hwnd); + + PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1 }; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 32; + pfd.cDepthBits = 24; + pfd.iLayerType = PFD_MAIN_PLANE; + + int pixelFormat = ChoosePixelFormat(hDC, &pfd); + SetPixelFormat(hDC, pixelFormat, &pfd); + + // Create and enable the OpenGL rendering context (RC) + HGLRC hGLRC = wglCreateContext(hDC); + wglMakeCurrent(hDC, hGLRC); + + engine = std::make_unique(f3d::engine::createExternalWGL()); + engine->getWindow().setSize(size[0], size[1]); + engine->getScene().add(std::string(argv[1]) + "/data/cow.vtp"); + + if (!TestSDKHelpers::RenderTest(engine->getWindow(), std::string(argv[1]) + "baselines/", argv[2], + "TestSDKExternalWindowWGL")) + { + return EXIT_FAILURE; + } + + // Disable the RC and delete it + wglMakeCurrent(nullptr, nullptr); + wglDeleteContext(hGLRC); + + // Release the DC + ReleaseDC(hwnd, hDC); + + return 0; +} diff --git a/library/testing/TestSDKHelpers.h b/library/testing/TestSDKHelpers.h index ac1d2ae3cf..70da1f484d 100644 --- a/library/testing/TestSDKHelpers.h +++ b/library/testing/TestSDKHelpers.h @@ -54,7 +54,7 @@ static bool RenderTest(const f3d::image& img, const std::string& baselinePath, } static bool RenderTest(f3d::window& win, const std::string& baselinePath, - const std::string& outputPath, const std::string& name, double threshold = 50, + const std::string& outputPath, const std::string& name, double threshold = 0.05, bool noBackground = false) { return TestSDKHelpers::RenderTest( diff --git a/library/testing/TestSDKImage.cxx b/library/testing/TestSDKImage.cxx index c5bf99683f..c3e379c830 100644 --- a/library/testing/TestSDKImage.cxx +++ b/library/testing/TestSDKImage.cxx @@ -1,3 +1,5 @@ +#include "PseudoUnitTest.h" + #include #include @@ -11,24 +13,16 @@ int TestSDKImage(int argc, char* argv[]) { + PseudoUnitTest test; + const std::string testingDir(argv[1]); const std::string tmpDir(argv[2]); // check supported formats std::vector formats = f3d::image::getSupportedFormats(); - - if (std::find(formats.begin(), formats.end(), ".png") == formats.end()) - { - std::cerr << "PNG is not in the list of supported files" << std::endl; - return EXIT_FAILURE; - } - + test("supported formats PNG", std::find(formats.begin(), formats.end(), ".png") != formats.end()); #if F3D_MODULE_EXR - if (std::find(formats.begin(), formats.end(), ".exr") == formats.end()) - { - std::cerr << "EXR is not in the list of supported files" << std::endl; - return EXIT_FAILURE; - } + test("supported formats EXR", std::find(formats.begin(), formats.end(), ".exr") != formats.end()); #endif constexpr unsigned int width = 64; @@ -69,187 +63,72 @@ int TestSDKImage(int argc, char* argv[]) // test saveBuffer in different formats std::vector bufferPNG = generated.saveBuffer(); - if (bufferPNG.size() == 0) - { - std::cerr << "PNG buffer empty" << std::endl; - return EXIT_FAILURE; - } + test("generated buffer not empty", bufferPNG.size() != 0); std::vector bufferJPG = generated.saveBuffer(f3d::image::SaveFormat::JPG); - if (bufferJPG.size() == 0) - { - std::cerr << "JPG buffer empty" << std::endl; - return EXIT_FAILURE; - } + test("generated JPG buffer not empty", bufferJPG.size() != 0); - try - { - generated.saveBuffer(f3d::image::SaveFormat::TIF); - std::cerr << "An exception has not been thrown when saving buffer to TIF format" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::image::write_exception&) - { - } + test.expect("save incompatible buffer to TIF format", + [&]() { generated.saveBuffer(f3d::image::SaveFormat::TIF); }); std::vector bufferBMP = generated.saveBuffer(f3d::image::SaveFormat::BMP); - if (bufferBMP.size() == 0) - { - std::cerr << "BMP buffer empty" << std::endl; - return EXIT_FAILURE; - } + test("generated BMP buffer not empty", bufferBMP.size() != 0); // test constructor with different channel sizes f3d::image img16(width, height, channels, f3d::image::ChannelType::SHORT); f3d::image img32(width, height, channels, f3d::image::ChannelType::FLOAT); // test exceptions - try - { - generated.save("/dummy/folder/img.png"); - std::cerr << "An exception has not been thrown when saving to an incorrect path" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::image::write_exception&) - { - } - try - { - img16.saveBuffer(f3d::image::SaveFormat::BMP); - std::cerr - << "An exception has not been thrown when saving to BMP format with an incompatible type" - << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::image::write_exception&) - { - } - try - { - img32.saveBuffer(f3d::image::SaveFormat::PNG); - std::cerr - << "An exception has not been thrown when saving to PNG format with an incompatible type" - << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::image::write_exception&) - { - } + test.expect( + "save buffer to incorrect path", [&]() { generated.save("/dummy/folder/img.png"); }); + test.expect("save incompatible buffer to BMP format", + [&]() { img16.saveBuffer(f3d::image::SaveFormat::BMP); }); + test.expect("save incompatible buffer to PNG format", + [&]() { img32.saveBuffer(f3d::image::SaveFormat::PNG); }); f3d::image img2Ch(4, 4, 2); f3d::image img5Ch(4, 4, 5); - try - { - img5Ch.saveBuffer(f3d::image::SaveFormat::BMP); - std::cerr << "An exception has not been thrown when saving to BMP format with an incompatible " - "channel count" - << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::image::write_exception&) - { - } - try - { - img2Ch.saveBuffer(f3d::image::SaveFormat::JPG); - std::cerr - << "An exception has not been thrown when saving to PNG format with an incompatible type" - << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::image::write_exception&) - { - } - try - { - f3d::image img("/dummy/folder/img.png"); + test.expect("save incompatible channel count to BMP format", + [&]() { img5Ch.saveBuffer(f3d::image::SaveFormat::BMP); }); + test.expect("save incompatible channel count to JPG format", + [&]() { img2Ch.saveBuffer(f3d::image::SaveFormat::JPG); }); - std::cerr << "An exception has not been thrown when reading an incorrect path" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::image::read_exception&) - { - } + test.expect( + "read image from incorrect path", [&]() { f3d::image img("/dummy/folder/img.png"); }); // check 16-bits image code paths f3d::image shortImg(testingDir + "/data/16bit.png"); - - if (shortImg.getChannelType() != f3d::image::ChannelType::SHORT) - { - std::cerr << "Cannot read a 16-bits image type" << std::endl; - return EXIT_FAILURE; - } - - if (shortImg.getChannelTypeSize() != 2) - { - std::cerr << "Cannot read a 16-bits image type size" << std::endl; - return EXIT_FAILURE; - } + test("check 16-bits image channel type", + shortImg.getChannelType() == f3d::image::ChannelType::SHORT); + test("check 16-bits image channel type size", shortImg.getChannelTypeSize() == 2); // check reading a 32-bits image f3d::image hdrImg(testingDir + "/data/palermo_park_1k.hdr"); - - if (hdrImg.getChannelType() != f3d::image::ChannelType::FLOAT) - { - std::cerr << "Cannot read a HDR 32-bits image" << std::endl; - return EXIT_FAILURE; - } - - if (hdrImg.getChannelTypeSize() != 4) - { - std::cerr << "Cannot read a HDR 32-bits image type size" << std::endl; - return EXIT_FAILURE; - } + test("check 32-bits HDR image channel type", + hdrImg.getChannelType() == f3d::image::ChannelType::FLOAT); + test("check 32-bits HDR image channel type size", hdrImg.getChannelTypeSize() == 4); hdrImg.save(tmpDir + "/TestSDKImage32hdr.tif", f3d::image::SaveFormat::TIF); #if F3D_MODULE_EXR // check reading EXR f3d::image exrImg(testingDir + "/data/kloofendal_43d_clear_1k.exr"); - - if (exrImg.getChannelType() != f3d::image::ChannelType::FLOAT) - { - std::cerr << "Cannot read a EXR 32-bits image" << std::endl; - return EXIT_FAILURE; - } + test("check 32-bits EXR image channel type", + exrImg.getChannelType() == f3d::image::ChannelType::FLOAT); #endif // check reading invalid image - try - { - f3d::image invalidImg(testingDir + "/data/invalid.png"); - - std::cerr << "An exception has not been thrown when reading an invalid file" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::image::read_exception&) - { - } + test.expect( + "read invalid image", [&]() { f3d::image invalidImg(testingDir + "/data/invalid.png"); }); // check generated image with baseline - if (generated.getWidth() != width || generated.getHeight() != height) - { - std::cerr << "Image has wrong dimensions" << std::endl; - return EXIT_FAILURE; - } - - if (generated.getChannelCount() != channels) - { - std::cerr << "Image has wrong number of channels" << std::endl; - return EXIT_FAILURE; - } - - if (generated.getChannelType() != f3d::image::ChannelType::BYTE) - { - std::cerr << "Image has wrong channel size" << std::endl; - return EXIT_FAILURE; - } - - if (generated.getContent() == nullptr) - { - std::cerr << "Image has no data" << std::endl; - return EXIT_FAILURE; - } - + test( + "check generated image size", generated.getWidth() == width && generated.getHeight() == height); + test("check generated image channel count", generated.getChannelCount() == channels); + test("check generated image channel type", + generated.getChannelType() == f3d::image::ChannelType::BYTE); + test("check generated image not empty", generated.getContent() != nullptr); + + // XXX: PseudoUnitTest could be improved for native image testing f3d::image baseline(testingDir + "/baselines/TestSDKImage.png"); if (generated != baseline) { @@ -275,29 +154,12 @@ int TestSDKImage(int argc, char* argv[]) // Remove this once VTK 9.3 support is removed #ifdef F3D_SSIM_COMPARE // check generated short image with baseline - if (generated16.getWidth() != width || generated16.getHeight() != height) - { - std::cerr << "Short image has wrong dimensions" << std::endl; - return EXIT_FAILURE; - } - - if (generated16.getChannelCount() != channels) - { - std::cerr << "Short image has wrong number of channels" << std::endl; - return EXIT_FAILURE; - } - - if (generated16.getChannelType() != f3d::image::ChannelType::SHORT) - { - std::cerr << "Short image has wrong channel size" << std::endl; - return EXIT_FAILURE; - } - - if (generated16.getContent() == nullptr) - { - std::cerr << "Short image has no data" << std::endl; - return EXIT_FAILURE; - } + test("check generated short image size", + generated16.getWidth() == width && generated16.getHeight() == height); + test("check generated short image channel count", generated16.getChannelCount() == channels); + test("check generated short image channel type", + generated16.getChannelType() == f3d::image::ChannelType::SHORT); + test("check generated short image not empty", generated16.getContent() != nullptr); f3d::image baseline16(testingDir + "/baselines/TestSDKImage16.png"); if (generated16 != baseline16) @@ -325,29 +187,13 @@ int TestSDKImage(int argc, char* argv[]) // XXX: Uncomment once https://github.com/f3d-app/f3d/issues/1558 is fixed // f3d::image baseline32(testingDir + "/baselines/TestSDKImage32.tif"); f3d::image baseline32 = generated32; - if (generated32.getWidth() != width || generated32.getHeight() != height) - { - std::cerr << "Float image has wrong dimensions" << std::endl; - return EXIT_FAILURE; - } - if (generated32.getChannelCount() != channels) - { - std::cerr << "Float image has wrong number of channels" << std::endl; - return EXIT_FAILURE; - } - - if (generated32.getChannelType() != f3d::image::ChannelType::FLOAT) - { - std::cerr << "Float image has wrong channel size" << std::endl; - return EXIT_FAILURE; - } - - if (generated32.getContent() == nullptr) - { - std::cerr << "Float image has no data" << std::endl; - return EXIT_FAILURE; - } + test("check generated float image size", + generated32.getWidth() == width && generated32.getHeight() == height); + test("check generated float image channel count", generated32.getChannelCount() == channels); + test("check generated float image channel type", + generated32.getChannelType() == f3d::image::ChannelType::FLOAT); + test("check generated float image not empty", generated32.getContent() != nullptr); if (generated32 != baseline32) { @@ -361,59 +207,23 @@ int TestSDKImage(int argc, char* argv[]) // test operators f3d::image imgCopy = generated; // copy constructor - - if (imgCopy != generated) - { - std::cerr << "Copy constructor failed" << std::endl; - return EXIT_FAILURE; - } + test("check copy constructor", imgCopy == generated); imgCopy = baseline; // copy assignment - - if (imgCopy != baseline) - { - std::cerr << "Copy assignment failed" << std::endl; - return EXIT_FAILURE; - } + test("check copy assignment", imgCopy == baseline); f3d::image imgMove = std::move(imgCopy); // move constructor - - if (imgMove != baseline) - { - std::cerr << "Move constructor failed" << std::endl; - return EXIT_FAILURE; - } + test("check move constructor", imgMove == baseline); imgCopy = std::move(imgMove); // move assignment + test("check move assignment", imgCopy == baseline); - if (imgCopy != baseline) + // test toTerminalText { - std::cerr << "Move assignment failed" << std::endl; - return EXIT_FAILURE; - } - - { - try - { - f3d::image(3, 3, 1, f3d::image::ChannelType::BYTE).toTerminalText(); - std::cerr << "An exception has not been thrown when using toTerminalText with BYTE" - << std::endl; - return EXIT_FAILURE; // expected to throw (wrong channel count) - } - catch (const f3d::image::write_exception&) - { - } - - try - { - f3d::image(3, 3, 4, f3d::image::ChannelType::SHORT).toTerminalText(); - std::cerr << "An exception has not been thrown when using toTerminalText with SHORT" - << std::endl; - return EXIT_FAILURE; // expected to throw (wrong channel type) - } - catch (const f3d::image::write_exception&) - { - } + test.expect("invalid toTerminalText with BYTE", + [&]() { f3d::image(3, 3, 1, f3d::image::ChannelType::BYTE).toTerminalText(); }); + test.expect("invalid toTerminalText with SHORT", + [&]() { f3d::image(3, 3, 4, f3d::image::ChannelType::SHORT).toTerminalText(); }); const auto fileToString = [](const std::string& path) { std::ifstream file(path); @@ -422,68 +232,36 @@ int TestSDKImage(int argc, char* argv[]) return ss.str(); }; - if (f3d::image(testingDir + "/data/toTerminalText-rgb.png").toTerminalText() != - fileToString(testingDir + "/data/toTerminalText-rgb.txt")) - { - std::cerr << "toTerminalText() (RGB image) failed" << std::endl; - return EXIT_FAILURE; - } - - if (f3d::image(testingDir + "/data/toTerminalText-rgba.png").toTerminalText() != - fileToString(testingDir + "/data/toTerminalText-rgba.txt")) - { - std::cerr << "toTerminalText() (RGBA image) failed" << std::endl; - return EXIT_FAILURE; - } + test("toTerminalText with RGB image", + f3d::image(testingDir + "/data/toTerminalText-rgb.png").toTerminalText() == + fileToString(testingDir + "/data/toTerminalText-rgb.txt")); + test("toTerminalText with RGBA image", + f3d::image(testingDir + "/data/toTerminalText-rgba.png").toTerminalText() == + fileToString(testingDir + "/data/toTerminalText-rgba.txt")); } + // test metadata { + f3d::image img(4, 2, 3); img.setMetadata("foo", "bar"); img.setMetadata("hello", "world"); - if (img.getMetadata("foo") != "bar" || img.getMetadata("hello") != "world") - { - std::cerr << "setMetadata() or getMetadata() failed" << std::endl; - return EXIT_FAILURE; - } + test("check metadata", img.getMetadata("foo") == "bar" && img.getMetadata("hello") == "world"); const std::vector keys = img.allMetadata(); - if (std::set(keys.begin(), keys.end()) != - std::set({ "foo", "hello" })) - { - std::cerr << "allMetadata() failed" << std::endl; - return EXIT_FAILURE; - } + test("check all metadata", + std::set(keys.begin(), keys.end()) == std::set({ "foo", "hello" })); - try - { - img.getMetadata("baz"); // expected to throw - std::cerr << "getMetadata() failed to throw" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::image::metadata_exception&) - { - /* expected, key doesn't exist */ - } + test.expect( + "invalid get metadata", [&]() { img.getMetadata("baz"); }); - try - { + test.expect("remove and get metadata", [&]() { img.setMetadata("foo", ""); // empty value, should remove key img.getMetadata("foo"); // expected to throw - std::cerr << "setMetadata() with empty value failed" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::image::metadata_exception&) - { - /* expected, key has been removed */ - } - - if (img.allMetadata() != std::vector({ "hello" })) - { - std::cerr << "allMetadata() failed" << std::endl; - return EXIT_FAILURE; - } + }); + test( + "check all metata after removal", img.allMetadata() == std::vector({ "hello" })); img.setMetadata("foo", ""); // make sure removing twice is ok } @@ -494,11 +272,8 @@ int TestSDKImage(int argc, char* argv[]) img1.save(tmpDir + "/metadata.png"); f3d::image img2(tmpDir + "/metadata.png"); - if (img2.getMetadata("foo") != "bar" || img2.getMetadata("hello") != "world") - { - std::cerr << "saving or loading file metadata failed" << std::endl; - return EXIT_FAILURE; - } + test("saving/loading file metadata", + img2.getMetadata("foo") == "bar" && img2.getMetadata("hello") == "world"); } { @@ -512,41 +287,27 @@ int TestSDKImage(int argc, char* argv[]) } f3d::image img2(tmpDir + "/metadata-buffer.png"); - if (img2.getMetadata("foo") != "bar" || img2.getMetadata("hello") != "world") - { - std::cerr << "saving or loading buffer metadata failed" << std::endl; - return EXIT_FAILURE; - } + test("saving/loading buffer metadata", + img2.getMetadata("foo") == "bar" && img2.getMetadata("hello") == "world"); } // Test image::compare dedicated code paths double error; - if (generated.compare(generated16, 0, error) || error != 1.) - { - std::cerr << "Unexpected result when comparing image with different channel types" << std::endl; - return EXIT_FAILURE; - } + test("compare images with different channel types", + !generated.compare(generated16, 0, error) && error == 1.); f3d::image generatedCount(width, height, channels + 1); - if (generated.compare(generatedCount, 0, error) || error != 1.) - { - std::cerr << "Unexpected result when comparing image with different channel count" << std::endl; - return EXIT_FAILURE; - } + test("compare images with different channel count", + !generated.compare(generatedCount, 0, error) && error == 1.); f3d::image generatedSize(width + 1, height, channels); - if (generated.compare(generatedSize, 0, error) || error != 1.) - { - std::cerr << "Unexpected result when comparing image with different sizes" << std::endl; - return EXIT_FAILURE; - } + test("compare images with different size", + !generated.compare(generatedSize, 0, error) && error == 1.); f3d::image empty(0, 0, 0); - if (!empty.compare(empty, 0, error) || error != 0.) - { - std::cerr << "Unexpected result when comparing empty images" << std::endl; - return EXIT_FAILURE; - } + test("compare empty images", empty.compare(empty, 0, error) && error == 0.); + test("compare with negative threshold", !empty.compare(empty, -1, error) && error == 1.); + test("compare with threshold == 1", !empty.compare(empty, 1, error) && error == 1.); return EXIT_SUCCESS; } diff --git a/library/testing/TestSDKInteractorCallBack.cxx b/library/testing/TestSDKInteractorCallBack.cxx index 21ed9b85b8..9a74d1a98a 100644 --- a/library/testing/TestSDKInteractorCallBack.cxx +++ b/library/testing/TestSDKInteractorCallBack.cxx @@ -1,7 +1,7 @@ #include #include -#include #include +#include #include #include @@ -10,9 +10,8 @@ int TestSDKInteractorCallBack(int argc, char* argv[]) { - f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); - f3d::options& options = eng.getOptions(); - f3d::loader& load = eng.getLoader(); + f3d::engine eng = f3d::engine::create(true); + f3d::scene& sce = eng.getScene(); f3d::window& win = eng.getWindow(); f3d::interactor& inter = eng.getInteractor(); win.setSize(300, 300); @@ -29,36 +28,71 @@ int TestSDKInteractorCallBack(int argc, char* argv[]) return EXIT_FAILURE; } - // Test callbacks + // Test callbacks with default interactions std::string filename = "TestSDKInteractorCallBack"; - std::string interactionFilePath = std::string(argv[2]) + "../../" + filename + ".log"; - inter.playInteraction(interactionFilePath); // Dragon.vtu; S + // Dragon.vtu; SYB; CTRL+S; CTRL+P; SHIFT+Y; CTRL+SHIFT+B; CTRL+SHIFT+A; 7 + if (!inter.playInteraction(interactionFilePath)) + { + std::cerr << "Unexcepted error playing interaction" << std::endl; + return EXIT_FAILURE; + } + if (!TestSDKHelpers::RenderTest( + win, std::string(argv[1]) + "baselines/", std::string(argv[2]), filename + "Default")) + { + return EXIT_FAILURE; + } - inter.setKeyPressCallBack([&](int, const std::string& keySym) -> bool { - if (keySym == "S") - { - options.interactor.axis = true; - win.render(); - return true; - } - return false; - }); + // Remove interactions that will be triggered later and should not have any effect + // Do not remove S as it will be replaced below + inter.removeInteractionCommands("7", f3d::interactor::ModifierKeys::ANY); + inter.removeInteractionCommands("Y", f3d::interactor::ModifierKeys::NONE); + inter.removeInteractionCommands("B", f3d::interactor::ModifierKeys::NONE); + + // Check that an interaction can be added and that it removes existing interaction + inter.addInteractionCommand("S", f3d::interactor::ModifierKeys::NONE, "toggle interactor.axis"); - inter.setDropFilesCallBack([&](std::vector filesVec) -> bool { - std::string path = filesVec[0]; + // Check CTRL modifier and that another interaction can be added on the same key with another + // modifier + inter.addInteractionCommand( + "S", f3d::interactor::ModifierKeys::CTRL, "toggle render.grid.enable"); + + // Check invalid command for coverage + inter.addInteractionCommand("P", f3d::interactor::ModifierKeys::CTRL, "invalid command"); + + // Check SHIFT modifier + inter.addInteractionCommand( + "Y", f3d::interactor::ModifierKeys::SHIFT, R"(set ui.filename_info "My Own Filename")"); + + // Check CTRL_SHIFT modifier + inter.addInteractionCommands("B", f3d::interactor::ModifierKeys::CTRL_SHIFT, + { "set ui.filename true", "set render.show_edges true" }); + + // Check ANY modifier + inter.addInteractionCommand( + "A", f3d::interactor::ModifierKeys::ANY, "toggle render.background.skybox"); + + // Modify the add_files command + inter.addCommandCallback("add_files", [&](const std::vector& filesVec) -> bool { + const std::string& path = filesVec[0]; size_t found = path.find_last_of("/\\"); - load.loadGeometry(path.substr(0, found + 1) + "suzanne.ply", true); - win.render(); + sce.clear(); + sce.add(path.substr(0, found + 1) + "suzanne.ply"); return true; }); // This time the interaction should result in a different rendering - inter.playInteraction(interactionFilePath); + // Dragon.vtu; SYB; CTRL+S; CTRL+P; SHIFT+Y; CTRL+SHIFT+B; CTRL+SHIFT+A; 7 + if (!inter.playInteraction(interactionFilePath)) + { + std::cerr << "Unexcepted error playing interaction" << std::endl; + return EXIT_FAILURE; + } - return TestSDKHelpers::RenderTest( - win, std::string(argv[1]) + "baselines/", std::string(argv[2]), filename, 50) + // With VTK 9.2.6 and 9.3.0, rendering is slightly different + return TestSDKHelpers::RenderTest(win, std::string(argv[1]) + "baselines/", std::string(argv[2]), + filename + "Modified", 0.11) ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/library/testing/TestSDKInteractorCommand.cxx b/library/testing/TestSDKInteractorCommand.cxx new file mode 100644 index 0000000000..ba319e0fbf --- /dev/null +++ b/library/testing/TestSDKInteractorCommand.cxx @@ -0,0 +1,79 @@ +#include "PseudoUnitTest.h" + +#include +#include +#include + +int TestSDKInteractorCommand(int argc, char* argv[]) +{ + f3d::engine eng = f3d::engine::create(true); + f3d::options& options = eng.getOptions(); + f3d::interactor& inter = eng.getInteractor(); + + PseudoUnitTest test; + + // Test `set` + inter.triggerCommand("set model.scivis.cells true"); + test("triggerCommand set", options.model.scivis.cells == true); + inter.triggerCommand("set render.hdri.file filepath"); + test("triggerCommand set double quotes", options.render.hdri.file.value() == "filepath"); + + // Test reset + inter.triggerCommand("reset model.scivis.cells"); + test("triggerCommand reset", options.model.scivis.cells == false); + inter.triggerCommand("reset render.hdri.file"); + test("triggerCommand reset optional", options.render.hdri.file.has_value() == false); + + // Test toggle + inter.triggerCommand("toggle model.scivis.cells"); + test("triggerCommand toggle", options.model.scivis.cells == true); + + // triggerCommand error codepaths + test("triggerCommand toggle incompatible", + inter.triggerCommand("toggle scene.animation.index") == false); + test("triggerCommand reset inexistent", inter.triggerCommand("reset inexistent") == false); + test("triggerCommand print not set", inter.triggerCommand("print render.hdri.file") == false); + test("triggerCommand set unparsable", + inter.triggerCommand("set scene.animation.index invalid") == false); + + // Add/Remove callback + inter.addCommandCallback("test_toggle", [&](const std::vector&) -> bool { + options.toggle("model.scivis.cells"); + return true; + }); + inter.triggerCommand("test_toggle"); + test("addCommandCallback", options.model.scivis.cells == false); + + inter.removeCommandCallback("test_toggle"); + test("removeCommandCallback", inter.triggerCommand("test_toggle") == false); + + // Coverage print + inter.triggerCommand("print model.scivis.cells"); + + // Coverage cycle_coloring + test("triggerCommand cycle_coloring invalid arg", + inter.triggerCommand("cycle_coloring invalid") == false); + + // Coverage set_camera + test( + "triggerCommand set_camera invalid arg", inter.triggerCommand("set_camera invalid") == false); + + // Coverage exception handling + test("triggerCommand exception handling", + inter.triggerCommand(R"(print "render.hdri.file)") == false); + + // Args check + test("triggerCommand set invalid args", inter.triggerCommand("set one") == false); + test("triggerCommand toggle invalid args", inter.triggerCommand("toggle one two") == false); + test("triggerCommand unset invalid args", inter.triggerCommand("unset one two") == false); + test("triggerCommand reset invalid args", inter.triggerCommand("reset one two") == false); + test("triggerCommand print invalid args", inter.triggerCommand("print one two") == false); + test("triggerCommand roll_camera invalid args", + inter.triggerCommand("roll_camera one two") == false); + test( + "triggerCommand set_camera invalid args", inter.triggerCommand("set_camera one two") == false); + test("triggerCommand cycle_coloring invalid args", + inter.triggerCommand("cycle_coloring one two") == false); + + return test.result(); +} diff --git a/library/testing/TestSDKInteractorDropFullScene.cxx b/library/testing/TestSDKInteractorDropFullScene.cxx index 1d3c19a8aa..596ed2f963 100644 --- a/library/testing/TestSDKInteractorDropFullScene.cxx +++ b/library/testing/TestSDKInteractorDropFullScene.cxx @@ -1,6 +1,5 @@ #include #include -#include #include #include @@ -10,9 +9,8 @@ int TestSDKInteractorDropFullScene(int argc, char* argv[]) { - f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); + f3d::engine eng = f3d::engine::create(true); const f3d::options& options = eng.getOptions(); - const f3d::loader& load = eng.getLoader(); f3d::window& win = eng.getWindow(); f3d::interactor& inter = eng.getInteractor(); win.setSize(300, 300); @@ -22,7 +20,7 @@ int TestSDKInteractorDropFullScene(int argc, char* argv[]) inter.playInteraction(interactionFilePath); // world.obj; S return TestSDKHelpers::RenderTest( - win, std::string(argv[1]) + "baselines/", std::string(argv[2]), filename, 50) + win, std::string(argv[1]) + "baselines/", std::string(argv[2]), filename) ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/library/testing/TestSDKLoadFromMemory.cxx b/library/testing/TestSDKLoadFromMemory.cxx deleted file mode 100644 index 04d55899da..0000000000 --- a/library/testing/TestSDKLoadFromMemory.cxx +++ /dev/null @@ -1,110 +0,0 @@ -#include -#include -#include -#include - -#include "TestSDKHelpers.h" - -#include - -int TestSDKLoadFromMemory(int argc, char* argv[]) -{ - f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG); - f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); - f3d::window& win = eng.getWindow().setSize(300, 300); - - std::string texturePath = std::string(argv[1]) + "data/world.png"; - eng.getOptions().model.color.texture = texturePath; - - // Load invalid number of points - try - { - eng.getLoader().loadGeometry( - { { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f }, {}, {}, { 3 }, { 0, 1, 2 } }); - std::cerr << "Should throw: invalid number of points" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - // Load empty point - try - { - eng.getLoader().loadGeometry(f3d::mesh_t{}); - std::cerr << "Should throw: no point" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - // Load invalid number of points - try - { - eng.getLoader().loadGeometry( - { { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f }, {}, {}, { 3 }, { 0, 1, 2, 3 } }); - std::cerr << "Should throw: invalid number of cell indices" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - // Load invalid with invalid index - try - { - eng.getLoader().loadGeometry( - { { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f }, {}, {}, { 3 }, { 0, 1, 4 } }); - std::cerr << "Should throw: invalid vertex index" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - // Load invalid with invalid normals - try - { - eng.getLoader().loadGeometry( - { { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f }, { 1.f }, {}, { 3 }, { 0, 1, 2, 4 } }); - std::cerr << "Should throw: invalid normals" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - // Load invalid with invalid texture coordinates - try - { - eng.getLoader().loadGeometry( - { { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f }, {}, { 1.f }, { 3 }, { 0, 1, 2, 4 } }); - std::cerr << "Should throw: invalid texture coordinates" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - // Load from memory (valid) - try - { - eng.getLoader().loadGeometry({ { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f, 1.f, 1.f, 0.f }, - { 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f }, - { 0.f, 0.f, 0.f, 1.f, 1.f, 0.f, 1.f, 1.f }, { 3, 3 }, { 0, 1, 2, 1, 3, 2 } }); - } - catch (const f3d::loader::load_failure_exception& ex) - { - std::cerr << "Unexpected loadMemory failure" << std::endl; - return EXIT_FAILURE; - } - - if (!TestSDKHelpers::RenderTest( - win, std::string(argv[1]) + "baselines/", argv[2], "TestSDKLoadMemory")) - { - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} diff --git a/library/testing/TestSDKLoader.cxx b/library/testing/TestSDKLoader.cxx deleted file mode 100644 index bd814db4cb..0000000000 --- a/library/testing/TestSDKLoader.cxx +++ /dev/null @@ -1,185 +0,0 @@ -#include -#include -#include -#include -#include - -#include - -int TestSDKLoader(int argc, char* argv[]) -{ - f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG); - f3d::engine eng(f3d::window::Type::NONE); - f3d::loader& load = eng.getLoader(); - - // Test file logic - std::string empty; - std::string dummyFilename = "dummy.foo"; - std::string nonExistentGeometryFilename = "nonExistent.vtp"; - std::string nonExistentFullSceneFilename = "nonExistent.obj"; - std::string unsupportedFilename = "unsupportedFile.dummy"; - std::string cowFilename = "cow.vtp"; - std::string dragonFilename = "dragon.vtu"; - std::string suzanneFilename = "suzanne.stl"; - std::string worldFilename = "world.obj"; - std::string botFilename = "bot2.wrl"; - std::string dummy = std::string(argv[1]) + "data/" + dummyFilename; - std::string nonExistentGeometry = std::string(argv[1]) + "data/" + nonExistentGeometryFilename; - std::string nonExistentFullScene = std::string(argv[1]) + "data/" + nonExistentFullSceneFilename; - std::string unsupported = std::string(argv[1]) + "data/" + unsupportedFilename; - std::string cow = std::string(argv[1]) + "data/" + cowFilename; - std::string dragon = std::string(argv[1]) + "data/" + dragonFilename; - std::string suzanne = std::string(argv[1]) + "data/" + suzanneFilename; - std::string world = std::string(argv[1]) + "data/" + worldFilename; - std::string bot = std::string(argv[1]) + "data/" + botFilename; - - // has*Reader methods - if (load.hasGeometryReader(empty) || load.hasSceneReader(empty)) - { - std::cerr << "Unexpected has*Reader output with empty filenames" << std::endl; - return EXIT_FAILURE; - } - if (load.hasGeometryReader(dummy) || load.hasSceneReader(dummy)) - { - std::cerr << "Unexpected has*Reader output with dummy filenames" << std::endl; - return EXIT_FAILURE; - } - if (!load.hasGeometryReader(nonExistentGeometry) || !load.hasSceneReader(nonExistentFullScene)) - { - std::cerr << "Unexpected has*Reader output with non existent filenames" << std::endl; - return EXIT_FAILURE; - } - if (load.hasGeometryReader(bot) || load.hasSceneReader(dragon)) - { - std::cerr << "Unexpected has*Reader output with incorrect formats" << std::endl; - return EXIT_FAILURE; - } - if (!load.hasGeometryReader(dragon) || !load.hasSceneReader(bot)) - { - std::cerr << "Unexpected has*Reader output with correct formats" << std::endl; - return EXIT_FAILURE; - } - if (!load.hasGeometryReader(world) || !load.hasSceneReader(world)) - { - std::cerr << "Unexpected has*Reader output with geometry and full scene format" << std::endl; - return EXIT_FAILURE; - } - - // Empty filename, success expected but nothing is loaded - try - { - load.loadGeometry(empty); - } - catch (const f3d::loader::load_failure_exception& ex) - { - std::cerr << "Unexpected loadGeometry failure with an empty file" << std::endl; - return EXIT_FAILURE; - } - - try - { - load.loadScene(empty); - } - catch (const f3d::loader::load_failure_exception& ex) - { - std::cerr << "Unexpected loadGeometry failure with an empty file" << std::endl; - return EXIT_FAILURE; - } - - // Dummy filename - try - { - load.loadGeometry(dummy); - std::cerr << "Unexpected loadGeometry success with a dummy file" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - try - { - load.loadScene(dummy); - std::cerr << "Unexpected loadGeometry success with a dummy file" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - // Non supported files - try - { - load.loadGeometry(unsupported); - std::cerr << "Unexpected loadGeometry success with an unsupported file" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - try - { - load.loadScene(unsupported); - std::cerr << "Unexpected loadScene success with an unsupported file" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - // Incorrect files - try - { - load.loadGeometry(bot); - std::cerr << "Unexpected loadGeometry success with an incorrect file" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - try - { - load.loadScene(cow); - std::cerr << "Unexpected loadScene success with an incorrect file" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - // Non existent files - try - { - load.loadGeometry(nonExistentGeometry); - std::cerr << "Unexpected loadGeometry success with a non existent file" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - try - { - load.loadScene(nonExistentFullScene); - std::cerr << "Unexpected loadScene success with a non existent file" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - // Multiple geometries - try - { - load.loadGeometry(cow).loadGeometry(suzanne).loadGeometry(dragon); - } - catch (const f3d::loader::load_failure_exception& ex) - { - std::cerr << "Unexpected loadGeometry failure with multiple files" << std::endl; - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} diff --git a/library/testing/TestSDKLoaderInvalid.cxx b/library/testing/TestSDKLoaderInvalid.cxx deleted file mode 100644 index 073e089eb3..0000000000 --- a/library/testing/TestSDKLoaderInvalid.cxx +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include -#include -#include -#include - -#include - -int TestSDKLoaderInvalid(int argc, char* argv[]) -{ - f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG); - f3d::engine eng(f3d::window::Type::NONE); - f3d::loader& load = eng.getLoader(); - - std::string invalidGeometryFilename = "invalid.vtp"; - std::string invalidFullSceneFilename = "duck_invalid.gltf"; - std::string invalidGeometry = std::string(argv[1]) + "data/" + invalidGeometryFilename; - std::string invalidFullScene = std::string(argv[1]) + "data/" + invalidFullSceneFilename; - - try - { - load.loadGeometry(invalidGeometry); - std::cerr << "Unexpected loadGeometry success with an invalid file" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - try - { - load.loadScene(invalidFullScene); - std::cerr << "Unexpected loadScene success with an invalid file" << std::endl; - return EXIT_FAILURE; - } - catch (const f3d::loader::load_failure_exception& ex) - { - } - - return EXIT_SUCCESS; -} diff --git a/library/testing/TestSDKMultiColoring.cxx b/library/testing/TestSDKMultiColoring.cxx index 921e35fbd2..de7d56a826 100644 --- a/library/testing/TestSDKMultiColoring.cxx +++ b/library/testing/TestSDKMultiColoring.cxx @@ -1,7 +1,7 @@ #include #include -#include #include +#include #include #include "TestSDKHelpers.h" @@ -11,8 +11,8 @@ int TestSDKMultiColoring(int argc, char* argv[]) { f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG); - f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); - f3d::loader& load = eng.getLoader(); + f3d::engine eng = f3d::engine::create(true); + f3d::scene& sce = eng.getScene(); f3d::options& opt = eng.getOptions(); // Test file logic @@ -24,12 +24,13 @@ int TestSDKMultiColoring(int argc, char* argv[]) std::string right = std::string(argv[1]) + "data/" + rightFilename; // Multiple geometries - load.loadGeometry(cube).loadGeometry(left).loadGeometry(right); + sce.add(std::vector{ cube, left, right }); + opt.model.scivis.enable = true; opt.model.scivis.array_name = "Normals"; return TestSDKHelpers::RenderTest(eng.getWindow(), std::string(argv[1]) + "baselines/", - std::string(argv[2]), "TestSDKMultiColoring", 50) + std::string(argv[2]), "TestSDKMultiColoring") ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/library/testing/TestSDKMultiOptions.cxx b/library/testing/TestSDKMultiOptions.cxx index e669866078..a5e7b444ac 100644 --- a/library/testing/TestSDKMultiOptions.cxx +++ b/library/testing/TestSDKMultiOptions.cxx @@ -1,7 +1,7 @@ #include #include -#include #include +#include #include #include "TestSDKHelpers.h" @@ -11,8 +11,8 @@ int TestSDKMultiOptions(int argc, char* argv[]) { f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG); - f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); - f3d::loader& load = eng.getLoader(); + f3d::engine eng = f3d::engine::create(true); + f3d::scene& sce = eng.getScene(); f3d::options& opt = eng.getOptions(); f3d::window& win = eng.getWindow(); @@ -22,7 +22,7 @@ int TestSDKMultiOptions(int argc, char* argv[]) std::string right = std::string(argv[1]) + "data/" + rightFilename; // Render one geometry with a render option - load.loadGeometry(left); + sce.add(left); opt.render.show_edges = true; opt.render.grid.enable = true; opt.ui.metadata = true; @@ -30,11 +30,11 @@ int TestSDKMultiOptions(int argc, char* argv[]) win.render(); // Add another geometry - load.loadGeometry(right); + sce.add(right); // Check rendering is correct return TestSDKHelpers::RenderTest(eng.getWindow(), std::string(argv[1]) + "baselines/", - std::string(argv[2]), "TestSDKMultiOptions", 50) + std::string(argv[2]), "TestSDKMultiOptions") ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/library/testing/TestSDKOptions.cxx b/library/testing/TestSDKOptions.cxx index b0e76a8fa9..40a06df591 100644 --- a/library/testing/TestSDKOptions.cxx +++ b/library/testing/TestSDKOptions.cxx @@ -1,8 +1,8 @@ +#include "PseudoUnitTest.h" + #include #include -#include "PseudoUnitTest.h" - #include #include @@ -28,6 +28,8 @@ int TestSDKOptions(int argc, char* argv[]) opt.set("model.scivis.cells", false); opt.toggle("model.scivis.cells"); test("toggle", opt.getAsString("model.scivis.cells") == "true"); + opt.toggle("render.show_edges"); + test("toggle optional", opt.getAsString("render.show_edges") == "true"); // Test int opt.setAsString("scene.animation.index", "2"); @@ -59,6 +61,18 @@ int TestSDKOptions(int argc, char* argv[]) opt.set("scene.animation.speed_factor", 3.17); test("set/get ratio_t", std::get(opt.get("scene.animation.speed_factor")) == 3.17); + // Test vector3_t + opt.setAsString("scene.up_direction2", "0, 1, 0"); + test("setAsString vector3_t", opt.getAsString("scene.up_direction2") == "0, 1, 0"); + + opt.scene.up_direction2 = { 1, 0, 0 }; + test("getAsString vector3_t", opt.getAsString("scene.up_direction2") == "1, 0, 0"); + + opt.set("scene.up_direction2", std::vector{ 0, 0, 1 }); + test("set/get vector3_t", + std::get>(opt.get("scene.up_direction2")) == + std::vector{ 0, 0, 1 }); + // Test string opt.setAsString("model.color.texture", "testAsString"); test("setAsString string", opt.getAsString("model.color.texture") == "testAsString"); @@ -214,9 +228,9 @@ int TestSDKOptions(int argc, char* argv[]) f3d::options opt7{}; // Test reset non-optional values - opt7.model.color.opacity = 0.5; - opt7.reset("model.color.opacity"); - test("reset non-optional values", opt7.model.color.opacity == 1.0); + opt7.scene.up_direction = "+Z"; + opt7.reset("scene.up_direction"); + test("reset non-optional values", opt7.scene.up_direction == "+Y"); // Test reset optional values opt7.model.scivis.array_name = "dummy"; diff --git a/library/testing/TestSDKOptionsIO.cxx b/library/testing/TestSDKOptionsIO.cxx index 333fb22321..ac146d2ef3 100644 --- a/library/testing/TestSDKOptionsIO.cxx +++ b/library/testing/TestSDKOptionsIO.cxx @@ -2,6 +2,7 @@ #include #include "PseudoUnitTest.h" +#include "types.h" #include @@ -71,5 +72,19 @@ int TestSDKOptionsIO(int argc, char* argv[]) test.parse>( "std::vector", " foo, bar , baz ", { "foo", "bar", "baz" }); + test.parse("vector3_t", "1, 2, 3", { 1, 2, 3 }); + test.parse("vector3_t", " 1, 2, 3 ", { 1, 2, 3 }); + test.parse("vector3_t", "+Y", { 0, 1, 0 }); + test.parse("vector3_t", " +Y ", { 0, 1, 0 }); + test.parse("vector3_t", "-Y", { 0, -1, 0 }); + test.parse("vector3_t", "+X", { 1, 0, 0 }); + test.parse("vector3_t", "-X", { -1, 0, 0 }); + test.parse("vector3_t", "+Z", { 0, 0, 1 }); + test.parse("vector3_t", "-Z", { 0, 0, -1 }); + test.parse("vector3_t", "0, 0", { 0, 0, 1 }); + test.expect("cannot create a vector3_t", [&]() { + f3d::vector3_t{ 1., 2., 3., 4. }; + }); + return test.result(); } diff --git a/library/testing/TestSDKRenderAndInteract.cxx b/library/testing/TestSDKRenderAndInteract.cxx index 0d8ba9e19a..eea0d7dff8 100644 --- a/library/testing/TestSDKRenderAndInteract.cxx +++ b/library/testing/TestSDKRenderAndInteract.cxx @@ -1,6 +1,6 @@ #include #include -#include +#include #include int TestSDKRenderAndInteract(int argc, char* argv[]) @@ -10,9 +10,9 @@ int TestSDKRenderAndInteract(int argc, char* argv[]) // an interactor works // Using an onscreen window to mimic standard usage - f3d::engine eng(f3d::window::Type::NATIVE); - f3d::loader& load = eng.getLoader(); - load.loadGeometry(std::string(argv[1]) + "/data/cow.vtp"); + f3d::engine eng = f3d::engine::create(); + f3d::scene& sce = eng.getScene(); + sce.add(std::string(argv[1]) + "/data/cow.vtp"); f3d::window& win = eng.getWindow(); win.render(); diff --git a/library/testing/TestSDKRenderFinalShader.cxx b/library/testing/TestSDKRenderFinalShader.cxx index f767a3df67..8abb50e3ee 100644 --- a/library/testing/TestSDKRenderFinalShader.cxx +++ b/library/testing/TestSDKRenderFinalShader.cxx @@ -1,19 +1,19 @@ #include -#include #include +#include #include #include "TestSDKHelpers.h" int TestSDKRenderFinalShader(int argc, char* argv[]) { - f3d::engine eng(f3d::window::Type::NATIVE_OFFSCREEN); + f3d::engine eng = f3d::engine::create(true); f3d::window& win = eng.getWindow(); win.setSize(300, 300); - f3d::loader& load = eng.getLoader(); - load.loadGeometry(std::string(argv[1]) + "/data/cow.vtp"); + f3d::scene& sce = eng.getScene(); + sce.add(std::string(argv[1]) + "/data/cow.vtp"); win.render(); @@ -37,7 +37,7 @@ int TestSDKRenderFinalShader(int argc, char* argv[]) options.render.effect.final_shader = negativeShader; if (!TestSDKHelpers::RenderTest(win, std::string(argv[1]) + "baselines/", std::string(argv[2]), - "TestSDKRenderFinalShaderNegative", 50)) + "TestSDKRenderFinalShaderNegative")) { std::cerr << "Negative shader failure"; return EXIT_FAILURE; @@ -47,7 +47,7 @@ int TestSDKRenderFinalShader(int argc, char* argv[]) options.render.effect.final_shader = vignetteShader; if (!TestSDKHelpers::RenderTest(win, std::string(argv[1]) + "baselines/", std::string(argv[2]), - "TestSDKRenderFinalShaderVignette", 50)) + "TestSDKRenderFinalShaderVignette")) { std::cerr << "Vignette shader failure"; return EXIT_FAILURE; diff --git a/library/testing/TestSDKScene.cxx b/library/testing/TestSDKScene.cxx new file mode 100644 index 0000000000..357bc4f831 --- /dev/null +++ b/library/testing/TestSDKScene.cxx @@ -0,0 +1,70 @@ +#include "PseudoUnitTest.h" +#include "TestSDKHelpers.h" + +#include +#include +#include +#include + +namespace fs = std::filesystem; + +int TestSDKScene(int argc, char* argv[]) +{ + PseudoUnitTest test; + + f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG); + f3d::engine eng = f3d::engine::create(true); + f3d::scene& sce = eng.getScene(); + f3d::window& win = eng.getWindow().setSize(300, 300); + + // Test file logic + std::string empty; + std::string dummyFilename = "dummy.foo"; + std::string nonExistentFilename = "nonExistent.vtp"; + std::string unsupportedFilename = "unsupportedFile.dummy"; + std::string logoFilename = "mb/recursive/f3d.glb"; + std::string sphere1Filename = "mb/recursive/mb_1_0.vtp"; + std::string sphere2Filename = "mb/recursive/mb_2_0.vtp"; + std::string cubeFilename = "mb/recursive/mb_0_0.vtu"; + std::string worldFilename = "world.obj"; + std::string dummy = std::string(argv[1]) + "data/" + dummyFilename; + std::string nonExistent = std::string(argv[1]) + "data/" + nonExistentFilename; + std::string unsupported = std::string(argv[1]) + "data/" + unsupportedFilename; + std::string logo = std::string(argv[1]) + "data/" + logoFilename; + std::string sphere1 = std::string(argv[1]) + "data/" + sphere1Filename; + std::string sphere2 = std::string(argv[1]) + "data/" + sphere2Filename; + std::string cube = std::string(argv[1]) + "data/" + cubeFilename; + std::string world = std::string(argv[1]) + "data/" + worldFilename; + + // supports method + test("supported with empty filename", !sce.supports(empty)); + test("supported with dummy filename", !sce.supports(dummy)); + test("supported with non existent filename", sce.supports(nonExistent)); + test("supported with default scene format", sce.supports(cube)); + test("supported with full scene format", sce.supports(logo)); + + // add error code paths + test.expect("add with dummy file", [&]() { sce.add(dummy); }); + test.expect( + "add with unsupported file", [&]() { sce.add(unsupported); }); + test.expect( + "add with inexistent file", [&]() { sce.add(nonExistent); }); + + // add standard code paths + test("add with empty file", [&]() { sce.add(std::vector{}); }); + test("add with empty file", [&]() { sce.add(empty); }); + test("add with a single path", [&]() { sce.add(fs::path(logo)); }); + test("add with multiples filepaths", [&]() { sce.add({ fs::path(sphere2), fs::path(cube) }); }); + test("add with multiples file strings", [&]() { sce.add({ sphere1, world }); }); + + // render test + test("render after add", [&]() { + if (!TestSDKHelpers::RenderTest( + win, std::string(argv[1]) + "baselines/", argv[2], "TestSDKScene")) + { + throw "rendering test failed"; + } + }); + + return test.result(); +} diff --git a/library/testing/TestSDKSceneFromMemory.cxx b/library/testing/TestSDKSceneFromMemory.cxx new file mode 100644 index 0000000000..77965d2da8 --- /dev/null +++ b/library/testing/TestSDKSceneFromMemory.cxx @@ -0,0 +1,73 @@ +#include "PseudoUnitTest.h" +#include "TestSDKHelpers.h" + +#include +#include +#include +#include + +int TestSDKSceneFromMemory(int argc, char* argv[]) +{ + PseudoUnitTest test; + + f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG); + f3d::engine eng = f3d::engine::create(true); + f3d::scene& sce = eng.getScene(); + f3d::window& win = eng.getWindow().setSize(300, 300); + + std::string texturePath = std::string(argv[1]) + "data/world.png"; + eng.getOptions().model.color.texture = texturePath; + + // Add mesh with invalid number of points + test.expect("add mesh with invalid number of points", [&]() { + sce.add(f3d::mesh_t{ { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f }, {}, {}, { 3 }, { 0, 1, 2 } }); + }); + + // Add mesh with empty points + test.expect( + "add mesh with empty points", [&]() { sce.add(f3d::mesh_t{}); }); + + // Add mesh with invalid number of cell indices + test.expect( + "add mesh with invalid number of cell indices", [&]() { + sce.add(f3d::mesh_t{ + { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f }, {}, {}, { 3 }, { 0, 1, 2, 3 } }); + }); + + // Add mesh with invalid vertex index + test.expect("add mesh with invalid vertex index", [&]() { + sce.add( + f3d::mesh_t{ { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f }, {}, {}, { 3 }, { 0, 1, 4 } }); + }); + + // Add mesh with invalid normals + test.expect("add mesh with invalid normals", [&]() { + sce.add(f3d::mesh_t{ + { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f }, { 1.f }, {}, { 3 }, { 0, 1, 2, 4 } }); + }); + + // Add mesh with invalid texture coordinates + test.expect( + "add mesh with invalid texture coordinates", [&]() { + sce.add(f3d::mesh_t{ + { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f }, {}, { 1.f }, { 3 }, { 0, 1, 2, 4 } }); + }); + + // Add mesh from memory and render it + test("add mesh from memory", [&]() { + sce.add(f3d::mesh_t{ { 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f, 1.f, 1.f, 0.f }, + { 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f }, + { 0.f, 0.f, 0.f, 1.f, 1.f, 0.f, 1.f, 1.f }, { 3, 3 }, { 0, 1, 2, 1, 3, 2 } }); + }); + + // Render test + test("render mesh from memory", [&]() { + if (!TestSDKHelpers::RenderTest( + win, std::string(argv[1]) + "baselines/", argv[2], "TestSDKSceneFromMemory")) + { + throw "rendering test failed"; + } + }); + + return test.result(); +} diff --git a/library/testing/TestSDKSceneInvalid.cxx b/library/testing/TestSDKSceneInvalid.cxx new file mode 100644 index 0000000000..5a2a4d9c5a --- /dev/null +++ b/library/testing/TestSDKSceneInvalid.cxx @@ -0,0 +1,26 @@ +#include "PseudoUnitTest.h" + +#include +#include +#include + +int TestSDKSceneInvalid(int argc, char* argv[]) +{ + PseudoUnitTest test; + + f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG); + f3d::engine eng = f3d::engine::createNone(); + f3d::scene& sce = eng.getScene(); + + std::string invalidDefaultSceneFilename = "invalid.vtp"; + std::string invalidFullSceneFilename = "duck_invalid.gltf"; + std::string invalidDefaultScene = std::string(argv[1]) + "data/" + invalidDefaultSceneFilename; + std::string invalidFullScene = std::string(argv[1]) + "data/" + invalidFullSceneFilename; + + test.expect( + "add with invalid default scene file", [&]() { sce.add(invalidDefaultScene); }); + test.expect( + "add with invalid full scene file", [&]() { sce.add(invalidFullScene); }); + + return test.result(); +} diff --git a/library/testing/TestSDKUtils.cxx b/library/testing/TestSDKUtils.cxx new file mode 100644 index 0000000000..1d206ba5dd --- /dev/null +++ b/library/testing/TestSDKUtils.cxx @@ -0,0 +1,60 @@ +#include "PseudoUnitTest.h" + +#include + +int TestSDKUtils(int argc, char* argv[]) +{ + PseudoUnitTest test; + + test("tokenize spaces", + f3d::utils::tokenize(R"(set render.hdri.file file path space)") == + std::vector{ "set", "render.hdri.file", "file", "path", "space" }); + + test("tokenize double quotes", + f3d::utils::tokenize(R"(set render.hdri.file "file path double")") == + std::vector{ "set", "render.hdri.file", "file path double" }); + + test("tokenize single quotes", + f3d::utils::tokenize(R"(set render.hdri.file 'file path single')") == + std::vector{ "set", "render.hdri.file", "file path single" }); + + test("tokenize backticks", + f3d::utils::tokenize(R"(set render.hdri.file `file path back`)") == + std::vector{ "set", "render.hdri.file", "file path back" }); + + test("tokenize double quotes with internal quotes", + f3d::utils::tokenize(R"(set render.hdri.file "file 'path' double")") == + std::vector{ "set", "render.hdri.file", "file 'path' double" }); + + test("tokenize single quotes with internal quotes", + f3d::utils::tokenize(R"(set render.hdri.file 'file "path" single')") == + std::vector{ "set", "render.hdri.file", R"(file "path" single)" }); + + test("tokenize backticks quotes with internal quotes", + f3d::utils::tokenize(R"(set render.hdri.file `file "path" back`)") == + std::vector{ "set", "render.hdri.file", R"(file "path" back)" }); + + test("tokenize double quotes with escape", + f3d::utils::tokenize(R"(set render.hdri.file "file \"path\" double")") == + std::vector{ "set", "render.hdri.file", R"(file "path" double)" }); + + test("tokenize single quotes with escape", + f3d::utils::tokenize(R"(set render.hdri.file 'file \'path\' single')") == + std::vector{ "set", "render.hdri.file", "file 'path' single" }); + + test("tokenize backticks quotes with escape", + f3d::utils::tokenize(R"(set render.hdri.file `file \`path\` single`)") == + std::vector{ "set", "render.hdri.file", "file `path` single" }); + + test("tokenize spaces with escape", + f3d::utils::tokenize(R"(set render.hdri.file file\ pa\th\ esc\ape)") == + std::vector{ "set", "render.hdri.file", "file path escape" }); + + test.expect("tokenize_exception with incomplete quotes", + [&]() { f3d::utils::tokenize(R"(set render.hdri.file "file path back)"); }); + + test.expect("tokenize_exception with unfinishied escape", + [&]() { f3d::utils::tokenize(R"(set render.hdri.file file path back\)"); }); + + return test.result(); +} diff --git a/library/testing/TestSDKWindowNative.cxx b/library/testing/TestSDKWindowAuto.cxx similarity index 63% rename from library/testing/TestSDKWindowNative.cxx rename to library/testing/TestSDKWindowAuto.cxx index 704334f5a9..4b3ef9af83 100644 --- a/library/testing/TestSDKWindowNative.cxx +++ b/library/testing/TestSDKWindowAuto.cxx @@ -5,10 +5,10 @@ #include "TestSDKHelpers.h" -int TestSDKWindowNative(int argc, char* argv[]) +int TestSDKWindowAuto(int argc, char* argv[]) { f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG); - f3d::engine eng; + f3d::engine eng = f3d::engine::create(); f3d::window& win = eng.getWindow(); win.setWindowName("Test").setSize(300, 300).setPosition(100, 100); @@ -19,18 +19,25 @@ int TestSDKWindowNative(int argc, char* argv[]) return EXIT_FAILURE; } - if (win.getType() != f3d::window::Type::NATIVE) + if (win.getType() == f3d::window::Type::UNKNOWN) { std::cerr << "Unexpected window type" << std::endl; return EXIT_FAILURE; } + if (win.isOffscreen()) + { + std::cerr << "Window should not be offscreen" << std::endl; + return EXIT_FAILURE; + } + f3d::options& options = eng.getOptions(); options.render.background.color = { 0.8, 0.2, 0.9 }; - // Use a higher threshold as background difference can be strong with mesa + // XXX: Use a higher threshold as background difference can be strong with older versions of VTK + // This can be removed once VTK 9.3 support is removed return TestSDKHelpers::RenderTest(win, std::string(argv[1]) + "baselines/", std::string(argv[2]), - "TestSDKWindowStandard", 150) + "TestSDKWindowStandard", 0.12) ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/plugins/alembic/configs/config.d/10_alembic.json b/plugins/alembic/configs/config.d/10_alembic.json index 6ad25f6d43..286f47fe09 100644 --- a/plugins/alembic/configs/config.d/10_alembic.json +++ b/plugins/alembic/configs/config.d/10_alembic.json @@ -1,6 +1,7 @@ { ".*(abc)": { + "scalar-coloring": true, "load-plugins": "alembic" } } diff --git a/plugins/alembic/configs/thumbnail.d/10_alembic.json b/plugins/alembic/configs/thumbnail.d/10_alembic.json index 6ad25f6d43..286f47fe09 100644 --- a/plugins/alembic/configs/thumbnail.d/10_alembic.json +++ b/plugins/alembic/configs/thumbnail.d/10_alembic.json @@ -1,6 +1,7 @@ { ".*(abc)": { + "scalar-coloring": true, "load-plugins": "alembic" } } diff --git a/plugins/draco/CMakeLists.txt b/plugins/draco/CMakeLists.txt index e824391367..4f9b326fdf 100644 --- a/plugins/draco/CMakeLists.txt +++ b/plugins/draco/CMakeLists.txt @@ -32,10 +32,8 @@ if(VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240214) SCORE 90 EXTENSIONS gltf glb MIMETYPES model/gltf-binary model/gltf+json - VTK_READER vtkF3DGLTFReader VTK_IMPORTER vtkF3DGLTFImporter FORMAT_DESCRIPTION "GL Transmission Format" - CUSTOM_CODE "${CMAKE_CURRENT_SOURCE_DIR}/gltf.inl" ) endif() diff --git a/plugins/draco/configs/config.d/10_draco.json b/plugins/draco/configs/config.d/10_draco.json index 041998db54..8428b17b2a 100644 --- a/plugins/draco/configs/config.d/10_draco.json +++ b/plugins/draco/configs/config.d/10_draco.json @@ -1,6 +1,7 @@ { ".*(drc)": { + "scalar-coloring": true, "load-plugins": "draco" } } diff --git a/plugins/draco/configs/thumbnail.d/10_draco.json b/plugins/draco/configs/thumbnail.d/10_draco.json index 041998db54..8428b17b2a 100644 --- a/plugins/draco/configs/thumbnail.d/10_draco.json +++ b/plugins/draco/configs/thumbnail.d/10_draco.json @@ -1,6 +1,7 @@ { ".*(drc)": { + "scalar-coloring": true, "load-plugins": "draco" } } diff --git a/plugins/draco/gltf.inl b/plugins/draco/gltf.inl deleted file mode 100644 index 1d7a52a6aa..0000000000 --- a/plugins/draco/gltf.inl +++ /dev/null @@ -1,17 +0,0 @@ -void applyCustomReader(vtkAlgorithm* algo, const std::string&) const override -{ - vtkGLTFReader* gltfReader = vtkGLTFReader::SafeDownCast(algo); - - // Enable all animations in the GLTFReader - // Specifying a non-zero framerate in the next call is not needed after VTK 9.2.20230603 : VTK_VERSION_CHECK(9, 2, 20230603) - gltfReader->SetFrameRate(30); - gltfReader->ApplyDeformationsToGeometryOn(); - gltfReader->UpdateInformation(); // Read model metadata to get the number of animations - for (vtkIdType i = 0; i < gltfReader->GetNumberOfAnimations(); i++) - { - gltfReader->EnableAnimation(i); - } - // It is needed to update the information directly in order to recover it later - // Not entirely understood, TODO - gltfReader->UpdateInformation(); -} diff --git a/plugins/draco/module/CMakeLists.txt b/plugins/draco/module/CMakeLists.txt index 717848ad94..e9de29edf3 100644 --- a/plugins/draco/module/CMakeLists.txt +++ b/plugins/draco/module/CMakeLists.txt @@ -7,7 +7,6 @@ if(VTK_VERSION VERSION_GREATER_EQUAL 9.3.20240214) set(classes ${classes} vtkF3DGLTFDocumentLoader vtkF3DGLTFImporter - vtkF3DGLTFReader ) endif() diff --git a/plugins/draco/module/vtkF3DGLTFReader.cxx b/plugins/draco/module/vtkF3DGLTFReader.cxx deleted file mode 100644 index 88dd6e019b..0000000000 --- a/plugins/draco/module/vtkF3DGLTFReader.cxx +++ /dev/null @@ -1,14 +0,0 @@ -#include "vtkF3DGLTFReader.h" - -#include "vtkF3DGLTFDocumentLoader.h" - -#include - -//---------------------------------------------------------------------------- -vtkStandardNewMacro(vtkF3DGLTFReader); - -//---------------------------------------------------------------------------- -void vtkF3DGLTFReader::InitializeLoader() -{ - this->Loader = vtkSmartPointer::New(); -} diff --git a/plugins/draco/module/vtkF3DGLTFReader.h b/plugins/draco/module/vtkF3DGLTFReader.h deleted file mode 100644 index 166ddd2851..0000000000 --- a/plugins/draco/module/vtkF3DGLTFReader.h +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @class vtkF3DGLTFReader - * @brief VTK GLTF reader with Draco support - * - * Subclasses the native importer to initialize our own loader. - * @sa vtkF3DGLTFDocumentLoader - */ - -#ifndef vtkF3DGLTFReader_h -#define vtkF3DGLTFReader_h - -#include - -class vtkF3DGLTFReader : public vtkGLTFReader -{ -public: - static vtkF3DGLTFReader* New(); - vtkTypeMacro(vtkF3DGLTFReader, vtkGLTFReader); - -protected: - vtkF3DGLTFReader() = default; - ~vtkF3DGLTFReader() override = default; - - /** - * Overridden to instantiate our own document loader - */ - void InitializeLoader() override; - -private: - vtkF3DGLTFReader(const vtkF3DGLTFReader&) = delete; - void operator=(const vtkF3DGLTFReader&) = delete; -}; - -#endif diff --git a/plugins/exodus/configs/config.d/10_exodus.json b/plugins/exodus/configs/config.d/10_exodus.json index db032e9584..4356640057 100644 --- a/plugins/exodus/configs/config.d/10_exodus.json +++ b/plugins/exodus/configs/config.d/10_exodus.json @@ -1,6 +1,7 @@ { ".*(ex2)": { + "scalar-coloring": true, "load-plugins": "exodus", "bar": true } diff --git a/plugins/exodus/configs/thumbnail.d/10_exodus.json b/plugins/exodus/configs/thumbnail.d/10_exodus.json index 375b1c7498..a9881e23ce 100644 --- a/plugins/exodus/configs/thumbnail.d/10_exodus.json +++ b/plugins/exodus/configs/thumbnail.d/10_exodus.json @@ -1,6 +1,7 @@ { ".*(ex2)": { + "scalar-coloring": true, "load-plugins": "exodus" } } diff --git a/plugins/native/CMakeLists.txt b/plugins/native/CMakeLists.txt index 14c3e39c43..15de60c48d 100644 --- a/plugins/native/CMakeLists.txt +++ b/plugins/native/CMakeLists.txt @@ -42,10 +42,8 @@ f3d_plugin_declare_reader( SCORE 80 EXTENSIONS gltf glb MIMETYPES model/gltf-binary model/gltf+json - VTK_READER vtkGLTFReader VTK_IMPORTER vtkGLTFImporter FORMAT_DESCRIPTION "GL Transmission Format" - CUSTOM_CODE "${CMAKE_CURRENT_SOURCE_DIR}/gltf.inl" ) f3d_plugin_declare_reader( @@ -68,7 +66,6 @@ f3d_plugin_declare_reader( NAME OBJ EXTENSIONS obj MIMETYPES model/obj - VTK_READER vtkOBJReader VTK_IMPORTER vtkOBJImporter FORMAT_DESCRIPTION "Wavefront OBJ" CUSTOM_CODE "${CMAKE_CURRENT_SOURCE_DIR}/obj.inl" diff --git a/plugins/native/configs/config.d/10_native.json b/plugins/native/configs/config.d/10_native.json index 1942c9a078..46be768c6a 100644 --- a/plugins/native/configs/config.d/10_native.json +++ b/plugins/native/configs/config.d/10_native.json @@ -1,9 +1,10 @@ { - ".*(dcm|nrrd|mhd|mha|vt.)": + ".*(dcm|nrrd|nhdr|mhd|mha|vt.)": { + "scalar-coloring": true, "bar": true }, - ".*(dcm|nrrd|mhd|mha|vti)": + ".*(dcm|nrrd|nhdr|mhd|mha|vti)": { "volume": true }, @@ -12,14 +13,20 @@ "up": "+Z", "camera-direction": "-1,1,-0.5" }, + ".*(stl|gml|pts)": + { + "scalar-coloring": true + }, ".*(tif|tiff)": { + "scalar-coloring": true, "up": "-Y", "comp": "-2", "camera-direction": "-1,0.5,1" }, ".*(ply)": { + "scalar-coloring": true, "comp": "-2", "translucency-support": true }, diff --git a/plugins/native/configs/thumbnail.d/10_native.json b/plugins/native/configs/thumbnail.d/10_native.json index 90d2239d6b..4e1fba656e 100644 --- a/plugins/native/configs/thumbnail.d/10_native.json +++ b/plugins/native/configs/thumbnail.d/10_native.json @@ -1,6 +1,7 @@ { ".*(dcm|nrrd|mhd|mha|vti)": { + "scalar-coloring": true, "volume": true }, ".*(3ds|stl)": @@ -8,8 +9,13 @@ "up": "+Z", "camera-direction": "-1,1,-0.5" }, + ".*(stl|gml|pts|vt.)": + { + "scalar-coloring": true + }, ".*(ply)": { + "scalar-coloring": true, "comp": "-2", "translucency-support": true }, diff --git a/plugins/native/gltf.inl b/plugins/native/gltf.inl deleted file mode 100644 index 1d7a52a6aa..0000000000 --- a/plugins/native/gltf.inl +++ /dev/null @@ -1,17 +0,0 @@ -void applyCustomReader(vtkAlgorithm* algo, const std::string&) const override -{ - vtkGLTFReader* gltfReader = vtkGLTFReader::SafeDownCast(algo); - - // Enable all animations in the GLTFReader - // Specifying a non-zero framerate in the next call is not needed after VTK 9.2.20230603 : VTK_VERSION_CHECK(9, 2, 20230603) - gltfReader->SetFrameRate(30); - gltfReader->ApplyDeformationsToGeometryOn(); - gltfReader->UpdateInformation(); // Read model metadata to get the number of animations - for (vtkIdType i = 0; i < gltfReader->GetNumberOfAnimations(); i++) - { - gltfReader->EnableAnimation(i); - } - // It is needed to update the information directly in order to recover it later - // Not entirely understood, TODO - gltfReader->UpdateInformation(); -} diff --git a/plugins/occt/configs/config.d/10_occt.json b/plugins/occt/configs/config.d/10_occt.json index 5d426efd0b..f14624bd71 100644 --- a/plugins/occt/configs/config.d/10_occt.json +++ b/plugins/occt/configs/config.d/10_occt.json @@ -1,6 +1,7 @@ { ".*(step|stp|iges|igs|brep|xbf)": { + "scalar-coloring": true, "load-plugins": "occt", "up": "+Z", "ambient-occlusion": true, diff --git a/plugins/occt/configs/thumbnail.d/10_occt.json b/plugins/occt/configs/thumbnail.d/10_occt.json index 5d426efd0b..f14624bd71 100644 --- a/plugins/occt/configs/thumbnail.d/10_occt.json +++ b/plugins/occt/configs/thumbnail.d/10_occt.json @@ -1,6 +1,7 @@ { ".*(step|stp|iges|igs|brep|xbf)": { + "scalar-coloring": true, "load-plugins": "occt", "up": "+Z", "ambient-occlusion": true, diff --git a/python/F3DPythonBindings.cxx b/python/F3DPythonBindings.cxx index 3c915d3b25..47f8754ff0 100644 --- a/python/F3DPythonBindings.cxx +++ b/python/F3DPythonBindings.cxx @@ -1,14 +1,16 @@ +#include #include #include #include +#include #include "camera.h" #include "engine.h" #include "image.h" #include "interactor.h" -#include "loader.h" #include "log.h" #include "options.h" +#include "scene.h" #include "types.h" #include "utils.h" #include "window.h" @@ -23,7 +25,7 @@ bool load_array(const py::handle& src, bool convert, std::array& value) return false; } const py::sequence l = py::reinterpret_borrow(src); - if (l.size() != S) + if (l.size() != 3) { return false; } @@ -36,6 +38,26 @@ bool load_array(const py::handle& src, bool convert, std::array& value) return true; } +bool load_array(const py::handle& src, bool convert, f3d::vector3_t& value) +{ + if (!py::isinstance(src)) + { + return false; + } + const py::sequence l = py::reinterpret_borrow(src); + if (l.size() != 3) + { + return false; + } + + size_t i = 0; + for (auto it : l) + { + value[i++] = py::cast(it); + } + return true; +} + template<> class py::detail::type_caster { @@ -222,8 +244,26 @@ PYBIND11_MODULE(pyf3d, module) .def("record_interaction", &f3d::interactor::recordInteraction, "Record an interaction file") .def("start", &f3d::interactor::start, "Start the interactor") .def("stop", &f3d::interactor::start, "Stop the interactor") + .def("add_command_callback", &f3d::interactor::addCommandCallback, "Add a new command callback") + .def("remove_command_callback", &f3d::interactor::removeCommandCallback, + "Remove a command callback") + .def("trigger_command", &f3d::interactor::triggerCommand, "Trigger a command") + .def("add_interaction_commands", &f3d::interactor::addInteractionCommands, + "Add interaction commands") + .def("add_interaction_command", &f3d::interactor::addInteractionCommand, + "Add an interaction command") + .def("remove_interaction_command", &f3d::interactor::removeInteractionCommands, + "Remove interaction commands") .def_static("get_default_interactions_info", &f3d::interactor::getDefaultInteractionsInfo); + py::enum_(interactor, "ModifierKeys") + .value("ANY", f3d::interactor::ModifierKeys::ANY) + .value("NONE", f3d::interactor::ModifierKeys::NONE) + .value("CTRL", f3d::interactor::ModifierKeys::CTRL) + .value("SHIFT", f3d::interactor::ModifierKeys::SHIFT) + .value("CTRL_SHIFT", f3d::interactor::ModifierKeys::CTRL_SHIFT) + .export_values(); + // f3d::mesh_t py::class_(module, "Mesh") .def(py::init<>()) @@ -239,16 +279,19 @@ PYBIND11_MODULE(pyf3d, module) .def_readwrite("face_sides", &f3d::mesh_t::face_sides) .def_readwrite("face_indices", &f3d::mesh_t::face_indices); - // f3d::loader - py::class_> loader(module, "Loader"); - loader // - .def("has_geometry_reader", &f3d::loader::hasGeometryReader) - .def("load_geometry", py::overload_cast(&f3d::loader::loadGeometry), - "load geometry to a default scene", py::arg("file_path"), py::arg("reset") = false) - .def("has_scene_reader", &f3d::loader::hasSceneReader) - .def("load_scene", &f3d::loader::loadScene, "Load a specific full scene file") - .def("load_geometry", py::overload_cast(&f3d::loader::loadGeometry), - "Load a surfacic mesh from memory", py::arg("mesh"), py::arg("reset") = false); + // f3d::scene + py::class_> scene(module, "Scene"); + scene // + .def("supports", &f3d::scene::supports) + .def("clear", &f3d::scene::clear) + .def("add", py::overload_cast(&f3d::scene::add), + "Add a file the scene", py::arg("file_path")) + .def("add", py::overload_cast&>(&f3d::scene::add), + "Add multiple filepaths to the scene", py::arg("file_path_vector")) + .def("add", py::overload_cast&>(&f3d::scene::add), + "Add multiple filenames to the scene", py::arg("file_name_vector")) + .def("add", py::overload_cast(&f3d::scene::add), + "Add a surfacic mesh from memory into the scene", py::arg("mesh")); // f3d::camera py::class_> camera(module, "Camera"); @@ -290,13 +333,18 @@ PYBIND11_MODULE(pyf3d, module) py::enum_(window, "Type") .value("NONE", f3d::window::Type::NONE) - .value("NATIVE", f3d::window::Type::NATIVE) - .value("NATIVE_OFFSCREEN", f3d::window::Type::NATIVE_OFFSCREEN) .value("EXTERNAL", f3d::window::Type::EXTERNAL) + .value("GLX", f3d::window::Type::GLX) + .value("WGL", f3d::window::Type::WGL) + .value("COCOA", f3d::window::Type::COCOA) + .value("EGL", f3d::window::Type::EGL) + .value("OSMESA", f3d::window::Type::OSMESA) + .value("UNKNOWN", f3d::window::Type::UNKNOWN) .export_values(); window // .def_property_readonly("type", &f3d::window::getType) + .def_property_readonly("offscreen", &f3d::window::isOffscreen) .def_property_readonly("camera", &f3d::window::getCamera, py::return_value_policy::reference) .def_property( "size", @@ -322,13 +370,32 @@ PYBIND11_MODULE(pyf3d, module) py::class_ engine(module, "Engine"); engine // - .def(py::init(), py::arg("window_type") = f3d::window::Type::NATIVE) + .def_static("create", &f3d::engine::create, "Create an engine with a automatic window") + .def_static("create_none", &f3d::engine::createNone, "Create an engine with no window") + .def_static( + "create_glx", &f3d::engine::createGLX, "Create an engine with an GLX window (Linux only)") + .def_static( + "create_wgl", &f3d::engine::createWGL, "Create an engine with an WGL window (Windows only)") + .def_static("create_egl", &f3d::engine::createEGL, + "Create an engine with an EGL window (Windows/Linux only)") + .def_static("create_osmesa", &f3d::engine::createOSMesa, + "Create an engine with an OSMesa window (Windows/Linux only)") + .def_static("create_external_glx", &f3d::engine::createExternalGLX, + "Create an engine with an existing GLX context (Linux only)") + .def_static("create_external_wgl", &f3d::engine::createExternalWGL, + "Create an engine with an existing WGL context (Windows only)") + .def_static("create_external_cocoa", &f3d::engine::createExternalCOCOA, + "Create an engine with an existing COCOA context (macOS only)") + .def_static("create_external_egl", &f3d::engine::createExternalEGL, + "Create an engine with an existing EGL context (Windows/Linux only)") + .def_static("create_external_osmesa", &f3d::engine::createExternalOSMesa, + "Create an engine with an existing OSMesa context (Windows/Linux only)") .def("set_cache_path", &f3d::engine::setCachePath, "Set the cache path directory") .def_property("options", &f3d::engine::getOptions, py::overload_cast(&f3d::engine::setOptions), py::return_value_policy::reference) .def_property_readonly("window", &f3d::engine::getWindow, py::return_value_policy::reference) - .def_property_readonly("loader", &f3d::engine::getLoader, py::return_value_policy::reference) + .def_property_readonly("scene", &f3d::engine::getScene, py::return_value_policy::reference) .def_property_readonly( "interactor", &f3d::engine::getInteractor, py::return_value_policy::reference) .def_static("load_plugin", &f3d::engine::loadPlugin, "Load a plugin") diff --git a/python/__init__.py.in b/python/__init__.py.in index 56bdfdbda9..1cbcd1ac0f 100644 --- a/python/__init__.py.in +++ b/python/__init__.py.in @@ -68,7 +68,7 @@ def deprecated_decorator(f, reason): def add_deprecation_warnings(): for f3d_class in ( Camera, - Loader, + Scene, Options, Interactor, Engine, diff --git a/python/testing/CMakeLists.txt b/python/testing/CMakeLists.txt index d68d52e84c..9d1562848d 100644 --- a/python/testing/CMakeLists.txt +++ b/python/testing/CMakeLists.txt @@ -1,5 +1,6 @@ list(APPEND pyf3dTests_list test_camera.py + test_interactor.py test_image.py test_options.py test_utils.py @@ -13,7 +14,7 @@ endif() list(APPEND pyf3dTests_list test_image_compare.py - test_loader.py + test_scene.py ) list(APPEND pyf3dTestsNoRender_list diff --git a/python/testing/test_camera.py b/python/testing/test_camera.py index 08dd9b6d7c..c875a3ead2 100644 --- a/python/testing/test_camera.py +++ b/python/testing/test_camera.py @@ -4,7 +4,7 @@ def test_properties(): - engine = f3d.Engine(f3d.Window.NATIVE_OFFSCREEN) + engine = f3d.Engine.create(True) camera = engine.window.camera pos = 1, 2, 3 @@ -23,7 +23,7 @@ def test_properties(): def test_get_state(): - engine = f3d.Engine(f3d.Window.NATIVE_OFFSCREEN) + engine = f3d.Engine.create(True) camera = engine.window.camera pos = 1, 2, 3 @@ -43,7 +43,7 @@ def test_get_state(): def test_set_state(): - engine = f3d.Engine(f3d.Window.NATIVE_OFFSCREEN) + engine = f3d.Engine.create(True) camera = engine.window.camera state = f3d.CameraState((1, 2, 3), (1, 22, 3), (0, 0, 1), 32) @@ -74,7 +74,7 @@ def test_state_compare(): def test_moves(): - engine = f3d.Engine(f3d.Window.NATIVE_OFFSCREEN) + engine = f3d.Engine.create(True) camera = engine.window.camera camera.dolly(10) @@ -87,7 +87,7 @@ def test_moves(): def test_pan(): - engine = f3d.Engine(f3d.Window.NATIVE_OFFSCREEN) + engine = f3d.Engine.create(True) camera = engine.window.camera camera.state = f3d.CameraState((1, 2, 3), (1, 2, 13), (0, 1, 0), 40) @@ -102,7 +102,7 @@ def test_pan(): def test_resets(): - engine = f3d.Engine(f3d.Window.NATIVE_OFFSCREEN) + engine = f3d.Engine.create(True) camera = engine.window.camera camera.set_current_as_default() camera.reset_to_bounds() diff --git a/python/testing/test_image.py b/python/testing/test_image.py index 60dc5bc711..39f4c9c189 100644 --- a/python/testing/test_image.py +++ b/python/testing/test_image.py @@ -8,7 +8,7 @@ @pytest.fixture def f3d_engine(): - engine = f3d.Engine(f3d.Window.NATIVE_OFFSCREEN) + engine = f3d.Engine.create(True) engine.window.size = 300, 200 return engine diff --git a/python/testing/test_image_compare.py b/python/testing/test_image_compare.py index 2dfa4346d9..e7fa2cf87b 100644 --- a/python/testing/test_image_compare.py +++ b/python/testing/test_image_compare.py @@ -12,18 +12,18 @@ def test_compare_with_file(): output = tempfile.gettempdir() + "/TestPythonCompareWithFile.png" outputDiff = tempfile.gettempdir() + "/TestPythonCompareWithFile.diff.png" - engine = f3d.Engine(f3d.Window.NATIVE_OFFSCREEN) + engine = f3d.Engine.create(True) engine.window.size = 300, 300 # verify the size is properly set assert engine.window.width == 300 assert engine.window.height == 300 - engine.loader.load_geometry(dataset, True) + engine.scene.add(dataset) img = engine.window.render_to_image() img.save(output) error = 0.0 - assert img.compare(f3d.Image(reference), 50, error) + assert img.compare(f3d.Image(reference), 0.05, error) diff --git a/python/testing/test_interactor.py b/python/testing/test_interactor.py new file mode 100644 index 0000000000..729c8226fd --- /dev/null +++ b/python/testing/test_interactor.py @@ -0,0 +1,40 @@ +import os + +import pytest + +import f3d + + +def callback_fn(args): + print(args) + return True + + +def test_command(capfd): + engine = f3d.Engine.create(True) + inter = engine.interactor + inter.add_command_callback("my_cmd", callback_fn) + inter.trigger_command("my_cmd arg1 arg2") + inter.remove_command_callback("my_cmd") + out, err = capfd.readouterr() + assert out == "['arg1', 'arg2']\n" + + +def test_interaction_command(): + # Smoke test + engine = f3d.Engine.create(True) + inter = engine.interactor + inter.add_interaction_command("P", f3d.Interactor.ModifierKeys.ANY, "dummy command") + inter.add_interaction_command( + "P", f3d.Interactor.ModifierKeys.NONE, "dummy command" + ) + inter.add_interaction_command( + "P", f3d.Interactor.ModifierKeys.CTRL, "dummy command" + ) + inter.add_interaction_command( + "P", f3d.Interactor.ModifierKeys.SHIFT, "dummy command" + ) + inter.add_interaction_commands( + "P", f3d.Interactor.ModifierKeys.CTRL_SHIFT, ["dummy command", "dummy command"] + ) + inter.remove_interaction_command("P", f3d.Interactor.ModifierKeys.ANY) diff --git a/python/testing/test_loader.py b/python/testing/test_loader.py deleted file mode 100644 index c028531bca..0000000000 --- a/python/testing/test_loader.py +++ /dev/null @@ -1,30 +0,0 @@ -from pathlib import Path -import pytest -import tempfile - -import f3d - - -def test_load_memory(): - testing_dir = Path(__file__).parent.parent.parent / "testing" - reference = f"{testing_dir}/baselines/TestPythonLoadMemory.png" - output = tempfile.gettempdir() + "/TestPythonLoadMemory.png" - outputDiff = tempfile.gettempdir() + "/TestPythonLoadMemory.diff.png" - - engine = f3d.Engine(f3d.Window.NATIVE_OFFSCREEN) - engine.window.size = 300, 300 - - engine.loader.load_geometry( - f3d.Mesh( - points=[0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0], - face_sides=[3], - face_indices=[0, 1, 2], - ) - ) - - img = engine.window.render_to_image() - img.save(output) - - error = 0.0 - - assert img.compare(f3d.Image(reference), 50, error) diff --git a/python/testing/test_options.py b/python/testing/test_options.py index ea381848af..bae9b6d4d5 100644 --- a/python/testing/test_options.py +++ b/python/testing/test_options.py @@ -12,22 +12,22 @@ def test_closest_option(): def test_setitem(): options = f3d.Options() options["interactor.axis"] = False - options["model.material.roughness"] = 0.3 + options["scene.animation.frame_rate"] = 33.33 options["scene.animation.speed_factor"] = 3.3 options["render.raytracing.samples"] = 5 - options["model.color.rgb"] = [1.0, 1.0, 1.0] + options["render.grid.color"] = [1.0, 1.0, 1.0] options["scene.up_direction"] = "+Y" def test_getitem(): - engine = f3d.Engine(f3d.Window.NONE) + engine = f3d.Engine.create_none() options = engine.options assert options["interactor.axis"] is False - assert options["model.material.roughness"] == 0.3 + assert options["scene.animation.frame_rate"] == 60.0 assert options["scene.animation.speed_factor"] == 1.0 assert options["render.raytracing.samples"] == 5 - assert options["model.color.rgb"] == [1.0, 1.0, 1.0] + assert options["render.grid.color"] == [0.0, 0.0, 0.0] assert options["scene.up_direction"] == "+Y" @@ -61,7 +61,7 @@ def test_iter(): def test_contains(): options = f3d.Options() - assert "model.color.rgb" in options + assert "render.grid.color" in options assert "hello.world" not in options @@ -74,7 +74,7 @@ def test_set_options(): options["model.color.rgb"] = [0.0, 1.0, 1.0] options["scene.up_direction"] = "-Z" - engine = f3d.Engine(f3d.Window.NONE) + engine = f3d.Engine.create_none() engine.options = options assert engine.options["interactor.axis"] is True assert engine.options["model.material.roughness"] == 0.7 diff --git a/python/testing/test_scene.py b/python/testing/test_scene.py new file mode 100644 index 0000000000..b18ab62a03 --- /dev/null +++ b/python/testing/test_scene.py @@ -0,0 +1,55 @@ +from pathlib import Path +import pytest +import tempfile + +import f3d + + +def test_scene_memory(): + testing_dir = Path(__file__).parent.parent.parent / "testing" + reference = f"{testing_dir}/baselines/TestPythonSceneMemory.png" + output = tempfile.gettempdir() + "/TestPythonSceneMemory.png" + + engine = f3d.Engine.create(True) + engine.window.size = 300, 300 + + engine.scene.add( + f3d.Mesh( + points=[0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0], + face_sides=[3], + face_indices=[0, 1, 2], + ) + ) + + img = engine.window.render_to_image() + img.save(output) + + error = 0.0 + + assert img.compare(f3d.Image(reference), 0.05, error) + + +def test_scene(): + testing_dir = Path(__file__).parent.parent.parent / "testing" + world = f"{testing_dir}/data/world.obj" + logo = f"{testing_dir}/data/f3d.glb" + sphere1 = f"{testing_dir}/data/mb/recursive/mb_1_0.vtp" + sphere2 = f"{testing_dir}/data/mb/recursive/mb_2_0.vtp" + cube = f"{testing_dir}/data/mb/recursive/mb_0_0.vtu" + cube = f"{testing_dir}/data/f3d.glb" + reference = f"{testing_dir}/baselines/TestPythonScene.png" + output = tempfile.gettempdir() + "/TestPythonScene.png" + + engine = f3d.Engine.create(True) + engine.window.size = 300, 300 + + engine.scene.add([world, logo]) + engine.scene.add(Path(sphere1)) + engine.scene.add([Path(sphere2), Path(cube)]) + + img = engine.window.render_to_image() + img.save(output) + + error = 0.0 + + assert img.compare(f3d.Image(reference), 0.05, error) diff --git a/resources/configs/config.d/05_all.json b/resources/configs/config.d/05_all.json index 5a3c4bbc75..863318b5e9 100644 --- a/resources/configs/config.d/05_all.json +++ b/resources/configs/config.d/05_all.json @@ -7,7 +7,6 @@ "progress": true, "anti-aliasing": true, "filename": true, - "scalar-coloring": true, "camera-direction": "-1,-0.5,-1", "hdri-ambient": true, "translucency-support": true, diff --git a/resources/configs/thumbnail.d/05_all.json b/resources/configs/thumbnail.d/05_all.json index 7c337897e7..edf80c7da0 100644 --- a/resources/configs/thumbnail.d/05_all.json +++ b/resources/configs/thumbnail.d/05_all.json @@ -7,7 +7,6 @@ "max-size":100, "no-background": true, "verbose": "quiet", - "scalar-coloring": true, "tone-mapping": true, "translucency-support": true } diff --git a/testing/baselines/TestConfigFileBuild.png b/testing/baselines/TestConfigFileBuild.png index bf9f9decfb..eb194fb8a9 100644 --- a/testing/baselines/TestConfigFileBuild.png +++ b/testing/baselines/TestConfigFileBuild.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64fe65124fae71f4bb2d6a4de807b4fb638e62dd45a3ccc79889f8262213f166 -size 38805 +oid sha256:5e91fb45cd3297dc478100aec28a645862848efdb755f9438764b2476ee9018d +size 38519 diff --git a/testing/baselines/TestConfigFileMultiFileSTL.png b/testing/baselines/TestConfigFileMultiFileSTL.png new file mode 100644 index 0000000000..774f74dec7 --- /dev/null +++ b/testing/baselines/TestConfigFileMultiFileSTL.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ecf2e33e585024ec0ea5b03783d0a2bc4b4aa7f8b24bb2a17129fc60d20c437 +size 33521 diff --git a/testing/baselines/TestConfigFileMultiFileVTP.png b/testing/baselines/TestConfigFileMultiFileVTP.png new file mode 100644 index 0000000000..a98f73117c --- /dev/null +++ b/testing/baselines/TestConfigFileMultiFileVTP.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6b6e661b980b215da9abd4b6c6ec516469bd6fb88e78b021247acbbb448f5a46 +size 47342 diff --git a/testing/baselines/TestConfigFileUpperCase.png b/testing/baselines/TestConfigFileUpperCase.png index 914a83e32c..f7340a207c 100644 --- a/testing/baselines/TestConfigFileUpperCase.png +++ b/testing/baselines/TestConfigFileUpperCase.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:17bf15bce32f4e6d8c8f2ea65e9bb862d3301320785da7d19137d23b67065d4a -size 30155 +oid sha256:0fab3f6ca9e1ef6b560e858339ea43ad3f41b7a8a798992723874a47c84232c9 +size 29656 diff --git a/testing/baselines/TestConfigStemBuild.png b/testing/baselines/TestConfigStemBuild.png index bf9f9decfb..eb194fb8a9 100644 --- a/testing/baselines/TestConfigStemBuild.png +++ b/testing/baselines/TestConfigStemBuild.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:64fe65124fae71f4bb2d6a4de807b4fb638e62dd45a3ccc79889f8262213f166 -size 38805 +oid sha256:5e91fb45cd3297dc478100aec28a645862848efdb755f9438764b2476ee9018d +size 38519 diff --git a/testing/baselines/TestDefaultConfigFileAlembic.png b/testing/baselines/TestDefaultConfigFileAlembic.png index ca950f9e09..c897214051 100644 --- a/testing/baselines/TestDefaultConfigFileAlembic.png +++ b/testing/baselines/TestDefaultConfigFileAlembic.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:82f38c692d20719e2d23ab00190288e86a455b4a22bc07dcd5c970329ebe6b9d -size 55044 +oid sha256:1dfece9ea774894b3af4303680ffdef64741e2218f0bb03d9234247d7198b50d +size 54772 diff --git a/testing/baselines/TestDefaultConfigFileAndCommand.png b/testing/baselines/TestDefaultConfigFileAndCommand.png index db50163fa9..236be94d03 100644 --- a/testing/baselines/TestDefaultConfigFileAndCommand.png +++ b/testing/baselines/TestDefaultConfigFileAndCommand.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9a71d0ed187b6f5b7d41bc68b9c833c4efe4dc506e83518fcc5e29a423a5e262 -size 55119 +oid sha256:6ce04e97aacddfc9df6c490a30003e830fe8851664c646a25ccee3b9a9aee743 +size 54056 diff --git a/testing/baselines/TestDefaultConfigFileAssimpDAE.png b/testing/baselines/TestDefaultConfigFileAssimpDAE.png index 08a71e828b..a2859db961 100644 --- a/testing/baselines/TestDefaultConfigFileAssimpDAE.png +++ b/testing/baselines/TestDefaultConfigFileAssimpDAE.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:08af4ae8cba944a2c22735db235524c92b4ef0065379e0add2b7327048d0938b -size 49911 +oid sha256:8bd35e83afbb608e801ed75e37005dbb81110f518ec53c427f0b3f31ed3e554a +size 47479 diff --git a/testing/baselines/TestDefaultConfigFileAssimpDXF.png b/testing/baselines/TestDefaultConfigFileAssimpDXF.png index 3ebd2b4a8a..9d65f42e05 100644 --- a/testing/baselines/TestDefaultConfigFileAssimpDXF.png +++ b/testing/baselines/TestDefaultConfigFileAssimpDXF.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3e7eca49473becb914b6a326cdee62978080036a114b0adfd5e5fc94e2595a5d -size 42904 +oid sha256:77db87dd407cc39de92e389c81548115515ec10c2157a6a7ec99b13892c063c2 +size 42213 diff --git a/testing/baselines/TestDefaultConfigFileAssimpFBX.png b/testing/baselines/TestDefaultConfigFileAssimpFBX.png index 44cf8bcffd..5fd11af13a 100644 --- a/testing/baselines/TestDefaultConfigFileAssimpFBX.png +++ b/testing/baselines/TestDefaultConfigFileAssimpFBX.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bf9be531af3b6a89a03d9771fe1927cd43c010a1d26cacb0a6a16f436f2a5d7b -size 44021 +oid sha256:e8ee1a46c3859fab2cce8825451ca9744700a61a2076723134a3a8038c1a299e +size 37972 diff --git a/testing/baselines/TestDefaultConfigFileAssimpOFF.png b/testing/baselines/TestDefaultConfigFileAssimpOFF.png index 384ac4acb6..ae31253bfa 100644 --- a/testing/baselines/TestDefaultConfigFileAssimpOFF.png +++ b/testing/baselines/TestDefaultConfigFileAssimpOFF.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:69f3e78389a00c277eb6bf03be455891a401ccc3efe1f1c13b9d14325268efb4 -size 47445 +oid sha256:13ffdbefd06762522e55eb61182bfa3e183a32d3eeed27d05ab99c3b5e6389ea +size 43225 diff --git a/testing/baselines/TestDefaultConfigFileDraco.png b/testing/baselines/TestDefaultConfigFileDraco.png index f3a13e6587..a639a0327b 100644 --- a/testing/baselines/TestDefaultConfigFileDraco.png +++ b/testing/baselines/TestDefaultConfigFileDraco.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79919f963dc17d379a043ad57d5deaa664add26a27d13b882e1d189be0c1d1e8 -size 71846 +oid sha256:3a03d4cbb1bf857f5429714b0bfea6d9cbf33702ca72d1b56ed5d93ac01983e9 +size 71861 diff --git a/testing/baselines/TestDefaultConfigFileExodus.png b/testing/baselines/TestDefaultConfigFileExodus.png index a17309929f..a1c94b7854 100644 --- a/testing/baselines/TestDefaultConfigFileExodus.png +++ b/testing/baselines/TestDefaultConfigFileExodus.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:401b26823ace1803a320edf8d996a5081bff440b10d2a26e15252c9b7bf2e24c -size 53420 +oid sha256:4019af19987f264e6142313abf605883de0646af92c7d45e23f5d07e269167d4 +size 50957 diff --git a/testing/baselines/TestDefaultConfigFileOCCT.png b/testing/baselines/TestDefaultConfigFileOCCT.png index 559790fd46..e04c41c994 100644 --- a/testing/baselines/TestDefaultConfigFileOCCT.png +++ b/testing/baselines/TestDefaultConfigFileOCCT.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3b39d87f37ad31fcb12183e617324aaa85f46b26b676bd96e3cd19eb18257538 -size 45064 +oid sha256:10bb41e7e2b49f3e636a7e2e4bbcb57dc715e034b237cc96542ec2141bd09c78 +size 41175 diff --git a/testing/baselines/TestDefaultConfigFilePLY.png b/testing/baselines/TestDefaultConfigFilePLY.png index 9c04cc80e7..7527af0974 100644 --- a/testing/baselines/TestDefaultConfigFilePLY.png +++ b/testing/baselines/TestDefaultConfigFilePLY.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:97133371b04c08e0f487a63d82e45d22bd53e235d0b919bf0eb35812b2baa887 -size 68387 +oid sha256:dc1d94356d4bfcd04ef55767a9e873f27242746a92d75cacbfa9717ec36d3fc7 +size 68341 diff --git a/testing/baselines/TestDefaultConfigFileSTL.png b/testing/baselines/TestDefaultConfigFileSTL.png index 8ad322aa38..3ba75d4d06 100644 --- a/testing/baselines/TestDefaultConfigFileSTL.png +++ b/testing/baselines/TestDefaultConfigFileSTL.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:438b4fe00cb7f0ab8cbc2322e53d92afa2eb248300cf5afb780aef89127e208f -size 55090 +oid sha256:75b654af3ebd04dc4f8f054a62977e63ca7979ef5e644eeb1d06b429d2297831 +size 54712 diff --git a/testing/baselines/TestDefaultConfigFileTIFF.png b/testing/baselines/TestDefaultConfigFileTIFF.png index bc3a9c3fca..10cb5ebd16 100644 --- a/testing/baselines/TestDefaultConfigFileTIFF.png +++ b/testing/baselines/TestDefaultConfigFileTIFF.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ee32ee5cf08e00caaf19097eadbb10240a1709e77d3de2f91397fb7e670a8f0e -size 39114 +oid sha256:f7af89b7d5b388d9dfaff9e9f59ab7df52fbae2dbf54d241f33af816f268e03a +size 38406 diff --git a/testing/baselines/TestDefaultConfigFileUSD.png b/testing/baselines/TestDefaultConfigFileUSD.png index f8412c1b19..7bda2807c4 100644 --- a/testing/baselines/TestDefaultConfigFileUSD.png +++ b/testing/baselines/TestDefaultConfigFileUSD.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f775cce963b17d0b00985c79a5482d0793c3628ddf24031c59d25acd6189007c -size 49413 +oid sha256:b8a46d3f26b7e7a0ec4e6a2ff8ec5cce2ed2cc71e94eb61936465b35950b3a06 +size 49374 diff --git a/testing/baselines/TestDefaultConfigFileVDB.png b/testing/baselines/TestDefaultConfigFileVDB.png index 1f8ad46fc3..e1ba46988c 100644 --- a/testing/baselines/TestDefaultConfigFileVDB.png +++ b/testing/baselines/TestDefaultConfigFileVDB.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a4bc224473e295d059f5ee304360fe6740884b18d597635206685f30ff85b87a -size 57443 +oid sha256:70a806c5a30e8f793b4cfe66c9fa91005bd5c40fa002ef4954aa8a3890c407af +size 54918 diff --git a/testing/baselines/TestDefaultConfigFileVTI.png b/testing/baselines/TestDefaultConfigFileVTI.png index e5365c44ca..8e8def6a1f 100644 --- a/testing/baselines/TestDefaultConfigFileVTI.png +++ b/testing/baselines/TestDefaultConfigFileVTI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0046d8829e047bb8ce62dfaf8b8196ab38aa8c2b824672537e63882103ff58c5 -size 57503 +oid sha256:e820e5780ccbc02f80f4521e498ef8fac019116841b4daffe985cb72c3a74b3e +size 53264 diff --git a/testing/baselines/TestDefaultConfigFileVTU.png b/testing/baselines/TestDefaultConfigFileVTU.png index ae195463bf..ec25761244 100644 --- a/testing/baselines/TestDefaultConfigFileVTU.png +++ b/testing/baselines/TestDefaultConfigFileVTU.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:26e32202d361bb2d4dfdb9ac2596c7ec3f9d0cb7f4da58441697a9e57dd22608 -size 67247 +oid sha256:3433f36c7e1d87e28591b23e0a16f9f753ac4c2926594ba9e84383be3857a330 +size 65317 diff --git a/testing/baselines/TestDefaultConfigTranslucent.png b/testing/baselines/TestDefaultConfigTranslucent.png index 161ba7a17a..821317aab2 100644 --- a/testing/baselines/TestDefaultConfigTranslucent.png +++ b/testing/baselines/TestDefaultConfigTranslucent.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:34e816cc5dff9ad4b440dedbdea0de207b27fe8b062ca16a7de95f9c059952bf -size 47955 +oid sha256:a9faa29e4b95919ea1d4d77d35819b1b9afdf2ece7306e0df1d9fcf7e16ea3bb +size 48193 diff --git a/testing/baselines/TestGLTFDracoImporterWithoutCompression.png b/testing/baselines/TestGLTFDracoImporterWithoutCompression.png new file mode 100644 index 0000000000..2b15acef59 --- /dev/null +++ b/testing/baselines/TestGLTFDracoImporterWithoutCompression.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8e3273b3cb9f35e786b59beaa7adc58ea00b456ba7fb9d0d080b9f2fdc681420 +size 1184 diff --git a/testing/baselines/TestGLTFDracoReader.png b/testing/baselines/TestGLTFDracoReader.png deleted file mode 100644 index 2baef14d72..0000000000 --- a/testing/baselines/TestGLTFDracoReader.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9d078ab66dbe6d8a5516274e9f7dc196fc6d76897a22e0b8b74a96974646c8f8 -size 2948 diff --git a/testing/baselines/TestGLTFDracoReaderWithoutCompression.png b/testing/baselines/TestGLTFDracoReaderWithoutCompression.png deleted file mode 100644 index 2c2db52fe9..0000000000 --- a/testing/baselines/TestGLTFDracoReaderWithoutCompression.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d1a0da6ad25f8e0843845d8356ce54e0476bd293d4d26768f008e5293d7a1a74 -size 2375 diff --git a/testing/baselines/TestGLTFReaderWithAnimation.png b/testing/baselines/TestGLTFReaderWithAnimation.png deleted file mode 100644 index 2c2db52fe9..0000000000 --- a/testing/baselines/TestGLTFReaderWithAnimation.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d1a0da6ad25f8e0843845d8356ce54e0476bd293d4d26768f008e5293d7a1a74 -size 2375 diff --git a/testing/baselines/TestGridAbsolute.png b/testing/baselines/TestGridAbsolute.png index fd4d8687cb..a2fd8403df 100644 --- a/testing/baselines/TestGridAbsolute.png +++ b/testing/baselines/TestGridAbsolute.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc7f7975106cfc15d6ecc560a35111af19cc724e67baa0678b720811059ce156 -size 43846 +oid sha256:fbbc1cf7bb1f684ba083bface79d8c51405ca36e691c252f173f7915f0a22145 +size 38914 diff --git a/testing/baselines/TestGridClipping.png b/testing/baselines/TestGridClipping.png index f675a9a778..2996d25766 100644 --- a/testing/baselines/TestGridClipping.png +++ b/testing/baselines/TestGridClipping.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:140e0c9d1adba138304614276b151c0b7e89f1712cec7f2eed085ff6c9d2408c -size 13218 +oid sha256:e247c3f0e43aefcb11355f21b476fc388a252407d3e6030c5acaae84e050146f +size 11133 diff --git a/testing/baselines/TestGridColor.png b/testing/baselines/TestGridColor.png index e513171bc4..efe120791a 100644 --- a/testing/baselines/TestGridColor.png +++ b/testing/baselines/TestGridColor.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:43b21cdd7ba242d178caa3a7aaddcef87328a90ebdb982ce9e41e139c8353638 -size 35527 +oid sha256:8bb502fbbe4f0d2045d8b9b3496396dd6985ca0b0dfb9e61fb7a9ce6d2a7c2fd +size 34426 diff --git a/testing/baselines/TestGridWithDepthPeeling.png b/testing/baselines/TestGridWithDepthPeeling.png index 92eb1d833e..d080c7a1a8 100644 --- a/testing/baselines/TestGridWithDepthPeeling.png +++ b/testing/baselines/TestGridWithDepthPeeling.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5a7b709ba097de3aca25f562f8b312303c1c066bb3b8b7cde5ffea3cbc49f0ec -size 26931 +oid sha256:6c83a93f0ec9c0ed5589601a469dd82e603bd6e0e0598db34df36068441cd056 +size 25770 diff --git a/testing/baselines/TestGridX.png b/testing/baselines/TestGridX.png index 3d133b013d..37f9ea9f12 100644 --- a/testing/baselines/TestGridX.png +++ b/testing/baselines/TestGridX.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f77f362288ecec58cce5a81b8ede81f2b9ee4a39a13890944bf5d0143641e17a -size 23757 +oid sha256:3e9fc0ae65a4020e0f48ffa333544fe61af8b2813803ecd4537eac3f5f702428 +size 23543 diff --git a/testing/baselines/TestGridY.png b/testing/baselines/TestGridY.png index 2478e92502..de934bb3bd 100644 --- a/testing/baselines/TestGridY.png +++ b/testing/baselines/TestGridY.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:317358f35dd1d55e215c16c8459ae486bc4aa2cd95a830ea7414039f84ff256b -size 31140 +oid sha256:b9a2fbbe262be5a992cfba84441d630b285fc75e434540015b53c3449e0175b8 +size 30166 diff --git a/testing/baselines/TestGridZ.png b/testing/baselines/TestGridZ.png index 19076e9576..c627afee81 100644 --- a/testing/baselines/TestGridZ.png +++ b/testing/baselines/TestGridZ.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c4e2f220c7f2df45fa82ed0be83011d5cf5d5f7af7184a1068d226bc49a6bb7 -size 21182 +oid sha256:808c6c4c967ca37832f628c37451208a6d00d721a707c0f4c0f01b9a76d7b43e +size 20366 diff --git a/testing/baselines/TestGroupGeometries.png b/testing/baselines/TestGroupGeometries.png deleted file mode 100644 index 95a8159d03..0000000000 --- a/testing/baselines/TestGroupGeometries.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f04ed30ffa6064f085098e443eaca62b16c8acbb563251bb7413efa53334761f -size 14332 diff --git a/testing/baselines/TestGroupGeometriesColoring.png b/testing/baselines/TestGroupGeometriesColoring.png deleted file mode 100644 index 5ada334e3f..0000000000 --- a/testing/baselines/TestGroupGeometriesColoring.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c4454d7f017687e721c2517a006cd0aac6f83c98520d4e4107a0a184a0d5f490 -size 23226 diff --git a/testing/baselines/TestInteractionActors.png b/testing/baselines/TestInteractionActors.png index aa4ba6aa73..7219bdf978 100644 --- a/testing/baselines/TestInteractionActors.png +++ b/testing/baselines/TestInteractionActors.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1d90d7d305176e65926a827bf958a938623d07644536714a5605dde88d60e2e5 -size 60066 +oid sha256:39e23188ffbfa201ec0f1f77c16933f3bdee3e803e4067b8d4b197cda2f3362f +size 59695 diff --git a/testing/baselines/TestInteractionCheatsheet.png b/testing/baselines/TestInteractionCheatsheet.png index 600df8c0b5..37d5a8e3ec 100644 --- a/testing/baselines/TestInteractionCheatsheet.png +++ b/testing/baselines/TestInteractionCheatsheet.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:50ed962c423e75685be6511b0aeffba19ae5c711a4ed9de61662b57b9dabaf04 -size 41064 +oid sha256:64698ebc267eead2ef0dba66e7e6f98239ec60eec27852bfe5ac3952f5db70f8 +size 41219 diff --git a/testing/baselines/TestInteractionCheatsheetBlackBG.png b/testing/baselines/TestInteractionCheatsheetBlackBG.png index 372d2d48f0..cdd799f588 100644 --- a/testing/baselines/TestInteractionCheatsheetBlackBG.png +++ b/testing/baselines/TestInteractionCheatsheetBlackBG.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3532eb19cee7a2edf27f50053f35e389ceda0c5414d02f8762c91dacadf848e0 -size 41594 +oid sha256:4aa47ddc44ee5d160f91ece7b70a225ac47308191cffa679c1d71997f7018985 +size 41806 diff --git a/testing/baselines/TestInteractionCheatsheetBlackBGRaytracing.png b/testing/baselines/TestInteractionCheatsheetBlackBGRaytracing.png index b7473f9197..a612617f5f 100644 --- a/testing/baselines/TestInteractionCheatsheetBlackBGRaytracing.png +++ b/testing/baselines/TestInteractionCheatsheetBlackBGRaytracing.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5f2a1ebb00c8e2929ceaf5233c4e72b5f8590af324b9052e8273dfc3332ab16f -size 41400 +oid sha256:3296fc3a0d4542385e850b92889975872fcc3be77a76be30345399efccfe054a +size 41681 diff --git a/testing/baselines/TestInteractionCheatsheetRaytracing.png b/testing/baselines/TestInteractionCheatsheetRaytracing.png index c932284142..a7e9fddc9f 100644 --- a/testing/baselines/TestInteractionCheatsheetRaytracing.png +++ b/testing/baselines/TestInteractionCheatsheetRaytracing.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5303a4ab0fc36d1d65c4faca792508c0a6daa10f033b64f2c7cc20c0190bb97 -size 40729 +oid sha256:022c9a0e8aac2e7141b93c27dc40c6dc35eeee3c5de995180392135f30bdef31 +size 40962 diff --git a/testing/baselines/TestInteractionCheatsheetScalars.png b/testing/baselines/TestInteractionCheatsheetScalars.png index e6ec418fb3..51ffbab243 100644 --- a/testing/baselines/TestInteractionCheatsheetScalars.png +++ b/testing/baselines/TestInteractionCheatsheetScalars.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1989f16bbbaf682fb902eee38d96710cac53dd30141ec24cde934773b3a5bed7 -size 46922 +oid sha256:1912259c7fed1df1c3a55cb215b4e7cd310b8c9630cfcd83212e5639fcc67c42 +size 47124 diff --git a/testing/baselines/TestInteractionCheatsheetScalarsRaytracing.png b/testing/baselines/TestInteractionCheatsheetScalarsRaytracing.png index f680936645..b74937c68e 100644 --- a/testing/baselines/TestInteractionCheatsheetScalarsRaytracing.png +++ b/testing/baselines/TestInteractionCheatsheetScalarsRaytracing.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dcd037c326bb40e155668c31aa12f02d1eae94ef0fed41908333110975f69191 -size 46355 +oid sha256:cfee50b4f2bb58c6e5542e8e9520f4446eb685dea0344b7a23312b369c78bcc1 +size 46576 diff --git a/testing/baselines/TestInteractionCheatsheetWhiteBG.png b/testing/baselines/TestInteractionCheatsheetWhiteBG.png index 437d196018..0b422fda55 100644 --- a/testing/baselines/TestInteractionCheatsheetWhiteBG.png +++ b/testing/baselines/TestInteractionCheatsheetWhiteBG.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c27f7d2a0429de420305bc94f8b27c7c372332352e0d9be0722264519f3b49d -size 40396 +oid sha256:6404b42c2cc1c3357320d7915dbe44158426303f725d6546e6eb0972b197aa7c +size 40541 diff --git a/testing/baselines/TestInteractionCheatsheetWhiteBGRaytracing.png b/testing/baselines/TestInteractionCheatsheetWhiteBGRaytracing.png index cfaf74a773..a4bf9a3ecf 100644 --- a/testing/baselines/TestInteractionCheatsheetWhiteBGRaytracing.png +++ b/testing/baselines/TestInteractionCheatsheetWhiteBGRaytracing.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:95db6a1e789859b0e70bfb22e6e9dc406770e43d24bfd5c8d0d6878346ee1569 -size 39913 +oid sha256:5551820e5a020ff16993d5478d6c292361dcb963015e05e60cb57ae2d4c69eb6 +size 40160 diff --git a/testing/baselines/TestInteractionConfigFileAndCommand.png b/testing/baselines/TestInteractionConfigFileAndCommand.png index de599d7f0f..a8c996a20d 100644 --- a/testing/baselines/TestInteractionConfigFileAndCommand.png +++ b/testing/baselines/TestInteractionConfigFileAndCommand.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd58f6ec010c36fbc9b88e79db89b5720fa56a27bf2ac83a5d1de62af31803b0 -size 33461 +oid sha256:7367cdaed41c3cfdc1e379293d0ff81d3640e7a18bc74b8fbdcf09d1d4c7e53b +size 32979 diff --git a/testing/baselines/TestInteractionConfigFileNoColorBar.png b/testing/baselines/TestInteractionConfigFileNoColorBar.png index 7757b7a16a..8f699b0652 100644 --- a/testing/baselines/TestInteractionConfigFileNoColorBar.png +++ b/testing/baselines/TestInteractionConfigFileNoColorBar.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6fe144a1686566c1d6deeffd635d8c9e7b8ff043de17762b56fb9bad883eb830 -size 41786 +oid sha256:733004645acf0652bfa419441dae729b81e28128c4d98ff29fd6fb1e219ff390 +size 41189 diff --git a/testing/baselines/TestInteractionCycleCellInvalidIndex.png b/testing/baselines/TestInteractionCycleCellInvalidIndex.png new file mode 100644 index 0000000000..697d40fe85 --- /dev/null +++ b/testing/baselines/TestInteractionCycleCellInvalidIndex.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b0ecb619cc937590f69d684618c0f9409dbeaad586a06a884af637a60ecf6f77 +size 20779 diff --git a/testing/baselines/TestInteractionDirectoryLoop.png b/testing/baselines/TestInteractionDirectoryLoop.png index 1c41df2dec..0eb8577265 100644 --- a/testing/baselines/TestInteractionDirectoryLoop.png +++ b/testing/baselines/TestInteractionDirectoryLoop.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:570b3abd89de71240e355bada85d288a63e91feea0f2eeffba07793485d882a6 -size 21106 +oid sha256:38f9875782356c86564fabe11b55eb2480dcbe3ac3013cbc8850015e1fc323fa +size 6648 diff --git a/testing/baselines/TestInteractionDropFiles.png b/testing/baselines/TestInteractionDropFiles.png index de06f83186..9a83c1f602 100644 --- a/testing/baselines/TestInteractionDropFiles.png +++ b/testing/baselines/TestInteractionDropFiles.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9da59e303c40c194438d72dcfeae9d5889cfef198ff5856c7bceb871de7279d6 -size 26206 +oid sha256:7a98d14c8352ad47d0531256ca44b6b41810cd988372014e350c717aff4cb15a +size 30087 diff --git a/testing/baselines/TestInteractionDropHDRI.png b/testing/baselines/TestInteractionDropHDRI.png index cd87f6e8c1..658f7f1b40 100644 --- a/testing/baselines/TestInteractionDropHDRI.png +++ b/testing/baselines/TestInteractionDropHDRI.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d1f224198859c00fe0173f22dc95aab4c9810f34ee81362cabf511e89fe2f6f6 -size 90284 +oid sha256:8f15c27bac0148862f26892bf79572ff551ddfd30c90f1457ee0288bf0b35fd7 +size 91129 diff --git a/testing/baselines/TestInteractionDropHDRIExr.png b/testing/baselines/TestInteractionDropHDRIExr.png index 18a3f96f55..bc27ecbb2e 100644 --- a/testing/baselines/TestInteractionDropHDRIExr.png +++ b/testing/baselines/TestInteractionDropHDRIExr.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7fa4842ccef46a4524bc33829cbda5cc2eac71028682f6b0c765a0533849d6c -size 78333 +oid sha256:a1275567aa3449aeaa19e5414f3d024d7a45b3b1aa5f36b2743ecf6d33711ca6 +size 79224 diff --git a/testing/baselines/TestInteractionDropHDRIInvert.png b/testing/baselines/TestInteractionDropHDRIInvert.png index cd87f6e8c1..658f7f1b40 100644 --- a/testing/baselines/TestInteractionDropHDRIInvert.png +++ b/testing/baselines/TestInteractionDropHDRIInvert.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d1f224198859c00fe0173f22dc95aab4c9810f34ee81362cabf511e89fe2f6f6 -size 90284 +oid sha256:8f15c27bac0148862f26892bf79572ff551ddfd30c90f1457ee0288bf0b35fd7 +size 91129 diff --git a/testing/baselines/TestInteractionDropHDRIModifiers.png b/testing/baselines/TestInteractionDropHDRIModifiers.png new file mode 100644 index 0000000000..5278e31d39 --- /dev/null +++ b/testing/baselines/TestInteractionDropHDRIModifiers.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2905cd7f89061e4cb8ab92249e360adf73896e645ea525d546d1be5fa5128abd +size 34787 diff --git a/testing/baselines/TestInteractionDropHDRIMulti.png b/testing/baselines/TestInteractionDropHDRIMulti.png index cd87f6e8c1..658f7f1b40 100644 --- a/testing/baselines/TestInteractionDropHDRIMulti.png +++ b/testing/baselines/TestInteractionDropHDRIMulti.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d1f224198859c00fe0173f22dc95aab4c9810f34ee81362cabf511e89fe2f6f6 -size 90284 +oid sha256:8f15c27bac0148862f26892bf79572ff551ddfd30c90f1457ee0288bf0b35fd7 +size 91129 diff --git a/testing/baselines/TestInteractionGroupGeometriesColoring.png b/testing/baselines/TestInteractionGroupGeometriesColoring.png deleted file mode 100644 index 795a41caf1..0000000000 --- a/testing/baselines/TestInteractionGroupGeometriesColoring.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9598b4c1bc2a8b484a167cff25f2530a14dce63e26887c93e590fbcc836e9ea0 -size 21303 diff --git a/testing/baselines/TestInteractionGroupGeometriesLoadParentDirectory.png b/testing/baselines/TestInteractionGroupGeometriesLoadParentDirectory.png deleted file mode 100644 index b3bd9c3af8..0000000000 --- a/testing/baselines/TestInteractionGroupGeometriesLoadParentDirectory.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d8761068ab79ab4eae6e6acc46b401fb469b5735cc22bac2de3e5810f29b06fb -size 19725 diff --git a/testing/baselines/TestInteractionHDRIChange.png b/testing/baselines/TestInteractionHDRIChange.png index a1d9a3ea29..8d8fe69501 100644 --- a/testing/baselines/TestInteractionHDRIChange.png +++ b/testing/baselines/TestInteractionHDRIChange.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:26460a376ccaddc8752fd2fa8c70837a62a52aafaa4d07de582c9c295cd998cf -size 90972 +oid sha256:b7edb743d03f34bd37121c3c546bcee5aeea1f4e582a8779d0b1993db1d580ef +size 97864 diff --git a/testing/baselines/TestInteractionMultiFileColoring.png b/testing/baselines/TestInteractionMultiFileColoring.png new file mode 100644 index 0000000000..ff4b16377a --- /dev/null +++ b/testing/baselines/TestInteractionMultiFileColoring.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5633d42bb617b0cdb9264bf68d33775f7fe41f011f62606791d55c65680944d9 +size 4754 diff --git a/testing/baselines/TestInteractionGroupGeometriesDrop.png b/testing/baselines/TestInteractionMultiFileDrop.png similarity index 100% rename from testing/baselines/TestInteractionGroupGeometriesDrop.png rename to testing/baselines/TestInteractionMultiFileDrop.png diff --git a/testing/baselines/TestInteractionMultiFileLoadParentDirectory.png b/testing/baselines/TestInteractionMultiFileLoadParentDirectory.png new file mode 100644 index 0000000000..7b074dcd47 --- /dev/null +++ b/testing/baselines/TestInteractionMultiFileLoadParentDirectory.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:426c0a2ff3bd05cdbaf5184d4654e749cdae60f54c9994fdb68d8c45e91f9cb9 +size 7748 diff --git a/testing/baselines/TestInteractionMultiFileVolume.png b/testing/baselines/TestInteractionMultiFileVolume.png new file mode 100644 index 0000000000..8518352d88 --- /dev/null +++ b/testing/baselines/TestInteractionMultiFileVolume.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2edf2ecb8bb456ff25a403d7a8274c305d5249f842dab0463c9410fa5a710869 +size 10730 diff --git a/testing/baselines/TestInteractionNoFileCheatsheet.png b/testing/baselines/TestInteractionNoFileCheatsheet.png index b14103d91b..0fc7c8aaec 100644 --- a/testing/baselines/TestInteractionNoFileCheatsheet.png +++ b/testing/baselines/TestInteractionNoFileCheatsheet.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:97ab0219e5782422e52c6dfb7f84bfb615a0e48b5f25eb81e8cacb0ce0963aa3 -size 31206 +oid sha256:d1daa826c3fe8ace330f32dccaa54dd2954fc0f2f3b53e333ee27fa4604292f4 +size 33670 diff --git a/testing/baselines/TestInteractionNoFileCheatsheetRaytracing.png b/testing/baselines/TestInteractionNoFileCheatsheetRaytracing.png index 34e0d5406e..60777643a0 100644 --- a/testing/baselines/TestInteractionNoFileCheatsheetRaytracing.png +++ b/testing/baselines/TestInteractionNoFileCheatsheetRaytracing.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc6f7d7759ac9a40be7df35ee0143d4479dea4f479fbe42fc9b63bea88bb9727 -size 31307 +oid sha256:57ab3737e19ba264e8b0b0cab213d5aef8f7dc788b7022429363881e5394f0a2 +size 33101 diff --git a/testing/baselines/TestInteractionResetCameraWithCameraIndex.png b/testing/baselines/TestInteractionResetCameraWithCameraIndex.png new file mode 100644 index 0000000000..a54719d91f --- /dev/null +++ b/testing/baselines/TestInteractionResetCameraWithCameraIndex.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:32292d819449d38ba59b53ab9c438407c6fd0b1ad39db2051823d3c220bb0f0f +size 2540 diff --git a/testing/baselines/TestInteractionVolumeAfterColoring.png b/testing/baselines/TestInteractionVolumeAfterColoring.png new file mode 100644 index 0000000000..15eb2cf6c5 --- /dev/null +++ b/testing/baselines/TestInteractionVolumeAfterColoring.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7d5c0729f018024243918690289e8a73b61f8e844ac939a0c81d6aeedb91ade9 +size 80342 diff --git a/testing/baselines/TestInteractionVolumeCycle.png b/testing/baselines/TestInteractionVolumeCycle.png new file mode 100644 index 0000000000..15eb2cf6c5 --- /dev/null +++ b/testing/baselines/TestInteractionVolumeCycle.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7d5c0729f018024243918690289e8a73b61f8e844ac939a0c81d6aeedb91ade9 +size 80342 diff --git a/testing/baselines/TestInvalidFileFileName.png b/testing/baselines/TestInvalidFileFileName.png new file mode 100644 index 0000000000..d0e291cfb5 --- /dev/null +++ b/testing/baselines/TestInvalidFileFileName.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d52bf15339fe32774f464e607cd33d036686f295553936599e010d7f0b8665b7 +size 10477 diff --git a/testing/baselines/TestMaterialFullScene.png b/testing/baselines/TestMaterialFullScene.png new file mode 100644 index 0000000000..a4df8df4e1 --- /dev/null +++ b/testing/baselines/TestMaterialFullScene.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:00b992a31481c9838b545b3c469631c21c366d9b0826fde6171b56639859f032 +size 15591 diff --git a/testing/baselines/TestMaxSizeAboveMultiFile.png b/testing/baselines/TestMaxSizeAboveMultiFile.png new file mode 100644 index 0000000000..9f5dec9678 --- /dev/null +++ b/testing/baselines/TestMaxSizeAboveMultiFile.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:103fd900202833d926a3662214a6abe92cb3d29af9f2f52d0b8bdb0c529b07ba +size 21417 diff --git a/testing/baselines/TestMetaData.png b/testing/baselines/TestMetaData.png index 7d00fbaea0..43e17e1679 100644 --- a/testing/baselines/TestMetaData.png +++ b/testing/baselines/TestMetaData.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3883d626fc703c5164c4acf9926fe301767976c70771122dc71c705353188b07 -size 6444 +oid sha256:899de118c9ea9a5a748503603c644e40815cd1b9d075076a3ae1d817b4d1fe32 +size 7282 diff --git a/testing/baselines/TestMetaDataImporter.png b/testing/baselines/TestMetaDataImporter.png index dd4e1b7d49..fbd8dae105 100644 --- a/testing/baselines/TestMetaDataImporter.png +++ b/testing/baselines/TestMetaDataImporter.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0fa5a91318ed6975f3a7fe7033eba264d166920e81c3c62a0664f28a686b1fd9 -size 2741 +oid sha256:afbdc55430e027dd101652a936f02b30224f0d1e2a519dfa5922e81f3e04c268 +size 5357 diff --git a/testing/baselines/TestMultiFile.png b/testing/baselines/TestMultiFile.png new file mode 100644 index 0000000000..4abd1ca44c --- /dev/null +++ b/testing/baselines/TestMultiFile.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:34534186de93d3dc1f9cff24292a4df5dbfabe8486b93f888af6dc47b72f78b1 +size 3335 diff --git a/testing/baselines/TestMultiFileAnimationIndex.png b/testing/baselines/TestMultiFileAnimationIndex.png new file mode 100644 index 0000000000..f8822c3462 --- /dev/null +++ b/testing/baselines/TestMultiFileAnimationIndex.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e788c601396836ab75734bb69ac22ba9605374c12fb635885768fc4e5ef51ebb +size 4947 diff --git a/testing/baselines/TestMultiFileCameraIndex.png b/testing/baselines/TestMultiFileCameraIndex.png new file mode 100644 index 0000000000..d21ae2aafd --- /dev/null +++ b/testing/baselines/TestMultiFileCameraIndex.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d7604226baaaf9eede4666f6cb91e9345b40c15546ea848af933e24ecc185ee +size 3286 diff --git a/testing/baselines/TestMultiFileColoring.png b/testing/baselines/TestMultiFileColoring.png new file mode 100644 index 0000000000..6aaf56bf45 --- /dev/null +++ b/testing/baselines/TestMultiFileColoring.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d6ae4ef38117103959dc33fee27c32a041f321568d34e35d9893d47c8bfb1304 +size 5203 diff --git a/testing/baselines/TestMultiFileColoringTexture.png b/testing/baselines/TestMultiFileColoringTexture.png new file mode 100644 index 0000000000..7a427c02a5 --- /dev/null +++ b/testing/baselines/TestMultiFileColoringTexture.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ae5299f32b0fb18e6d075dd1a0d2da4d0f56e706d68e99c016ff67280fa9cc27 +size 32236 diff --git a/testing/baselines/TestMultiFileInvalidFilesFileName.png b/testing/baselines/TestMultiFileInvalidFilesFileName.png new file mode 100644 index 0000000000..96e26d5183 --- /dev/null +++ b/testing/baselines/TestMultiFileInvalidFilesFileName.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:78cfda815b050f116044e73a6582c9574787f642f0d2e938d6fb3dae8920b37d +size 10396 diff --git a/testing/baselines/TestMultiFileMetaData.png b/testing/baselines/TestMultiFileMetaData.png new file mode 100644 index 0000000000..de1422e587 --- /dev/null +++ b/testing/baselines/TestMultiFileMetaData.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9b9ba8716f0d3575e34be5bad284b4458075fc8511bfa0f0d9ff32db64dc3b68 +size 9365 diff --git a/testing/baselines/TestMultiFileNonCoherentComponentNames.png b/testing/baselines/TestMultiFileNonCoherentComponentNames.png new file mode 100644 index 0000000000..b6c4c9f89a --- /dev/null +++ b/testing/baselines/TestMultiFileNonCoherentComponentNames.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:837d60136b5004b0153c3dc7134951a98759ea2abcb0848d6be2505ac386d0a1 +size 13943 diff --git a/testing/baselines/TestMultiFilePositionals.png b/testing/baselines/TestMultiFilePositionals.png new file mode 100644 index 0000000000..005a648478 --- /dev/null +++ b/testing/baselines/TestMultiFilePositionals.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6490419711a39f15432152c92805d9a62316760d3d9dde7f39bba3b03b3387b +size 22177 diff --git a/testing/baselines/TestMultiFileVolume.png b/testing/baselines/TestMultiFileVolume.png new file mode 100644 index 0000000000..babdbc51ef --- /dev/null +++ b/testing/baselines/TestMultiFileVolume.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec6f83fda925dd5987e05008df07517bff8d33c52c13c1a3030f545f244bb3be +size 22628 diff --git a/testing/baselines/TestMultiInputArg.png b/testing/baselines/TestMultiInputArg.png index ee490b03f5..005a648478 100644 --- a/testing/baselines/TestMultiInputArg.png +++ b/testing/baselines/TestMultiInputArg.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ed967b1fa921b99bb161d2a0a4fc233fcf446f9b41b4a67e6b911f884125fd03 -size 19417 +oid sha256:e6490419711a39f15432152c92805d9a62316760d3d9dde7f39bba3b03b3387b +size 22177 diff --git a/testing/baselines/TestMultiInputMultiArgs.png b/testing/baselines/TestMultiInputMultiArgs.png index ee490b03f5..005a648478 100644 --- a/testing/baselines/TestMultiInputMultiArgs.png +++ b/testing/baselines/TestMultiInputMultiArgs.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ed967b1fa921b99bb161d2a0a4fc233fcf446f9b41b4a67e6b911f884125fd03 -size 19417 +oid sha256:e6490419711a39f15432152c92805d9a62316760d3d9dde7f39bba3b03b3387b +size 22177 diff --git a/testing/baselines/TestMultiInputPositionals.png b/testing/baselines/TestMultiInputPositionals.png deleted file mode 100644 index ee490b03f5..0000000000 --- a/testing/baselines/TestMultiInputPositionals.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ed967b1fa921b99bb161d2a0a4fc233fcf446f9b41b4a67e6b911f884125fd03 -size 19417 diff --git a/testing/baselines/TestMultiblockMetaData.png b/testing/baselines/TestMultiblockMetaData.png index 890436e284..dd07c67d85 100644 --- a/testing/baselines/TestMultiblockMetaData.png +++ b/testing/baselines/TestMultiblockMetaData.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1c1c24d6933d5e6a5e9ee99917c498959483c4ab432cc5b2d23df15013737572 -size 12772 +oid sha256:e5405e2a1b3dc4d8611c6daba93cafc4618345c4e53984b91fedddcedc5ba9f7 +size 14072 diff --git a/testing/baselines/TestOBJ.png b/testing/baselines/TestOBJ.png index 2d2f517ce7..73957078b6 100644 --- a/testing/baselines/TestOBJ.png +++ b/testing/baselines/TestOBJ.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:904ab06cd17ad6d94322f654b748c67c215c5acfa96617abe6931f8999290bc3 -size 28210 +oid sha256:09ffdd064a6410bdd7a950cd0d0a956163fa270c8180b8b965a4b85ce3232e9e +size 62120 diff --git a/testing/baselines/TestOBJImporter.png b/testing/baselines/TestOBJImporter.png deleted file mode 100644 index 15902b6e88..0000000000 --- a/testing/baselines/TestOBJImporter.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ec623038fe6d6fa1147eced9127d544bf0ac0fafc30c32585d10a5bb50a9d4d5 -size 62120 diff --git a/testing/baselines/TestPythonScene.png b/testing/baselines/TestPythonScene.png new file mode 100644 index 0000000000..a288159800 --- /dev/null +++ b/testing/baselines/TestPythonScene.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c2ba67a525ce2c4ed14afcb5dd09c88eec4fef6e5791dcb1a1547bf0e19b32e4 +size 4262 diff --git a/testing/baselines/TestPythonLoadMemory.png b/testing/baselines/TestPythonSceneMemory.png similarity index 100% rename from testing/baselines/TestPythonLoadMemory.png rename to testing/baselines/TestPythonSceneMemory.png diff --git a/testing/baselines/TestRenderingBackendAuto.png b/testing/baselines/TestRenderingBackendAuto.png new file mode 100644 index 0000000000..88639a1cae --- /dev/null +++ b/testing/baselines/TestRenderingBackendAuto.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6294c9979ff8f55ba879a6802fa2ebbfd84089944ef46f8c774c82b4164d8549 +size 18646 diff --git a/testing/baselines/TestRenderingBackendEGL.png b/testing/baselines/TestRenderingBackendEGL.png new file mode 100644 index 0000000000..88639a1cae --- /dev/null +++ b/testing/baselines/TestRenderingBackendEGL.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6294c9979ff8f55ba879a6802fa2ebbfd84089944ef46f8c774c82b4164d8549 +size 18646 diff --git a/testing/baselines/TestRenderingBackendGLX.png b/testing/baselines/TestRenderingBackendGLX.png new file mode 100644 index 0000000000..88639a1cae --- /dev/null +++ b/testing/baselines/TestRenderingBackendGLX.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6294c9979ff8f55ba879a6802fa2ebbfd84089944ef46f8c774c82b4164d8549 +size 18646 diff --git a/testing/baselines/TestRenderingBackendOSMesa.png b/testing/baselines/TestRenderingBackendOSMesa.png new file mode 100644 index 0000000000..88639a1cae --- /dev/null +++ b/testing/baselines/TestRenderingBackendOSMesa.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6294c9979ff8f55ba879a6802fa2ebbfd84089944ef46f8c774c82b4164d8549 +size 18646 diff --git a/testing/baselines/TestRenderingBackendWGL.png b/testing/baselines/TestRenderingBackendWGL.png new file mode 100644 index 0000000000..88639a1cae --- /dev/null +++ b/testing/baselines/TestRenderingBackendWGL.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6294c9979ff8f55ba879a6802fa2ebbfd84089944ef46f8c774c82b4164d8549 +size 18646 diff --git a/testing/baselines/TestSDKExternalWindowCOCOA.png b/testing/baselines/TestSDKExternalWindowCOCOA.png new file mode 100644 index 0000000000..9ba0579043 --- /dev/null +++ b/testing/baselines/TestSDKExternalWindowCOCOA.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4d150f0f05557755ce090ae86de2dcace77af0dfd4c219ddacd129210183b8b1 +size 17518 diff --git a/testing/baselines/TestSDKExternalWindowEGL.png b/testing/baselines/TestSDKExternalWindowEGL.png new file mode 100644 index 0000000000..9ba0579043 --- /dev/null +++ b/testing/baselines/TestSDKExternalWindowEGL.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4d150f0f05557755ce090ae86de2dcace77af0dfd4c219ddacd129210183b8b1 +size 17518 diff --git a/testing/baselines/TestSDKExternalWindowGLX.png b/testing/baselines/TestSDKExternalWindowGLX.png new file mode 100644 index 0000000000..9ba0579043 --- /dev/null +++ b/testing/baselines/TestSDKExternalWindowGLX.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4d150f0f05557755ce090ae86de2dcace77af0dfd4c219ddacd129210183b8b1 +size 17518 diff --git a/testing/baselines/TestSDKExternalWindowOSMesa.png b/testing/baselines/TestSDKExternalWindowOSMesa.png new file mode 100644 index 0000000000..0469e984c7 --- /dev/null +++ b/testing/baselines/TestSDKExternalWindowOSMesa.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:29601bc3245e7519ea04f9471aa027aae4ac4896b79015c1f57712d75c21c200 +size 17479 diff --git a/testing/baselines/TestSDKExternalWindowWGL.png b/testing/baselines/TestSDKExternalWindowWGL.png new file mode 100644 index 0000000000..9ba0579043 --- /dev/null +++ b/testing/baselines/TestSDKExternalWindowWGL.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4d150f0f05557755ce090ae86de2dcace77af0dfd4c219ddacd129210183b8b1 +size 17518 diff --git a/testing/baselines/TestSDKInteractorCallBack.png b/testing/baselines/TestSDKInteractorCallBack.png deleted file mode 100644 index bc7149b3bb..0000000000 --- a/testing/baselines/TestSDKInteractorCallBack.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e1380a8b6a4d74f57b83109207f9d011cd6867324c46113fc127fd3c804f624c -size 42342 diff --git a/testing/baselines/TestSDKInteractorCallBackDefault.png b/testing/baselines/TestSDKInteractorCallBackDefault.png new file mode 100644 index 0000000000..2d5ebae43b --- /dev/null +++ b/testing/baselines/TestSDKInteractorCallBackDefault.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:564256ae217540c534e369fae268841b108d1fbb66c3acd159df5ec591a83286 +size 29306 diff --git a/testing/baselines/TestSDKInteractorCallBackModified.png b/testing/baselines/TestSDKInteractorCallBackModified.png new file mode 100644 index 0000000000..0c4e9ec5e2 --- /dev/null +++ b/testing/baselines/TestSDKInteractorCallBackModified.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2ff08ceaa5436cf0ef5cee1bea85c1c6871017691ed90f8a97c82ec35039ff3d +size 61290 diff --git a/testing/baselines/TestSDKMultiOptions.png b/testing/baselines/TestSDKMultiOptions.png index 14ee3bcb96..2750ea3f0b 100644 --- a/testing/baselines/TestSDKMultiOptions.png +++ b/testing/baselines/TestSDKMultiOptions.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8cb421bc19b068da8b48821d2acaa9840ac59ee10815e6d4011b0b7b4a86b3ed -size 48999 +oid sha256:213958070447fcc0213ac35c9300421c20aa5e982e537fe346352b8f19897f36 +size 49180 diff --git a/testing/baselines/TestSDKScene.png b/testing/baselines/TestSDKScene.png new file mode 100644 index 0000000000..a5a8ecd0b3 --- /dev/null +++ b/testing/baselines/TestSDKScene.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5203ccd71c4a97024f68063431ed1b570dfa4fba3b1d375abbf48addf4c84c62 +size 4104 diff --git a/testing/baselines/TestSDKLoadMemory.png b/testing/baselines/TestSDKSceneFromMemory.png similarity index 100% rename from testing/baselines/TestSDKLoadMemory.png rename to testing/baselines/TestSDKSceneFromMemory.png diff --git a/testing/baselines/TestTextureColor.png b/testing/baselines/TestTextureColor.png index dacd0edc6c..1fd3daa810 100644 --- a/testing/baselines/TestTextureColor.png +++ b/testing/baselines/TestTextureColor.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e563e6a98ca1f4b9c0d7ef1f4074cbcd1165a6b9eb1255a6721f67acca8dcce -size 12736 +oid sha256:d5fec1e684be6ba383e30c1d822c65c68be62f676c406404528e9bc2c7549023 +size 25382 diff --git a/testing/baselines/TestTextureColorWithOptions.png b/testing/baselines/TestTextureColorWithOptions.png index f554a2a245..59d682ce36 100644 --- a/testing/baselines/TestTextureColorWithOptions.png +++ b/testing/baselines/TestTextureColorWithOptions.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:58dbaf2048f57d2aa8e895286261da8f85ed16d4b49c172e9a94e9179f55e8e7 -size 13492 +oid sha256:2d4daebd837f6eb890867fc9fc7357679f1a1eceb238ba767eb79200dbf4a185 +size 16018 diff --git a/testing/baselines/TestTextureEmissive.png b/testing/baselines/TestTextureEmissive.png index 1f29a1ede5..ba2b2ca443 100644 --- a/testing/baselines/TestTextureEmissive.png +++ b/testing/baselines/TestTextureEmissive.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:be0eaad6b2290a570321bf45cafc8178f65626dd120b449d0e5bd1339ad899d7 -size 10517 +oid sha256:330b9f1e4f006e436177209b78a932a0d39894d6ec5c1debfbbf1593fa24e55e +size 21923 diff --git a/testing/baselines/TestTextureMaterial.png b/testing/baselines/TestTextureMaterial.png index 80aea6ec01..a7564e7eb8 100644 --- a/testing/baselines/TestTextureMaterial.png +++ b/testing/baselines/TestTextureMaterial.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:608dd867ccedd9b4eaed6cd0168dddf3c04980d16871650bd9b21242c81b8160 -size 13506 +oid sha256:0968fc83e3bd4533269e9d3f6588af5e9858f4c625f2ffe93f9282bd2667e4e9 +size 19369 diff --git a/testing/baselines/TestTextureMaterialWithOptions.png b/testing/baselines/TestTextureMaterialWithOptions.png index b405571d37..f03e9bceae 100644 --- a/testing/baselines/TestTextureMaterialWithOptions.png +++ b/testing/baselines/TestTextureMaterialWithOptions.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:254ca2285e7d16fcd36ff3b5e38d4d2488d8dc73c6fb5dc7cccf7fa9427ef729 -size 13861 +oid sha256:258098529ba46b22e6658708f9a478b2fb05c8e8f9f3ec6457fce0f2c70341f6 +size 22529 diff --git a/testing/baselines/TestTextureNormal.png b/testing/baselines/TestTextureNormal.png index 5744c83845..4e71b60551 100644 --- a/testing/baselines/TestTextureNormal.png +++ b/testing/baselines/TestTextureNormal.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3a7672b696c57c7b3c8eb36a1ed8dd1a41e7853fe631e08065974a93157182f -size 18072 +oid sha256:0831b8159e803b250b9ccfb5ba4c02632329f6374edbf56c6d97eb7a4c72de0f +size 23351 diff --git a/testing/baselines/TestTextures.png b/testing/baselines/TestTextures.png index cf73c8c65a..67abad3396 100644 --- a/testing/baselines/TestTextures.png +++ b/testing/baselines/TestTextures.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:53a47abd2f4b99ed66d5b9c8e94ef330db3105da2bc0e5bf79560425840d6324 -size 25337 +oid sha256:d198c6fa4592560fe7a6a2b8b5d7674a3994e9d80b2296a931938fcdbe2dba4c +size 25632 diff --git a/testing/baselines/TestVolumeColoringArray.png b/testing/baselines/TestVolumeColoringArray.png new file mode 100644 index 0000000000..cadd5b8dc5 --- /dev/null +++ b/testing/baselines/TestVolumeColoringArray.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b4c0d14c52ee07aa5d72df760849ce9e14d7ff3322471ee847d856df8749d385 +size 8493 diff --git a/testing/baselines/TestVolumeNonScalars.png b/testing/baselines/TestVolumeNonScalars.png deleted file mode 100644 index 2c75bc3ec6..0000000000 --- a/testing/baselines/TestVolumeNonScalars.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:973771bb434dad7a720e3c7d77509684380f4e48f8c7ee4386082b1b7ed90e8c -size 82040 diff --git a/testing/configs/complex.json b/testing/configs/complex.json index 7bded1e66c..177d1d77ea 100644 --- a/testing/configs/complex.json +++ b/testing/configs/complex.json @@ -7,6 +7,7 @@ }, ".*(vt.)": { + "up": "+Y", "bar": true, "render.effect.ambient_occlusion": true, "filename": true diff --git a/testing/data/mb/recursive/f3d.glb b/testing/data/mb/recursive/f3d.glb new file mode 100644 index 0000000000..bdce4cdb98 --- /dev/null +++ b/testing/data/mb/recursive/f3d.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8f4b8990cb6a45a366a724de79f6c996cc2e92abe6161bd78735780d4a2db5b8 +size 3652 diff --git a/testing/data/mb/recursive/mb_1_0.vtp b/testing/data/mb/recursive/mb_1_0.vtp index 24538ba351..4d94f5ba41 100644 --- a/testing/data/mb/recursive/mb_1_0.vtp +++ b/testing/data/mb/recursive/mb_1_0.vtp @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da39f580f748e2fbd03ad270b8e3fee354f7165f3652a002610c80172616e02c -size 7084 +oid sha256:96790d19cab995d59dc96556987c281bbfbda1bd46f451d59c556086cdb90138 +size 6519 diff --git a/testing/data/mb/recursive/mb_2_0.vtp b/testing/data/mb/recursive/mb_2_0.vtp index 499c235399..8baae99d75 100644 --- a/testing/data/mb/recursive/mb_2_0.vtp +++ b/testing/data/mb/recursive/mb_2_0.vtp @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a35af390f451b6729124015a8b79e6a56bfc1104c98bc5b8e0e01d8776f9f402 -size 6860 +oid sha256:08c5cfddd3aa673f62bbaa16b2201305d6937c91f4cb01c9a1d33c726e14b8e8 +size 6297 diff --git a/testing/data/palermo_park_1k.tif b/testing/data/palermo_park_1k.tif new file mode 100644 index 0000000000..2063b2a62d --- /dev/null +++ b/testing/data/palermo_park_1k.tif @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:328b336bc363d30fca58681372557d4d8975aad3c5555c8b1e5e81b9cc709c7b +size 1573095 diff --git a/testing/recordings/TestInteractionAnimationInvalid.log b/testing/recordings/TestInteractionAnimationInvalid.log deleted file mode 100644 index 30018c65bb..0000000000 --- a/testing/recordings/TestInteractionAnimationInvalid.log +++ /dev/null @@ -1,14 +0,0 @@ -# StreamVersion 1.2 -ConfigureEvent 2 1409 0 0 0 0 0 -ExposeEvent 0 1409 0 0 0 0 0 -RenderEvent 0 1409 0 0 0 0 0 -KeyPressEvent 1698 319 0 32 1 space 0 -CharEvent 1698 319 0 32 1 space 0 -KeyReleaseEvent 1698 319 0 32 1 space 0 -TimerEvent 1698 319 0 32 1 space 0 -TimerEvent 1698 319 0 32 1 space 0 -TimerEvent 1698 319 0 32 1 space 0 -TimerEvent 1698 319 0 32 1 space 0 -KeyPressEvent 1698 319 0 32 1 space 0 -CharEvent 1698 319 0 32 1 space 0 -KeyReleaseEvent 1698 319 0 32 1 space 0 diff --git a/testing/recordings/TestInteractionCycleCellInvalidIndex.log b/testing/recordings/TestInteractionCycleCellInvalidIndex.log new file mode 100644 index 0000000000..388711686b --- /dev/null +++ b/testing/recordings/TestInteractionCycleCellInvalidIndex.log @@ -0,0 +1,16 @@ +# StreamVersion 1.2 +ConfigureEvent 1249 1374 0 0 0 0 0 +ExposeEvent 0 1406 0 0 0 0 0 +RenderEvent 0 1406 0 0 0 0 0 +TimerEvent 0 1406 0 0 0 0 0 +KeyPressEvent -658 227 0 115 1 s 0 +CharEvent -658 227 0 115 1 s 0 +KeyReleaseEvent -658 227 0 115 1 s 0 +TimerEvent -658 227 0 115 1 s 0 +KeyPressEvent -658 227 0 115 1 s 0 +CharEvent -658 227 0 115 1 s 0 +KeyReleaseEvent -658 227 0 115 1 s 0 +TimerEvent -658 227 0 115 1 s 0 +KeyPressEvent -658 227 0 99 1 c 0 +CharEvent -658 227 0 99 1 c 0 +KeyReleaseEvent -658 227 0 99 1 c 0 diff --git a/testing/recordings/TestInteractionDirectoryLoop.log b/testing/recordings/TestInteractionDirectoryLoop.log index db211d0bd4..881ad4ac41 100644 --- a/testing/recordings/TestInteractionDirectoryLoop.log +++ b/testing/recordings/TestInteractionDirectoryLoop.log @@ -10,3 +10,9 @@ KeyReleaseEvent 354 650 0 0 1 Left KeyPressEvent 354 650 0 0 1 Left CharEvent 354 650 0 0 1 Left KeyReleaseEvent 354 650 0 0 1 Left +KeyPressEvent 354 650 0 0 1 Left +CharEvent 354 650 0 0 1 Left +KeyReleaseEvent 354 650 0 0 1 Left +KeyPressEvent 354 650 0 0 1 Left +CharEvent 354 650 0 0 1 Left +KeyReleaseEvent 354 650 0 0 1 Left diff --git a/testing/recordings/TestInteractionDropHDRI.log.in b/testing/recordings/TestInteractionDropHDRI.log.in index afd4fa30f9..72270333ad 100644 --- a/testing/recordings/TestInteractionDropHDRI.log.in +++ b/testing/recordings/TestInteractionDropHDRI.log.in @@ -1,9 +1,12 @@ # StreamVersion 1.2 ExposeEvent 0 599 0 0 0 0 0 RenderEvent 0 599 0 0 0 0 0 -KeyPressEvent 474 416 0 115 1 x 0 -CharEvent 474 416 0 115 1 x 0 -KeyReleaseEvent 474 416 0 115 1 x 0 +KeyPressEvent 351 169 0 120 1 x 0 +CharEvent 351 169 0 120 1 x 0 +TimerEvent 351 169 0 120 1 x 0 +TimerEvent 351 169 0 120 1 x 0 +TimerEvent 351 169 0 120 1 x 0 +KeyReleaseEvent 351 169 0 120 1 x 0 KeyPressEvent 677 699 0 0 1 Super_L 0 CharEvent 677 699 0 0 1 Super_L 0 UpdateDropLocationEvent 677 699 0 0 1 Super_L 0 diff --git a/testing/recordings/TestInteractionDropHDRIExr.log.in b/testing/recordings/TestInteractionDropHDRIExr.log.in index e3acb4aa46..d13a9a6853 100644 --- a/testing/recordings/TestInteractionDropHDRIExr.log.in +++ b/testing/recordings/TestInteractionDropHDRIExr.log.in @@ -1,9 +1,12 @@ # StreamVersion 1.2 ExposeEvent 0 599 0 0 0 0 0 RenderEvent 0 599 0 0 0 0 0 -KeyPressEvent 474 416 0 115 1 x 0 -CharEvent 474 416 0 115 1 x 0 -KeyReleaseEvent 474 416 0 115 1 x 0 +KeyPressEvent 351 169 0 120 1 x 0 +CharEvent 351 169 0 120 1 x 0 +TimerEvent 351 169 0 120 1 x 0 +TimerEvent 351 169 0 120 1 x 0 +TimerEvent 351 169 0 120 1 x 0 +KeyReleaseEvent 351 169 0 120 1 x 0 KeyPressEvent 677 699 0 0 1 Super_L 0 CharEvent 677 699 0 0 1 Super_L 0 UpdateDropLocationEvent 677 699 0 0 1 Super_L 0 diff --git a/testing/recordings/TestInteractionDropHDRIInvert.log.in b/testing/recordings/TestInteractionDropHDRIInvert.log.in index b6ad81e102..87998aa4d3 100644 --- a/testing/recordings/TestInteractionDropHDRIInvert.log.in +++ b/testing/recordings/TestInteractionDropHDRIInvert.log.in @@ -1,9 +1,12 @@ # StreamVersion 1.2 ExposeEvent 0 599 0 0 0 0 0 RenderEvent 0 599 0 0 0 0 0 -KeyPressEvent 474 416 0 115 1 x 0 -CharEvent 474 416 0 115 1 x 0 -KeyReleaseEvent 474 416 0 115 1 x 0 +KeyPressEvent 351 169 0 120 1 x 0 +CharEvent 351 169 0 120 1 x 0 +TimerEvent 351 169 0 120 1 x 0 +TimerEvent 351 169 0 120 1 x 0 +TimerEvent 351 169 0 120 1 x 0 +KeyReleaseEvent 351 169 0 120 1 x 0 KeyPressEvent 677 699 0 0 1 Super_L 0 CharEvent 677 699 0 0 1 Super_L 0 UpdateDropLocationEvent 677 699 0 0 1 Super_L 0 diff --git a/testing/recordings/TestInteractionDropHDRIModifiers.log.in b/testing/recordings/TestInteractionDropHDRIModifiers.log.in new file mode 100644 index 0000000000..6e3ef9b8e7 --- /dev/null +++ b/testing/recordings/TestInteractionDropHDRIModifiers.log.in @@ -0,0 +1,89 @@ +# StreamVersion 1.2 +ExposeEvent 0 599 0 0 0 0 0 +RenderEvent 0 599 0 0 0 0 0 +KeyPressEvent -604 609 0 0 1 Control_L 0 +CharEvent -604 609 0 0 1 Control_L 0 +TimerEvent -604 609 0 0 1 Control_L 0 +TimerEvent -604 609 0 0 1 Control_L 0 +UpdateDropLocationEvent 677 699 2 16 1 Control_L 0 +EnterEvent 781 494 2 16 1 Control_L 0 +DropFilesEvent 781 494 2 16 1 Control_L 1 1 ${F3D_SOURCE_DIR}/testing/data/f3d.tif +RenderEvent 781 494 2 16 1 Control_L 0 +TimerEvent -604 609 2 16 1 p 0 +TimerEvent -604 609 2 16 1 p 0 +KeyReleaseEvent -604 609 2 0 1 Control_L 0 +TimerEvent -604 609 2 0 1 Control_L 0 +TimerEvent -604 609 2 0 1 Control_L 0 +KeyPressEvent -604 609 0 0 0 Shift_L 0 +CharEvent -604 609 0 0 0 Shift_L 0 +TimerEvent -604 609 1 80 1 Shift_L 0 +TimerEvent -604 609 1 80 1 Shift_L 0 +UpdateDropLocationEvent 677 699 1 80 1 Shift_L 0 +EnterEvent 781 494 1 80 1 Shift_L 0 +DropFilesEvent 781 494 1 80 1 Shift_L 1 0 +DropFilesEvent 781 494 1 80 1 Shift_L 1 1 ${F3D_SOURCE_DIR}/testing/data/palermo_park_1k.tif +RenderEvent 781 494 1 80 1 Control_L 0 +TimerEvent -604 609 1 80 1 Shift_L 0 +TimerEvent -604 609 1 80 1 Shift_L 0 +KeyReleaseEvent -604 609 1 80 1 Shift_L 0 +TimerEvent -604 609 1 80 1 Shift_L 0 +TimerEvent 459 308 0 0 0 0 0 +TimerEvent 459 308 0 0 0 0 0 +KeyPressEvent 459 308 0 115 1 s 0 +CharEvent 459 308 0 115 1 s 0 +TimerEvent 459 308 0 115 1 s 0 +TimerEvent 459 308 0 115 1 s 0 +TimerEvent 459 308 0 115 1 s 0 +KeyReleaseEvent 459 308 0 115 1 s 0 +TimerEvent 459 308 0 115 1 s 0 +TimerEvent 459 308 0 115 1 s 0 +TimerEvent 459 308 0 115 1 s 0 +TimerEvent 459 308 0 115 1 s 0 +TimerEvent 459 308 0 115 1 s 0 +TimerEvent 459 308 0 115 1 s 0 +KeyPressEvent 459 308 0 121 1 y 0 +CharEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +KeyReleaseEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +KeyPressEvent 459 308 0 121 1 y 0 +CharEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +KeyReleaseEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +KeyPressEvent 459 308 0 121 1 y 0 +CharEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +KeyReleaseEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +KeyPressEvent 459 308 0 121 1 y 0 +CharEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +KeyReleaseEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 +TimerEvent 459 308 0 121 1 y 0 diff --git a/testing/recordings/TestInteractionDropHDRIMulti.log.in b/testing/recordings/TestInteractionDropHDRIMulti.log.in index 365d746306..fab8628371 100644 --- a/testing/recordings/TestInteractionDropHDRIMulti.log.in +++ b/testing/recordings/TestInteractionDropHDRIMulti.log.in @@ -1,9 +1,12 @@ # StreamVersion 1.2 ExposeEvent 0 599 0 0 0 0 0 RenderEvent 0 599 0 0 0 0 0 -KeyPressEvent 474 416 0 115 1 x 0 -CharEvent 474 416 0 115 1 x 0 -KeyReleaseEvent 474 416 0 115 1 x 0 +KeyPressEvent 351 169 0 120 1 x 0 +CharEvent 351 169 0 120 1 x 0 +TimerEvent 351 169 0 120 1 x 0 +TimerEvent 351 169 0 120 1 x 0 +TimerEvent 351 169 0 120 1 x 0 +KeyReleaseEvent 351 169 0 120 1 x 0 KeyPressEvent 677 699 0 0 1 Super_L 0 CharEvent 677 699 0 0 1 Super_L 0 UpdateDropLocationEvent 677 699 0 0 1 Super_L 0 diff --git a/testing/recordings/TestInteractionInvalidComponent.log b/testing/recordings/TestInteractionInvalidComponent.log new file mode 100644 index 0000000000..1354832c8c --- /dev/null +++ b/testing/recordings/TestInteractionInvalidComponent.log @@ -0,0 +1,10 @@ +# StreamVersion 1.2 +ExposeEvent 0 599 0 0 0 0 0 +RenderEvent 0 599 0 0 0 0 0 +TimerEvent 0 599 0 0 0 0 0 +TimerEvent 0 599 0 0 0 0 0 +KeyPressEvent 291 357 0 104 1 h 0 +CharEvent 291 357 0 104 1 h 0 +TimerEvent 0 599 0 0 0 0 0 +KeyReleaseEvent 291 357 0 104 1 h 0 +TimerEvent 291 357 0 104 1 h 0 diff --git a/testing/recordings/TestInteractionGroupGeometriesColoring.log b/testing/recordings/TestInteractionMultiFileColoring.log similarity index 78% rename from testing/recordings/TestInteractionGroupGeometriesColoring.log rename to testing/recordings/TestInteractionMultiFileColoring.log index 964080dfb4..2392cf2816 100644 --- a/testing/recordings/TestInteractionGroupGeometriesColoring.log +++ b/testing/recordings/TestInteractionMultiFileColoring.log @@ -7,6 +7,9 @@ KeyReleaseEvent 474 416 0 115 1 s 0 KeyPressEvent 474 416 0 115 1 s 0 CharEvent 474 416 0 115 1 s 0 KeyReleaseEvent 474 416 0 115 1 s 0 +KeyPressEvent 474 416 0 115 1 s 0 +CharEvent 474 416 0 115 1 s 0 +KeyReleaseEvent 474 416 0 115 1 s 0 KeyPressEvent 474 416 0 98 1 b 0 CharEvent 474 416 0 98 1 b 0 KeyReleaseEvent 474 416 0 98 1 b 0 diff --git a/testing/recordings/TestInteractionGroupGeometriesDrop.log.in b/testing/recordings/TestInteractionMultiFileDrop.log.in similarity index 70% rename from testing/recordings/TestInteractionGroupGeometriesDrop.log.in rename to testing/recordings/TestInteractionMultiFileDrop.log.in index b1bb544aed..3b2c562a2a 100644 --- a/testing/recordings/TestInteractionGroupGeometriesDrop.log.in +++ b/testing/recordings/TestInteractionMultiFileDrop.log.in @@ -5,6 +5,6 @@ KeyPressEvent 677 699 0 0 1 Super_L 0 CharEvent 677 699 0 0 1 Super_L 0 UpdateDropLocationEvent 677 699 0 0 1 Super_L 0 EnterEvent 781 494 0 0 0 Super_L 0 -DropFilesEvent 781 494 0 0 0 Super_L 1 1 ${F3D_SOURCE_DIR}/testing/data/mb/mb_2_0.vtp +DropFilesEvent 781 494 0 0 0 Super_L 1 2 ${F3D_SOURCE_DIR}/testing/data/mb/mb_2_0.vtp ${F3D_SOURCE_DIR}/testing/data/mb/mb_1_0.vtp RenderEvent 781 494 0 0 0 Super_L 0 MouseMoveEvent 781 498 0 0 0 Super_L 0 diff --git a/testing/recordings/TestInteractionGroupGeometriesLoadParentDirectory.log b/testing/recordings/TestInteractionMultiFileLoadParentDirectory.log similarity index 100% rename from testing/recordings/TestInteractionGroupGeometriesLoadParentDirectory.log rename to testing/recordings/TestInteractionMultiFileLoadParentDirectory.log diff --git a/testing/recordings/TestInteractionMultiFileVolume.log b/testing/recordings/TestInteractionMultiFileVolume.log new file mode 100644 index 0000000000..62157df0ce --- /dev/null +++ b/testing/recordings/TestInteractionMultiFileVolume.log @@ -0,0 +1,21 @@ +# StreamVersion 1.2 +ConfigureEvent 1249 1374 0 0 0 0 0 +ExposeEvent 0 1406 0 0 0 0 0 +RenderEvent 0 1406 0 0 0 0 0 +TimerEvent 0 1406 0 0 0 0 0 +KeyPressEvent 237 552 0 115 1 s 0 +CharEvent 237 552 0 115 1 s 0 +KeyReleaseEvent 237 552 0 115 1 s 0 +TimerEvent 237 552 0 115 1 s 0 +KeyPressEvent 237 552 0 115 1 s 0 +CharEvent 237 552 0 115 1 s 0 +KeyReleaseEvent 237 552 0 115 1 s 0 +TimerEvent 237 552 0 115 1 s 0 +KeyPressEvent 237 552 0 118 1 v 0 +CharEvent 237 552 0 118 1 v 0 +KeyReleaseEvent 237 552 0 118 1 v 0 +TimerEvent 237 552 0 118 1 v 0 +TimerEvent 237 552 0 118 1 v 0 +KeyPressEvent 237 552 0 98 1 b 0 +CharEvent 237 552 0 98 1 b 0 +KeyReleaseEvent 237 552 0 98 1 b 0 diff --git a/testing/recordings/TestInteractionResetCameraWithCameraIndex.log b/testing/recordings/TestInteractionResetCameraWithCameraIndex.log new file mode 100644 index 0000000000..173631252a --- /dev/null +++ b/testing/recordings/TestInteractionResetCameraWithCameraIndex.log @@ -0,0 +1,517 @@ +# StreamVersion 1.1 +ExposeEvent 0 599 0 0 0 0 +RenderEvent 0 599 0 0 0 0 +MouseMoveEvent 443 279 0 0 0 0 +MouseMoveEvent 442 279 0 0 0 0 +MouseMoveEvent 441 280 0 0 0 0 +MouseMoveEvent 439 281 0 0 0 0 +MouseMoveEvent 436 283 0 0 0 0 +MouseMoveEvent 430 286 0 0 0 0 +MouseMoveEvent 421 290 0 0 0 0 +MouseMoveEvent 413 293 0 0 0 0 +MouseMoveEvent 405 298 0 0 0 0 +MouseMoveEvent 397 304 0 0 0 0 +MouseMoveEvent 392 311 0 0 0 0 +MouseMoveEvent 382 319 0 0 0 0 +MouseMoveEvent 375 325 0 0 0 0 +MouseMoveEvent 369 332 0 0 0 0 +MouseMoveEvent 366 336 0 0 0 0 +MouseMoveEvent 364 339 0 0 0 0 +MouseMoveEvent 362 341 0 0 0 0 +MouseMoveEvent 360 343 0 0 0 0 +MouseMoveEvent 358 346 0 0 0 0 +MouseMoveEvent 356 351 0 0 0 0 +MouseMoveEvent 354 355 0 0 0 0 +MouseMoveEvent 351 360 0 0 0 0 +MouseMoveEvent 348 365 0 0 0 0 +MouseMoveEvent 345 370 0 0 0 0 +MouseMoveEvent 343 372 0 0 0 0 +MouseMoveEvent 342 374 0 0 0 0 +MouseMoveEvent 342 375 0 0 0 0 +MouseMoveEvent 341 375 0 0 0 0 +MouseMoveEvent 341 374 0 0 0 0 +MouseMoveEvent 341 372 0 0 0 0 +MouseMoveEvent 342 371 0 0 0 0 +MouseMoveEvent 344 368 0 0 0 0 +LeftButtonPressEvent 344 368 0 0 0 0 +StartInteractionEvent 344 368 0 0 0 0 +MouseMoveEvent 346 364 0 0 0 0 +RenderEvent 346 364 0 0 0 0 +InteractionEvent 346 364 0 0 0 0 +MouseMoveEvent 351 358 0 0 0 0 +RenderEvent 351 358 0 0 0 0 +InteractionEvent 351 358 0 0 0 0 +MouseMoveEvent 358 354 0 0 0 0 +RenderEvent 358 354 0 0 0 0 +InteractionEvent 358 354 0 0 0 0 +MouseMoveEvent 367 348 0 0 0 0 +RenderEvent 367 348 0 0 0 0 +InteractionEvent 367 348 0 0 0 0 +MouseMoveEvent 376 344 0 0 0 0 +RenderEvent 376 344 0 0 0 0 +InteractionEvent 376 344 0 0 0 0 +MouseMoveEvent 385 339 0 0 0 0 +RenderEvent 385 339 0 0 0 0 +InteractionEvent 385 339 0 0 0 0 +MouseMoveEvent 410 329 0 0 0 0 +RenderEvent 410 329 0 0 0 0 +InteractionEvent 410 329 0 0 0 0 +MouseMoveEvent 428 323 0 0 0 0 +RenderEvent 428 323 0 0 0 0 +InteractionEvent 428 323 0 0 0 0 +MouseMoveEvent 445 317 0 0 0 0 +RenderEvent 445 317 0 0 0 0 +InteractionEvent 445 317 0 0 0 0 +MouseMoveEvent 464 311 0 0 0 0 +RenderEvent 464 311 0 0 0 0 +InteractionEvent 464 311 0 0 0 0 +MouseMoveEvent 483 305 0 0 0 0 +RenderEvent 483 305 0 0 0 0 +InteractionEvent 483 305 0 0 0 0 +MouseMoveEvent 507 299 0 0 0 0 +RenderEvent 507 299 0 0 0 0 +InteractionEvent 507 299 0 0 0 0 +MouseMoveEvent 543 289 0 0 0 0 +RenderEvent 543 289 0 0 0 0 +InteractionEvent 543 289 0 0 0 0 +MouseMoveEvent 559 285 0 0 0 0 +RenderEvent 559 285 0 0 0 0 +InteractionEvent 559 285 0 0 0 0 +MouseMoveEvent 572 280 0 0 0 0 +RenderEvent 572 280 0 0 0 0 +InteractionEvent 572 280 0 0 0 0 +MouseMoveEvent 582 277 0 0 0 0 +RenderEvent 582 277 0 0 0 0 +InteractionEvent 582 277 0 0 0 0 +MouseMoveEvent 588 275 0 0 0 0 +RenderEvent 588 275 0 0 0 0 +InteractionEvent 588 275 0 0 0 0 +MouseMoveEvent 592 272 0 0 0 0 +RenderEvent 592 272 0 0 0 0 +InteractionEvent 592 272 0 0 0 0 +MouseMoveEvent 596 270 0 0 0 0 +RenderEvent 596 270 0 0 0 0 +InteractionEvent 596 270 0 0 0 0 +MouseMoveEvent 599 270 0 0 0 0 +RenderEvent 599 270 0 0 0 0 +InteractionEvent 599 270 0 0 0 0 +MouseMoveEvent 600 268 0 0 0 0 +RenderEvent 600 268 0 0 0 0 +InteractionEvent 600 268 0 0 0 0 +MouseMoveEvent 601 267 0 0 0 0 +RenderEvent 601 267 0 0 0 0 +InteractionEvent 601 267 0 0 0 0 +MouseMoveEvent 602 267 0 0 0 0 +RenderEvent 602 267 0 0 0 0 +InteractionEvent 602 267 0 0 0 0 +MouseMoveEvent 603 267 0 0 0 0 +RenderEvent 603 267 0 0 0 0 +InteractionEvent 603 267 0 0 0 0 +MouseMoveEvent 603 267 0 0 0 0 +RenderEvent 603 267 0 0 0 0 +InteractionEvent 603 267 0 0 0 0 +LeftButtonReleaseEvent 603 267 0 0 0 0 +EndInteractionEvent 603 267 0 0 0 0 +RenderEvent 603 267 0 0 0 0 +MouseMoveEvent 604 268 0 0 0 0 +MouseMoveEvent 611 275 0 0 0 0 +MouseMoveEvent 613 277 0 0 0 0 +MouseMoveEvent 615 279 0 0 0 0 +MouseMoveEvent 617 281 0 0 0 0 +MouseMoveEvent 619 283 0 0 0 0 +MouseMoveEvent 622 285 0 0 0 0 +MouseMoveEvent 631 289 0 0 0 0 +MouseMoveEvent 637 291 0 0 0 0 +MouseMoveEvent 642 294 0 0 0 0 +MouseMoveEvent 648 295 0 0 0 0 +MouseMoveEvent 656 296 0 0 0 0 +MouseMoveEvent 665 298 0 0 0 0 +MouseMoveEvent 674 299 0 0 0 0 +MouseMoveEvent 683 299 0 0 0 0 +MouseMoveEvent 692 301 0 0 0 0 +MouseMoveEvent 700 302 0 0 0 0 +MouseMoveEvent 707 302 0 0 0 0 +MouseMoveEvent 710 303 0 0 0 0 +MouseMoveEvent 712 303 0 0 0 0 +MouseMoveEvent 713 304 0 0 0 0 +MouseMoveEvent 714 305 0 0 0 0 +MouseMoveEvent 715 306 0 0 0 0 +MouseMoveEvent 716 307 0 0 0 0 +MouseMoveEvent 717 308 0 0 0 0 +MouseMoveEvent 717 309 0 0 0 0 +MouseMoveEvent 716 309 0 0 0 0 +MouseMoveEvent 710 309 0 0 0 0 +MouseMoveEvent 702 309 0 0 0 0 +MouseMoveEvent 689 311 0 0 0 0 +MouseMoveEvent 679 311 0 0 0 0 +MouseMoveEvent 673 311 0 0 0 0 +MouseMoveEvent 665 311 0 0 0 0 +MouseMoveEvent 654 308 0 0 0 0 +MouseMoveEvent 641 305 0 0 0 0 +MouseMoveEvent 628 301 0 0 0 0 +MouseMoveEvent 617 296 0 0 0 0 +MouseMoveEvent 610 288 0 0 0 0 +MouseMoveEvent 605 281 0 0 0 0 +MouseMoveEvent 602 273 0 0 0 0 +MouseMoveEvent 600 267 0 0 0 0 +MouseMoveEvent 597 262 0 0 0 0 +MouseMoveEvent 596 257 0 0 0 0 +MouseMoveEvent 595 253 0 0 0 0 +MouseMoveEvent 594 249 0 0 0 0 +MouseMoveEvent 592 246 0 0 0 0 +MouseMoveEvent 591 243 0 0 0 0 +MouseMoveEvent 589 241 0 0 0 0 +MouseMoveEvent 588 239 0 0 0 0 +MouseMoveEvent 585 237 0 0 0 0 +MouseMoveEvent 584 235 0 0 0 0 +MouseMoveEvent 584 234 0 0 0 0 +MouseMoveEvent 584 234 0 0 0 0 +RightButtonPressEvent 584 234 0 0 0 0 +StartInteractionEvent 584 234 0 0 0 0 +MouseMoveEvent 583 239 0 0 0 0 +RenderEvent 583 239 0 0 0 0 +InteractionEvent 583 239 0 0 0 0 +MouseMoveEvent 582 244 0 0 0 0 +RenderEvent 582 244 0 0 0 0 +InteractionEvent 582 244 0 0 0 0 +MouseMoveEvent 579 252 0 0 0 0 +RenderEvent 579 252 0 0 0 0 +InteractionEvent 579 252 0 0 0 0 +MouseMoveEvent 578 258 0 0 0 0 +RenderEvent 578 258 0 0 0 0 +InteractionEvent 578 258 0 0 0 0 +MouseMoveEvent 576 263 0 0 0 0 +RenderEvent 576 263 0 0 0 0 +InteractionEvent 576 263 0 0 0 0 +MouseMoveEvent 575 271 0 0 0 0 +RenderEvent 575 271 0 0 0 0 +InteractionEvent 575 271 0 0 0 0 +MouseMoveEvent 573 278 0 0 0 0 +RenderEvent 573 278 0 0 0 0 +InteractionEvent 573 278 0 0 0 0 +MouseMoveEvent 571 290 0 0 0 0 +RenderEvent 571 290 0 0 0 0 +InteractionEvent 571 290 0 0 0 0 +MouseMoveEvent 568 296 0 0 0 0 +RenderEvent 568 296 0 0 0 0 +InteractionEvent 568 296 0 0 0 0 +MouseMoveEvent 567 301 0 0 0 0 +RenderEvent 567 301 0 0 0 0 +InteractionEvent 567 301 0 0 0 0 +MouseMoveEvent 566 308 0 0 0 0 +RenderEvent 566 308 0 0 0 0 +InteractionEvent 566 308 0 0 0 0 +MouseMoveEvent 563 316 0 0 0 0 +RenderEvent 563 316 0 0 0 0 +InteractionEvent 563 316 0 0 0 0 +MouseMoveEvent 562 322 0 0 0 0 +RenderEvent 562 322 0 0 0 0 +InteractionEvent 562 322 0 0 0 0 +MouseMoveEvent 560 330 0 0 0 0 +RenderEvent 560 330 0 0 0 0 +InteractionEvent 560 330 0 0 0 0 +MouseMoveEvent 558 337 0 0 0 0 +RenderEvent 558 337 0 0 0 0 +InteractionEvent 558 337 0 0 0 0 +MouseMoveEvent 557 342 0 0 0 0 +RenderEvent 557 342 0 0 0 0 +InteractionEvent 557 342 0 0 0 0 +MouseMoveEvent 555 347 0 0 0 0 +RenderEvent 555 347 0 0 0 0 +InteractionEvent 555 347 0 0 0 0 +MouseMoveEvent 553 354 0 0 0 0 +RenderEvent 553 354 0 0 0 0 +InteractionEvent 553 354 0 0 0 0 +MouseMoveEvent 551 362 0 0 0 0 +RenderEvent 551 362 0 0 0 0 +InteractionEvent 551 362 0 0 0 0 +MouseMoveEvent 551 373 0 0 0 0 +RenderEvent 551 373 0 0 0 0 +InteractionEvent 551 373 0 0 0 0 +MouseMoveEvent 551 380 0 0 0 0 +RenderEvent 551 380 0 0 0 0 +InteractionEvent 551 380 0 0 0 0 +MouseMoveEvent 551 386 0 0 0 0 +RenderEvent 551 386 0 0 0 0 +InteractionEvent 551 386 0 0 0 0 +MouseMoveEvent 551 390 0 0 0 0 +RenderEvent 551 390 0 0 0 0 +InteractionEvent 551 390 0 0 0 0 +MouseMoveEvent 551 393 0 0 0 0 +RenderEvent 551 393 0 0 0 0 +InteractionEvent 551 393 0 0 0 0 +MouseMoveEvent 551 397 0 0 0 0 +RenderEvent 551 397 0 0 0 0 +InteractionEvent 551 397 0 0 0 0 +MouseMoveEvent 551 401 0 0 0 0 +RenderEvent 551 401 0 0 0 0 +InteractionEvent 551 401 0 0 0 0 +MouseMoveEvent 551 404 0 0 0 0 +RenderEvent 551 404 0 0 0 0 +InteractionEvent 551 404 0 0 0 0 +MouseMoveEvent 551 407 0 0 0 0 +RenderEvent 551 407 0 0 0 0 +InteractionEvent 551 407 0 0 0 0 +MouseMoveEvent 551 411 0 0 0 0 +RenderEvent 551 411 0 0 0 0 +InteractionEvent 551 411 0 0 0 0 +MouseMoveEvent 550 415 0 0 0 0 +RenderEvent 550 415 0 0 0 0 +InteractionEvent 550 415 0 0 0 0 +MouseMoveEvent 550 419 0 0 0 0 +RenderEvent 550 419 0 0 0 0 +InteractionEvent 550 419 0 0 0 0 +MouseMoveEvent 550 422 0 0 0 0 +RenderEvent 550 422 0 0 0 0 +InteractionEvent 550 422 0 0 0 0 +MouseMoveEvent 550 424 0 0 0 0 +RenderEvent 550 424 0 0 0 0 +InteractionEvent 550 424 0 0 0 0 +MouseMoveEvent 550 425 0 0 0 0 +RenderEvent 550 425 0 0 0 0 +InteractionEvent 550 425 0 0 0 0 +MouseMoveEvent 550 426 0 0 0 0 +RenderEvent 550 426 0 0 0 0 +InteractionEvent 550 426 0 0 0 0 +MouseMoveEvent 550 426 0 0 0 0 +RenderEvent 550 426 0 0 0 0 +InteractionEvent 550 426 0 0 0 0 +MouseMoveEvent 550 427 0 0 0 0 +RenderEvent 550 427 0 0 0 0 +InteractionEvent 550 427 0 0 0 0 +MouseMoveEvent 550 428 0 0 0 0 +RenderEvent 550 428 0 0 0 0 +InteractionEvent 550 428 0 0 0 0 +RightButtonReleaseEvent 550 428 0 0 0 0 +EndInteractionEvent 550 428 0 0 0 0 +RenderEvent 550 428 0 0 0 0 +MouseMoveEvent 549 428 0 0 0 0 +MouseMoveEvent 548 428 0 0 0 0 +MouseMoveEvent 548 428 0 0 0 0 +MouseMoveEvent 547 428 0 0 0 0 +MouseMoveEvent 546 428 0 0 0 0 +MouseMoveEvent 545 428 0 0 0 0 +MouseMoveEvent 544 429 0 0 0 0 +MouseMoveEvent 543 429 0 0 0 0 +MouseMoveEvent 544 430 0 0 0 0 +MouseMoveEvent 544 430 0 0 0 0 +MiddleButtonPressEvent 544 430 0 0 0 0 +StartInteractionEvent 544 430 0 0 0 0 +MouseMoveEvent 543 430 0 0 0 0 +RenderEvent 543 430 0 0 0 0 +InteractionEvent 543 430 0 0 0 0 +MouseMoveEvent 541 429 0 0 0 0 +RenderEvent 541 429 0 0 0 0 +InteractionEvent 541 429 0 0 0 0 +MouseMoveEvent 537 428 0 0 0 0 +RenderEvent 537 428 0 0 0 0 +InteractionEvent 537 428 0 0 0 0 +MouseMoveEvent 530 426 0 0 0 0 +RenderEvent 530 426 0 0 0 0 +InteractionEvent 530 426 0 0 0 0 +MouseMoveEvent 517 425 0 0 0 0 +RenderEvent 517 425 0 0 0 0 +InteractionEvent 517 425 0 0 0 0 +MouseMoveEvent 502 421 0 0 0 0 +RenderEvent 502 421 0 0 0 0 +InteractionEvent 502 421 0 0 0 0 +MouseMoveEvent 454 413 0 0 0 0 +RenderEvent 454 413 0 0 0 0 +InteractionEvent 454 413 0 0 0 0 +MouseMoveEvent 410 405 0 0 0 0 +RenderEvent 410 405 0 0 0 0 +InteractionEvent 410 405 0 0 0 0 +MouseMoveEvent 379 398 0 0 0 0 +RenderEvent 379 398 0 0 0 0 +InteractionEvent 379 398 0 0 0 0 +MouseMoveEvent 365 392 0 0 0 0 +RenderEvent 365 392 0 0 0 0 +InteractionEvent 365 392 0 0 0 0 +MouseMoveEvent 359 388 0 0 0 0 +RenderEvent 359 388 0 0 0 0 +InteractionEvent 359 388 0 0 0 0 +MouseMoveEvent 347 381 0 0 0 0 +RenderEvent 347 381 0 0 0 0 +InteractionEvent 347 381 0 0 0 0 +MouseMoveEvent 332 374 0 0 0 0 +RenderEvent 332 374 0 0 0 0 +InteractionEvent 332 374 0 0 0 0 +MouseMoveEvent 319 369 0 0 0 0 +RenderEvent 319 369 0 0 0 0 +InteractionEvent 319 369 0 0 0 0 +MouseMoveEvent 305 363 0 0 0 0 +RenderEvent 305 363 0 0 0 0 +InteractionEvent 305 363 0 0 0 0 +MouseMoveEvent 287 358 0 0 0 0 +RenderEvent 287 358 0 0 0 0 +InteractionEvent 287 358 0 0 0 0 +MouseMoveEvent 265 351 0 0 0 0 +RenderEvent 265 351 0 0 0 0 +InteractionEvent 265 351 0 0 0 0 +MouseMoveEvent 237 342 0 0 0 0 +RenderEvent 237 342 0 0 0 0 +InteractionEvent 237 342 0 0 0 0 +MouseMoveEvent 220 338 0 0 0 0 +RenderEvent 220 338 0 0 0 0 +InteractionEvent 220 338 0 0 0 0 +MouseMoveEvent 203 332 0 0 0 0 +RenderEvent 203 332 0 0 0 0 +InteractionEvent 203 332 0 0 0 0 +MouseMoveEvent 189 328 0 0 0 0 +RenderEvent 189 328 0 0 0 0 +InteractionEvent 189 328 0 0 0 0 +MouseMoveEvent 184 324 0 0 0 0 +RenderEvent 184 324 0 0 0 0 +InteractionEvent 184 324 0 0 0 0 +MouseMoveEvent 183 323 0 0 0 0 +RenderEvent 183 323 0 0 0 0 +InteractionEvent 183 323 0 0 0 0 +MouseMoveEvent 182 323 0 0 0 0 +RenderEvent 182 323 0 0 0 0 +InteractionEvent 182 323 0 0 0 0 +MouseMoveEvent 180 323 0 0 0 0 +RenderEvent 180 323 0 0 0 0 +InteractionEvent 180 323 0 0 0 0 +MouseMoveEvent 174 321 0 0 0 0 +RenderEvent 174 321 0 0 0 0 +InteractionEvent 174 321 0 0 0 0 +MouseMoveEvent 166 318 0 0 0 0 +RenderEvent 166 318 0 0 0 0 +InteractionEvent 166 318 0 0 0 0 +MouseMoveEvent 155 315 0 0 0 0 +RenderEvent 155 315 0 0 0 0 +InteractionEvent 155 315 0 0 0 0 +MouseMoveEvent 143 311 0 0 0 0 +RenderEvent 143 311 0 0 0 0 +InteractionEvent 143 311 0 0 0 0 +MouseMoveEvent 120 306 0 0 0 0 +RenderEvent 120 306 0 0 0 0 +InteractionEvent 120 306 0 0 0 0 +MouseMoveEvent 111 305 0 0 0 0 +RenderEvent 111 305 0 0 0 0 +InteractionEvent 111 305 0 0 0 0 +MouseMoveEvent 105 304 0 0 0 0 +RenderEvent 105 304 0 0 0 0 +InteractionEvent 105 304 0 0 0 0 +MouseMoveEvent 98 300 0 0 0 0 +RenderEvent 98 300 0 0 0 0 +InteractionEvent 98 300 0 0 0 0 +MouseMoveEvent 92 297 0 0 0 0 +RenderEvent 92 297 0 0 0 0 +InteractionEvent 92 297 0 0 0 0 +MouseMoveEvent 76 292 0 0 0 0 +RenderEvent 76 292 0 0 0 0 +InteractionEvent 76 292 0 0 0 0 +MouseMoveEvent 60 286 0 0 0 0 +RenderEvent 60 286 0 0 0 0 +InteractionEvent 60 286 0 0 0 0 +MouseMoveEvent 47 282 0 0 0 0 +RenderEvent 47 282 0 0 0 0 +InteractionEvent 47 282 0 0 0 0 +MouseMoveEvent 40 281 0 0 0 0 +RenderEvent 40 281 0 0 0 0 +InteractionEvent 40 281 0 0 0 0 +MouseMoveEvent 39 281 0 0 0 0 +RenderEvent 39 281 0 0 0 0 +InteractionEvent 39 281 0 0 0 0 +MiddleButtonReleaseEvent 39 281 0 0 0 0 +EndInteractionEvent 39 281 0 0 0 0 +RenderEvent 39 281 0 0 0 0 +MouseMoveEvent 39 282 0 0 0 0 +MouseMoveEvent 40 284 0 0 0 0 +MouseMoveEvent 42 285 0 0 0 0 +MouseMoveEvent 46 287 0 0 0 0 +MouseMoveEvent 58 290 0 0 0 0 +MouseMoveEvent 72 294 0 0 0 0 +MouseMoveEvent 90 299 0 0 0 0 +MouseMoveEvent 114 305 0 0 0 0 +MouseMoveEvent 140 311 0 0 0 0 +MouseMoveEvent 170 315 0 0 0 0 +MouseMoveEvent 202 319 0 0 0 0 +MouseMoveEvent 234 325 0 0 0 0 +MouseMoveEvent 264 329 0 0 0 0 +MouseMoveEvent 290 333 0 0 0 0 +MouseMoveEvent 316 335 0 0 0 0 +MouseMoveEvent 340 337 0 0 0 0 +MouseMoveEvent 360 337 0 0 0 0 +MouseMoveEvent 371 337 0 0 0 0 +MouseMoveEvent 376 337 0 0 0 0 +MouseMoveEvent 377 337 0 0 0 0 +MouseMoveEvent 376 336 0 0 0 0 +MouseMoveEvent 375 335 0 0 0 0 +MouseMoveEvent 374 334 0 0 0 0 +MouseMoveEvent 373 333 0 0 0 0 +MouseMoveEvent 372 332 0 0 0 0 +MouseMoveEvent 372 331 0 0 0 0 +MouseMoveEvent 372 330 0 0 0 0 +MouseMoveEvent 371 330 0 0 0 0 +MouseWheelBackwardEvent 371 330 0 0 0 0 +StartInteractionEvent 371 330 0 0 0 0 +RenderEvent 371 330 0 0 0 0 +EndInteractionEvent 371 330 0 0 0 0 +RenderEvent 371 330 0 0 0 0 +MouseWheelBackwardEvent 371 330 0 0 1 0 +StartInteractionEvent 371 330 0 0 1 0 +RenderEvent 371 330 0 0 1 0 +EndInteractionEvent 371 330 0 0 1 0 +RenderEvent 371 330 0 0 1 0 +MouseWheelBackwardEvent 371 330 0 0 0 0 +StartInteractionEvent 371 330 0 0 0 0 +RenderEvent 371 330 0 0 0 0 +EndInteractionEvent 371 330 0 0 0 0 +RenderEvent 371 330 0 0 0 0 +MouseWheelBackwardEvent 371 330 0 0 1 0 +StartInteractionEvent 371 330 0 0 1 0 +RenderEvent 371 330 0 0 1 0 +EndInteractionEvent 371 330 0 0 1 0 +RenderEvent 371 330 0 0 1 0 +MouseWheelBackwardEvent 371 330 0 0 0 0 +StartInteractionEvent 371 330 0 0 0 0 +RenderEvent 371 330 0 0 0 0 +EndInteractionEvent 371 330 0 0 0 0 +RenderEvent 371 330 0 0 0 0 +KeyPressEvent 300 277 0 0 1 Control_L +CharEvent 300 277 0 0 1 Control_L +LeftButtonPressEvent 300 277 2 0 0 Control_L +StartInteractionEvent 300 277 2 0 0 Control_L +MouseMoveEvent 300 278 2 0 0 Control_L +RenderEvent 300 278 2 0 0 Control_L +InteractionEvent 300 278 2 0 0 Control_L +MouseMoveEvent 299 280 2 0 0 Control_L +RenderEvent 299 280 2 0 0 Control_L +InteractionEvent 299 280 2 0 0 Control_L +MouseMoveEvent 299 282 2 0 0 Control_L +RenderEvent 299 282 2 0 0 Control_L +InteractionEvent 299 282 2 0 0 Control_L +MouseMoveEvent 298 284 2 0 0 Control_L +RenderEvent 298 284 2 0 0 Control_L +InteractionEvent 298 284 2 0 0 Control_L +MouseMoveEvent 297 285 2 0 0 Control_L +RenderEvent 297 285 2 0 0 Control_L +InteractionEvent 297 285 2 0 0 Control_L +MouseMoveEvent 297 286 2 0 0 Control_L +RenderEvent 297 286 2 0 0 Control_L +InteractionEvent 297 286 2 0 0 Control_L +MouseMoveEvent 297 287 2 0 0 Control_L +RenderEvent 297 287 2 0 0 Control_L +InteractionEvent 297 287 2 0 0 Control_L +MouseMoveEvent 297 288 2 0 0 Control_L +RenderEvent 297 288 2 0 0 Control_L +InteractionEvent 297 288 2 0 0 Control_L +MouseMoveEvent 296 290 2 0 0 Control_L +RenderEvent 296 290 2 0 0 Control_L +InteractionEvent 296 290 2 0 0 Control_L +MouseMoveEvent 296 291 2 0 0 Control_L +RenderEvent 296 291 2 0 0 Control_L +InteractionEvent 296 291 2 0 0 Control_L +LeftButtonReleaseEvent 296 291 2 0 0 Control_L +EndInteractionEvent 296 291 2 0 0 Control_L +RenderEvent 296 291 2 0 0 Control_L +MouseMoveEvent 295 291 2 0 0 Control_L +MouseMoveEvent 293 292 2 0 0 Control_L +MouseMoveEvent 291 294 2 0 0 Control_L +MouseMoveEvent 290 294 2 0 0 Control_L +KeyReleaseEvent 290 294 2 0 1 Control_L +KeyPressEvent 371 330 0 13 1 Return +CharEvent 371 330 0 13 1 Return +KeyReleaseEvent 371 330 0 13 1 Return diff --git a/testing/recordings/TestInteractionVolume.log b/testing/recordings/TestInteractionVolume.log deleted file mode 100644 index 7cefc84c39..0000000000 --- a/testing/recordings/TestInteractionVolume.log +++ /dev/null @@ -1,3 +0,0 @@ -# StreamVersion 1.1 -ExposeEvent 0 599 0 0 0 0 -RenderEvent 0 599 0 0 0 0 diff --git a/testing/recordings/TestInteractionVolumeAfterColoring.log b/testing/recordings/TestInteractionVolumeAfterColoring.log new file mode 100644 index 0000000000..67ea597153 --- /dev/null +++ b/testing/recordings/TestInteractionVolumeAfterColoring.log @@ -0,0 +1,28 @@ +# StreamVersion 1.2 +ExposeEvent 0 599 0 0 0 0 0 +RenderEvent 0 599 0 0 0 0 0 +TimerEvent 0 599 0 0 0 0 0 +KeyPressEvent 1394 -281 0 115 1 s 0 +CharEvent 1394 -281 0 115 1 s 0 +TimerEvent 1394 -281 0 115 1 s 0 +KeyReleaseEvent 1394 -281 0 115 1 s 0 +TimerEvent 1394 -281 0 115 1 s 0 +TimerEvent 1394 -281 0 115 1 s 0 +KeyPressEvent 1394 -281 0 115 1 s 0 +CharEvent 1394 -281 0 115 1 s 0 +TimerEvent 1394 -281 0 115 1 s 0 +KeyReleaseEvent 1394 -281 0 115 1 s 0 +TimerEvent 1394 -281 0 115 1 s 0 +TimerEvent 1394 -281 0 115 1 s 0 +KeyPressEvent 1394 -281 0 115 1 s 0 +CharEvent 1394 -281 0 115 1 s 0 +TimerEvent 1394 -281 0 115 1 s 0 +KeyReleaseEvent 1394 -281 0 115 1 s 0 +TimerEvent 1394 -281 0 115 1 s 0 +TimerEvent 1394 -281 0 115 1 s 0 +KeyPressEvent 1394 -281 0 118 1 v 0 +CharEvent 1394 -281 0 118 1 v 0 +TimerEvent 1394 -281 0 118 1 v 0 +KeyReleaseEvent 1394 -281 0 118 1 v 0 +TimerEvent 1394 -281 0 118 1 v 0 +TimerEvent 1394 -281 0 118 1 v 0 diff --git a/testing/recordings/TestInteractionVolumeCycle.log b/testing/recordings/TestInteractionVolumeCycle.log new file mode 100644 index 0000000000..71cfe01cb8 --- /dev/null +++ b/testing/recordings/TestInteractionVolumeCycle.log @@ -0,0 +1,23 @@ +# StreamVersion 1.2 +ExposeEvent 0 599 0 0 0 0 0 +RenderEvent 0 599 0 0 0 0 0 +TimerEvent 0 599 0 0 0 0 0 +TimerEvent 0 599 0 0 0 0 0 +TimerEvent 363 259 0 0 0 0 0 +KeyPressEvent 363 259 0 118 1 v 0 +CharEvent 363 259 0 118 1 v 0 +TimerEvent 363 259 0 118 1 v 0 +KeyReleaseEvent 363 259 0 118 1 v 0 +TimerEvent 363 259 0 118 1 v 0 +TimerEvent 363 259 0 118 1 v 0 +KeyPressEvent 363 259 0 115 1 s 0 +CharEvent 363 259 0 115 1 s 0 +TimerEvent 363 259 0 115 1 s 0 +KeyReleaseEvent 363 259 0 115 1 s 0 +TimerEvent 363 259 0 115 1 s 0 +TimerEvent 363 259 0 115 1 s 0 +KeyPressEvent 363 259 0 115 1 s 0 +CharEvent 363 259 0 115 1 s 0 +TimerEvent 363 259 0 115 1 s 0 +KeyReleaseEvent 363 259 0 115 1 s 0 +TimerEvent 363 259 0 115 1 s 0 diff --git a/testing/recordings/TestSDKInteractorCallBack.log.in b/testing/recordings/TestSDKInteractorCallBack.log.in index 2fb9f661b1..2c6fcc6456 100644 --- a/testing/recordings/TestSDKInteractorCallBack.log.in +++ b/testing/recordings/TestSDKInteractorCallBack.log.in @@ -10,6 +10,94 @@ RenderEvent 781 494 0 0 0 Super_L 0 MouseMoveEvent 781 498 0 0 0 Super_L 0 LeaveEvent 751 614 0 0 0 Super_L 0 EnterEvent 700 570 0 0 0 Super_L 0 -KeyPressEvent 823 628 0 115 1 s 0 -CharEvent 823 628 0 115 1 s 0 -KeyReleaseEvent 823 628 0 115 1 s 0 +TimerEvent 0 1406 0 0 0 0 0 +TimerEvent 0 1406 0 0 0 0 0 +KeyPressEvent -604 609 0 115 1 s 0 +CharEvent -604 609 0 115 1 s 0 +TimerEvent -604 609 0 115 1 s 0 +TimerEvent -604 609 0 115 1 s 0 +KeyReleaseEvent -604 609 0 115 1 s 0 +TimerEvent 0 1406 0 0 0 0 0 +TimerEvent 0 1406 0 0 0 0 0 +KeyPressEvent -604 609 0 115 1 y 0 +CharEvent -604 609 0 115 1 y 0 +TimerEvent -604 609 0 115 1 y 0 +TimerEvent -604 609 0 115 1 y 0 +KeyReleaseEvent -604 609 0 115 1 y 0 +TimerEvent 0 1406 0 0 0 0 0 +TimerEvent 0 1406 0 0 0 0 0 +KeyPressEvent -604 609 0 115 1 b 0 +CharEvent -604 609 0 115 1 b 0 +TimerEvent -604 609 0 115 1 b 0 +TimerEvent -604 609 0 115 1 b 0 +KeyReleaseEvent -604 609 0 115 1 b 0 +TimerEvent -604 609 0 115 1 s 0 +TimerEvent -604 609 0 115 1 s 0 +KeyPressEvent -604 609 0 0 1 Control_L 0 +CharEvent -604 609 0 0 1 Control_L 0 +TimerEvent -604 609 0 0 1 Control_L 0 +TimerEvent -604 609 0 0 1 Control_L 0 +KeyPressEvent -604 609 2 16 1 s 0 +CharEvent -604 609 2 16 1 s 0 +TimerEvent -604 609 2 16 1 s 0 +TimerEvent -604 609 2 16 1 s 0 +KeyReleaseEvent -604 609 2 16 1 s 0 +TimerEvent -604 609 2 16 1 s 0 +TimerEvent -604 609 2 16 1 s 0 +KeyPressEvent -604 609 2 16 1 p 0 +CharEvent -604 609 2 16 1 p 0 +TimerEvent -604 609 2 16 1 p 0 +TimerEvent -604 609 2 16 1 p 0 +KeyReleaseEvent -604 609 2 16 1 p 0 +TimerEvent -604 609 2 16 1 p 0 +TimerEvent -604 609 2 16 1 p 0 +KeyReleaseEvent -604 609 2 0 1 Control_L 0 +TimerEvent -604 609 2 0 1 Control_L 0 +TimerEvent -604 609 2 0 1 Control_L 0 +KeyPressEvent -604 609 0 0 1 Shift_L 0 +CharEvent -604 609 0 0 1 Shift_L 0 +TimerEvent -604 609 0 0 1 Shift_L 0 +TimerEvent -604 609 0 0 1 Shift_L 0 +KeyPressEvent -604 609 1 89 1 Y 0 +CharEvent -604 609 1 89 1 Y 0 +TimerEvent -604 609 1 89 1 Y 0 +TimerEvent -604 609 1 89 1 Y 0 +KeyReleaseEvent -604 609 1 89 1 Y 0 +TimerEvent -604 609 1 89 1 Y 0 +TimerEvent -604 609 1 89 1 Y 0 +KeyReleaseEvent -604 609 1 0 1 Shift_L 0 +TimerEvent -604 609 1 0 1 Shift_L 0 +TimerEvent -604 609 1 0 1 Shift_L 0 +KeyPressEvent -604 609 0 0 1 Control_L 0 +CharEvent -604 609 0 0 1 Control_L 0 +TimerEvent -604 609 0 0 1 Control_L 0 +KeyPressEvent -604 609 2 0 1 Shift_L 0 +CharEvent -604 609 2 0 1 Shift_L 0 +TimerEvent -604 609 2 0 1 Shift_L 0 +TimerEvent -604 609 2 0 1 Shift_L 0 +KeyPressEvent -604 609 3 11 1 B 0 +CharEvent -604 609 3 11 1 B 0 +TimerEvent -604 609 3 11 1 B 0 +TimerEvent -604 609 3 11 1 B 0 +KeyReleaseEvent -604 609 3 11 1 B 0 +TimerEvent -604 609 3 11 1 B 0 +TimerEvent -604 609 3 11 1 B 0 +KeyPressEvent -604 609 3 11 1 A 0 +CharEvent -604 609 3 11 1 A 0 +TimerEvent -604 609 3 11 1 A 0 +TimerEvent -604 609 3 11 1 A 0 +KeyReleaseEvent -604 609 3 11 1 A 0 +TimerEvent -604 609 3 11 1 A 0 +TimerEvent -604 609 3 11 1 A 0 +KeyReleaseEvent -604 609 3 0 1 Shift_L 0 +TimerEvent -604 609 3 0 1 Shift_L 0 +KeyReleaseEvent -604 609 2 0 1 Control_L 0 +TimerEvent -604 609 2 0 1 Control_L 0 +TimerEvent -604 609 2 0 1 Control_L 0 +KeyPressEvent -604 609 3 11 1 7 0 +CharEvent -604 609 3 11 1 7 0 +TimerEvent -604 609 3 11 1 7 0 +TimerEvent -604 609 3 11 1 7 0 +KeyReleaseEvent -604 609 3 11 1 7 0 +TimerEvent -604 609 3 11 1 7 0 +TimerEvent -604 609 3 11 1 7 0 diff --git a/vtkext/private/CMakeLists.txt b/vtkext/private/CMakeLists.txt index bc0f5981f5..0357f86914 100644 --- a/vtkext/private/CMakeLists.txt +++ b/vtkext/private/CMakeLists.txt @@ -11,18 +11,10 @@ vtk_module_build( INSTALL_HEADERS OFF PACKAGE "f3d_vtkext_private") -# We need non empty cmake vars for these calls for VTK <= 9.1 -# See https://gitlab.kitware.com/vtk/vtk/-/merge_requests/9084 foreach (module IN LISTS modules) - if(NOT "${f3d_compile_options_public}" STREQUAL "") - vtk_module_compile_options(${module} PUBLIC ${f3d_compile_options_public}) - endif() - if(NOT "${f3d_compile_options_private}" STREQUAL "") - vtk_module_compile_options(${module} PRIVATE ${f3d_compile_options_private}) - endif() - if (NOT "${f3d_link_options_public}" STREQUAL "") - vtk_module_link_options(${module} PUBLIC ${f3d_link_options_public}) - endif() + vtk_module_compile_options(${module} PUBLIC ${f3d_compile_options_public}) + vtk_module_compile_options(${module} PRIVATE ${f3d_compile_options_private}) + vtk_module_link_options(${module} PUBLIC ${f3d_link_options_public}) vtk_module_set_properties(${module} CXX_STANDARD 17) if(F3D_STRICT_BUILD AND MSVC) # There are warnings in VTK related to deprecated features in C++17 diff --git a/vtkext/private/module/CMakeLists.txt b/vtkext/private/module/CMakeLists.txt index 357dd6372f..26086ede7b 100644 --- a/vtkext/private/module/CMakeLists.txt +++ b/vtkext/private/module/CMakeLists.txt @@ -31,6 +31,7 @@ endforeach() set(classes F3DLog + F3DColoringInfoHandler vtkF3DCachedLUTTexture vtkF3DCachedSpecularTexture vtkF3DConsoleOutputWindow @@ -40,6 +41,7 @@ set(classes vtkF3DInteractorEventRecorder vtkF3DInteractorStyle vtkF3DMemoryMesh + vtkF3DMetaImporter vtkF3DNoRenderWindow vtkF3DObjectFactory vtkF3DOpenGLGridMapper @@ -47,7 +49,6 @@ set(classes vtkF3DPostProcessFilter vtkF3DRenderPass vtkF3DRenderer - vtkF3DRendererWithColoring vtkF3DUserRenderPass ) diff --git a/vtkext/private/module/F3DColoringInfoHandler.cxx b/vtkext/private/module/F3DColoringInfoHandler.cxx new file mode 100644 index 0000000000..11b943fb82 --- /dev/null +++ b/vtkext/private/module/F3DColoringInfoHandler.cxx @@ -0,0 +1,184 @@ +#include "F3DColoringInfoHandler.h" + +#include "F3DLog.h" + +#include +#include +#include +#include + +#include +#include + +//---------------------------------------------------------------------------- +void F3DColoringInfoHandler::ClearColoringInfo() +{ + this->PointDataColoringInfo.clear(); + this->CellDataColoringInfo.clear(); +} + +//---------------------------------------------------------------------------- +void F3DColoringInfoHandler::UpdateColoringInfo(vtkDataSet* dataset, bool useCellData) +{ + // XXX: This assumes importer do not import actors with an empty input + assert(dataset); + + // Recover all possible names + std::set arrayNames; + + vtkDataSetAttributes* attr = useCellData + ? static_cast(dataset->GetCellData()) + : static_cast(dataset->GetPointData()); + + for (int i = 0; i < attr->GetNumberOfArrays(); i++) + { + vtkDataArray* array = attr->GetArray(i); + if (array && array->GetName()) + { + arrayNames.insert(array->GetName()); + } + } + + auto& data = useCellData ? this->CellDataColoringInfo : this->PointDataColoringInfo; + + for (const std::string& arrayName : arrayNames) + { + // Recover/Create a coloring info + F3DColoringInfoHandler::ColoringInfo& info = data[arrayName]; + info.Name = arrayName; + + vtkDataArray* array = useCellData ? dataset->GetCellData()->GetArray(arrayName.c_str()) + : dataset->GetPointData()->GetArray(arrayName.c_str()); + if (array) + { + info.MaximumNumberOfComponents = + std::max(info.MaximumNumberOfComponents, array->GetNumberOfComponents()); + + // Set ranges + // XXX this does not take animation into account + std::array range; + array->GetRange(range.data(), -1); + info.MagnitudeRange[0] = std::min(info.MagnitudeRange[0], range[0]); + info.MagnitudeRange[1] = std::max(info.MagnitudeRange[1], range[1]); + + for (size_t i = 0; i < static_cast(array->GetNumberOfComponents()); i++) + { + array->GetRange(range.data(), static_cast(i)); + if (i < info.ComponentRanges.size()) + { + info.ComponentRanges[i][0] = std::min(info.ComponentRanges[i][0], range[0]); + info.ComponentRanges[i][1] = std::max(info.ComponentRanges[i][1], range[1]); + } + else + { + info.ComponentRanges.emplace_back(range); + } + } + + // Set component names + if (array->HasAComponentName()) + { + for (size_t i = 0; i < static_cast(array->GetNumberOfComponents()); i++) + { + const char* compName = array->GetComponentName(i); + if (i < info.ComponentNames.size()) + { + if (compName && info.ComponentNames[i] != std::string(compName)) + { + // set non-coherent component names to empty string + info.ComponentNames[i] = ""; + } + } + else + { + // Add components names to the back of the component names vector + info.ComponentNames.emplace_back(compName ? compName : ""); + } + } + } + } + } +} + +//---------------------------------------------------------------------------- +std::optional F3DColoringInfoHandler::SetCurrentColoring(bool enable, bool useCellData, const std::optional& arrayName, bool quiet) +{ + this->CurrentUsingCellData = useCellData; + auto& data = + this->CurrentUsingCellData ? this->CellDataColoringInfo : this->PointDataColoringInfo; + int nIndices = static_cast(data.size()); + + if (!enable) + { + // Not coloring + this->CurrentColoringIter.reset(); + } + else if (nIndices == 0) + { + // Trying to color but no array available + this->CurrentColoringIter.reset(); + + if (!quiet) + { + F3DLog::Print(F3DLog::Severity::Debug, "No array to color with"); + } + } + else if (!arrayName.has_value()) + { + // Coloring with first array + this->CurrentColoringIter = data.begin(); + } + else + { + // Coloring with named array + this->CurrentColoringIter = data.find(arrayName.value()); + if (this->CurrentColoringIter.value() == data.end()) + { + // Could not find named array + this->CurrentColoringIter.reset(); + if (!quiet) + { + F3DLog::Print(F3DLog::Severity::Warning, "Unknown scalar array: \"" + arrayName.value() + "\""); + } + } + } + return this->GetCurrentColoringInfo(); +} + +//---------------------------------------------------------------------------- +std::optional F3DColoringInfoHandler::GetCurrentColoringInfo() const +{ + if (this->CurrentColoringIter.has_value()) + { + return this->CurrentColoringIter.value()->second; + } + return std::nullopt; +} + +//---------------------------------------------------------------------------- +void F3DColoringInfoHandler::CycleColoringArray(bool cycleToNonColoring) +{ + auto& data = + this->CurrentUsingCellData ? this->CellDataColoringInfo : this->PointDataColoringInfo; + if (!this->CurrentColoringIter.has_value()) + { + if(!data.empty()) + { + this->CurrentColoringIter = data.begin(); + } + } + else + { + if (++this->CurrentColoringIter.value() == data.end()) + { + if (cycleToNonColoring) + { + this->CurrentColoringIter.reset(); + } + else + { + this->CurrentColoringIter = data.begin(); + } + } + } +} diff --git a/vtkext/private/module/F3DColoringInfoHandler.h b/vtkext/private/module/F3DColoringInfoHandler.h new file mode 100644 index 0000000000..d21519f412 --- /dev/null +++ b/vtkext/private/module/F3DColoringInfoHandler.h @@ -0,0 +1,78 @@ +/** + * @class F3DColoringInfoHandler + * @brief A stateful handler to handle coloring info + */ +#ifndef F3DColoringInfoHandler_h +#define F3DColoringInfoHandler_h + +#include +#include +#include +#include +#include +#include + +class vtkDataSet; +class F3DColoringInfoHandler +{ +public: + /** + * A struct containing information about possible coloring + */ + struct ColoringInfo + { + std::string Name; + int MaximumNumberOfComponents = 0; + std::vector ComponentNames; + std::vector> ComponentRanges; + std::array MagnitudeRange = { std::numeric_limits::max(), + std::numeric_limits::min() }; + }; + + /** + * Update internal coloring maps using provided dataset + * useCellData control if point data or cell data should be updated + */ + void UpdateColoringInfo(vtkDataSet* dataset, bool useCellData); + + /** + * Clear all internal coloring maps + */ + void ClearColoringInfo(); + + /** + * Set the current coloring state + * @param enable: If coloring should be enabled or not + * @param useCellData: If cell data or point data should be used + * @param arrayName: An optional arrayName to color with + * @param quiet: If true, no log will be done by this method, even when failing to find an array to color with + * @return: current coloring info if any, unset optional otherwise + */ + std::optional SetCurrentColoring(bool enable, bool useCellData, const std::optional& arrayName, bool quiet); + + /** + * Get the current coloring state + * Return current coloring info if any, unset optional otherwise + */ + std::optional GetCurrentColoringInfo() const; + + /** + * Cycle the current coloring + * If not coloring, this will try to find an array to color with + * This does not change the cell/point data status + * @param cycleToNonColoring: Control whether to cycle to non coloring after reaching the last array or not + */ + void CycleColoringArray(bool cycleToNonColoring); + +private: + // Map of arrayName -> coloring info + using ColoringMap = std::map; + ColoringMap PointDataColoringInfo; + ColoringMap CellDataColoringInfo; + + // Current coloring state + bool CurrentUsingCellData = false; + std::optional CurrentColoringIter; +}; + +#endif diff --git a/vtkext/private/module/Testing/CMakeLists.txt b/vtkext/private/module/Testing/CMakeLists.txt index bd1b4fcbaa..93f6579b7c 100644 --- a/vtkext/private/module/Testing/CMakeLists.txt +++ b/vtkext/private/module/Testing/CMakeLists.txt @@ -1,11 +1,11 @@ set(test_sources TestF3DCachedTexturesPrint.cxx TestF3DGenericImporter.cxx - TestF3DGenericImporterMultiColoring.cxx TestF3DInteractorEventRecorder.cxx TestF3DLog.cxx + TestF3DMetaImporterMultiColoring.cxx TestF3DObjectFactory.cxx - TestF3DOpenGLGridMapper.cxx + TestF3DOpenGLGridMapper.cxx TestF3DRenderPass.cxx TestF3DRendererWithColoring.cxx ) @@ -15,7 +15,7 @@ if(WIN32 AND F3D_WINDOWS_GUI) endif() if(F3D_MODULE_EXR) - list(APPEND test_sources + list(APPEND test_sources TestF3DEXRReader.cxx TestF3DEXRReaderInvalid.cxx) endif() @@ -32,8 +32,12 @@ if(UNIX) set_tests_properties(f3d::vtkextPrivateCxx-TestF3DLog PROPERTIES PASS_REGULAR_EXPRESSION "Test Info Test Warning Test Error\nTest Debug Test Info Test Warning Test Error\nTest Warning Test Error\nTest Error\nTest Info Coloring Test Warning Coloring Test Error Coloring\n") endif() +set_tests_properties(f3d::vtkextPrivateCxx-TestF3DGenericImporter + PROPERTIES + FAIL_REGULAR_EXPRESSION "") + if(F3D_MODULE_EXR) set_tests_properties(f3d::vtkextPrivateCxx-TestF3DEXRReaderInvalid - PROPERTIES + PROPERTIES FAIL_REGULAR_EXPRESSION "") endif() diff --git a/vtkext/private/module/Testing/TestF3DGenericImporter.cxx b/vtkext/private/module/Testing/TestF3DGenericImporter.cxx index fbe9ffacd8..5aedef939c 100644 --- a/vtkext/private/module/Testing/TestF3DGenericImporter.cxx +++ b/vtkext/private/module/Testing/TestF3DGenericImporter.cxx @@ -3,6 +3,7 @@ #include #include #include +#include #include "vtkF3DGenericImporter.h" @@ -11,15 +12,6 @@ int TestF3DGenericImporter(int argc, char* argv[]) { vtkNew importer; - - // Test without any reader - if (importer->CanReadFile()) - { - std::cerr << "Importer unexpectedly can read a file without internal reader" << std::endl; - return EXIT_FAILURE; - } - importer->Update(); - importer->Print(cout); if (importer->GetNumberOfAnimations() != 0) { std::cerr << "Unexpected number of animations" << std::endl; @@ -44,13 +36,7 @@ int TestF3DGenericImporter(int argc, char* argv[]) reader->UpdateInformation(); reader->EnableAnimation(0); - importer->AddInternalReader("Test", reader); - if (!importer->CanReadFile()) - { - std::cerr << "Importer unexpectedly can not read a valid file" << std::endl; - return EXIT_FAILURE; - } - + importer->SetInternalReader(reader); importer->Update(); importer->Print(cout); if (importer->GetNumberOfAnimations() != 1) @@ -73,24 +59,20 @@ int TestF3DGenericImporter(int argc, char* argv[]) return EXIT_FAILURE; } - // Test with multiple readers - vtkNew reader2; - filename = std::string(argv[1]) + "data/suzanne.ply"; - reader2->SetFileName(filename.c_str()); - importer->AddInternalReader("Test2", reader2); +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240910) + filename = std::string(argv[1]) + "data/BoxAnimated_invalid_animation.gltf"; + reader->SetFileName(filename.c_str()); + reader->UpdateInformation(); + reader->EnableAnimation(0); - if (!importer->CanReadFile()) - { - std::cerr << "Importer unexpectedly can not read a valid file" << std::endl; - return EXIT_FAILURE; - } + importer->SetInternalReader(reader); importer->Update(); - std::string description = importer->GetMetaDataDescription(); - if (description.find("Number of geometries: 2") == std::string::npos) + if (importer->UpdateAtTimeValue(0.1)) { - std::cerr << "Unexpected meta data description with multiple geometries" << std::endl; + std::cerr << "Unexpected UpdateAtTimeValue success" << std::endl; return EXIT_FAILURE; } +#endif // Static method testing if (vtkF3DGenericImporter::GetDataObjectDescription(nullptr) != "") diff --git a/vtkext/private/module/Testing/TestF3DGenericImporterMultiColoring.cxx b/vtkext/private/module/Testing/TestF3DGenericImporterMultiColoring.cxx deleted file mode 100644 index 8b7cddcecb..0000000000 --- a/vtkext/private/module/Testing/TestF3DGenericImporterMultiColoring.cxx +++ /dev/null @@ -1,94 +0,0 @@ -#include -#include -#include -#include -#include - -#include "vtkF3DGenericImporter.h" - -#include - -int TestF3DGenericImporterMultiColoring(int argc, char* argv[]) -{ - vtkNew importer; - - // Read a vts and a vtu with same array names - // but different component names and array ranges - vtkNew readerVTU; - std::string filename = std::string(argv[1]) + "data/bluntfin_t.vtu"; - readerVTU->SetFileName(filename.c_str()); - importer->AddInternalReader("VTU", readerVTU); - - vtkNew readerVTS; - filename = std::string(argv[1]) + "data/bluntfin.vts"; - readerVTS->SetFileName(filename.c_str()); - importer->AddInternalReader("VTS", readerVTS); - - if (!importer->CanReadFile()) - { - std::cerr << "Importer unexpectedly can not read valid files" << std::endl; - return EXIT_FAILURE; - } - - importer->Update(); - - if (importer->GetNumberOfIndexesForColoring(false) != 3) - { - std::cerr << "Importer provide unexpected number of indexes for coloring" << std::endl; - return EXIT_FAILURE; - } - - int idx = importer->FindIndexForColoring(false, "Momentum"); - if (idx < 0) - { - std::cerr << "Importer unable to find an expected coloring array" << std::endl; - return EXIT_FAILURE; - } - - vtkF3DGenericImporter::ColoringInfo info; - importer->GetInfoForColoring(false, idx, info); - if (info.Name != "Momentum") - { - std::cerr << "Unexpected coloring name" << std::endl; - return EXIT_FAILURE; - } - if (info.Arrays.size() != 2) - { - std::cerr << "Unexpected number of arrays" << std::endl; - return EXIT_FAILURE; - } - if (info.MaximumNumberOfComponents != 3) - { - std::cerr << "Unexpected coloring nComp" << std::endl; - return EXIT_FAILURE; - } - if (info.ComponentNames[0] != "LX Momentum") - { - std::cerr << "Unexpected coloring component name 0" << std::endl; - return EXIT_FAILURE; - } - if (info.ComponentNames[1] != "LY Momentum_t") - { - std::cerr << "Unexpected coloring component name 1" << std::endl; - return EXIT_FAILURE; - } - if (info.ComponentNames[2] != "") - { - std::cerr << "Unexpected coloring component name 2" << std::endl; - return EXIT_FAILURE; - } - if (!vtkMathUtilities::FuzzyCompare(info.ComponentRanges[0][0], -5.49586, 1e-5) || - !vtkMathUtilities::FuzzyCompare(info.ComponentRanges[0][1], 5.0455, 1e-5)) - { - std::cerr << "Unexpected coloring component range" << std::endl; - return EXIT_FAILURE; - } - if (!vtkMathUtilities::FuzzyCompare(info.MagnitudeRange[0], 0., 1e-5) || - !vtkMathUtilities::FuzzyCompare(info.MagnitudeRange[1], 5.71596, 1e-5)) - { - std::cerr << "Unexpected coloring magnitude range" << std::endl; - return EXIT_FAILURE; - } - - return EXIT_SUCCESS; -} diff --git a/vtkext/private/module/Testing/TestF3DMetaImporterMultiColoring.cxx b/vtkext/private/module/Testing/TestF3DMetaImporterMultiColoring.cxx new file mode 100644 index 0000000000..d85a0ce138 --- /dev/null +++ b/vtkext/private/module/Testing/TestF3DMetaImporterMultiColoring.cxx @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "vtkF3DGenericImporter.h" +#include "vtkF3DMetaImporter.h" + +#include + +int TestF3DMetaImporterMultiColoring(int argc, char* argv[]) +{ + vtkNew importer; + + // Check importer error code path + if (!importer->GetAnimationName(0).empty()) + { + std::cerr << "Unexpected animation name that should be empty" << std::endl; + return EXIT_FAILURE; + } + if (importer->IsAnimationEnabled(0)) + { + std::cerr << "Unexpected enabled animation that should not be" << std::endl; + return EXIT_FAILURE; + } + + int nbTimeSteps; + double timeRange[2]; + if (importer->GetTemporalInformation(0, 60, nbTimeSteps, timeRange, nullptr)) + { + std::cerr << "Unexpected enabled animation that should not be" << std::endl; + return EXIT_FAILURE; + } + + if (!importer->GetCameraName(0).empty()) + { + std::cerr << "Unexpected camera name that should be empty" << std::endl; + return EXIT_FAILURE; + } + + // Read a vts and a vtu with same array names + // but different component names and array ranges + vtkNew readerVTU; + std::string filename = std::string(argv[1]) + "data/bluntfin_t.vtu"; + readerVTU->SetFileName(filename.c_str()); + vtkNew importerVTU; + importerVTU->SetInternalReader(readerVTU); + + vtkNew readerVTS; + filename = std::string(argv[1]) + "data/bluntfin.vts"; + readerVTS->SetFileName(filename.c_str()); + vtkNew importerVTS; + importerVTS->SetInternalReader(readerVTS); + + importer->AddImporter(importerVTU); + importer->AddImporter(importerVTS); + + vtkNew window; + vtkNew renderer; + window->AddRenderer(renderer); + importer->SetRenderWindow(window); + importer->Update(); + + // Test coloring handler + F3DColoringInfoHandler& coloringHandler = importer->GetColoringInfoHandler(); + + auto info = coloringHandler.SetCurrentColoring(true, false, "Momentum", false); + if (!info.has_value()) + { + std::cerr << "Coloring handler unable to set coloring as expected" << std::endl; + return EXIT_FAILURE; + } + if (info.value().Name != "Momentum") + { + std::cerr << "Unexpected coloring name: " << info.value().Name << std::endl; + return EXIT_FAILURE; + } + if (info.value().MaximumNumberOfComponents != 3) + { + std::cerr << "Unexpected coloring nComp" << std::endl; + return EXIT_FAILURE; + } + if (info.value().ComponentNames[0] != "LX Momentum") + { + std::cerr << "Unexpected coloring component name 0" << std::endl; + return EXIT_FAILURE; + } + if (info.value().ComponentNames[1] != "LY Momentum_t") + { + std::cerr << "Unexpected coloring component name 1" << std::endl; + return EXIT_FAILURE; + } + if (info.value().ComponentNames[2] != "") + { + std::cerr << "Unexpected coloring component name 2" << std::endl; + return EXIT_FAILURE; + } + if (!vtkMathUtilities::FuzzyCompare(info.value().ComponentRanges[0][0], -5.49586, 1e-5) || + !vtkMathUtilities::FuzzyCompare(info.value().ComponentRanges[0][1], 5.79029, 1e-5)) + { + std::cerr << "Unexpected coloring component range" << std::endl; + return EXIT_FAILURE; + } + if (!vtkMathUtilities::FuzzyCompare(info.value().MagnitudeRange[0], 0., 1e-5) || + !vtkMathUtilities::FuzzyCompare(info.value().MagnitudeRange[1], 6.25568, 1e-5)) + { + std::cerr << "Unexpected coloring magnitude range" << std::endl; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/vtkext/private/module/Testing/TestF3DRendererWithColoring.cxx b/vtkext/private/module/Testing/TestF3DRendererWithColoring.cxx index dc1db79335..1f5ac4c12b 100644 --- a/vtkext/private/module/Testing/TestF3DRendererWithColoring.cxx +++ b/vtkext/private/module/Testing/TestF3DRendererWithColoring.cxx @@ -1,69 +1,65 @@ #include #include +#include #include #include "vtkF3DConfigure.h" #include "vtkF3DGenericImporter.h" -#include "vtkF3DRendererWithColoring.h" +#include "vtkF3DRenderer.h" int TestF3DRendererWithColoring(int argc, char* argv[]) { - vtkNew renderer; + vtkNew renderer; + vtkNew importer; + vtkNew window; - // Check some invalid code path - renderer->ShowGrid(true); - - // Check error paths - if (renderer->GetColoringArrayName().has_value()) - { - std::cerr << "Unexpected coloring information without an importer" << std::endl; - return EXIT_FAILURE; - } - if (!renderer->GetColoringDescription().empty()) - { - std::cerr << "Unexpected coloring description without an importer" << std::endl; - return EXIT_FAILURE; - } - - vtkNew importer; + window->AddRenderer(renderer); + importer->SetRenderWindow(window); renderer->SetImporter(importer); - // Read a vts and a vtu with same array names - // but different component names and array ranges + // Check invalid bounding box code path + renderer->ShowGrid(true); + renderer->UpdateActors(); + vtkNew readerVTU; std::string filename = std::string(argv[1]) + "data/bluntfin_t.vtu"; readerVTU->SetFileName(filename.c_str()); - importer->AddInternalReader("VTU", readerVTU); + vtkNew importerVTU; + importerVTU->SetInternalReader(readerVTU); + + importer->AddImporter(importerVTU); importer->Update(); // Check invalid array code path - renderer->SetColoring(true, false, "Invalid", 0); + renderer->SetEnableColoring(true); + renderer->SetArrayNameForColoring("Invalid"); renderer->SetUseVolume(false); renderer->UpdateActors(); - renderer->CycleScalars(vtkF3DRendererWithColoring::CycleType::COMPONENT); + renderer->CycleComponentForColoring(); renderer->SetUseVolume(true); renderer->UpdateActors(); - if (renderer->GetColoringArrayName() != "Density" || renderer->GetColoringComponent() != 0) + if (renderer->GetArrayNameForColoring() != "Invalid" || renderer->GetComponentForColoring() != -1) { std::cerr << "Unexpected coloring information with invalid array" << std::endl; return EXIT_FAILURE; } // Check invalid component code path - renderer->SetColoring(true, false, "Momentum", 5); + renderer->SetArrayNameForColoring("Momentum"); + renderer->SetComponentForColoring(5); renderer->UpdateActors(); renderer->SetUseVolume(true); renderer->UpdateActors(); - if (renderer->GetColoringArrayName() != "Momentum" || renderer->GetColoringComponent() != 5) + if (renderer->GetArrayNameForColoring() != "Momentum" || renderer->GetComponentForColoring() != 5) { std::cerr << "Unexpected coloring information with invalid component" << std::endl; return EXIT_FAILURE; } - renderer->CycleScalars(vtkF3DRendererWithColoring::CycleType::COMPONENT); - if (renderer->GetColoringArrayName() != "Momentum" || renderer->GetColoringComponent() != 1) + renderer->CycleComponentForColoring(); + if (renderer->GetArrayNameForColoring() != "Momentum" || renderer->GetComponentForColoring() != 1) { std::cerr << "Unexpected coloring information after cycling component" << std::endl; return EXIT_FAILURE; diff --git a/vtkext/private/module/vtk.module b/vtkext/private/module/vtk.module index 2373d44423..8186398b45 100644 --- a/vtkext/private/module/vtk.module +++ b/vtkext/private/module/vtk.module @@ -14,7 +14,6 @@ DEPENDS VTK::RenderingCore VTK::RenderingOpenGL2 VTK::RenderingVolumeOpenGL2 - VTK::opengl f3d::vtkext PRIVATE_DEPENDS VTK::CommonExecutionModel @@ -27,6 +26,7 @@ PRIVATE_DEPENDS VTK::RenderingCore VTK::RenderingVolumeOpenGL2 OPTIONAL_DEPENDS + VTK::opengl VTK::RenderingExternal VTK::RenderingRayTracing TEST_DEPENDS diff --git a/vtkext/private/module/vtkF3DCachedLUTTexture.cxx b/vtkext/private/module/vtkF3DCachedLUTTexture.cxx index 175bef25d4..bc3068bb80 100644 --- a/vtkext/private/module/vtkF3DCachedLUTTexture.cxx +++ b/vtkext/private/module/vtkF3DCachedLUTTexture.cxx @@ -1,13 +1,18 @@ #include "vtkF3DCachedLUTTexture.h" -#include "vtkImageData.h" -#include "vtkObjectFactory.h" -#include "vtkOpenGLRenderWindow.h" -#include "vtkRenderer.h" -#include "vtkTextureObject.h" -#include "vtkXMLImageDataReader.h" +#include +#include +#include +#include +#include +#include +#include +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240914) +#include +#else #include +#endif vtkStandardNewMacro(vtkF3DCachedLUTTexture); diff --git a/vtkext/private/module/vtkF3DCachedSpecularTexture.cxx b/vtkext/private/module/vtkF3DCachedSpecularTexture.cxx index 1ae5a32694..0c1cd1d597 100644 --- a/vtkext/private/module/vtkF3DCachedSpecularTexture.cxx +++ b/vtkext/private/module/vtkF3DCachedSpecularTexture.cxx @@ -1,14 +1,20 @@ #include "vtkF3DCachedSpecularTexture.h" -#include "vtkImageData.h" -#include "vtkMultiBlockDataSet.h" -#include "vtkObjectFactory.h" -#include "vtkOpenGLRenderWindow.h" -#include "vtkRenderer.h" -#include "vtkTextureObject.h" -#include "vtkXMLMultiBlockDataReader.h" - +#include +#include +#include +#include +#include +#include +#include +#include + +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240914) +#include +#else #include +#endif + vtkStandardNewMacro(vtkF3DCachedSpecularTexture); diff --git a/vtkext/private/module/vtkF3DGenericImporter.cxx b/vtkext/private/module/vtkF3DGenericImporter.cxx index 49c07d72b7..d888d35ca3 100644 --- a/vtkext/private/module/vtkF3DGenericImporter.cxx +++ b/vtkext/private/module/vtkF3DGenericImporter.cxx @@ -1,65 +1,37 @@ #include "vtkF3DGenericImporter.h" +#include "vtkF3DPostProcessFilter.h" #include "F3DLog.h" #include -#include -#include -#include -#include -#include +#include #include #include #include #include #include #include -#include -#include #include #include #include #include -#include -#include +#include #include -#include +#include +#include #include -struct ReaderPipeline +struct vtkF3DGenericImporter::Internals { - ReaderPipeline() - { - this->GeometryActor->GetProperty()->SetInterpolationToPBR(); - this->PolyDataMapper->InterpolateScalarsBeforeMappingOn(); - } - - std::string Name; - bool Imported = false; - vtkSmartPointer Reader; + vtkSmartPointer Reader = nullptr; vtkNew PostPro; - std::string OutputDescription; - vtkNew GeometryActor; - vtkNew PointSpritesActor; - vtkNew VolumeProp; vtkNew PolyDataMapper; - vtkNew PointGaussianMapper; - vtkSmartPointer VolumeMapper; - - vtkDataSet* Output = nullptr; - vtkDataSetAttributes* PointDataForColoring = nullptr; - vtkDataSetAttributes* CellDataForColoring = nullptr; -}; - -struct vtkF3DGenericImporter::Internals -{ - std::vector Readers; + std::string OutputDescription; - std::vector PointDataArrayVectorForColoring; - std::vector CellDataArrayVectorForColoring; - vtkBoundingBox GeometryBoundingBox; + vtkPolyData* ImportedPoints = nullptr; + vtkImageData* ImportedImage = nullptr; bool HasAnimation = false; bool AnimationEnabled = false; @@ -77,23 +49,16 @@ vtkF3DGenericImporter::vtkF3DGenericImporter() //---------------------------------------------------------------------------- void vtkF3DGenericImporter::UpdateTemporalInformation() { + assert(this->Pimpl->Reader); this->Pimpl->HasAnimation = false; - this->Pimpl->TimeRange[0] = std::numeric_limits::infinity(); - this->Pimpl->TimeRange[1] = -std::numeric_limits::infinity(); - - // Update each reader - for (ReaderPipeline& pipe : this->Pimpl->Readers) + this->Pimpl->Reader->UpdateInformation(); + vtkInformation* readerInfo = this->Pimpl->Reader->GetOutputInformation(0); + if (readerInfo->Has(vtkStreamingDemandDrivenPipeline::TIME_RANGE())) { - pipe.Reader->UpdateInformation(); - vtkInformation* readerInfo = pipe.Reader->GetOutputInformation(0); - if (readerInfo->Has(vtkStreamingDemandDrivenPipeline::TIME_RANGE())) - { - // Accumulate time ranges - double* readerTimeRange = readerInfo->Get(vtkStreamingDemandDrivenPipeline::TIME_RANGE()); - this->Pimpl->TimeRange[0] = std::min(this->Pimpl->TimeRange[0], readerTimeRange[0]); - this->Pimpl->TimeRange[1] = std::max(this->Pimpl->TimeRange[1], readerTimeRange[1]); - this->Pimpl->HasAnimation = true; - } + double* readerTimeRange = readerInfo->Get(vtkStreamingDemandDrivenPipeline::TIME_RANGE()); + this->Pimpl->TimeRange[0] = readerTimeRange[0]; + this->Pimpl->TimeRange[1] = readerTimeRange[1]; + this->Pimpl->HasAnimation = true; } } @@ -104,42 +69,47 @@ vtkIdType vtkF3DGenericImporter::GetNumberOfAnimations() } //---------------------------------------------------------------------------- -std::string vtkF3DGenericImporter::GetAnimationName(vtkIdType animationIndex) +std::string vtkF3DGenericImporter::GetAnimationName([[maybe_unused]] vtkIdType animationIndex) { - return animationIndex < this->GetNumberOfAnimations() ? "default" : ""; + assert(animationIndex == 0); + return this->Pimpl->HasAnimation ? "default" : ""; } //---------------------------------------------------------------------------- -void vtkF3DGenericImporter::EnableAnimation(vtkIdType animationIndex) +void vtkF3DGenericImporter::EnableAnimation([[maybe_unused]] vtkIdType animationIndex) { - if (animationIndex < this->GetNumberOfAnimations()) + assert(animationIndex == 0); + if (this->Pimpl->HasAnimation) { this->Pimpl->AnimationEnabled = true; } } //---------------------------------------------------------------------------- -void vtkF3DGenericImporter::DisableAnimation(vtkIdType animationIndex) +void vtkF3DGenericImporter::DisableAnimation([[maybe_unused]] vtkIdType animationIndex) { - if (animationIndex < this->GetNumberOfAnimations()) + assert(animationIndex == 0); + if (this->Pimpl->HasAnimation) { this->Pimpl->AnimationEnabled = false; } } //---------------------------------------------------------------------------- -bool vtkF3DGenericImporter::IsAnimationEnabled(vtkIdType animationIndex) +bool vtkF3DGenericImporter::IsAnimationEnabled([[maybe_unused]] vtkIdType animationIndex) { - return animationIndex < this->GetNumberOfAnimations() ? this->Pimpl->AnimationEnabled : false; + assert(animationIndex == 0); + return this->Pimpl->AnimationEnabled; } //---------------------------------------------------------------------------- -bool vtkF3DGenericImporter::GetTemporalInformation(vtkIdType animationIndex, +bool vtkF3DGenericImporter::GetTemporalInformation([[maybe_unused]] vtkIdType animationIndex, double vtkNotUsed(frameRate), int& vtkNotUsed(nbTimeSteps), double timeRange[2], vtkDoubleArray* vtkNotUsed(timeSteps)) { + assert(animationIndex == 0); // F3D do not care about timesteps - if (animationIndex < this->GetNumberOfAnimations()) + if (this->Pimpl->HasAnimation) { timeRange[0] = this->Pimpl->TimeRange[0]; timeRange[1] = this->Pimpl->TimeRange[1]; @@ -151,153 +121,68 @@ bool vtkF3DGenericImporter::GetTemporalInformation(vtkIdType animationIndex, //---------------------------------------------------------------------------- void vtkF3DGenericImporter::ImportActors(vtkRenderer* ren) { - this->Pimpl->GeometryBoundingBox.Reset(); - bool hasGeometry = false; - - // Update each reader - for (size_t readerIndex = 0; readerIndex < this->Pimpl->Readers.size(); readerIndex++) + assert(this->Pimpl->Reader); + + // Read file and forward progress + vtkNew progressForwarder; + progressForwarder->SetTarget(this); + this->Pimpl->Reader->AddObserver(vtkCommand::ProgressEvent, progressForwarder); + bool status = this->Pimpl->PostPro->GetExecutive()->Update(); + if (!status || !this->Pimpl->Reader->GetOutputDataObject(0)) { - ReaderPipeline& pipe = this->Pimpl->Readers[readerIndex]; - if (pipe.Imported) - { - continue; - } - - { - // Forward progress event - vtkNew progressCallback; - double progressRatio = static_cast(readerIndex + 1) / this->Pimpl->Readers.size(); - std::pair progressPair = { this, progressRatio }; - progressCallback->SetClientData(&progressPair); - progressCallback->SetCallback( - [](vtkObject*, unsigned long, void* clientData, void* callData) - { - auto localPair = static_cast*>(clientData); - vtkF3DGenericImporter* self = localPair->first; - double progress = *static_cast(callData) * localPair->second; - self->InvokeEvent(vtkCommand::ProgressEvent, &progress); - }); - pipe.Reader->AddObserver(vtkCommand::ProgressEvent, progressCallback); - bool status = pipe.PostPro->GetExecutive()->Update(); - pipe.Reader->RemoveObservers(vtkCommand::ProgressEvent); - - if (!status || !pipe.Reader->GetOutputDataObject(0)) - { - F3DLog::Print(F3DLog::Severity::Warning, "A reader failed to update"); - pipe.Output = nullptr; - continue; - } - } - - // Cast to dataset types - vtkPolyData* surface = vtkPolyData::SafeDownCast(pipe.PostPro->GetOutput()); - vtkImageData* image = vtkImageData::SafeDownCast(pipe.PostPro->GetOutput(2)); - vtkDataSet* dataSet = vtkImageData::SafeDownCast(pipe.PostPro->GetInput()) - ? vtkDataSet::SafeDownCast(image) - : vtkDataSet::SafeDownCast(surface); - pipe.Output = dataSet; - - // Recover output description from the reader - pipe.OutputDescription = vtkF3DGenericImporter::GetDataObjectDescription(pipe.Reader->GetOutputDataObject(0)); - - // Recover data for coloring - pipe.PointDataForColoring = vtkDataSetAttributes::SafeDownCast(dataSet->GetPointData()); - pipe.CellDataForColoring = vtkDataSetAttributes::SafeDownCast(dataSet->GetCellData()); - - // Increase bounding box size if needed - double bounds[6]; - surface->GetBounds(bounds); - this->Pimpl->GeometryBoundingBox.AddBounds(bounds); + this->SetFailureStatus(); + return; + } - // Add filter outputs to mapper inputs - pipe.PolyDataMapper->SetInputConnection(pipe.PostPro->GetOutputPort(0)); - pipe.PointGaussianMapper->SetInputConnection(pipe.PostPro->GetOutputPort(1)); + // Cast to dataset types + this->Pimpl->ImportedPoints = vtkPolyData::SafeDownCast(this->Pimpl->PostPro->GetOutput(1)); + vtkImageData* image = vtkImageData::SafeDownCast(this->Pimpl->PostPro->GetOutput(2)); + this->Pimpl->ImportedImage = image->GetNumberOfCells() > 0 ? image : nullptr; - if (!pipe.VolumeMapper) - { - pipe.VolumeMapper = vtkSmartPointer::New(); - pipe.VolumeMapper->SetRequestedRenderModeToGPU(); - } - pipe.VolumeMapper->SetInputConnection(pipe.PostPro->GetOutputPort(2)); + // Recover output description from the reader + this->Pimpl->OutputDescription = vtkF3DGenericImporter::GetDataObjectDescription(this->Pimpl->Reader->GetOutputDataObject(0)); - // Set geometry actor default properties - pipe.GeometryActor->GetProperty()->SetPointSize(10.0); - pipe.GeometryActor->GetProperty()->SetLineWidth(1.0); + // Add filter outputs to mapper inputs + this->Pimpl->PolyDataMapper->SetInputConnection(this->Pimpl->PostPro->GetOutputPort(0)); + this->Pimpl->PolyDataMapper->ScalarVisibilityOff(); - // add mappers - pipe.VolumeProp->SetMapper(pipe.VolumeMapper); - pipe.GeometryActor->SetMapper(pipe.PolyDataMapper); - pipe.PointSpritesActor->SetMapper(pipe.PointGaussianMapper); + // Set geometry actor default properties + // Rely on vtkProperty default for other properties + this->Pimpl->GeometryActor->GetProperty()->SetPointSize(10.0); + this->Pimpl->GeometryActor->GetProperty()->SetLineWidth(1.0); + this->Pimpl->GeometryActor->GetProperty()->SetRoughness(0.3); + this->Pimpl->GeometryActor->GetProperty()->SetInterpolationToPBR(); - // add props - ren->AddActor(pipe.GeometryActor); - ren->AddActor(pipe.PointSpritesActor); - ren->AddVolume(pipe.VolumeProp); + // add mappers + // TODO implement proper composite support + this->Pimpl->GeometryActor->SetMapper(this->Pimpl->PolyDataMapper); - // Set visibilities - pipe.GeometryActor->VisibilityOff(); - pipe.PointSpritesActor->VisibilityOff(); - pipe.VolumeProp->VisibilityOff(); + // add props + ren->AddActor(this->Pimpl->GeometryActor); +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240707) + this->ActorCollection->AddItem(this->Pimpl->GeometryActor); +#endif - pipe.Imported = true; - hasGeometry = true; - } + // Set visibilities + this->Pimpl->GeometryActor->VisibilityOn(); this->UpdateTemporalInformation(); - this->UpdateColoringVectors(false); - this->UpdateColoringVectors(true); - if (!hasGeometry) - { - this->SetFailureStatus(); - } } //---------------------------------------------------------------------------- -void vtkF3DGenericImporter::PrintSelf(std::ostream& os, vtkIndent indent) -{ - vtkImporter::PrintSelf(os, indent); -} - -//---------------------------------------------------------------------------- -void vtkF3DGenericImporter::AddInternalReader(const std::string& name, vtkAlgorithm* reader) +void vtkF3DGenericImporter::SetInternalReader(vtkAlgorithm* reader) { if (reader) { - ReaderPipeline pipe; - pipe.Name = name; - pipe.Reader = reader; - pipe.PostPro->SetInputConnection(pipe.Reader->GetOutputPort()); - this->Pimpl->Readers.push_back(std::move(pipe)); - this->Modified(); + this->Pimpl->Reader = reader; + this->Pimpl->PostPro->SetInputConnection(this->Pimpl->Reader->GetOutputPort()); } } -//---------------------------------------------------------------------------- -void vtkF3DGenericImporter::RemoveInternalReaders() -{ - this->Pimpl->Readers.clear(); - this->Modified(); -} - -//---------------------------------------------------------------------------- -bool vtkF3DGenericImporter::CanReadFile() -{ - return this->Pimpl->Readers.size() > 0 && this->Pimpl->Readers[0].Reader != nullptr; -} - //---------------------------------------------------------------------------- std::string vtkF3DGenericImporter::GetOutputsDescription() { - std::string description; - for (const ReaderPipeline& pipe : this->Pimpl->Readers) - { - if (this->Pimpl->Readers.size() > 0) - { - description += "=== " + pipe.Name + " ===\n"; - } - description += pipe.OutputDescription; - } - return description; + return this->Pimpl->OutputDescription; } //---------------------------------------------------------------------------- @@ -371,257 +256,37 @@ std::string vtkF3DGenericImporter::GetDataObjectDescription(vtkDataObject* objec return ""; } -//---------------------------------------------------------------------------- -std::string vtkF3DGenericImporter::GetMetaDataDescription() -{ - vtkIdType nPoints = 0; - vtkIdType nCells = 0; - for (ReaderPipeline& pipe : this->Pimpl->Readers) - { - vtkDataObject* object = pipe.Reader->GetOutputDataObject(0); - if (object) - { - nPoints += object->GetNumberOfElements(vtkDataObject::POINT); - nCells += object->GetNumberOfElements(vtkDataObject::CELL); - } - } - - std::string description; - if (this->Pimpl->Readers.size() > 1) - { - description += "Number of geometries: "; - description += std::to_string(this->Pimpl->Readers.size()); - description += "\n"; - } - description += "Number of points: "; - description += std::to_string(nPoints); - description += "\n"; - description += "Number of cells: "; - description += std::to_string(nCells); - return description; -} - //---------------------------------------------------------------------------- bool vtkF3DGenericImporter::UpdateAtTimeValue(double timeValue) { - // Update each reader - bool hasGeometry = false; - for (ReaderPipeline& pipe : this->Pimpl->Readers) + assert(this->Pimpl->Reader); + if(!this->Pimpl->PostPro->UpdateTimeStep(timeValue) || !this->Pimpl->Reader->GetOutputDataObject(0)) { - if(!pipe.PostPro->UpdateTimeStep(timeValue) || !pipe.Reader->GetOutputDataObject(0)) - { - F3DLog::Print(F3DLog::Severity::Warning, "A reader failed to update at a timeValue"); - pipe.Output = nullptr; - pipe.Imported = false; - continue; - } - hasGeometry = true; + F3DLog::Print(F3DLog::Severity::Warning, "A reader failed to update at a timeValue"); + return false; } - this->UpdateColoringVectors(false); - this->UpdateColoringVectors(true); this->UpdateOutputDescriptions(); - return hasGeometry; -} - -//---------------------------------------------------------------------------- -std::vector> -vtkF3DGenericImporter::GetGeometryActorsAndMappers() -{ - std::vector> actorsAndMappers( - this->Pimpl->Readers.size()); - - std::transform(this->Pimpl->Readers.cbegin(), this->Pimpl->Readers.cend(), - actorsAndMappers.begin(), - [](const ReaderPipeline& pipe) - { return std::make_pair(pipe.GeometryActor.Get(), pipe.PolyDataMapper.Get()); }); - - return actorsAndMappers; -} - -//---------------------------------------------------------------------------- -std::vector> -vtkF3DGenericImporter::GetPointSpritesActorsAndMappers() -{ - std::vector> actorsAndMappers( - this->Pimpl->Readers.size()); - - std::transform(this->Pimpl->Readers.cbegin(), this->Pimpl->Readers.cend(), - actorsAndMappers.begin(), - [](const ReaderPipeline& pipe) - { return std::make_pair(pipe.PointSpritesActor.Get(), pipe.PointGaussianMapper.Get()); }); - - return actorsAndMappers; -} - -//---------------------------------------------------------------------------- -std::vector> -vtkF3DGenericImporter::GetVolumePropsAndMappers() -{ - std::vector> propsAndMappers( - this->Pimpl->Readers.size()); - - std::transform(this->Pimpl->Readers.cbegin(), this->Pimpl->Readers.cend(), - propsAndMappers.begin(), - [](const ReaderPipeline& pipe) - { return std::make_pair(pipe.VolumeProp.Get(), pipe.VolumeMapper.Get()); }); - - return propsAndMappers; -} - -//---------------------------------------------------------------------------- -void vtkF3DGenericImporter::UpdateColoringVectors(bool useCellData) -{ - // Recover all possible names - std::set arrayNames; - for (ReaderPipeline& pipe : this->Pimpl->Readers) - { - if (!pipe.Output) - { - continue; - } - - vtkDataSetAttributes* attr = useCellData - ? static_cast(pipe.Output->GetCellData()) - : static_cast(pipe.Output->GetPointData()); - - for (int i = 0; i < attr->GetNumberOfArrays(); i++) - { - vtkDataArray* array = attr->GetArray(i); - if (array && array->GetName()) - { - arrayNames.insert(array->GetName()); - } - } - } - - auto& data = useCellData ? this->Pimpl->CellDataArrayVectorForColoring - : this->Pimpl->PointDataArrayVectorForColoring; - data.clear(); - - // Create a vector of arrays by name - for (const std::string& arrayName : arrayNames) - { - vtkF3DGenericImporter::ColoringInfo info; - info.Name = arrayName; - for (ReaderPipeline& pipe : this->Pimpl->Readers) - { - if (!pipe.Output) - { - continue; - } - - vtkDataArray* array = useCellData ? pipe.Output->GetCellData()->GetArray(arrayName.c_str()) - : pipe.Output->GetPointData()->GetArray(arrayName.c_str()); - if (array) - { - info.MaximumNumberOfComponents = - std::max(info.MaximumNumberOfComponents, array->GetNumberOfComponents()); - - // Set ranges - // XXX this does not take animation into account - std::array range; - array->GetRange(range.data(), -1); - info.MagnitudeRange[0] = std::min(info.MagnitudeRange[0], range[0]); - info.MagnitudeRange[1] = std::max(info.MagnitudeRange[1], range[1]); - - for (size_t i = 0; i < static_cast(array->GetNumberOfComponents()); i++) - { - array->GetRange(range.data(), static_cast(i)); - if (i < info.ComponentRanges.size()) - { - info.ComponentRanges[i][0] = std::min(info.ComponentRanges[i][0], range[0]); - info.ComponentRanges[i][1] = std::max(info.ComponentRanges[i][1], range[1]); - } - else - { - info.ComponentRanges.emplace_back(range); - } - } - - // Set component names - if (array->HasAComponentName()) - { - for (size_t i = 0; i < static_cast(array->GetNumberOfComponents()); i++) - { - const char* compName = array->GetComponentName(i); - if (i < info.ComponentNames.size()) - { - if (compName && info.ComponentNames[i] != std::string(compName)) - { - // set non-coherent component names to empty string - info.ComponentNames[i] = ""; - } - } - else - { - // Add components names to the back of the component names vector - info.ComponentNames.emplace_back(compName ? compName : ""); - } - } - } - } - info.Arrays.emplace_back(array); - } - - data.emplace_back(info); - } -} - -//---------------------------------------------------------------------------- -bool vtkF3DGenericImporter::GetInfoForColoring( - bool useCellData, int index, vtkF3DGenericImporter::ColoringInfo& info) -{ - auto& data = useCellData ? this->Pimpl->CellDataArrayVectorForColoring - : this->Pimpl->PointDataArrayVectorForColoring; - - if (index < 0 || index >= static_cast(data.size())) - { - return false; - } - info = data[index]; return true; } //---------------------------------------------------------------------------- -int vtkF3DGenericImporter::GetNumberOfIndexesForColoring(bool useCellData) -{ - auto& data = useCellData ? this->Pimpl->CellDataArrayVectorForColoring - : this->Pimpl->PointDataArrayVectorForColoring; - return static_cast(data.size()); -} - -//---------------------------------------------------------------------------- -int vtkF3DGenericImporter::FindIndexForColoring(bool useCellData, const std::string& arrayName) +void vtkF3DGenericImporter::UpdateOutputDescriptions() { - auto& data = useCellData ? this->Pimpl->CellDataArrayVectorForColoring - : this->Pimpl->PointDataArrayVectorForColoring; - for (size_t i = 0; i < data.size(); i++) - { - if (data[i].Name == arrayName) - { - return static_cast(i); - } - } - return -1; + assert(this->Pimpl->Reader); + // Recover output description + vtkDataObject* readerOutput = this->Pimpl->Reader->GetOutputDataObject(0); + this->Pimpl->OutputDescription = vtkF3DGenericImporter::GetDataObjectDescription(readerOutput); } //---------------------------------------------------------------------------- -const vtkBoundingBox& vtkF3DGenericImporter::GetGeometryBoundingBox() +vtkPolyData* vtkF3DGenericImporter::GetImportedPoints() { - return this->Pimpl->GeometryBoundingBox; + return this->Pimpl->ImportedPoints; } //---------------------------------------------------------------------------- -void vtkF3DGenericImporter::UpdateOutputDescriptions() +vtkImageData* vtkF3DGenericImporter::GetImportedImage() { - for (ReaderPipeline& pipe : this->Pimpl->Readers) - { - if (pipe.Imported) - { - // Recover output description - vtkDataObject* readerOutput = pipe.Reader->GetOutputDataObject(0); - pipe.OutputDescription = vtkF3DGenericImporter::GetDataObjectDescription(readerOutput); - } - } + return this->Pimpl->ImportedImage; } diff --git a/vtkext/private/module/vtkF3DGenericImporter.h b/vtkext/private/module/vtkF3DGenericImporter.h index e7fff6d65b..2b1b7b2ed5 100644 --- a/vtkext/private/module/vtkF3DGenericImporter.h +++ b/vtkext/private/module/vtkF3DGenericImporter.h @@ -1,63 +1,37 @@ /** * @class vtkF3DGenericImporter - * @brief create a scene from the meta reader + * @brief create a scene for any vtkAlgorithm */ #ifndef vtkF3DGenericImporter_h #define vtkF3DGenericImporter_h #include "vtkF3DImporter.h" -#include "vtkF3DPostProcessFilter.h" -#include -#include -#include -#include -#include - -#include -#include #include -#include -#include -class vtkActor; -class vtkVolume; +class vtkAlgorithm; +class vtkDataObject; +class vtkImageData; class vtkMultiBlockDataSet; class vtkPartitionedDataSetCollection; -class vtkPointGaussianMapper; -class vtkPolyDataMapper; -class vtkSmartVolumeMapper; -class vtkTexture; - +class vtkPolyData; class vtkF3DGenericImporter : public vtkF3DImporter { public: static vtkF3DGenericImporter* New(); vtkTypeMacro(vtkF3DGenericImporter, vtkF3DImporter); - void PrintSelf(ostream& os, vtkIndent indent) override; - - /** - * Add an internal reader to generate actors from - */ - void AddInternalReader(const std::string& name, vtkAlgorithm* reader); /** - * Remove all internal readers + * Set the internal reader to recover actors and data from */ - void RemoveInternalReaders(); - - /** - * Check if the file can be read. - */ - bool CanReadFile(); + void SetInternalReader(vtkAlgorithm* reader); /** * Get a string describing the outputs */ std::string GetOutputsDescription() override; - std::string GetMetaDataDescription(); // TODO add to vtkImporter in VTK ? ///@{ /** @@ -67,59 +41,10 @@ class vtkF3DGenericImporter : public vtkF3DImporter static std::string GetPartitionedDataSetCollectionDescription( vtkPartitionedDataSetCollection* pdc, vtkIndent indent); static std::string GetDataObjectDescription(vtkDataObject* object); - static std::string GetMetaDataDescription(vtkDataObject* object); - ///@} - - ///@{ - /** - * Access to actors vectors. They all have the same size, which correspond to the number - * of added internal readers. - */ - std::vector> GetGeometryActorsAndMappers(); - std::vector> GetPointSpritesActorsAndMappers(); - std::vector> GetVolumePropsAndMappers(); ///@} /** - * A struct containing information about possible coloring - */ - struct ColoringInfo - { - std::string Name; - int MaximumNumberOfComponents = 0; - std::vector ComponentNames; - std::vector> ComponentRanges; - std::array MagnitudeRange = { std::numeric_limits::max(), - std::numeric_limits::min() }; - std::vector Arrays; - }; - - /** - * Recover information about coloring by index - * Should be called after actors have been imported - */ - bool GetInfoForColoring(bool useCellData, int index, ColoringInfo& info); - - /** - * Get the maximum index possible for coloring - * Should be called after actors have been imported - */ - int GetNumberOfIndexesForColoring(bool useCellData); - - /** - * Find an index for coloring corresponding to provided arrayName if available - * Should be called after actors have been imported - */ - int FindIndexForColoring(bool useCellData, const std::string& arrayName); - - /** - * Get the bounding box of all geometry actors - * Should be called after actors have been imported - */ - const vtkBoundingBox& GetGeometryBoundingBox(); - - /** - * Update readers and all pipelines on the specified timestep + * Update internal reader on the specified timestep */ bool UpdateAtTimeValue(double timeValue) override; @@ -131,7 +56,7 @@ class vtkF3DGenericImporter : public vtkF3DImporter vtkIdType GetNumberOfAnimations() override; /** - * Return a dummy name of the first animation if any, empty string otherwise. + * Return "default" for the first animation if any, empty string otherwise. */ std::string GetAnimationName(vtkIdType animationIndex) override; @@ -153,12 +78,20 @@ class vtkF3DGenericImporter : public vtkF3DImporter bool GetTemporalInformation(vtkIdType animationIndex, double frameRate, int& nbTimeSteps, double timeRange[2], vtkDoubleArray* timeSteps) override; + ///@{ + /** + * Direct access to generic importer specific datasets + */ + vtkPolyData* GetImportedPoints(); + vtkImageData* GetImportedImage(); + ///@} + protected: vtkF3DGenericImporter(); ~vtkF3DGenericImporter() override = default; - /* Standard ImportActors - * None of the actors are shown by default + /* + * Import surface from the internal reader output as actors */ void ImportActors(vtkRenderer*) override; @@ -167,12 +100,6 @@ class vtkF3DGenericImporter : public vtkF3DImporter */ void UpdateTemporalInformation(); - /** - * Update coloring information vectors according to - * currently added vectors - */ - void UpdateColoringVectors(bool useCellData); - /** * Update output descriptions according to current outputs */ diff --git a/vtkext/private/module/vtkF3DMetaImporter.cxx b/vtkext/private/module/vtkF3DMetaImporter.cxx new file mode 100644 index 0000000000..4cb32c6706 --- /dev/null +++ b/vtkext/private/module/vtkF3DMetaImporter.cxx @@ -0,0 +1,560 @@ +#include "vtkF3DMetaImporter.h" + +#include "F3DLog.h" +#include "vtkF3DGenericImporter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +struct vtkF3DMetaImporter::Internals +{ + // Actors related vectors + std::vector ColoringActorsAndMappers; + std::vector PointSpritesActorsAndMappers; + std::vector VolumePropsAndMappers; + + struct ImporterPair + { + vtkSmartPointer Importer; + bool Updated = false; + }; + std::vector Importers; + std::optional CameraIndex; + vtkBoundingBox GeometryBoundingBox; + bool ColoringInfoUpdated = false; + + F3DColoringInfoHandler ColoringInfoHandler; + +#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 3, 20240707) + std::map> ActorsForImporterMap; +#endif +}; + +vtkStandardNewMacro(vtkF3DMetaImporter); + +//---------------------------------------------------------------------------- +vtkF3DMetaImporter::vtkF3DMetaImporter() + : Pimpl(new Internals()) +{ +} + +//---------------------------------------------------------------------------- +vtkF3DMetaImporter::~vtkF3DMetaImporter() +{ + // XXX by doing this we ensure ~vtkImporter does not delete it + // As we have our own way of handling renderer lifetime + this->Renderer = nullptr; +} + +//---------------------------------------------------------------------------- +void vtkF3DMetaImporter::Clear() +{ + this->Pimpl->Importers.clear(); + this->Pimpl->GeometryBoundingBox.Reset(); + this->ActorCollection->RemoveAllItems(); + this->Pimpl->ColoringActorsAndMappers.clear(); + this->Pimpl->PointSpritesActorsAndMappers.clear(); + this->Pimpl->VolumePropsAndMappers.clear(); + this->Pimpl->ColoringInfoHandler.ClearColoringInfo(); + this->Modified(); +} + +//---------------------------------------------------------------------------- +void vtkF3DMetaImporter::AddImporter(const vtkSmartPointer& importer) +{ + this->Pimpl->Importers.emplace_back(vtkF3DMetaImporter::Internals::ImporterPair {importer, false}); + this->Modified(); + + // Add a progress event observer + vtkNew progressCallback; + progressCallback->SetClientData(this); + progressCallback->SetCallback( + [](vtkObject* const caller, unsigned long, void* clientData, void* callData) + { + vtkF3DMetaImporter* self = static_cast(clientData); + double progress = *static_cast(callData); + double actualProgress = 0.0; + for (size_t i = 0; i < self->Pimpl->Importers.size(); i++) + { + if (self->Pimpl->Importers[i].Importer == caller) + { + // XXX: This does not consider that some importer may already have been updated + // or that some importers may take much longer than other. + actualProgress = (i + progress) / self->Pimpl->Importers.size(); + } + } + self->InvokeEvent(vtkCommand::ProgressEvent, &actualProgress); + }); + importer->AddObserver(vtkCommand::ProgressEvent, progressCallback); +} + +//---------------------------------------------------------------------------- +const vtkBoundingBox& vtkF3DMetaImporter::GetGeometryBoundingBox() +{ + return this->Pimpl->GeometryBoundingBox; +} + +//---------------------------------------------------------------------------- +const std::vector& +vtkF3DMetaImporter::GetColoringActorsAndMappers() +{ + return this->Pimpl->ColoringActorsAndMappers; +} + +//---------------------------------------------------------------------------- +const std::vector& +vtkF3DMetaImporter::GetPointSpritesActorsAndMappers() +{ + return this->Pimpl->PointSpritesActorsAndMappers; +} + +//---------------------------------------------------------------------------- +const std::vector& vtkF3DMetaImporter::GetVolumePropsAndMappers() +{ + return this->Pimpl->VolumePropsAndMappers; +} + +//---------------------------------------------------------------------------- +bool vtkF3DMetaImporter::Update() +{ + assert(this->RenderWindow); + this->Renderer = this->RenderWindow->GetRenderers()->GetFirstRenderer(); + assert(this->Renderer); + + // XXX: Doing this means that coloring information + // is recomputed from scratch when doing incremental loading + // This is the simplest way to implement coloring + // and should not have too big of an overhead. + this->Pimpl->ColoringInfoUpdated = false; + + vtkIdType localCameraIndex = -1; + + if (this->Pimpl->CameraIndex.has_value()) + { + if (this->Pimpl->CameraIndex < 0) + { + F3DLog::Print(F3DLog::Severity::Warning, + "Invalid camera index: " + std::to_string(this->Pimpl->CameraIndex.value()) + + ". Camera may be incorrect."); + } + localCameraIndex = this->Pimpl->CameraIndex.value(); + } + + for (auto& importerPair : this->Pimpl->Importers) + { + vtkImporter* importer = importerPair.Importer; + + // Importer has already been updated + if (importerPair.Updated) + { + localCameraIndex -= importer->GetNumberOfCameras(); + continue; + } + + importer->SetRenderWindow(this->RenderWindow); + + // This is required to avoid updating two times + // but may cause a warning in VTK + if (localCameraIndex >= 0) + { + importer->SetCamera(localCameraIndex); + } + +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240707) + if (!importer->Update()) + { + return false; + } +#else + vtkSmartPointer actorCollection = + vtkSmartPointer::New(); + + vtkNew previousActorCollection; + vtkActorCollection* currentCollection = this->Renderer->GetActors(); + vtkCollectionSimpleIterator tmpIt; + currentCollection->InitTraversal(tmpIt); + while (auto* actor = currentCollection->GetNextActor(tmpIt)) + { + previousActorCollection->AddItem(actor); + } + + importer->Update(); + + currentCollection = this->Renderer->GetActors(); + currentCollection->InitTraversal(tmpIt); + + vtkCollectionSimpleIterator tmpIt2; + previousActorCollection->InitTraversal(tmpIt2); + while (auto* actor = currentCollection->GetNextActor(tmpIt)) + { + bool found = false; + while (auto* previousActor = previousActorCollection->GetNextActor(tmpIt2)) + { + // This is a N^2 loop + if (previousActor == actor) + { + found = true; + break; + } + } + if (!found) + { + actorCollection->AddItem(actor); + } + } + + // Store the actor collection for further use + this->Pimpl->ActorsForImporterMap[importer] = actorCollection; +#endif + + localCameraIndex -= importer->GetNumberOfCameras(); + +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240707) + vtkActorCollection* actorCollection = importer->GetImportedActors(); +#endif + vtkCollectionSimpleIterator ait; + actorCollection->InitTraversal(ait); + while (auto* actor = actorCollection->GetNextActor(ait)) + { + // Add to the actor collection + this->ActorCollection->AddItem(actor); + + vtkPolyDataMapper* pdMapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); + vtkPolyData* surface = pdMapper->GetInput(); + + // Increase bounding box size if needed + double bounds[6]; + surface->GetBounds(bounds); + this->Pimpl->GeometryBoundingBox.AddBounds(bounds); + + // Recover generic importer if any + vtkF3DGenericImporter* genericImporter = vtkF3DGenericImporter::SafeDownCast(importer); + + // Create and configure coloring actors + this->Pimpl->ColoringActorsAndMappers.emplace_back(vtkF3DMetaImporter::ColoringStruct(actor)); + vtkF3DMetaImporter::ColoringStruct& cs = this->Pimpl->ColoringActorsAndMappers.back(); + cs.Mapper->SetInputData(surface); + this->Renderer->AddActor(cs.Actor); + cs.Actor->VisibilityOff(); + + // Create and configure point sprites actors + this->Pimpl->PointSpritesActorsAndMappers.emplace_back( + vtkF3DMetaImporter::PointSpritesStruct()); + vtkF3DMetaImporter::PointSpritesStruct& pss = + this->Pimpl->PointSpritesActorsAndMappers.back(); + + vtkPolyData* points = surface; + if (genericImporter) + { + // For generic importer, use the single imported points + // TODO when supporting composite, handle with an actor based index + points = genericImporter->GetImportedPoints(); + } + pss.Mapper->SetInputData(points); + this->Renderer->AddActor(pss.Actor); + pss.Actor->VisibilityOff(); + + // Create and configure volume props + if (genericImporter) + { + vtkImageData* image = genericImporter->GetImportedImage(); + if (image) + { + // XXX: Note that creating this struct takes some time + this->Pimpl->VolumePropsAndMappers.emplace_back(vtkF3DMetaImporter::VolumeStruct()); + vtkF3DMetaImporter::VolumeStruct& vs = this->Pimpl->VolumePropsAndMappers.back(); + vs.Mapper->SetInputData(image); + this->Renderer->AddVolume(vs.Prop); + vs.Prop->VisibilityOff(); + } + } + } + + importerPair.Updated = true; + } + + if (localCameraIndex > 0) + { + // Here we know that CameraIndex has a value + F3DLog::Print(F3DLog::Severity::Warning, + "Camera index " + std::to_string(this->Pimpl->CameraIndex.value()) + + " is higher than the number of available camera in the files. Camera may be incorrect."); + } + + // XXX: UpdateStatus is not set, but libf3d does not use it + return true; +} + +//---------------------------------------------------------------------------- +std::string vtkF3DMetaImporter::GetOutputsDescription() +{ + std::string description = "Number of files: " + std::to_string(this->Pimpl->Importers.size()) + "\n"; + description += "Number of actors: " + std::to_string(this->ActorCollection->GetNumberOfItems()) + "\n"; + description += std::accumulate(this->Pimpl->Importers.begin(), this->Pimpl->Importers.end(), + std::string(), [](const std::string& a, const auto& importerPair) + { return a + "----------\n" + importerPair.Importer->GetOutputsDescription(); }); + return description; +} + +//---------------------------------------------------------------------------- +vtkIdType vtkF3DMetaImporter::GetNumberOfAnimations() +{ + return std::accumulate(this->Pimpl->Importers.begin(), this->Pimpl->Importers.end(), 0, + [](vtkIdType a, const auto& importerPair) + { return a + importerPair.Importer->GetNumberOfAnimations(); }); +} + +//---------------------------------------------------------------------------- +std::string vtkF3DMetaImporter::GetAnimationName(vtkIdType animationIndex) +{ + vtkIdType localAnimationIndex = animationIndex; + for (const auto& importerPair : this->Pimpl->Importers) + { + vtkIdType nAnim = importerPair.Importer->GetNumberOfAnimations(); + if (localAnimationIndex < nAnim) + { + std::string name = importerPair.Importer->GetAnimationName(localAnimationIndex); + if (name.empty()) + { + name = "unnamed_" + std::to_string(animationIndex); + } + return name; + } + else + { + localAnimationIndex -= nAnim; + } + } + return ""; +} + +//---------------------------------------------------------------------------- +void vtkF3DMetaImporter::EnableAnimation(vtkIdType animationIndex) +{ + vtkIdType localAnimationIndex = animationIndex; + for (const auto& importerPair : this->Pimpl->Importers) + { + vtkIdType nAnim = importerPair.Importer->GetNumberOfAnimations(); + if (localAnimationIndex < nAnim) + { + importerPair.Importer->EnableAnimation(localAnimationIndex); + return; + } + else + { + localAnimationIndex -= nAnim; + } + } +} + +//---------------------------------------------------------------------------- +void vtkF3DMetaImporter::DisableAnimation(vtkIdType animationIndex) +{ + vtkIdType localAnimationIndex = animationIndex; + for (const auto& importerPair : this->Pimpl->Importers) + { + vtkIdType nAnim = importerPair.Importer->GetNumberOfAnimations(); + if (localAnimationIndex < nAnim) + { + importerPair.Importer->DisableAnimation(localAnimationIndex); + return; + } + else + { + localAnimationIndex -= nAnim; + } + } +} + +//---------------------------------------------------------------------------- +bool vtkF3DMetaImporter::IsAnimationEnabled(vtkIdType animationIndex) +{ + vtkIdType localAnimationIndex = animationIndex; + for (const auto& importerPair : this->Pimpl->Importers) + { + vtkIdType nAnim = importerPair.Importer->GetNumberOfAnimations(); + if (localAnimationIndex < nAnim) + { + return importerPair.Importer->IsAnimationEnabled(localAnimationIndex); + } + else + { + localAnimationIndex -= nAnim; + } + } + return false; +} + +//---------------------------------------------------------------------------- +vtkIdType vtkF3DMetaImporter::GetNumberOfCameras() +{ + return std::accumulate(this->Pimpl->Importers.begin(), this->Pimpl->Importers.end(), 0, + [](vtkIdType a, const auto& importerPair) + { return a + importerPair.Importer->GetNumberOfCameras(); }); +} + +//---------------------------------------------------------------------------- +std::string vtkF3DMetaImporter::GetCameraName(vtkIdType camIndex) +{ + vtkIdType localCameraIndex = camIndex; + for (const auto& importerPair : this->Pimpl->Importers) + { + vtkIdType nCam = importerPair.Importer->GetNumberOfCameras(); + if (localCameraIndex < nCam) + { + std::string name = importerPair.Importer->GetCameraName(localCameraIndex); + if (name.empty()) + { + name = "unnamed_" + std::to_string(camIndex); + } + return name; + } + else + { + localCameraIndex -= nCam; + } + } + return ""; +} + +//---------------------------------------------------------------------------- +void vtkF3DMetaImporter::SetCameraIndex(std::optional camIndex) +{ + this->Pimpl->CameraIndex = camIndex; +} + +//---------------------------------------------------------------------------- +bool vtkF3DMetaImporter::GetTemporalInformation(vtkIdType animationIndex, double frameRate, + int& nbTimeSteps, double timeRange[2], vtkDoubleArray* timeSteps) +{ + vtkIdType localAnimationIndex = animationIndex; + for (const auto& importerPair : this->Pimpl->Importers) + { + vtkIdType nAnim = importerPair.Importer->GetNumberOfAnimations(); + if (localAnimationIndex < nAnim) + { + return importerPair.Importer->GetTemporalInformation( + localAnimationIndex, frameRate, nbTimeSteps, timeRange, timeSteps); + } + else + { + localAnimationIndex -= nAnim; + } + } + return false; +} + +//---------------------------------------------------------------------------- +bool vtkF3DMetaImporter::UpdateAtTimeValue(double timeValue) +{ + this->Pimpl->ColoringInfoUpdated = false; + bool ret = true; + for (const auto& importerPair : this->Pimpl->Importers) + { +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240707) + ret = ret && importerPair.Importer->UpdateAtTimeValue(timeValue); +#else + importerPair.Importer->UpdateTimeStep(timeValue); +#endif + } + return ret; +} + +//---------------------------------------------------------------------------- +void vtkF3DMetaImporter::UpdateInfoForColoring() +{ + for (const auto& importerPair : this->Pimpl->Importers) + { +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240707) + vtkActorCollection* actorCollection = importerPair.Importer->GetImportedActors(); +#else + vtkActorCollection* actorCollection = + this->Pimpl->ActorsForImporterMap.at(importerPair.Importer).Get(); +#endif + vtkCollectionSimpleIterator ait; + actorCollection->InitTraversal(ait); + while (auto* actor = actorCollection->GetNextActor(ait)) + { + vtkPolyDataMapper* pdMapper = vtkPolyDataMapper::SafeDownCast(actor->GetMapper()); + assert(pdMapper); + + // Update coloring vectors, with a dedicated logic for generic importer + vtkDataSet* datasetForColoring = pdMapper->GetInput(); + vtkF3DGenericImporter* genericImporter = vtkF3DGenericImporter::SafeDownCast(importerPair.Importer); + if (genericImporter) + { + // TODO This will be improved with proper composite support + // Currently generic importer always has a single actor + if (genericImporter->GetImportedImage()) + { + datasetForColoring = genericImporter->GetImportedImage(); + } + else if (genericImporter->GetImportedPoints()) + { + datasetForColoring = genericImporter->GetImportedPoints(); + } + } + this->Pimpl->ColoringInfoHandler.UpdateColoringInfo(datasetForColoring, false); + this->Pimpl->ColoringInfoHandler.UpdateColoringInfo(datasetForColoring, true); + } + } + this->Pimpl->ColoringInfoUpdated = true; +} + +//---------------------------------------------------------------------------- +std::string vtkF3DMetaImporter::GetMetaDataDescription() const +{ + std::string description; + if (this->Pimpl->Importers.size() > 1) + { + description += "Number of files: "; + description += std::to_string(this->Pimpl->Importers.size()); + description += "\n"; + } + + description += "Number of actors: "; + description += std::to_string(this->ActorCollection->GetNumberOfItems()); + description += "\n"; + + vtkIdType nPoints = 0; + vtkIdType nCells = 0; + vtkCollectionSimpleIterator ait; + this->ActorCollection->InitTraversal(ait); + while (auto* actor = this->ActorCollection->GetNextActor(ait)) + { + vtkPolyData* surface = vtkPolyDataMapper::SafeDownCast(actor->GetMapper())->GetInput(); + nPoints += surface->GetNumberOfPoints(); + nCells += surface->GetNumberOfCells(); + } + + description += "Number of points: "; + description += std::to_string(nPoints); + description += "\n"; + description += "Number of cells: "; + description += std::to_string(nCells); + return description; +} + +//---------------------------------------------------------------------------- +F3DColoringInfoHandler& vtkF3DMetaImporter::GetColoringInfoHandler() +{ + if (!this->Pimpl->ColoringInfoUpdated) + { + this->UpdateInfoForColoring(); + } + + return this->Pimpl->ColoringInfoHandler; +} diff --git a/vtkext/private/module/vtkF3DMetaImporter.h b/vtkext/private/module/vtkF3DMetaImporter.h new file mode 100644 index 0000000000..7a9c1850a3 --- /dev/null +++ b/vtkext/private/module/vtkF3DMetaImporter.h @@ -0,0 +1,183 @@ +/** + * @class vtkF3DMetaImporter + * @brief + */ + +#ifndef vtkF3DMetaImporter_h +#define vtkF3DMetaImporter_h + +#include "vtkF3DImporter.h" +#include "F3DColoringInfoHandler.h" + +#include +#include +#include +#include +#include +#include +#include + +#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 3, 20240707) +#include +#endif + +#include +#include +#include +#include + +class vtkF3DMetaImporter : public vtkF3DImporter +{ +public: + static vtkF3DMetaImporter* New(); + vtkTypeMacro(vtkF3DMetaImporter, vtkF3DImporter); + + ///@{ + /** + * Structs used to transfer actors information to the F3D renderer + */ + struct VolumeStruct + { + VolumeStruct() + { + this->Mapper->SetRequestedRenderModeToGPU(); + this->Prop->SetMapper(this->Mapper); + } + vtkNew Prop; + vtkNew Mapper; + }; + + struct PointSpritesStruct + { + PointSpritesStruct() + { + this->Actor->SetMapper(this->Mapper); + } + + vtkNew Actor; + vtkNew Mapper; + }; + + struct ColoringStruct + { + explicit ColoringStruct(vtkActor* originalActor) + : OriginalActor(originalActor) + { + this->Actor->GetProperty()->SetPointSize(10.0); + this->Actor->GetProperty()->SetLineWidth(1.0); + this->Actor->GetProperty()->SetRoughness(0.3); + this->Actor->GetProperty()->SetInterpolationToPBR(); + this->Actor->SetMapper(this->Mapper); + this->Mapper->InterpolateScalarsBeforeMappingOn(); + } + vtkNew Actor; + vtkNew Mapper; + vtkActor* OriginalActor; + }; + ///@} + + /** + * Clear all importers and internal structures + */ + void Clear(); + + /** + * Add an importer to update when importer all actors + */ + void AddImporter(const vtkSmartPointer& importer); + + /** + * Get the bounding box of all geometry actors + * Should be called after actors have been imported + */ + const vtkBoundingBox& GetGeometryBoundingBox(); + + /** + * Get a meta data description of all imported data + */ + std::string GetMetaDataDescription() const; + + + F3DColoringInfoHandler& GetColoringInfoHandler(); + + ///@{ + /** + * API to recover information about all imported actors, point sprites and volume if any + */ + const std::vector& GetColoringActorsAndMappers(); + const std::vector& GetPointSpritesActorsAndMappers(); + const std::vector& GetVolumePropsAndMappers(); + ///@} + + /** + * XXX: HIDE the vtkImporter::Update method and declare our own + * Import each of of the add importers into the first renderer of the render window. + * Importers that have already been imported will be skipped + * Also handles camera index if specified + * After import, create point sprites actors for all importers, and volume props + * for generic importer if compatible. + */ + bool Update(); + + /** + * Concatenate individual importers output description into one and return it + */ + std::string GetOutputsDescription() override; + + ///@{ + /** + * Implement vtkImporter animation API by adding animations for each individual importers one after the other + * No input checking on animationIndex + */ + vtkIdType GetNumberOfAnimations() override; + std::string GetAnimationName(vtkIdType animationIndex) override; + void EnableAnimation(vtkIdType animationIndex) override; + void DisableAnimation(vtkIdType animationIndex) override; + bool IsAnimationEnabled(vtkIdType animationIndex) override; + bool GetTemporalInformation(vtkIdType animationIndex, double frameRate, int& nbTimeSteps, double timeRange[2], vtkDoubleArray* timeSteps) override; + ///@} + + ///@{ + /** + * Implement vtkImporter camera API by adding cameras for each individual importers one after the other + * No input checking on camIndex + * Please note `void SetCamera(vtkIdType camIndex);` is not reimplemented nor used. + */ + vtkIdType GetNumberOfCameras() override; + std::string GetCameraName(vtkIdType camIndex) override; + void SetCameraIndex(std::optional camIndex); + ///@} + + /** + * Update each individual importer at the provided value + */ + bool UpdateAtTimeValue(double timeValue) override; + +protected: + vtkF3DMetaImporter(); + ~vtkF3DMetaImporter() override; + +private: + vtkF3DMetaImporter(const vtkF3DMetaImporter&) = delete; + void operator=(const vtkF3DMetaImporter&) = delete; + + /** + * Hide vtkImporter::SetCamera to ensure it is not being used + */ + using vtkImporter::SetCamera; + + /** + * Recover coloring information from each individual importer + * and store result in internal fields + */ + void UpdateInfoForColoring(); + + struct Internals; + std::unique_ptr Pimpl; + +#if VTK_VERSION_NUMBER < VTK_VERSION_CHECK(9, 3, 20240707) + vtkNew ActorCollection; +#endif +}; + +#endif diff --git a/vtkext/private/module/vtkF3DOpenGLGridMapper.cxx b/vtkext/private/module/vtkF3DOpenGLGridMapper.cxx index ee53aa89f5..f09a52e580 100644 --- a/vtkext/private/module/vtkF3DOpenGLGridMapper.cxx +++ b/vtkext/private/module/vtkF3DOpenGLGridMapper.cxx @@ -75,7 +75,7 @@ void vtkF3DOpenGLGridMapper::ReplaceShaderValues( " float alpha = min(linewidth, 1.0);\n" " float d = dist - lw;\n" " return d < .0 ? alpha\n" - " : d < aa ? (1.0 - d / aa) * alpha\n" + " : d < aa ? pow((1.0 - d / aa) * alpha, 3.0)\n" " : 0.0;\n" "}\n" ); @@ -91,13 +91,13 @@ void vtkF3DOpenGLGridMapper::ReplaceShaderValues( " float minorAlpha = antialias(min(minorGrid.x, minorGrid.y), gridLineWidth);\n" " float zoomFadeFactor = 1.0 - clamp(fwidth(majorCoord.x / unitSquare * fadeDist), 0.0, 1.0);" " float alpha = max(majorAlpha, minorAlpha * minorOpacity * zoomFadeFactor);\n" + " vec4 color = vec4(diffuseColorUniform, alpha);\n" " float axis1Weight = abs(majorCoord.y) < 0.5 ? antialias(majorGrid.y, axesLineWidth) : 0.0;\n" " float axis2Weight = abs(majorCoord.x) < 0.5 ? antialias(majorGrid.x, axesLineWidth) : 0.0;\n" " color = mix(color, axis2Color, axis2Weight);\n" " color = mix(color, axis1Color, axis1Weight);\n" - " float sqDist = unitSquare * unitSquare * dot(fromCenter, fromCenter);\n" " float radialFadeFactor = 1.0 - sqDist / (fadeDist * fadeDist);\n" " color.w *= radialFadeFactor;\n" diff --git a/vtkext/private/module/vtkF3DPointSplatMapper.cxx b/vtkext/private/module/vtkF3DPointSplatMapper.cxx index 9b2c347aaf..52c47aa148 100644 --- a/vtkext/private/module/vtkF3DPointSplatMapper.cxx +++ b/vtkext/private/module/vtkF3DPointSplatMapper.cxx @@ -1,5 +1,8 @@ #include "vtkF3DPointSplatMapper.h" +#include "vtkF3DBitonicSort.h" +#include "vtkF3DComputeDepthCS.h" + #include #include #include @@ -14,10 +17,13 @@ #include #include #include -#include +#include -#include "vtkF3DBitonicSort.h" -#include "vtkF3DComputeDepthCS.h" +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240914) +#include +#else +#include +#endif //---------------------------------------------------------------------------- class vtkF3DSplatMapperHelper : public vtkOpenGLPointGaussianMapperHelper diff --git a/vtkext/private/module/vtkF3DRenderer.cxx b/vtkext/private/module/vtkF3DRenderer.cxx index 4006db417d..0d35c3f870 100644 --- a/vtkext/private/module/vtkF3DRenderer.cxx +++ b/vtkext/private/module/vtkF3DRenderer.cxx @@ -2,6 +2,7 @@ #include "F3DDefaultHDRI.h" #include "F3DLog.h" +#include "F3DColoringInfoHandler.h" #include "vtkF3DCachedLUTTexture.h" #include "vtkF3DCachedSpecularTexture.h" #include "vtkF3DConfigure.h" @@ -13,6 +14,8 @@ #include #include #include +#include +#include #include #include #include @@ -30,9 +33,13 @@ #include #include #include +#include #include +#include +#include #include #include +#include #include #include #include @@ -40,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -63,7 +71,11 @@ #include #endif +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240914) +#include +#else #include +#endif #include #include @@ -126,7 +138,50 @@ vtkSmartPointer SaveTextureToImage( } #endif #endif + +//---------------------------------------------------------------------------- +// TODO : add this function in a utils file for rendering in VTK directly +vtkSmartPointer GetTexture(const std::string& filePath, bool isSRGB = false) +{ + vtkSmartPointer texture; + if (!filePath.empty()) + { + std::string fullPath = vtksys::SystemTools::CollapseFullPath(filePath); + if (!vtksys::SystemTools::FileExists(fullPath)) + { + F3DLog::Print(F3DLog::Severity::Warning, "Texture file does not exist " + fullPath); + } + else + { + auto reader = vtkSmartPointer::Take( + vtkImageReader2Factory::CreateImageReader2(fullPath.c_str())); + if (reader) + { + reader->SetFileName(fullPath.c_str()); + reader->Update(); + texture = vtkSmartPointer::New(); + texture->SetInputConnection(reader->GetOutputPort()); + if (isSRGB) + { + texture->UseSRGBColorSpaceOn(); + } + texture->InterpolateOn(); + texture->SetColorModeToDirectScalars(); + return texture; + } + else + { + F3DLog::Print(F3DLog::Severity::Warning, "Cannot open texture file " + fullPath); + } + } + } + + return texture; } +} + +//---------------------------------------------------------------------------- +vtkStandardNewMacro(vtkF3DRenderer); //---------------------------------------------------------------------------- vtkF3DRenderer::vtkF3DRenderer() @@ -177,6 +232,9 @@ vtkF3DRenderer::vtkF3DRenderer() this->CheatSheetActor->VisibilityOff(); this->DropZoneActor->VisibilityOff(); this->SkyboxActor->VisibilityOff(); + + // Make sure an active camera is available on the renderer + this->GetActiveCamera(); } //---------------------------------------------------------------------------- @@ -194,7 +252,7 @@ void vtkF3DRenderer::ReleaseGraphicsResources(vtkWindow* w) } //---------------------------------------------------------------------------- -void vtkF3DRenderer::Initialize(const std::string& up) +void vtkF3DRenderer::Initialize() { this->OriginalLightIntensities.clear(); this->RemoveAllViewProps(); @@ -224,10 +282,24 @@ void vtkF3DRenderer::Initialize(const std::string& up) this->AnimationNameInfo = ""; this->GridInfo = ""; - // Importer rely on the Environment being set, so this is needed in the initialization + this->AddActor2D(this->ScalarBarActor); + this->ScalarBarActor->VisibilityOff(); + + this->ColorTransferFunctionConfigured = false; + this->ColoringMappersConfigured = false; + this->PointSpritesMappersConfigured = false; + this->VolumePropsAndMappersConfigured = false; + this->ScalarBarActorConfigured = false; + this->CheatSheetConfigured = false; + this->ColoringConfigured = false; +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::InitializeUpVector(const std::string& upString) +{ const std::regex re("([-+]?)([XYZ])", std::regex_constants::icase); std::smatch match; - if (std::regex_match(up, match, re)) + if (std::regex_match(upString, match, re)) { const float sign = match[1].str() == "-" ? -1.0 : +1.0; const int index = std::toupper(match[2].str()[0]) - 'X'; @@ -245,6 +317,8 @@ void vtkF3DRenderer::Initialize(const std::string& up) vtkMath::Cross(this->UpVector, this->RightVector, pos); vtkMath::MultiplyScalar(pos, -1.0); + // XXX: Initialize the camera to a default position + // Note that camera reset is expected to be called later during importing vtkCamera* cam = this->GetActiveCamera(); cam->SetFocalPoint(0.0, 0.0, 0.0); cam->SetPosition(pos); @@ -262,7 +336,7 @@ void vtkF3DRenderer::Initialize(const std::string& up) } else { - F3DLog::Print(F3DLog::Severity::Warning, up + " is not a valid up direction"); + F3DLog::Print(F3DLog::Severity::Warning, upString + " is not a valid up direction"); } } @@ -407,27 +481,21 @@ void vtkF3DRenderer::ShowAxis(bool show) this->AxisWidget = nullptr; if (show) { - if (this->RenderWindow->GetInteractor()) - { - vtkNew axes; + assert(this->RenderWindow->GetInteractor()); + vtkNew axes; #if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 2, 20220907) - this->AxisWidget = vtkSmartPointer::New(); + this->AxisWidget = vtkSmartPointer::New(); #else - this->AxisWidget = vtkSmartPointer::New(); + this->AxisWidget = vtkSmartPointer::New(); #endif - this->AxisWidget->SetOrientationMarker(axes); - this->AxisWidget->SetInteractor(this->RenderWindow->GetInteractor()); - this->AxisWidget->SetViewport(0.85, 0.0, 1.0, 0.15); - this->AxisWidget->On(); + this->AxisWidget->SetOrientationMarker(axes); + this->AxisWidget->SetInteractor(this->RenderWindow->GetInteractor()); + this->AxisWidget->SetViewport(0.85, 0.0, 1.0, 0.15); + this->AxisWidget->On(); #if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 2, 20220907) - this->AxisWidget->InteractiveOff(); + this->AxisWidget->InteractiveOff(); #endif - this->AxisWidget->SetKeyPressActivation(false); - } - else - { - F3DLog::Print(F3DLog::Severity::Error, "Axis widget cannot be shown without an interactor"); - } + this->AxisWidget->SetKeyPressActivation(false); } this->AxisVisible = show; @@ -546,7 +614,7 @@ void vtkF3DRenderer::ConfigureGridUsingCurrentActors() std::stringstream stream; stream << "Using grid unit square size = " << tmpUnitSquare << "\n" << "Grid origin set to [" << gridPos[0] << ", " << gridPos[1] << ", " << gridPos[2] - << "]\n\n"; + << "]\n"; this->GridInfo = stream.str(); vtkNew gridMapper; @@ -1293,11 +1361,51 @@ void vtkF3DRenderer::ShowCheatSheet(bool show) //---------------------------------------------------------------------------- void vtkF3DRenderer::ConfigureCheatSheet() { + assert(this->Importer); if (this->CheatSheetVisible) { + auto info = this->Importer->GetColoringInfoHandler().GetCurrentColoringInfo(); std::stringstream cheatSheetText; cheatSheetText << "\n"; - this->FillCheatSheetHotkeys(cheatSheetText); + cheatSheetText << " C: Cell scalars coloring [" << (this->UseCellColoring ? "ON" : "OFF") + << "]\n"; + cheatSheetText << " S: Scalars coloring [" + << (info.has_value() ? vtkF3DRenderer::ShortName(info.value().Name, 19) + (this->EnableColoring ? "" : "(forced)") : "OFF") << "]\n"; + cheatSheetText << " Y: Coloring component [" + << vtkF3DRenderer::ComponentToString(this->ComponentForColoring) + << "]\n"; + cheatSheetText << " B: Scalar bar " << (this->ScalarBarVisible ? "[ON]" : "[OFF]") << "\n"; + cheatSheetText << " V: Volume representation " << (this->UseVolume ? "[ON]" : "[OFF]") << "\n"; + cheatSheetText << " I: Inverse volume opacity " + << (this->UseInverseOpacityFunction ? "[ON]" : "[OFF]") << "\n"; + cheatSheetText << " O: Point sprites " << (this->UsePointSprites ? "[ON]" : "[OFF]") << "\n"; + cheatSheetText << " W: Cycle animation [" + << vtkF3DRenderer::ShortName(this->AnimationNameInfo, 22) << "]\n"; + cheatSheetText << " P: Translucency support " << (this->UseDepthPeelingPass ? "[ON]" : "[OFF]") + << "\n"; + cheatSheetText << " Q: Ambient occlusion " << (this->UseSSAOPass ? "[ON]" : "[OFF]") << "\n"; + cheatSheetText << " A: Anti-aliasing " << (this->UseFXAAPass ? "[ON]" : "[OFF]") << "\n"; + cheatSheetText << " T: Tone mapping " << (this->UseToneMappingPass ? "[ON]" : "[OFF]") << "\n"; + cheatSheetText << " E: Edge visibility " << (this->EdgeVisible.has_value() ? (this->EdgeVisible.value() ? "[ON]" : "[OFF]") : "[NOT SET]") << "\n"; + cheatSheetText << " X: Axis " << (this->AxisVisible ? "[ON]" : "[OFF]") << "\n"; + cheatSheetText << " G: Grid " << (this->GridVisible ? "[ON]" : "[OFF]") << "\n"; + cheatSheetText << " N: File name " << (this->FilenameVisible ? "[ON]" : "[OFF]") << "\n"; + cheatSheetText << " M: Metadata " << (this->MetaDataVisible ? "[ON]" : "[OFF]") << "\n"; + cheatSheetText << " Z: FPS Timer " << (this->TimerVisible ? "[ON]" : "[OFF]") << "\n"; +#if F3D_MODULE_RAYTRACING + cheatSheetText << " R: Raytracing " << (this->UseRaytracing ? "[ON]" : "[OFF]") << "\n"; + cheatSheetText << " D: Denoiser " << (this->UseRaytracingDenoiser ? "[ON]" : "[OFF]") << "\n"; +#endif + cheatSheetText << " U: Blur background " << (this->UseBlurBackground ? "[ON]" : "[OFF]") << "\n"; + cheatSheetText << " K: Trackball interaction " << (this->UseTrackball ? "[ON]" : "[OFF]") << "\n"; + cheatSheetText << " F: HDRI ambient lighting " + << (this->GetUseImageBasedLighting() ? "[ON]" : "[OFF]") << "\n"; + cheatSheetText << " J: HDRI skybox " << (this->HDRISkyboxVisible ? "[ON]" : "[OFF]") << "\n"; + cheatSheetText.precision(2); + cheatSheetText << std::fixed; + cheatSheetText << " L: Light (increase, shift+L: decrease) [" << this->LightIntensity << "]" + << " \n"; + cheatSheetText << "\n H : Cheat sheet \n"; cheatSheetText << " ? : Print scene descr to terminal\n"; cheatSheetText << " ESC : Quit \n"; @@ -1310,7 +1418,7 @@ void vtkF3DRenderer::ConfigureCheatSheet() cheatSheetText << " 3: Right View camera\n"; cheatSheetText << " 4: Roll the camera left by 90 degrees\n"; cheatSheetText << " 5: Toggle Orthographic Projection " - << (this->UseOrthographicProjection ? "[ON]" : "[OFF]") << "\n"; + << (this->UseOrthographicProjection.has_value() ? (this->UseOrthographicProjection.value() ? "[ON]" : "[OFF]") : "[NOT SET]") << "\n"; cheatSheetText << " 6: Roll the camera right by 90 degrees\n"; cheatSheetText << " 7: Top View camera\n"; cheatSheetText << " 9: Isometric View camera\n"; @@ -1349,141 +1457,101 @@ void vtkF3DRenderer::ShowHDRISkybox(bool show) } //---------------------------------------------------------------------------- -void vtkF3DRenderer::FillCheatSheetHotkeys(std::stringstream& cheatSheetText) +void vtkF3DRenderer::ShowEdge(const std::optional& show) { - - cheatSheetText << " W: Cycle animation [" - << vtkF3DRenderer::ShortName(this->AnimationNameInfo, 22) << "]\n"; - cheatSheetText << " P: Translucency support " << (this->UseDepthPeelingPass ? "[ON]" : "[OFF]") - << "\n"; - cheatSheetText << " Q: Ambient occlusion " << (this->UseSSAOPass ? "[ON]" : "[OFF]") << "\n"; - cheatSheetText << " A: Anti-aliasing " << (this->UseFXAAPass ? "[ON]" : "[OFF]") << "\n"; - cheatSheetText << " T: Tone mapping " << (this->UseToneMappingPass ? "[ON]" : "[OFF]") << "\n"; - cheatSheetText << " E: Edge visibility " << (this->EdgeVisible ? "[ON]" : "[OFF]") << "\n"; - cheatSheetText << " X: Axis " << (this->AxisVisible ? "[ON]" : "[OFF]") << "\n"; - cheatSheetText << " G: Grid " << (this->GridVisible ? "[ON]" : "[OFF]") << "\n"; - cheatSheetText << " N: File name " << (this->FilenameVisible ? "[ON]" : "[OFF]") << "\n"; - cheatSheetText << " M: Metadata " << (this->MetaDataVisible ? "[ON]" : "[OFF]") << "\n"; - cheatSheetText << " Z: FPS Timer " << (this->TimerVisible ? "[ON]" : "[OFF]") << "\n"; -#if F3D_MODULE_RAYTRACING - cheatSheetText << " R: Raytracing " << (this->UseRaytracing ? "[ON]" : "[OFF]") << "\n"; - cheatSheetText << " D: Denoiser " << (this->UseRaytracingDenoiser ? "[ON]" : "[OFF]") << "\n"; -#endif - cheatSheetText << " U: Blur background " << (this->UseBlurBackground ? "[ON]" : "[OFF]") << "\n"; - cheatSheetText << " K: Trackball interaction " << (this->UseTrackball ? "[ON]" : "[OFF]") << "\n"; - cheatSheetText << " F: HDRI ambient lighting " - << (this->GetUseImageBasedLighting() ? "[ON]" : "[OFF]") << "\n"; - cheatSheetText << " J: HDRI skybox " << (this->HDRISkyboxVisible ? "[ON]" : "[OFF]") << "\n"; - cheatSheetText.precision(2); - cheatSheetText << std::fixed; - cheatSheetText << " L: Light (increase, shift+L: decrease) [" << this->LightIntensity << "]" - << " \n"; + if (this->EdgeVisible != show) + { + this->EdgeVisible = show; + this->ActorsPropertiesConfigured = false; + this->CheatSheetConfigured = false; + } } //---------------------------------------------------------------------------- -void vtkF3DRenderer::ConfigureActorsProperties() +void vtkF3DRenderer::SetUseOrthographicProjection(const std::optional& use) { - vtkActor* anActor; - vtkActorCollection* ac = this->GetActors(); - vtkCollectionSimpleIterator ait; - for (ac->InitTraversal(ait); (anActor = ac->GetNextActor(ait));) + // if the internal state is already the same as the target state there's nothing to do + if (this->UseOrthographicProjection != use) { - if (vtkSkybox::SafeDownCast(anActor) == nullptr) - { - anActor->GetProperty()->SetEdgeVisibility(this->EdgeVisible); + this->UseOrthographicProjection = use; - if (this->LineWidth.has_value()) - { - anActor->GetProperty()->SetLineWidth(this->LineWidth.value()); - } + // XXX This could be done in UpdateActors for coherency + if (this->UseOrthographicProjection.has_value()) + { + vtkCamera* camera = GetActiveCamera(); + const double angle = vtkMath::RadiansFromDegrees(camera->GetViewAngle()); + const double* position = camera->GetPosition(); + const double* focal = camera->GetFocalPoint(); - if (this->PointSize.has_value()) + if (this->UseOrthographicProjection.value()) { - anActor->GetProperty()->SetPointSize(this->PointSize.value()); + const double distance = std::sqrt(vtkMath::Distance2BetweenPoints(position, focal)); + const double parallelScale = distance * tan(angle / 2); + camera->SetParallelScale(parallelScale); } - - if (this->BackfaceType.has_value()) + else { - if (this->BackfaceType.value() == "visible") - { - anActor->GetProperty()->SetBackfaceCulling(false); - } - else if (this->BackfaceType.value() == "hidden") - { - anActor->GetProperty()->SetBackfaceCulling(true); - } - else - { - F3DLog::Print(F3DLog::Severity::Warning, this->BackfaceType.value() + " is not a valid backface type, assuming it is not set"); - } + const double distance = camera->GetParallelScale() / tan(angle / 2); + double direction[3]; + vtkMath::Subtract(position, focal, direction); + vtkMath::Normalize(direction); + vtkMath::MultiplyScalar(direction, distance); + double newPosition[3]; + vtkMath::Add(focal, direction, newPosition); + camera->SetPosition(newPosition); } + camera->SetParallelProjection(this->UseOrthographicProjection.value()); + this->ResetCameraClippingRange(); } + this->CheatSheetConfigured = false; } - this->ActorsPropertiesConfigured = true; } //---------------------------------------------------------------------------- -void vtkF3DRenderer::ShowEdge(bool show) +void vtkF3DRenderer::SetUseTrackball(bool use) { - // XXX EdgeVisible should be an optional - if (this->EdgeVisible != show) + if (this->UseTrackball != use) { - this->EdgeVisible = show; - this->ActorsPropertiesConfigured = false; + this->UseTrackball = use; this->CheatSheetConfigured = false; } } //---------------------------------------------------------------------------- -void vtkF3DRenderer::SetUseOrthographicProjection(bool use) +void vtkF3DRenderer::UpdateActors() { - // if the internal state is already the same as the target state there's nothing to do - // XXX UseOrthographicProjection should be an optional - if (this->UseOrthographicProjection == use) + assert(this->Importer); + + // Handle importer changes + // XXX: Importer only modify itself when adding a new importer, + // not when updating at a time step + vtkMTimeType importerMTime = this->Importer->GetMTime(); + bool importerChanged = this->Importer->GetMTime() > this->ImporterTimeStamp; + if (importerChanged) { - return; + this->ColorTransferFunctionConfigured = false; + this->ColoringMappersConfigured = false; + this->PointSpritesMappersConfigured = false; + this->VolumePropsAndMappersConfigured = false; + this->ScalarBarActorConfigured = false; + this->ActorsPropertiesConfigured = false; + this->GridConfigured = false; + this->MetaDataConfigured = false; + this->ActorsPropertiesConfigured = false; + this->ColoringConfigured = false; } + this->ImporterTimeStamp = importerMTime; - vtkCamera* camera = GetActiveCamera(); - const double angle = vtkMath::RadiansFromDegrees(camera->GetViewAngle()); - const double* position = camera->GetPosition(); - const double* focal = camera->GetFocalPoint(); - - if (use) + if (!this->ActorsPropertiesConfigured) { - const double distance = std::sqrt(vtkMath::Distance2BetweenPoints(position, focal)); - const double parallelScale = distance * tan(angle / 2); - camera->SetParallelScale(parallelScale); + this->ConfigureActorsProperties(); } - else - { - const double distance = camera->GetParallelScale() / tan(angle / 2); - double direction[3]; - vtkMath::Subtract(position, focal, direction); - vtkMath::Normalize(direction); - vtkMath::MultiplyScalar(direction, distance); - double newPosition[3]; - vtkMath::Add(focal, direction, newPosition); - camera->SetPosition(newPosition); - } - this->UseOrthographicProjection = use; - camera->SetParallelProjection(use); - this->ResetCameraClippingRange(); -} -//---------------------------------------------------------------------------- -void vtkF3DRenderer::SetUseTrackball(bool use) -{ - if (this->UseTrackball != use) + if (!this->ColoringConfigured) { - this->UseTrackball = use; - this->CheatSheetConfigured = false; + this->ConfigureColoring(); } -} -//---------------------------------------------------------------------------- -void vtkF3DRenderer::UpdateActors() -{ this->ConfigureHDRI(); if (!this->MetaDataConfigured) @@ -1501,6 +1569,7 @@ void vtkF3DRenderer::UpdateActors() this->ConfigureRenderPasses(); } + // Grid need all actors setup to be configured correctly if (!this->GridConfigured) { this->ConfigureGridUsingCurrentActors(); @@ -1510,11 +1579,6 @@ void vtkF3DRenderer::UpdateActors() //---------------------------------------------------------------------------- void vtkF3DRenderer::Render() { - if (!this->ActorsPropertiesConfigured) - { - this->ConfigureActorsProperties(); - } - if (!this->CheatSheetConfigured) { this->ConfigureCheatSheet(); @@ -1659,3 +1723,998 @@ std::string vtkF3DRenderer::ShortName(const std::string& name, int maxChar) return name.substr(0, maxChar - 3) + "..."; } } + +//---------------------------------------------------------------------------- +std::string vtkF3DRenderer::GenerateMetaDataDescription() +{ + assert(this->Importer); + + // XXX Padding should not be handled by manipulating string + // but on the actor directly, but it is not supported by VTK yet. + + // add eol before/after the string + std::string description = "\n" + this->Importer->GetMetaDataDescription() + "\n"; + size_t index = 0; + while (true) + { + index = description.find('\n', index); + if (index == std::string::npos) + { + break; + } + // Add spaces after/before eol + description.insert(index + 1, " "); + description.insert(index, " "); + index += 3; + } + + return description; +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetImporter(vtkF3DMetaImporter* importer) +{ + this->Importer = importer; +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetRoughness(const std::optional& roughness) +{ + if (this->Roughness != roughness) + { + this->Roughness = roughness; + this->ActorsPropertiesConfigured = false; + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetOpacity(const std::optional& opacity) +{ + if (this->Opacity != opacity) + { + this->Opacity = opacity; + this->ActorsPropertiesConfigured = false; + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetMetallic(const std::optional& metallic) +{ + if (this->Metallic != metallic) + { + this->Metallic = metallic; + this->ActorsPropertiesConfigured = false; + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetNormalScale(const std::optional& normalScale) +{ + if (this->NormalScale != normalScale) + { + this->NormalScale = normalScale; + this->ActorsPropertiesConfigured = false; + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetSurfaceColor(const std::optional>& color) +{ + if (this->SurfaceColor != color) + { + this->SurfaceColor = color; + this->ActorsPropertiesConfigured = false; + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetEmissiveFactor(const std::optional>& factor) +{ + if (this->EmissiveFactor != factor) + { + this->EmissiveFactor = factor; + this->ActorsPropertiesConfigured = false; + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetTextureMatCap(const std::optional& tex) +{ + if (this->TextureMatCap != tex) + { + this->TextureMatCap = tex; + this->ActorsPropertiesConfigured = false; + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetTextureBaseColor(const std::optional& tex) +{ + if (this->TextureBaseColor != tex) + { + this->TextureBaseColor = tex; + this->ActorsPropertiesConfigured = false; + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetTextureMaterial(const std::optional& tex) +{ + if (this->TextureMaterial != tex) + { + this->TextureMaterial = tex; + this->ActorsPropertiesConfigured = false; + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetTextureEmissive(const std::optional& tex) +{ + if (this->TextureEmissive != tex) + { + this->TextureEmissive = tex; + this->ActorsPropertiesConfigured = false; + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetTextureNormal(const std::optional& tex) +{ + if (this->TextureNormal != tex) + { + this->TextureNormal = tex; + this->ActorsPropertiesConfigured = false; + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::ConfigureActorsProperties() +{ + assert(this->Importer); + + double* surfaceColor = nullptr; + if (this->SurfaceColor.has_value()) + { + if (this->SurfaceColor.value().size() != 3) + { + F3DLog::Print(F3DLog::Severity::Warning, + std::string("Invalid surface color provided, not applying")); + } + else + { + surfaceColor = this->SurfaceColor.value().data(); + } + } + + double* emissiveFactor = nullptr; + if (this->EmissiveFactor.has_value()) + { + if (this->EmissiveFactor.value().size() != 3) + { + F3DLog::Print(F3DLog::Severity::Warning, + std::string("Invalid emissive factor provided, not applying")); + } + else + { + emissiveFactor = this->EmissiveFactor.value().data(); + } + } + + bool setBackfaceCulling = false; + bool backfaceCulling = true; + if (this->BackfaceType.has_value()) + { + setBackfaceCulling = true; + if (this->BackfaceType.value() == "visible") + { + backfaceCulling = false; + } + else if (this->BackfaceType.value() == "hidden") + { + backfaceCulling = true; + } + else + { + setBackfaceCulling = false; + F3DLog::Print(F3DLog::Severity::Warning, this->BackfaceType.value() + " is not a valid backface type, assuming it is not set"); + } + } + + for ([[maybe_unused]] const auto& [actor, mapper, originalActor] : this->Importer->GetColoringActorsAndMappers()) + { + if (this->EdgeVisible.has_value()) + { + actor->GetProperty()->SetEdgeVisibility(this->EdgeVisible.value()); + originalActor->GetProperty()->SetEdgeVisibility(this->EdgeVisible.value()); + } + + if (this->LineWidth.has_value()) + { + actor->GetProperty()->SetLineWidth(this->LineWidth.value()); + originalActor->GetProperty()->SetLineWidth(this->LineWidth.value()); + } + + if (this->PointSize.has_value()) + { + actor->GetProperty()->SetPointSize(this->PointSize.value()); + originalActor->GetProperty()->SetPointSize(this->PointSize.value()); + } + + if (setBackfaceCulling) + { + actor->GetProperty()->SetBackfaceCulling(backfaceCulling); + originalActor->GetProperty()->SetBackfaceCulling(backfaceCulling); + } + + if(surfaceColor) + { + actor->GetProperty()->SetColor(surfaceColor); + originalActor->GetProperty()->SetColor(surfaceColor); + } + + if (this->Opacity.has_value()) + { + actor->GetProperty()->SetOpacity(this->Opacity.value()); + originalActor->GetProperty()->SetOpacity(this->Opacity.value()); + } + + if (this->Roughness.has_value()) + { + actor->GetProperty()->SetRoughness(this->Roughness.value()); + originalActor->GetProperty()->SetRoughness(this->Roughness.value()); + } + + if (this->Metallic.has_value()) + { + actor->GetProperty()->SetMetallic(this->Metallic.value()); + originalActor->GetProperty()->SetMetallic(this->Metallic.value()); + } + + // Textures + if (this->TextureBaseColor.has_value()) + { + auto colorTex = ::GetTexture(this->TextureBaseColor.value(), true); + actor->GetProperty()->SetBaseColorTexture(colorTex); + originalActor->GetProperty()->SetBaseColorTexture(colorTex); + + // If the input texture is RGBA, flag the actor as translucent + if (colorTex && colorTex->GetImageDataInput(0)->GetNumberOfScalarComponents() == 4) + { + actor->ForceTranslucentOn(); + originalActor->ForceTranslucentOn(); + } + } + + if (this->TextureMaterial.has_value()) + { + auto matTex = ::GetTexture(this->TextureMaterial.value()); + actor->GetProperty()->SetORMTexture(matTex); + originalActor->GetProperty()->SetORMTexture(matTex); + } + + if (this->TextureEmissive.has_value()) + { + auto emissTex = ::GetTexture(this->TextureEmissive.value(), true); + actor->GetProperty()->SetEmissiveTexture(emissTex); + originalActor->GetProperty()->SetEmissiveTexture(emissTex); + } + + if (emissiveFactor) + { + actor->GetProperty()->SetEmissiveFactor(emissiveFactor); + originalActor->GetProperty()->SetEmissiveFactor(emissiveFactor); + } + + if (this->TextureNormal.has_value()) + { + auto normTex = ::GetTexture(this->TextureNormal.value()); + actor->GetProperty()->SetNormalTexture(normTex); + originalActor->GetProperty()->SetNormalTexture(normTex); + } + + if (this->NormalScale.has_value()) + { + actor->GetProperty()->SetNormalScale(this->NormalScale.value()); + originalActor->GetProperty()->SetNormalScale(this->NormalScale.value()); + } + + if (this->TextureMatCap.has_value()) + { + auto matCapTex = ::GetTexture(this->TextureMatCap.value()); + actor->GetProperty()->SetTexture("matcap", matCapTex); + originalActor->GetProperty()->SetTexture("matcap", matCapTex); + } + } + + for ([[maybe_unused]] const auto& [actor, mapper] : this->Importer->GetPointSpritesActorsAndMappers()) + { + if(surfaceColor) + { + actor->GetProperty()->SetColor(surfaceColor); + } + + if (this->Opacity.has_value()) + { + actor->GetProperty()->SetOpacity(this->Opacity.value()); + } + } + + this->ActorsPropertiesConfigured = true; +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetPointSpritesProperties(SplatType type, double pointSpritesSize) +{ + assert(this->Importer); + + if (type == SplatType::GAUSSIAN) + { +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20231102) + if (!vtkShader::IsComputeShaderSupported()) + { + F3DLog::Print(F3DLog::Severity::Warning, + "Compute shaders are not supported, gaussians are not sorted, resulting in blending " + "artifacts"); + } +#endif + } + + const vtkBoundingBox& bbox = this->Importer->GetGeometryBoundingBox(); + + double scaleFactor = 1.0; + if (bbox.IsValid()) + { + scaleFactor = pointSpritesSize * bbox.GetDiagonalLength() * 0.001; + } + + for (const auto& [actor, mapper] : this->Importer->GetPointSpritesActorsAndMappers()) + { + + mapper->EmissiveOff(); + if (type == SplatType::GAUSSIAN) + { + mapper->SetScaleFactor(1.0); + mapper->SetSplatShaderCode(nullptr); // gaussian is the default VTK shader + mapper->SetScaleArray("scale"); + +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20231102) + mapper->AnisotropicOn(); + mapper->SetBoundScale(3.0); + mapper->SetRotationArray("rotation"); + + int* viewport = this->GetSize(); + + float lowPass[3] = { 0.3f / (viewport[0] * viewport[0]), 0.f, + 0.3f / (viewport[1] * viewport[1]) }; + mapper->SetLowpassMatrix(lowPass); +#else + F3DLog::Print(F3DLog::Severity::Warning, + "Gaussian splatting selected but VTK <= 9.3 only supports isotropic gaussians"); +#endif + + actor->ForceTranslucentOn(); + } + else + { +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20231102) + mapper->AnisotropicOff(); + mapper->SetLowpassMatrix(0., 0., 0.); +#endif + + mapper->SetScaleFactor(scaleFactor); + + mapper->SetSplatShaderCode("//VTK::Color::Impl\n" + "float dist = dot(offsetVCVSOutput.xy, offsetVCVSOutput.xy);\n" + "if (dist > 1.0) {\n" + " discard;\n" + "} else {\n" + " float scale = (1.0 - dist);\n" + " ambientColor *= scale;\n" + " diffuseColor *= scale;\n" + "}\n"); + + actor->ForceTranslucentOff(); + } + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::ShowScalarBar(bool show) +{ + if (this->ScalarBarVisible != show) + { + this->ScalarBarVisible = show; + this->CheatSheetConfigured = false; + this->ColoringConfigured = false; + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetUsePointSprites(bool use) +{ + if (this->UsePointSprites != use) + { + this->UsePointSprites = use; + this->CheatSheetConfigured = false; + this->ColoringConfigured = false; + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetUseVolume(bool use) +{ + if (this->UseVolume != use) + { + this->UseVolume = use; + this->CheatSheetConfigured = false; + this->ColoringConfigured = false; + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetUseInverseOpacityFunction(bool use) +{ + assert(this->Importer); + + if (this->UseInverseOpacityFunction != use) + { + this->UseInverseOpacityFunction = use; + for ([[maybe_unused]] const auto& [prop, mapper] : this->Importer->GetVolumePropsAndMappers()) + { + if (prop) + { + vtkPiecewiseFunction* pwf = prop->GetProperty()->GetScalarOpacity(); + if (pwf->GetSize() == 2) + { + double range[2]; + pwf->GetRange(range); + + pwf->RemoveAllPoints(); + pwf->AddPoint(range[0], this->UseInverseOpacityFunction ? 1.0 : 0.0); + pwf->AddPoint(range[1], this->UseInverseOpacityFunction ? 0.0 : 1.0); + } + } + } + this->VolumePropsAndMappersConfigured = false; + this->CheatSheetConfigured = false; + this->ColoringConfigured = false; + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetScalarBarRange(const std::optional>& range) +{ + if (this->UserScalarBarRange != range) + { + this->UserScalarBarRange = range; + this->ColorTransferFunctionConfigured = false; + this->ColoringMappersConfigured = false; + this->PointSpritesMappersConfigured = false; + this->VolumePropsAndMappersConfigured = false; + this->ScalarBarActorConfigured = false; + this->ColoringConfigured = false; + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetColormap(const std::vector& colormap) +{ + if (this->Colormap != colormap) + { + this->Colormap = colormap; + + this->ColorTransferFunctionConfigured = false; + this->ColoringMappersConfigured = false; + this->PointSpritesMappersConfigured = false; + this->VolumePropsAndMappersConfigured = false; + + this->ScalarBarActorConfigured = false; + this->ColoringConfigured = false; + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetEnableColoring(bool enable) +{ + if (enable != this->EnableColoring) + { + this->EnableColoring = enable; + this->CheatSheetConfigured = false; + this->ColoringConfigured = false; + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetUseCellColoring(bool useCell) +{ + if (useCell != this->UseCellColoring) + { + this->UseCellColoring = useCell; + this->ColorTransferFunctionConfigured = false; + this->ColoringMappersConfigured = false; + this->PointSpritesMappersConfigured = false; + this->VolumePropsAndMappersConfigured = false; + this->ScalarBarActorConfigured = false; + this->CheatSheetConfigured = false; + this->ColoringConfigured = false; + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetArrayNameForColoring(const std::optional& arrayName) +{ + if (arrayName != this->ArrayNameForColoring) + { + this->ArrayNameForColoring = arrayName; + this->ColorTransferFunctionConfigured = false; + this->ColoringMappersConfigured = false; + this->PointSpritesMappersConfigured = false; + this->VolumePropsAndMappersConfigured = false; + this->ScalarBarActorConfigured = false; + this->CheatSheetConfigured = false; + this->ColoringConfigured = false; + } +} + +//---------------------------------------------------------------------------- +std::optional vtkF3DRenderer::GetArrayNameForColoring() +{ + return this->ArrayNameForColoring; +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::SetComponentForColoring(int component) +{ + if (component != this->ComponentForColoring) + { + this->ComponentForColoring = component; + this->ColorTransferFunctionConfigured = false; + this->ColoringMappersConfigured = false; + this->PointSpritesMappersConfigured = false; + this->VolumePropsAndMappersConfigured = false; + this->ScalarBarActorConfigured = false; + this->CheatSheetConfigured = false; + this->ColoringConfigured = false; + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::ConfigureColoring() +{ + assert(this->Importer); + + // Recover coloring information and update handler + bool enableColoring = this->EnableColoring || (!this->UseRaytracing && this->UseVolume); + F3DColoringInfoHandler& coloringHandler = this->Importer->GetColoringInfoHandler(); + auto info = coloringHandler.SetCurrentColoring(enableColoring, this->UseCellColoring, this->ArrayNameForColoring, false); + bool hasColoring = info.has_value(); + if (hasColoring && !this->ColorTransferFunctionConfigured) + { + this->ConfigureRangeAndCTFForColoring(info.value()); + this->ColorTransferFunctionConfigured = true; + } + + // Handle surface geometry + bool geometriesVisible = this->UseRaytracing || (!this->UseVolume && !this->UsePointSprites); + for (const auto& [actor, mapper, originalActor] : this->Importer->GetColoringActorsAndMappers()) + { + if (geometriesVisible) + { + bool visible = false; + if (hasColoring) + { + // Rely on the previous state of scalar visibility to know if we should show the actor by default + visible = mapper->GetScalarVisibility(); + if (!this->ColoringMappersConfigured) + { + visible = vtkF3DRenderer::ConfigureMapperForColoring(mapper, info.value().Name, + this->ComponentForColoring, this->ColorTransferFunction, this->ColorRange, + this->UseCellColoring); + } + } + actor->SetVisibility(visible); + originalActor->SetVisibility(!visible); + } + else + { + actor->SetVisibility(false); + originalActor->SetVisibility(false); + } + } + if (geometriesVisible) + { + this->ColoringMappersConfigured = true; + } + + // Handle point sprites + bool pointSpritesVisible = !this->UseRaytracing && !this->UseVolume && this->UsePointSprites; + for (const auto& [actor, mapper] : this->Importer->GetPointSpritesActorsAndMappers()) + { + actor->SetVisibility(pointSpritesVisible); + if (pointSpritesVisible) + { + if (hasColoring) + { + if (!this->PointSpritesMappersConfigured) + { + vtkF3DRenderer::ConfigureMapperForColoring(mapper, info.value().Name, + this->ComponentForColoring, this->ColorTransferFunction, this->ColorRange, + this->UseCellColoring); + } + } + mapper->SetScalarVisibility(hasColoring); + } + } + if (pointSpritesVisible) + { + this->PointSpritesMappersConfigured = true; + } + + // Handle Volume prop + bool volumeVisible = !this->UseRaytracing && this->UseVolume; + const auto& volPropsAndMappers = this->Importer->GetVolumePropsAndMappers(); + for (const auto& [prop, mapper] : volPropsAndMappers) + { + if (!volumeVisible) + { + prop->VisibilityOff(); + } + else + { + bool visible = false; + if (hasColoring) + { + // Initialize the visibility based on the mapper configuration + visible = !std::string(mapper->GetArrayName()).empty(); + if (!this->VolumePropsAndMappersConfigured) + { + visible = vtkF3DRenderer::ConfigureVolumeForColoring(mapper, + prop, info.value().Name, this->ComponentForColoring, + this->ColorTransferFunction, this->ColorRange, this->UseCellColoring, + this->UseInverseOpacityFunction); + if (!visible) + { + F3DLog::Print( + F3DLog::Severity::Warning, "Cannot find the array \"" + info.value().Name + "\" to display volume with"); + } + } + } + prop->SetVisibility(visible); + } + } + if (volumeVisible) + { + if (!this->VolumePropsAndMappersConfigured && volPropsAndMappers.size() == 0) + { + F3DLog::Print( + F3DLog::Severity::Error, "Cannot use volume with this data"); + } + this->VolumePropsAndMappersConfigured = true; + } + + // Handle scalar bar + bool barVisible = this->ScalarBarVisible && hasColoring && this->ComponentForColoring >= -1; + this->ScalarBarActor->SetVisibility(barVisible); + if (barVisible && !this->ScalarBarActorConfigured) + { + vtkF3DRenderer::ConfigureScalarBarActorForColoring( + this->ScalarBarActor, info.value().Name, this->ComponentForColoring, this->ColorTransferFunction); + this->ScalarBarActorConfigured = true; + } + + this->RenderPassesConfigured = false; + this->ColoringConfigured = true; +} + +//---------------------------------------------------------------------------- +std::string vtkF3DRenderer::GetColoringDescription() +{ + assert(this->Importer); + + std::stringstream stream; + auto info = this->Importer->GetColoringInfoHandler().GetCurrentColoringInfo(); + if (info.has_value()) + { + stream << "Coloring using " << (this->UseCellColoring ? "cell" : "point") << " array named " + << info.value().Name << (this->EnableColoring ? ", " : " (forced), ") + << vtkF3DRenderer::ComponentToString(this->ComponentForColoring); + } + else + { + stream << "Not coloring"; + } + return stream.str(); +} + +//---------------------------------------------------------------------------- +bool vtkF3DRenderer::ConfigureMapperForColoring(vtkPolyDataMapper* mapper, const std::string& name, + int component, vtkColorTransferFunction* ctf, double range[2], bool cellFlag) +{ + vtkDataSetAttributes* data = cellFlag ? + static_cast(mapper->GetInput()->GetCellData()) : + static_cast(mapper->GetInput()->GetPointData()); + vtkDataArray* array = data->GetArray(name.c_str()); + if (!array || component >= array->GetNumberOfComponents()) + { + mapper->ScalarVisibilityOff(); + return false; + } + + mapper->SetColorModeToMapScalars(); + mapper->SelectColorArray(name.c_str()); + mapper->SetScalarMode( + cellFlag ? VTK_SCALAR_MODE_USE_CELL_FIELD_DATA : VTK_SCALAR_MODE_USE_POINT_FIELD_DATA); + mapper->ScalarVisibilityOn(); + + if (component == -2) + { + if (array->GetNumberOfComponents() > 4) + { + // comp > 4 is actually not supported and would fail with a vtk error + F3DLog::Print(F3DLog::Severity::Warning, + "Direct scalars rendering not supported by array with more than 4 components"); + return false; + } + else + { + mapper->SetColorModeToDirectScalars(); + } + } + else + { + mapper->SetColorModeToMapScalars(); + mapper->SetScalarRange(range); + mapper->SetLookupTable(ctf); + } + return true; +} + +//---------------------------------------------------------------------------- +bool vtkF3DRenderer::ConfigureVolumeForColoring(vtkSmartVolumeMapper* mapper, + vtkVolume* volume, const std::string& name, int component, vtkColorTransferFunction* ctf, + double range[2], bool cellFlag, bool inverseOpacityFlag) +{ + vtkDataSetAttributes* data = cellFlag ? + static_cast(mapper->GetInput()->GetCellData()) : + static_cast(mapper->GetInput()->GetPointData()); + vtkDataArray* array = data->GetArray(name.c_str()); + if (!array || component >= array->GetNumberOfComponents()) + { + // We rely on the selected scalar array to check if this mapper can be shown or not + mapper->SelectScalarArray(""); + return false; + } + + mapper->SetScalarMode( + cellFlag ? VTK_SCALAR_MODE_USE_CELL_FIELD_DATA : VTK_SCALAR_MODE_USE_POINT_FIELD_DATA); + mapper->SelectScalarArray(name.c_str()); + + if (component >= 0) + { + mapper->SetVectorMode(vtkSmartVolumeMapper::COMPONENT); + mapper->SetVectorComponent(component); + } + else if (component == -1) + { + mapper->SetVectorMode(vtkSmartVolumeMapper::MAGNITUDE); + } + else if (component == -2) + { + if (array->GetNumberOfComponents() > 4) + { + // comp > 4 is actually not supported and would fail with a vtk error + F3DLog::Print(F3DLog::Severity::Warning, + "Direct scalars rendering not supported by array with more than 4 components"); + return false; + } + else + { + mapper->SetVectorMode(vtkSmartVolumeMapper::DISABLED); + } + } + + vtkNew otf; + otf->AddPoint(range[0], inverseOpacityFlag ? 1.0 : 0.0); + otf->AddPoint(range[1], inverseOpacityFlag ? 0.0 : 1.0); + + vtkNew property; + property->SetColor(ctf); + property->SetScalarOpacity(otf); + property->ShadeOff(); + property->SetInterpolationTypeToLinear(); + + volume->SetProperty(property); + return true; +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::ConfigureScalarBarActorForColoring( + vtkScalarBarActor* scalarBar, std::string arrayName, int component, vtkColorTransferFunction* ctf) +{ + arrayName += " ("; + arrayName += this->ComponentToString(component); + arrayName += ")"; + + scalarBar->SetLookupTable(ctf); + scalarBar->SetTitle(arrayName.c_str()); + scalarBar->SetNumberOfLabels(4); + scalarBar->SetOrientationToHorizontal(); + scalarBar->SetWidth(0.8); + scalarBar->SetHeight(0.07); + scalarBar->SetPosition(0.1, 0.01); +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::ConfigureRangeAndCTFForColoring( + const F3DColoringInfoHandler::ColoringInfo& info) +{ + if (this->ComponentForColoring == -2) + { + return; + } + + if (this->ComponentForColoring >= info.MaximumNumberOfComponents) + { + F3DLog::Print(F3DLog::Severity::Warning, + std::string("Invalid component index: ") + std::to_string(this->ComponentForColoring)); + return; + } + + // Set range + bool autoRange = true; + if (this->UserScalarBarRange.has_value()) + { + if (this->UserScalarBarRange.value().size() == 2 && this->UserScalarBarRange.value()[0] <= this->UserScalarBarRange.value()[1]) + { + autoRange = false; + this->ColorRange[0] = this->UserScalarBarRange.value()[0]; + this->ColorRange[1] = this->UserScalarBarRange.value()[1]; + } + else + { + F3DLog::Print(F3DLog::Severity::Warning, + std::string("Invalid scalar range provided, using automatic range")); + } + } + + if (autoRange) + { + if (this->ComponentForColoring >= 0) + { + this->ColorRange[0] = info.ComponentRanges[this->ComponentForColoring][0]; + this->ColorRange[1] = info.ComponentRanges[this->ComponentForColoring][1]; + } + else + { + this->ColorRange[0] = info.MagnitudeRange[0]; + this->ColorRange[1] = info.MagnitudeRange[1]; + } + } + + // Create lookup table + this->ColorTransferFunction = vtkSmartPointer::New(); + if (this->Colormap.size() > 0) + { + if (this->Colormap.size() % 4 == 0) + { + for (size_t i = 0; i < this->Colormap.size(); i += 4) + { + double val = this->Colormap[i]; + double r = this->Colormap[i + 1]; + double g = this->Colormap[i + 2]; + double b = this->Colormap[i + 3]; + this->ColorTransferFunction->AddRGBPoint( + this->ColorRange[0] + val * (this->ColorRange[1] - this->ColorRange[0]), r, g, b); + } + } + else + { + F3DLog::Print(F3DLog::Severity::Warning, + "Specified color map list count is not a multiple of 4, ignoring it."); + } + } + + if (this->ComponentForColoring >= 0) + { + this->ColorTransferFunction->SetVectorModeToComponent(); + this->ColorTransferFunction->SetVectorComponent(this->ComponentForColoring); + } + else + { + this->ColorTransferFunction->SetVectorModeToMagnitude(); + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::CycleFieldForColoring() +{ + // XXX: A generic approach will be better when adding categorical field data coloring + this->SetUseCellColoring(!this->UseCellColoring); + bool enableColoring = this->EnableColoring || (!this->UseRaytracing && this->UseVolume); + F3DColoringInfoHandler& coloringHandler = this->Importer->GetColoringInfoHandler(); + auto info = coloringHandler.SetCurrentColoring(enableColoring, this->UseCellColoring, this->ArrayNameForColoring, true); + if (!info.has_value()) + { + // Cycle array if the current one is not valid + this->CycleArrayForColoring(); + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::CycleArrayForColoring() +{ + assert(this->Importer); + this->Importer->GetColoringInfoHandler().CycleColoringArray(!this->UseVolume); //TODO check this cond + auto info = this->Importer->GetColoringInfoHandler().GetCurrentColoringInfo(); + bool enable = info.has_value(); + + this->SetEnableColoring(enable); + if (this->EnableColoring) + { + this->SetArrayNameForColoring(info.value().Name); + if (this->ComponentForColoring >= info.value().MaximumNumberOfComponents) + { + // Cycle component if the current one is not valid + this->CycleComponentForColoring(); + } + } + else + { + this->SetArrayNameForColoring(std::nullopt); + } +} + +//---------------------------------------------------------------------------- +void vtkF3DRenderer::CycleComponentForColoring() +{ + assert(this->Importer); + + auto info = this->Importer->GetColoringInfoHandler().GetCurrentColoringInfo(); + if (!info.has_value()) + { + return; + } + + // -2 -1 0 1 2 3 4 + this->SetComponentForColoring( + (this->ComponentForColoring + 3) % (info.value().MaximumNumberOfComponents + 2) - 2); +} + +//---------------------------------------------------------------------------- +std::string vtkF3DRenderer::ComponentToString(int component) +{ + assert(this->Importer); + + if (component == -2) + { + return "Direct Scalars"; + } + else if (component == -1) + { + return "Magnitude"; + } + else + { + auto info = this->Importer->GetColoringInfoHandler().GetCurrentColoringInfo(); + if (!info.has_value()) + { + return ""; + } + if (component >= info.value().MaximumNumberOfComponents) + { + return ""; + } + + std::string componentName; + if (component < static_cast(info.value().ComponentNames.size())) + { + componentName = info.value().ComponentNames[component]; + } + if (componentName.empty()) + { + componentName = "Component #"; + componentName += std::to_string(component); + } + return componentName; + } +} diff --git a/vtkext/private/module/vtkF3DRenderer.h b/vtkext/private/module/vtkF3DRenderer.h index c76f9de57c..5ebb99fc35 100644 --- a/vtkext/private/module/vtkF3DRenderer.h +++ b/vtkext/private/module/vtkF3DRenderer.h @@ -11,22 +11,27 @@ #ifndef vtkF3DRenderer_h #define vtkF3DRenderer_h +#include "vtkF3DMetaImporter.h" + #include #include #include #include +class vtkColorTransferFunction; class vtkCornerAnnotation; class vtkF3DDropZoneActor; class vtkImageReader2; class vtkOrientationMarkerWidget; +class vtkScalarBarActor; class vtkSkybox; class vtkTextActor; class vtkF3DRenderer : public vtkOpenGLRenderer { public: + static vtkF3DRenderer* New(); vtkTypeMacro(vtkF3DRenderer, vtkOpenGLRenderer); ///@{ @@ -35,7 +40,7 @@ class vtkF3DRenderer : public vtkOpenGLRenderer */ void ShowAxis(bool show); void ShowGrid(bool show); - void ShowEdge(bool show); + void ShowEdge(const std::optional& show); void ShowTimer(bool show); void ShowMetaData(bool show); void ShowFilename(bool show); @@ -82,11 +87,12 @@ class vtkF3DRenderer : public vtkOpenGLRenderer void SetFinalShader(const std::optional& finalShader); ///@} - ///@{ /** * Set SetUseOrthographicProjection */ - void SetUseOrthographicProjection(bool use); + void SetUseOrthographicProjection(const std::optional& use); + + ///@{ /** * Set/Get UseTrackball */ @@ -117,10 +123,15 @@ class vtkF3DRenderer : public vtkOpenGLRenderer void ResetCameraClippingRange() override; /** - * Update actors according to the properties of this class: + * Set properties on each imported actors and also configure the coloring + * Then update dedicated actors and logics according to the properties of this class: + * - HDRI + * - MetaData + * - Texts + * - RenderPasses * - Grid */ - virtual void UpdateActors(); + void UpdateActors(); /** * Reimplemented to handle light creation when no lights are added @@ -132,7 +143,12 @@ class vtkF3DRenderer : public vtkOpenGLRenderer * Initialize the renderer actors and flags. * Should be called after being added to a vtkRenderWindow. */ - virtual void Initialize(const std::string& up); + void Initialize(); + + /** + * Initialize actors properties related to the up vector using the provided upString, including the camera + */ + void InitializeUpVector(const std::string& upString); /** * Get the OpenGL skybox @@ -142,8 +158,9 @@ class vtkF3DRenderer : public vtkOpenGLRenderer /** * Return description about the current rendering status * Currently contains information about the camera and the grid if any + * Returns a multiline string containing the scene description */ - virtual std::string GetSceneDescription(); + std::string GetSceneDescription(); /** * Get up vector @@ -160,7 +177,176 @@ class vtkF3DRenderer : public vtkOpenGLRenderer */ void SetCachePath(const std::string& cachePath); -protected: + /** + * Set the roughness on all actors + */ + void SetRoughness(const std::optional& roughness); + + /** + * Set the surface color on all actors + */ + void SetSurfaceColor(const std::optional>& color); + + /** + * Set the emmissive factors on all actors + */ + void SetEmissiveFactor(const std::optional>& factors); + + /** + * Set the opacity on all actors + */ + void SetOpacity(const std::optional& opacity); + + /** + * Set the metallic on all actors + */ + void SetMetallic(const std::optional& metallic); + + /** + * Set the normal scale on all actors + */ + void SetNormalScale(const std::optional& normalScale); + + /** + * Set the material capture texture on all actors. + * This texture includes baked lighting effect, + * so all other material textures are ignored. + */ + void SetTextureMatCap(const std::optional& tex); + + /** + * Set the base color texture on all actors + */ + void SetTextureBaseColor(const std::optional& tex); + + /** + * Set the material texture on all actors + */ + void SetTextureMaterial(const std::optional& tex); + + /** + * Set the emissive texture on all actors + */ + void SetTextureEmissive(const std::optional& tex); + + /** + * Set the normal texture on all actors + */ + void SetTextureNormal(const std::optional& tex); + + enum class SplatType + { + SPHERE, + GAUSSIAN + }; + + /** + * Set the point sprites size and the splat type on the pointGaussianMapper + */ + void SetPointSpritesProperties(SplatType splatType, double pointSpritesSize); + + /** + * Set the visibility of the scalar bar. + * It will only be shown when coloring and not shown + * when using direct scalars rendering. + */ + void ShowScalarBar(bool show); + + /** + * Set the visibility of the point sprites actor. + * It will only be shown if raytracing and volume are not enabled + */ + void SetUsePointSprites(bool use); + + /** + * Set the visibility of the volume actor. + * It will only be shown if the data is compatible with volume rendering + * and raytracing is not enabled + */ + void SetUseVolume(bool use); + + /** + * Set the use of an inverted opacity function + * for volume rendering.. + */ + void SetUseInverseOpacityFunction(bool use); + + /** + * Set the range of the scalar bar + * Setting an empty vector will use automatic range + */ + void SetScalarBarRange(const std::optional>& range); + + /** + * Set the colormap to use + * Setting an empty vector will use default color map + */ + void SetColormap(const std::vector& colormap); + + /** + * Set the meta importer to recover coloring information from + */ + void SetImporter(vtkF3DMetaImporter* importer); + + ///@{ + /** + * Set/Get if coloring is enabled + */ + void SetEnableColoring(bool enable); + vtkGetMacro(EnableColoring, bool); + ///@} + + ///@{ + /** + * Set/Get if using point or cell data coloring + */ + void SetUseCellColoring(bool useCell); + vtkGetMacro(UseCellColoring, bool); + ///@} + + ///@{ + /** + * Set/Get the name of the array to use for coloring + */ + void SetArrayNameForColoring(const std::optional& arrayName); + std::optional GetArrayNameForColoring(); + ///@} + + ///@{ + /** + * Set/Get the name of the component to use for coloring + */ + void SetComponentForColoring(int component); + vtkGetMacro(ComponentForColoring, int); + ///@} + + /** + * Get information about the current coloring + * Returns a single line string containing the coloring description + */ + virtual std::string GetColoringDescription(); + + /** + * Switch between point data and cell data coloring, actually setting UseCellColoring member. + * This can trigger CycleArrayForColoring if current array is not valid. + */ + void CycleFieldForColoring(); + + /** + * Cycle the current array for coloring, actually setting EnableColoring and ArrayNameForColoring members. + * This loops back to not coloring if volume is not enabled. + * This can trigger CycleComponentForColoring if current component is not valid. + */ + void CycleArrayForColoring(); + + /** + * Cycle the component in used for rendering + * looping back to direct scalars + */ + void CycleComponentForColoring(); + + +private: vtkF3DRenderer(); ~vtkF3DRenderer() override; @@ -201,15 +387,12 @@ class vtkF3DRenderer : public vtkOpenGLRenderer ///@} /** - * Configure all actors properties according to what has been set for: - * - point size - * - line width - * - show edges + * Configure all actors properties */ void ConfigureActorsProperties(); /** - * Configure the cheatsheet text and mark it for rendering + * Configure the cheatsheet text and hotkeys and mark it for rendering */ void ConfigureCheatSheet(); @@ -224,15 +407,11 @@ class vtkF3DRenderer : public vtkOpenGLRenderer void ConfigureRenderPasses(); /** - * Add related hotkeys options to the cheatsheet. - * Override to add other hotkeys + * Generate a padded metadata description + * using the internal importer. + * Returns a multiline string containing the meta data description */ - virtual void FillCheatSheetHotkeys(std::stringstream& sheet); - - /** - * Override to generate a data description - */ - virtual std::string GenerateMetaDataDescription() = 0; + std::string GenerateMetaDataDescription(); /** * Create a cache directory if a HDRIHash is set @@ -244,7 +423,44 @@ class vtkF3DRenderer : public vtkOpenGLRenderer */ static std::string ShortName(const std::string& name, int maxChar); - vtkNew InitialCamera; + /** + * Configure coloring for all actors + */ + void ConfigureColoring(); + + /** + * Convenience method for configuring a poly data mapper for coloring + * Return true if mapper was configured for coloring, false otherwise. + */ + static bool ConfigureMapperForColoring(vtkPolyDataMapper* mapper, const std::string& name, + int component, vtkColorTransferFunction* ctf, double range[2], bool cellFlag = false); + + /** + * Convenience method for configuring a volume mapper and volume prop for coloring + * Return true if they were configured for coloring, false otherwise. + */ + static bool ConfigureVolumeForColoring(vtkSmartVolumeMapper* mapper, vtkVolume* volume, + const std::string& name, int component, vtkColorTransferFunction* ctf, double range[2], + bool cellFlag = false, bool inverseOpacityFlag = false); + + /** + * Convenience method for configuring a scalar bar actor for coloring + */ + void ConfigureScalarBarActorForColoring(vtkScalarBarActor* scalarBar, std::string arrayName, + int component, vtkColorTransferFunction* ctf); + + /** + * Configure internal range and color transfer function according to provided + * coloring info + */ + void ConfigureRangeAndCTFForColoring(const F3DColoringInfoHandler::ColoringInfo& info); + + /** + * Convert a component index into a string + * If there is a component name defined in the current coloring information, display it. + * Otherwise, use component #index as the default value. + */ + std::string ComponentToString(int component); vtkSmartPointer AxisWidget; @@ -277,7 +493,7 @@ class vtkF3DRenderer : public vtkOpenGLRenderer bool GridVisible = false; bool GridAbsolute = false; bool AxisVisible = false; - bool EdgeVisible = false; + std::optional EdgeVisible; bool TimerVisible = false; bool FilenameVisible = false; bool MetaDataVisible = false; @@ -291,7 +507,7 @@ class vtkF3DRenderer : public vtkOpenGLRenderer bool UseSSAOPass = false; bool UseToneMappingPass = false; bool UseBlurBackground = false; - bool UseOrthographicProjection = false; + std::optional UseOrthographicProjection = false; bool UseTrackball = false; bool InvertZoom = false; @@ -331,6 +547,46 @@ class vtkF3DRenderer : public vtkOpenGLRenderer std::optional BackfaceType; std::optional FinalShader; + + vtkF3DMetaImporter* Importer = nullptr; + vtkMTimeType ImporterTimeStamp = 0; + + vtkNew ScalarBarActor; + bool ScalarBarActorConfigured = false; + + bool ColoringMappersConfigured = false; + bool PointSpritesMappersConfigured = false; + bool VolumePropsAndMappersConfigured = false; + bool ColoringConfigured = false; + + std::optional Opacity; + std::optional Roughness; + std::optional Metallic; + std::optional NormalScale; + std::optional> SurfaceColor; + std::optional> EmissiveFactor; + std::optional TextureMatCap; + std::optional TextureBaseColor; + std::optional TextureMaterial; + std::optional TextureEmissive; + std::optional TextureNormal; + + vtkSmartPointer ColorTransferFunction; + double ColorRange[2] = { 0.0, 1.0 }; + bool ColorTransferFunctionConfigured = false; + + bool EnableColoring = false; + bool UseCellColoring = false; + int ComponentForColoring = -1; + std::optional ArrayNameForColoring; + + bool ScalarBarVisible = false; + bool UsePointSprites = false; + bool UseVolume = false; + bool UseInverseOpacityFunction = false; + + std::optional> UserScalarBarRange; + std::vector Colormap; }; #endif diff --git a/vtkext/private/module/vtkF3DRendererWithColoring.cxx b/vtkext/private/module/vtkF3DRendererWithColoring.cxx deleted file mode 100644 index 0b06bdf75e..0000000000 --- a/vtkext/private/module/vtkF3DRendererWithColoring.cxx +++ /dev/null @@ -1,1121 +0,0 @@ -#include "vtkF3DRendererWithColoring.h" - -#include "F3DLog.h" -#include "vtkF3DConfigure.h" -#include "vtkF3DGenericImporter.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -namespace -{ -//---------------------------------------------------------------------------- -// TODO : add this function in a utils file for rendering in VTK directly -vtkSmartPointer GetTexture(const std::string& filePath, bool isSRGB = false) -{ - vtkSmartPointer texture; - if (!filePath.empty()) - { - std::string fullPath = vtksys::SystemTools::CollapseFullPath(filePath); - if (!vtksys::SystemTools::FileExists(fullPath)) - { - F3DLog::Print(F3DLog::Severity::Warning, "Texture file does not exist " + fullPath + "\n"); - } - else - { - auto reader = vtkSmartPointer::Take( - vtkImageReader2Factory::CreateImageReader2(fullPath.c_str())); - if (reader) - { - reader->SetFileName(fullPath.c_str()); - reader->Update(); - texture = vtkSmartPointer::New(); - texture->SetInputConnection(reader->GetOutputPort()); - if (isSRGB) - { - texture->UseSRGBColorSpaceOn(); - } - texture->InterpolateOn(); - texture->SetColorModeToDirectScalars(); - return texture; - } - else - { - F3DLog::Print(F3DLog::Severity::Warning, "Cannot open texture file " + fullPath + "\n"); - } - } - } - - return texture; -} -} - -vtkStandardNewMacro(vtkF3DRendererWithColoring); - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetImporter(vtkF3DGenericImporter* importer) -{ - this->Importer = importer; - this->Modified(); -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::Initialize(const std::string& up) -{ - // This remove all actors and view props - this->Superclass::Initialize(up); - - this->ArrayIndexForColoring = -1; - this->ComponentForColoring = -1; - - this->AddActor2D(this->ScalarBarActor); - this->ScalarBarActor->VisibilityOff(); - - this->ColorTransferFunctionConfigured = false; - this->GeometryMappersConfigured = false; - this->PointSpritesMappersConfigured = false; - this->VolumePropsAndMappersConfigured = false; - this->ScalarBarActorConfigured = false; - this->CheatSheetConfigured = false; - this->ColoringActorsPropertiesConfigured = false; - this->ColoringConfigured = false; -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetRoughness(double roughness) -{ - if (this->Roughness != roughness) - { - this->Roughness = roughness; - this->ColoringActorsPropertiesConfigured = false; - } -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetOpacity(double opacity) -{ - if (this->Opacity != opacity) - { - this->Opacity = opacity; - this->ColoringActorsPropertiesConfigured = false; - } -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetMetallic(double metallic) -{ - if (this->Metallic != metallic) - { - this->Metallic = metallic; - this->ColoringActorsPropertiesConfigured = false; - } -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetNormalScale(double normalScale) -{ - if (this->NormalScale != normalScale) - { - this->NormalScale = normalScale; - this->ColoringActorsPropertiesConfigured = false; - } -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetSurfaceColor(double* color) -{ - if (!std::equal(color, color + 3, this->SurfaceColor)) - { - std::copy(color, color + 3, this->SurfaceColor); - this->ColoringActorsPropertiesConfigured = false; - } -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetEmissiveFactor(double* factor) -{ - if (!std::equal(factor, factor + 3, this->EmissiveFactor)) - { - std::copy(factor, factor + 3, this->EmissiveFactor); - this->ColoringActorsPropertiesConfigured = false; - } -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetTextureMatCap(const std::string& tex) -{ - if (this->TextureMatCap != tex) - { - this->TextureMatCap = tex; - this->ColoringActorsPropertiesConfigured = false; - } -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetTextureBaseColor(const std::string& tex) -{ - if (this->TextureBaseColor != tex) - { - this->TextureBaseColor = tex; - this->ColoringActorsPropertiesConfigured = false; - } -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetTextureMaterial(const std::string& tex) -{ - if (this->TextureMaterial != tex) - { - this->TextureMaterial = tex; - this->ColoringActorsPropertiesConfigured = false; - } -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetTextureEmissive(const std::string& tex) -{ - if (this->TextureEmissive != tex) - { - this->TextureEmissive = tex; - this->ColoringActorsPropertiesConfigured = false; - } -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetTextureNormal(const std::string& tex) -{ - if (this->TextureNormal != tex) - { - this->TextureNormal = tex; - this->ColoringActorsPropertiesConfigured = false; - } -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::ConfigureColoringActorsProperties() -{ - assert(this->Importer); - - for (auto& actorAndMapper : this->Importer->GetGeometryActorsAndMappers()) - { - // XXX: All properties below should be optional once we can apply them - // on full scenes importers - actorAndMapper.first->GetProperty()->SetColor(this->SurfaceColor); - actorAndMapper.first->GetProperty()->SetOpacity(this->Opacity); - actorAndMapper.first->GetProperty()->SetRoughness(this->Roughness); - actorAndMapper.first->GetProperty()->SetMetallic(this->Metallic); - - // Textures - auto colorTex = ::GetTexture(this->TextureBaseColor, true); - actorAndMapper.first->GetProperty()->SetBaseColorTexture(colorTex); - actorAndMapper.first->GetProperty()->SetORMTexture(::GetTexture(this->TextureMaterial)); - actorAndMapper.first->GetProperty()->SetEmissiveTexture( - ::GetTexture(this->TextureEmissive, true)); - actorAndMapper.first->GetProperty()->SetEmissiveFactor(this->EmissiveFactor); - actorAndMapper.first->GetProperty()->SetNormalTexture(::GetTexture(this->TextureNormal)); - actorAndMapper.first->GetProperty()->SetNormalScale(this->NormalScale); - actorAndMapper.first->GetProperty()->SetTexture("matcap", ::GetTexture(this->TextureMatCap)); - - // If the input texture is RGBA, flag the actor as translucent - if (colorTex && colorTex->GetImageDataInput(0)->GetNumberOfScalarComponents() == 4) - { - actorAndMapper.first->ForceTranslucentOn(); - } - } - - for (auto& psActorAndMapper : this->Importer->GetPointSpritesActorsAndMappers()) - { - psActorAndMapper.first->GetProperty()->SetColor(this->SurfaceColor); - psActorAndMapper.first->GetProperty()->SetOpacity(this->Opacity); - } - - this->ColoringActorsPropertiesConfigured = true; -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetPointSpritesProperties(SplatType type, double pointSpritesSize) -{ - if (!this->Importer) - { - return; - } - - if (type == SplatType::GAUSSIAN) - { -#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20231102) - if (!vtkShader::IsComputeShaderSupported()) - { - F3DLog::Print(F3DLog::Severity::Warning, - "Compute shaders are not supported, gaussians are not sorted, resulting in blending " - "artifacts"); - } -#endif - } - - const vtkBoundingBox& bbox = this->Importer->GetGeometryBoundingBox(); - - double scaleFactor = 1.0; - if (bbox.IsValid()) - { - scaleFactor = pointSpritesSize * bbox.GetDiagonalLength() * 0.001; - } - - const auto& psActorsAndMappers = this->Importer->GetPointSpritesActorsAndMappers(); - for (auto& [actor, mapper] : psActorsAndMappers) - { - mapper->EmissiveOff(); - - if (type == SplatType::GAUSSIAN) - { - mapper->SetScaleFactor(1.0); - mapper->SetSplatShaderCode(nullptr); // gaussian is the default VTK shader - mapper->SetScaleArray("scale"); - -#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20231102) - mapper->AnisotropicOn(); - mapper->SetBoundScale(3.0); - mapper->SetRotationArray("rotation"); - - int* viewport = this->GetSize(); - - float lowPass[3] = { 0.3f / (viewport[0] * viewport[0]), 0.f, - 0.3f / (viewport[1] * viewport[1]) }; - mapper->SetLowpassMatrix(lowPass); -#else - F3DLog::Print(F3DLog::Severity::Warning, - "Gaussian splatting selected but VTK <= 9.3 only supports isotropic gaussians"); -#endif - - actor->ForceTranslucentOn(); - } - else - { -#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20231102) - mapper->AnisotropicOff(); - mapper->SetLowpassMatrix(0., 0., 0.); -#endif - - mapper->SetScaleFactor(scaleFactor); - - mapper->SetSplatShaderCode("//VTK::Color::Impl\n" - "float dist = dot(offsetVCVSOutput.xy, offsetVCVSOutput.xy);\n" - "if (dist > 1.0) {\n" - " discard;\n" - "} else {\n" - " float scale = (1.0 - dist);\n" - " ambientColor *= scale;\n" - " diffuseColor *= scale;\n" - "}\n"); - - actor->ForceTranslucentOff(); - } - } -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::ShowScalarBar(bool show) -{ - if (this->ScalarBarVisible != show) - { - this->ScalarBarVisible = show; - this->CheatSheetConfigured = false; - this->ColoringConfigured = false; - } -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetUsePointSprites(bool use) -{ - if (this->UsePointSprites != use) - { - this->UsePointSprites = use; - this->CheatSheetConfigured = false; - this->ColoringConfigured = false; - } -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetUseVolume(bool use) -{ - if (this->UseVolume != use) - { - this->UseVolume = use; - this->CheatSheetConfigured = false; - this->ColoringConfigured = false; - } -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetUseInverseOpacityFunction(bool use) -{ - if (!this->Importer) - { - return; - } - - if (this->UseInverseOpacityFunction != use) - { - this->UseInverseOpacityFunction = use; - - const auto& volPropsAndMappers = this->Importer->GetVolumePropsAndMappers(); - for (auto& volPropAndMapper : volPropsAndMappers) - { - if (volPropAndMapper.first) - { - vtkPiecewiseFunction* pwf = volPropAndMapper.first->GetProperty()->GetScalarOpacity(); - if (pwf->GetSize() == 2) - { - double range[2]; - pwf->GetRange(range); - - pwf->RemoveAllPoints(); - pwf->AddPoint(range[0], this->UseInverseOpacityFunction ? 1.0 : 0.0); - pwf->AddPoint(range[1], this->UseInverseOpacityFunction ? 0.0 : 1.0); - } - } - } - this->VolumePropsAndMappersConfigured = false; - this->CheatSheetConfigured = false; - this->ColoringConfigured = false; - } -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetScalarBarRange(const std::optional>& range) -{ - if (this->UserScalarBarRange != range) - { - this->UserScalarBarRange = range; - this->ColorTransferFunctionConfigured = false; - this->GeometryMappersConfigured = false; - this->PointSpritesMappersConfigured = false; - this->VolumePropsAndMappersConfigured = false; - this->ScalarBarActorConfigured = false; - this->ColoringConfigured = false; - } -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetColormap(const std::vector& colormap) -{ - if (this->Colormap != colormap) - { - this->Colormap = colormap; - - this->ColorTransferFunctionConfigured = false; - this->GeometryMappersConfigured = false; - this->PointSpritesMappersConfigured = false; - this->VolumePropsAndMappersConfigured = false; - this->ScalarBarActorConfigured = false; - this->ColoringConfigured = false; - } -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::CycleScalars(CycleType type) -{ - if (!this->Importer) - { - return; - } - - switch (type) - { - case (CycleType::NONE): - return; - break; - case (CycleType::FIELD): - this->CycleFieldForColoring(); - break; - case (CycleType::ARRAY_INDEX): - this->CycleArrayIndexForColoring(); - break; - case (CycleType::COMPONENT): - this->CycleComponentForColoring(); - break; - default: - break; - } - - // Check attributes are valid and cycle recursively if needed - this->CycleScalars(this->CheckColoring()); - - this->ColorTransferFunctionConfigured = false; - this->GeometryMappersConfigured = false; - this->PointSpritesMappersConfigured = false; - this->VolumePropsAndMappersConfigured = false; - this->ScalarBarActorConfigured = false; - this->CheatSheetConfigured = false; - this->ColoringConfigured = false; -} - -//---------------------------------------------------------------------------- -vtkF3DRendererWithColoring::CycleType vtkF3DRendererWithColoring::CheckColoring() -{ - assert(this->Importer); - - // Never force change of anything if we are currently not coloring - if (this->ArrayIndexForColoring < 0) - { - return CycleType::NONE; - } - - // Never force change of CellData/PointData coloring on the user - if (this->Importer->GetNumberOfIndexesForColoring(this->UseCellColoring) == 0) - { - return CycleType::NONE; - } - - // Suggest to change the array index only if current index is not valid - vtkF3DGenericImporter::ColoringInfo info; - if (!this->Importer->GetInfoForColoring(this->UseCellColoring, this->ArrayIndexForColoring, info)) - { - return CycleType::ARRAY_INDEX; - } - - // Suggest to change the component if current component is invalid - if (this->ComponentForColoring >= info.MaximumNumberOfComponents) - { - return CycleType::COMPONENT; - } - - return CycleType::NONE; -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::SetColoring(bool enable, - bool useCellData, const std::optional& arrayName, int component) -{ - if (!this->Importer) - { - return; - } - - // XXX This should be reworked to avoid handling multiple information in one parameters - // while still being future-proof and flexible enough. - if (enable != (this->ArrayIndexForColoring >= 0) - || useCellData != this->UseCellColoring - || component != this->ComponentForColoring - || arrayName != this->GetColoringArrayName()) - { - this->UseCellColoring = useCellData; - this->ComponentForColoring = component; - - int nIndexes = this->Importer->GetNumberOfIndexesForColoring(this->UseCellColoring); - if (!enable) - { - // Not coloring - this->ArrayIndexForColoring = -1; - } - else if (nIndexes == 0) - { - // Trying to color but no array available - F3DLog::Print(F3DLog::Severity::Debug, "No array to color with"); - this->ArrayIndexForColoring = -1; - } - else if (!arrayName.has_value()) - { - // Coloring with first array - this->ArrayIndexForColoring = 0; - } - else - { - // Coloring with named array - this->ArrayIndexForColoring = this->Importer->FindIndexForColoring(useCellData, arrayName.value()); - if (this->ArrayIndexForColoring == -1) - { - // Could not find named array - F3DLog::Print(F3DLog::Severity::Warning, "Unknown scalar array: \"" + arrayName.value() + "\"\n"); - } - } - - this->ColorTransferFunctionConfigured = false; - this->GeometryMappersConfigured = false; - this->PointSpritesMappersConfigured = false; - this->VolumePropsAndMappersConfigured = false; - this->ScalarBarActorConfigured = false; - this->ColoringConfigured = false; - } -} - -//---------------------------------------------------------------------------- -bool vtkF3DRendererWithColoring::GetColoringEnabled() -{ - return this->ArrayIndexForColoring >= 0; -} - -//---------------------------------------------------------------------------- -bool vtkF3DRendererWithColoring::GetColoringUseCell() -{ - return this->UseCellColoring; -} - -//---------------------------------------------------------------------------- -std::optional vtkF3DRendererWithColoring::GetColoringArrayName() -{ - std::optional arrayName; - vtkF3DGenericImporter::ColoringInfo info; - if (this->Importer && this->Importer->GetInfoForColoring(this->UseCellColoring, this->ArrayIndexForColoring, info)) - { - arrayName = info.Name; - } - return arrayName; -} - -//---------------------------------------------------------------------------- -int vtkF3DRendererWithColoring::GetColoringComponent() -{ - return this->ComponentForColoring; -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::UpdateActors() -{ - if (!this->Importer) - { - // Importer is nullptr, still call superclass to render other actors - this->Superclass::UpdateActors(); - return; - } - - // Handle importer changes - vtkMTimeType importerMTime = this->Importer->GetMTime(); - bool importerChanged = this->Importer->GetMTime() >= this->ImporterTimeStamp; - if (importerChanged) - { - this->ColorTransferFunctionConfigured = false; - this->GeometryMappersConfigured = false; - this->PointSpritesMappersConfigured = false; - this->VolumePropsAndMappersConfigured = false; - this->ScalarBarActorConfigured = false; - this->ActorsPropertiesConfigured = false; - this->GridConfigured = false; - this->MetaDataConfigured = false; - this->ColoringActorsPropertiesConfigured = false; - this->ColoringConfigured = false; - } - this->ImporterTimeStamp = importerMTime; - - if (!this->ColoringActorsPropertiesConfigured) - { - this->ConfigureColoringActorsProperties(); - } - - if (!this->ColoringConfigured) - { - this->ConfigureColoring(); - } - - // Call superclass update actors after everything, - // as the grid need all actors visible to be configured correctly - this->Superclass::UpdateActors(); -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::ConfigureColoring() -{ - assert(this->Importer); - - // Recover coloring information - vtkF3DGenericImporter::ColoringInfo info; - bool hasColoring = - this->Importer->GetInfoForColoring(this->UseCellColoring, this->ArrayIndexForColoring, info); - - bool volumeVisible = !this->UseRaytracing && this->UseVolume; - if (!hasColoring && volumeVisible) - { - // When showing volume, always try to find an array to color with - this->CycleScalars(vtkF3DRendererWithColoring::CycleType::ARRAY_INDEX); - hasColoring = - this->Importer->GetInfoForColoring(this->UseCellColoring, this->ArrayIndexForColoring, info); - } - - if (hasColoring && !this->ColorTransferFunctionConfigured) - { - this->ConfigureRangeAndCTFForColoring(info); - this->ColorTransferFunctionConfigured = true; - } - - // Handle surface geometry - bool geometriesVisible = this->UseRaytracing || (!this->UseVolume && !this->UsePointSprites); - const auto& actorsAndMappers = this->Importer->GetGeometryActorsAndMappers(); - for (size_t i = 0; i < actorsAndMappers.size(); i++) - { - auto& actorAndMapper = actorsAndMappers[i]; - vtkDataArray* coloringArray = nullptr; - if (hasColoring && info.Arrays.size() > i) - { - coloringArray = info.Arrays[i]; - } - actorAndMapper.first->SetVisibility(geometriesVisible); - if (geometriesVisible && coloringArray) - { - if (!this->GeometryMappersConfigured) - { - vtkF3DRendererWithColoring::ConfigureMapperForColoring(actorAndMapper.second, coloringArray, - this->ComponentForColoring, this->ColorTransferFunction, this->ColorRange, - this->UseCellColoring); - } - actorAndMapper.second->ScalarVisibilityOn(); - } - else - { - actorAndMapper.second->ScalarVisibilityOff(); - } - } - if (geometriesVisible) - { - this->GeometryMappersConfigured = true; - } - - // Handle point sprites - bool pointSpritesVisible = !this->UseRaytracing && !this->UseVolume && this->UsePointSprites; - const auto& psActorsAndMappers = this->Importer->GetPointSpritesActorsAndMappers(); - for (size_t i = 0; i < psActorsAndMappers.size(); i++) - { - auto& actorAndMapper = psActorsAndMappers[i]; - vtkDataArray* coloringArray = nullptr; - if (hasColoring && info.Arrays.size() > i) - { - coloringArray = info.Arrays[i]; - } - actorAndMapper.first->SetVisibility(pointSpritesVisible); - if (pointSpritesVisible && coloringArray) - { - if (!this->PointSpritesMappersConfigured) - { - vtkF3DRendererWithColoring::ConfigureMapperForColoring(actorAndMapper.second, coloringArray, - this->ComponentForColoring, this->ColorTransferFunction, this->ColorRange, - this->UseCellColoring); - } - actorAndMapper.second->ScalarVisibilityOn(); - } - else - { - actorAndMapper.second->ScalarVisibilityOff(); - } - } - if (pointSpritesVisible) - { - this->PointSpritesMappersConfigured = true; - } - - // Handle Volume prop - const auto& volPropsAndMappers = this->Importer->GetVolumePropsAndMappers(); - for (size_t i = 0; i < volPropsAndMappers.size(); i++) - { - auto& propAndMapper = volPropsAndMappers[i]; - vtkDataArray* coloringArray = nullptr; - if (hasColoring && info.Arrays.size() > i) - { - coloringArray = info.Arrays[i]; - } - if (!volumeVisible) - { - propAndMapper.first->VisibilityOff(); - } - else if (!coloringArray) - { - F3DLog::Print( - F3DLog::Severity::Error, "Cannot use volume with this dataset or with the requested array"); - propAndMapper.first->VisibilityOff(); - } - else - { - if (!this->VolumePropsAndMappersConfigured) - { - vtkF3DRendererWithColoring::ConfigureVolumeForColoring(propAndMapper.second, - propAndMapper.first, coloringArray, this->ComponentForColoring, - this->ColorTransferFunction, this->ColorRange, this->UseCellColoring, - this->UseInverseOpacityFunction); - } - propAndMapper.first->VisibilityOn(); - } - } - if (volumeVisible) - { - this->VolumePropsAndMappersConfigured = true; - } - - // Handle scalar bar - bool barVisible = this->ScalarBarVisible && hasColoring && this->ComponentForColoring >= -1; - this->ScalarBarActor->SetVisibility(barVisible); - if (barVisible && !this->ScalarBarActorConfigured) - { - vtkF3DRendererWithColoring::ConfigureScalarBarActorForColoring( - this->ScalarBarActor, info.Name, this->ComponentForColoring, this->ColorTransferFunction); - this->ScalarBarActorConfigured = true; - } - - this->RenderPassesConfigured = false; - this->ColoringConfigured = true; -} - -//---------------------------------------------------------------------------- -std::string vtkF3DRendererWithColoring::GetColoringDescription() -{ - if (!this->Importer) - { - return ""; - } - - std::stringstream stream; - vtkF3DGenericImporter::ColoringInfo info; - if (this->Importer->GetInfoForColoring(this->UseCellColoring, this->ArrayIndexForColoring, info)) - { - stream << "Coloring using " << (this->UseCellColoring ? "cell" : "point") << " array named " - << info.Name << ", " - << vtkF3DRendererWithColoring::ComponentToString(this->ComponentForColoring) << "\n"; - } - else - { - stream << "Not coloring\n"; - } - return stream.str(); -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::ConfigureMapperForColoring(vtkPolyDataMapper* mapper, - vtkDataArray* array, int component, vtkColorTransferFunction* ctf, double range[2], bool cellFlag) -{ - if (!array || component >= array->GetNumberOfComponents()) - { - return; - } - - mapper->SetColorModeToMapScalars(); - mapper->SelectColorArray(array->GetName()); - mapper->SetScalarMode( - cellFlag ? VTK_SCALAR_MODE_USE_CELL_FIELD_DATA : VTK_SCALAR_MODE_USE_POINT_FIELD_DATA); - mapper->ScalarVisibilityOn(); - - if (component == -2) - { - if (array->GetNumberOfComponents() > 4) - { - // comp > 4 is actually not supported and would fail with a vtk error - F3DLog::Print(F3DLog::Severity::Warning, - "Direct scalars rendering not supported by array with more than 4 components"); - } - else - { - mapper->SetColorModeToDirectScalars(); - } - } - else - { - mapper->SetColorModeToMapScalars(); - mapper->SetScalarRange(range); - mapper->SetLookupTable(ctf); - } -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::ConfigureVolumeForColoring(vtkSmartVolumeMapper* mapper, - vtkVolume* volume, vtkDataArray* array, int component, vtkColorTransferFunction* ctf, - double range[2], bool cellFlag, bool inverseOpacityFlag) -{ - if (!array || component >= array->GetNumberOfComponents()) - { - return; - } - - mapper->SetScalarMode( - cellFlag ? VTK_SCALAR_MODE_USE_CELL_FIELD_DATA : VTK_SCALAR_MODE_USE_POINT_FIELD_DATA); - mapper->SelectScalarArray(array->GetName()); - - if (component >= 0) - { - mapper->SetVectorMode(vtkSmartVolumeMapper::COMPONENT); - mapper->SetVectorComponent(component); - } - else if (component == -1) - { - mapper->SetVectorMode(vtkSmartVolumeMapper::MAGNITUDE); - } - else if (component == -2) - { - if (array->GetNumberOfComponents() > 4) - { - // comp > 4 is actually not supported and would fail with a vtk error - F3DLog::Print(F3DLog::Severity::Warning, - "Direct scalars rendering not supported by array with more than 4 components"); - } - else - { - mapper->SetVectorMode(vtkSmartVolumeMapper::DISABLED); - } - } - - vtkNew otf; - otf->AddPoint(range[0], inverseOpacityFlag ? 1.0 : 0.0); - otf->AddPoint(range[1], inverseOpacityFlag ? 0.0 : 1.0); - - vtkNew property; - property->SetColor(ctf); - property->SetScalarOpacity(otf); - property->ShadeOff(); - property->SetInterpolationTypeToLinear(); - - volume->SetProperty(property); -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::ConfigureScalarBarActorForColoring( - vtkScalarBarActor* scalarBar, std::string arrayName, int component, vtkColorTransferFunction* ctf) -{ - arrayName += " ("; - arrayName += this->ComponentToString(component); - arrayName += ")"; - - scalarBar->SetLookupTable(ctf); - scalarBar->SetTitle(arrayName.c_str()); - scalarBar->SetNumberOfLabels(4); - scalarBar->SetOrientationToHorizontal(); - scalarBar->SetWidth(0.8); - scalarBar->SetHeight(0.07); - scalarBar->SetPosition(0.1, 0.01); -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::ConfigureRangeAndCTFForColoring( - const vtkF3DGenericImporter::ColoringInfo& info) -{ - if (this->ComponentForColoring == -2) - { - return; - } - - if (this->ComponentForColoring >= info.MaximumNumberOfComponents) - { - F3DLog::Print(F3DLog::Severity::Warning, - std::string("Invalid component index: ") + std::to_string(this->ComponentForColoring) + "\n"); - return; - } - - // Set range - bool autoRange = true; - if (this->UserScalarBarRange.has_value()) - { - if (this->UserScalarBarRange.value().size() == 2 && this->UserScalarBarRange.value()[0] <= this->UserScalarBarRange.value()[1]) - { - autoRange = false; - this->ColorRange[0] = this->UserScalarBarRange.value()[0]; - this->ColorRange[1] = this->UserScalarBarRange.value()[1]; - } - else - { - F3DLog::Print(F3DLog::Severity::Warning, - std::string("Invalid scalar range provided, using automatic range\n")); - } - } - - if (autoRange) - { - if (this->ComponentForColoring >= 0) - { - this->ColorRange[0] = info.ComponentRanges[this->ComponentForColoring][0]; - this->ColorRange[1] = info.ComponentRanges[this->ComponentForColoring][1]; - } - else - { - this->ColorRange[0] = info.MagnitudeRange[0]; - this->ColorRange[1] = info.MagnitudeRange[1]; - } - } - - // Create lookup table - this->ColorTransferFunction = vtkSmartPointer::New(); - if (this->Colormap.size() > 0) - { - if (this->Colormap.size() % 4 == 0) - { - for (size_t i = 0; i < this->Colormap.size(); i += 4) - { - double val = this->Colormap[i]; - double r = this->Colormap[i + 1]; - double g = this->Colormap[i + 2]; - double b = this->Colormap[i + 3]; - this->ColorTransferFunction->AddRGBPoint( - this->ColorRange[0] + val * (this->ColorRange[1] - this->ColorRange[0]), r, g, b); - } - } - else - { - F3DLog::Print(F3DLog::Severity::Warning, - "Specified color map list count is not a multiple of 4, ignoring it."); - } - } - - if (this->ComponentForColoring >= 0) - { - this->ColorTransferFunction->SetVectorModeToComponent(); - this->ColorTransferFunction->SetVectorComponent(this->ComponentForColoring); - } - else - { - this->ColorTransferFunction->SetVectorModeToMagnitude(); - } -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::FillCheatSheetHotkeys(std::stringstream& cheatSheetText) -{ - if (!this->Importer) - { - this->Superclass::FillCheatSheetHotkeys(cheatSheetText); - return; - } - - vtkF3DGenericImporter::ColoringInfo info; - bool hasColoring = - this->Importer->GetInfoForColoring(this->UseCellColoring, this->ArrayIndexForColoring, info); - - cheatSheetText << " C: Cell scalars coloring [" << (this->UseCellColoring ? "ON" : "OFF") - << "]\n"; - cheatSheetText << " S: Scalars coloring [" - << (hasColoring ? vtkF3DRenderer::ShortName(info.Name, 19) : "OFF") << "]\n"; - cheatSheetText << " Y: Coloring component [" - << vtkF3DRendererWithColoring::ComponentToString(this->ComponentForColoring) - << "]\n"; - cheatSheetText << " B: Scalar bar " << (this->ScalarBarVisible ? "[ON]" : "[OFF]") << "\n"; - - cheatSheetText << " V: Volume representation " << (this->UseVolume ? "[ON]" : "[OFF]") << "\n"; - cheatSheetText << " I: Inverse volume opacity " - << (this->UseInverseOpacityFunction ? "[ON]" : "[OFF]") << "\n"; - cheatSheetText << " O: Point sprites " << (this->UsePointSprites ? "[ON]" : "[OFF]") << "\n"; - this->Superclass::FillCheatSheetHotkeys(cheatSheetText); -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::CycleFieldForColoring() -{ - // A generic approach will be better when adding categorical field data coloring - this->UseCellColoring = !this->UseCellColoring; -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::CycleArrayIndexForColoring() -{ - assert(this->Importer); - - int nIndex = this->Importer->GetNumberOfIndexesForColoring(this->UseCellColoring); - if (nIndex <= 0) - { - return; - } - - if (this->UseVolume) - { - this->ArrayIndexForColoring = (this->ArrayIndexForColoring + 1) % nIndex; - } - else - { - // Cycle through arrays looping back to -1 - // -1 0 1 2 -1 0 1 2 ... - this->ArrayIndexForColoring = (this->ArrayIndexForColoring + 2) % (nIndex + 1) - 1; - } -} - -//---------------------------------------------------------------------------- -void vtkF3DRendererWithColoring::CycleComponentForColoring() -{ - assert(this->Importer); - - vtkF3DGenericImporter::ColoringInfo info; - if (!this->Importer->GetInfoForColoring(this->UseCellColoring, this->ArrayIndexForColoring, info)) - { - return; - } - - // -2 -1 0 1 2 3 4 - this->ComponentForColoring = - (this->ComponentForColoring + 3) % (info.MaximumNumberOfComponents + 2) - 2; -} - -//---------------------------------------------------------------------------- -std::string vtkF3DRendererWithColoring::GenerateMetaDataDescription() -{ - if (!this->Importer) - { - return " Unavailable\n"; - } - - // XXX Padding should not be handled by manipulating string - // but on the actor directly, but it is not supported by VTK yet. - - // add eol before/after the string - std::string description = "\n" + this->Importer->GetMetaDataDescription() + "\n"; - size_t index = 0; - while (true) - { - index = description.find('\n', index); - if (index == std::string::npos) - { - break; - } - // Add spaces after/before eol - description.insert(index + 1, " "); - description.insert(index, " "); - index += 3; - } - - return description; -} - -//---------------------------------------------------------------------------- -std::string vtkF3DRendererWithColoring::ComponentToString(int component) -{ - assert(this->Importer); - - if (component == -2) - { - return "Direct Scalars"; - } - else if (component == -1) - { - return "Magnitude"; - } - else - { - vtkF3DGenericImporter::ColoringInfo info; - if (!this->Importer->GetInfoForColoring( - this->UseCellColoring, this->ArrayIndexForColoring, info)) - { - return ""; - } - if (component >= info.MaximumNumberOfComponents) - { - return ""; - } - - std::string componentName; - if (component < static_cast(info.ComponentNames.size())) - { - componentName = info.ComponentNames[component]; - } - if (componentName.empty()) - { - componentName = "Component #"; - componentName += std::to_string(component); - } - return componentName; - } -} diff --git a/vtkext/private/module/vtkF3DRendererWithColoring.h b/vtkext/private/module/vtkF3DRendererWithColoring.h deleted file mode 100644 index 7558527d26..0000000000 --- a/vtkext/private/module/vtkF3DRendererWithColoring.h +++ /dev/null @@ -1,313 +0,0 @@ -/** - * @class vtkF3DRendererWithColoring - * @brief A specialiwed renderer that can handle coloring - * - * A specialization of vtkF3DRenderer that can handle coloring - * with actors such as a geometry actor, a point sprites actor, - * a volume mapper. It also handle the showing of the related scalar bar. - */ - -#ifndef vtkF3DRendererWithColoring_h -#define vtkF3DRendererWithColoring_h - -#include "vtkF3DRenderer.h" - -#include "vtkF3DGenericImporter.h" - -#include - -class vtkColorTransferFunction; -class vtkDataArray; -class vtkDataSetAttributes; -class vtkScalarBarActor; - -class vtkF3DRendererWithColoring : public vtkF3DRenderer -{ -public: - static vtkF3DRendererWithColoring* New(); - vtkTypeMacro(vtkF3DRendererWithColoring, vtkF3DRenderer); - - /** - * Initialize all actors and flags - */ - void Initialize(const std::string& up) override; - - /** - * Set the roughness on all coloring actors - */ - void SetRoughness(double roughness); - - /** - * Set the surface color on all coloring actors - */ - void SetSurfaceColor(double* color); - - /** - * Set the emmissive factors on all coloring actors - */ - void SetEmissiveFactor(double* factors); - - /** - * Set the opacity on all coloring actors - */ - void SetOpacity(double opacity); - - /** - * Set the metallic on all coloring actors - */ - void SetMetallic(double metallic); - - /** - * Set the normal scale on all coloring actors - */ - void SetNormalScale(double normalScale); - - /** - * Set the material capture texture on all coloring actors. - * This texture includes baked lighting effect, - * so all other material textures are ignored. - */ - void SetTextureMatCap(const std::string& tex); - - /** - * Set the base color texture on all coloring actors - */ - void SetTextureBaseColor(const std::string& tex); - - /** - * Set the metarial texture on all coloring actors - */ - void SetTextureMaterial(const std::string& tex); - - /** - * Set the emissive texture on all coloring actors - */ - void SetTextureEmissive(const std::string& tex); - - /** - * Set the normal texture on all coloring actors - */ - void SetTextureNormal(const std::string& tex); - - enum class SplatType - { - SPHERE, - GAUSSIAN - }; - - /** - * Set the point sprites size and the splat type on the pointGaussianMapper - */ - void SetPointSpritesProperties(SplatType splatType, double pointSpritesSize); - - /** - * Set the visibility of the scalar bar. - * It will only be shown when coloring and not - * using direct scalars rendering. - */ - void ShowScalarBar(bool show); - - /** - * Set the visibility of the point sprites actor. - * It will only be shown if raytracing and volume are not enabled - */ - void SetUsePointSprites(bool use); - - /** - * Set the visibility of the volume actor. - * It will only be shown if the data is compatible with volume rendering - * and raytracing is not enabled - */ - void SetUseVolume(bool use); - - /** - * Set the use of an inverted opacity function - * for volume rendering.. - */ - void SetUseInverseOpacityFunction(bool use); - - /** - * Set the range of the scalar bar - * Setting an empty vector will use automatic range - */ - void SetScalarBarRange(const std::optional>& range); - - /** - * Set the colormap to use - * Setting an empty vector will use default color map - */ - void SetColormap(const std::vector& colormap); - - enum class CycleType - { - NONE, - FIELD, - ARRAY_INDEX, - COMPONENT - }; - - /** - * Cycle the shown scalars according to the cycle type - */ - void CycleScalars(CycleType type); - - /** - * Set the generic importer to handle coloring and other actors related actions with. - */ - void SetImporter(vtkF3DGenericImporter* importer); - - /** - * Set coloring information. - * This method will try to find the corresponding array in the coloring attributes and will - * position ArrayIndexForColoring and DataForColoring accordingly. - */ - void SetColoring(bool enable, bool useCellData, const std::optional& arrayName, int component); - - ///@{ - /** - * Get current coloring information, - * Useful after using Cycle methods - */ - bool GetColoringEnabled(); - bool GetColoringUseCell(); - std::optional GetColoringArrayName(); - int GetColoringComponent(); - ///@} - - /** - * Reimplemented to update the visibility and coloring of internal actors - * as well as the scalar bar actors. - * Call Superclass::UpdateActors at the end. - */ - void UpdateActors() override; - - /** - * Get information about the current coloring - */ - virtual std::string GetColoringDescription(); - -protected: - vtkF3DRendererWithColoring() = default; - ~vtkF3DRendererWithColoring() override = default; - - /** - * Configure all coloring actors properties: - * - roughness - * - */ - void ConfigureColoringActorsProperties(); - - /** - * Configure coloring for all coloring actors - */ - void ConfigureColoring(); - - /** - * Convenience method for configuring a poly data mapper for coloring - */ - static void ConfigureMapperForColoring(vtkPolyDataMapper* mapper, vtkDataArray* array, - int component, vtkColorTransferFunction* ctf, double range[2], bool cellFlag = false); - - /** - * Convenience method for configuring a volume mapper and volume for coloring - */ - static void ConfigureVolumeForColoring(vtkSmartVolumeMapper* mapper, vtkVolume* volume, - vtkDataArray* array, int component, vtkColorTransferFunction* ctf, double range[2], - bool cellFlag = false, bool inverseOpacityFlag = false); - - /** - * Convenience method for configuring a scalar bar actor for coloring - */ - void ConfigureScalarBarActorForColoring(vtkScalarBarActor* scalarBar, std::string arrayName, - int component, vtkColorTransferFunction* ctf); - - /** - * Configure internal range and color transfer function according to provided - * coloring info - */ - void ConfigureRangeAndCTFForColoring(const vtkF3DGenericImporter::ColoringInfo& info); - - /** - * Fill cheatsheet hotkeys string stream - */ - void FillCheatSheetHotkeys(std::stringstream& sheet) override; - - /** - * Switch between point data and cell data coloring - */ - void CycleFieldForColoring(); - - /** - * Increment the array index or loop it back - * When not using volume, it will loop back - * to not coloring - */ - void CycleArrayIndexForColoring(); - - /** - * Cycle the component in used for rendering - * looping back to direct scalars - */ - void CycleComponentForColoring(); - - /** - * Check coloring is currently valid and return a cycle type to perform if not - */ - CycleType CheckColoring(); - - /** - * Generate a padded metadata description - * using the internal importer - */ - std::string GenerateMetaDataDescription() override; - - /** - * Convert a component index into a string - * If there is a component name defined in the current coloring information, display it. - * Otherwise, use component #index as the default value. - */ - std::string ComponentToString(int component); - - vtkWeakPointer Importer = nullptr; - - vtkNew ScalarBarActor; - bool ScalarBarActorConfigured = false; - - bool GeometryMappersConfigured = false; - bool PointSpritesMappersConfigured = false; - bool VolumePropsAndMappersConfigured = false; - bool ColoringActorsPropertiesConfigured = false; - bool ColoringConfigured = false; - - double Opacity = 1.; - double Roughness = 0.3; - double Metallic = 0.; - double NormalScale = 1.; - double SurfaceColor[3] = { 1., 1., 1. }; - double EmissiveFactor[3] = { 1., 1., 1. }; - std::string TextureMatCap; - std::string TextureBaseColor; - std::string TextureMaterial; - std::string TextureEmissive; - std::string TextureNormal; - - vtkSmartPointer ColorTransferFunction; - double ColorRange[2] = { 0.0, 1.0 }; - bool ColorTransferFunctionConfigured = false; - - bool UseCellColoring = false; - int ArrayIndexForColoring = -1; - int ComponentForColoring = -1; - - bool ScalarBarVisible = false; - bool UsePointSprites = false; - bool UseVolume = false; - bool UseInverseOpacityFunction = false; - - std::optional> UserScalarBarRange; - std::vector Colormap; - - vtkMTimeType ImporterTimeStamp = 0; -}; - -#endif diff --git a/vtkext/public/CMakeLists.txt b/vtkext/public/CMakeLists.txt index 0e488e1fb3..d6bebf6cfc 100644 --- a/vtkext/public/CMakeLists.txt +++ b/vtkext/public/CMakeLists.txt @@ -41,18 +41,10 @@ vtk_module_build( TARGETS_COMPONENT library PACKAGE "f3d_vtkext") -# We need non empty cmake vars for these calls for VTK <= 9.1 -# See https://gitlab.kitware.com/vtk/vtk/-/merge_requests/9084 foreach (module IN LISTS modules) - if(NOT "${f3d_compile_options_public}" STREQUAL "") - vtk_module_compile_options(${module} PUBLIC ${f3d_compile_options_public}) - endif() - if(NOT "${f3d_compile_options_private}" STREQUAL "") - vtk_module_compile_options(${module} PRIVATE ${f3d_compile_options_private}) - endif() - if (NOT "${f3d_link_options_public}" STREQUAL "") - vtk_module_link_options(${module} PUBLIC ${f3d_link_options_public}) - endif() + vtk_module_compile_options(${module} PUBLIC ${f3d_compile_options_public}) + vtk_module_compile_options(${module} PRIVATE ${f3d_compile_options_private}) + vtk_module_link_options(${module} PUBLIC ${f3d_link_options_public}) vtk_module_set_properties(${module} CXX_STANDARD 17) if(F3D_STRICT_BUILD AND MSVC) # There are warnings in VTK related to deprecated features in C++17 diff --git a/vtkext/public/module/vtkF3DBitonicSort.cxx b/vtkext/public/module/vtkF3DBitonicSort.cxx index cd08c78e5b..2b47729916 100644 --- a/vtkext/public/module/vtkF3DBitonicSort.cxx +++ b/vtkext/public/module/vtkF3DBitonicSort.cxx @@ -1,18 +1,24 @@ #include "vtkF3DBitonicSort.h" +#include "vtkF3DBitonicSortFunctions.h" +#include "vtkF3DBitonicSortGlobalDisperseCS.h" +#include "vtkF3DBitonicSortGlobalFlipCS.h" +#include "vtkF3DBitonicSortLocalDisperseCS.h" +#include "vtkF3DBitonicSortLocalSortCS.h" + #include #include #include #include #include #include -#include +#include -#include "vtkF3DBitonicSortFunctions.h" -#include "vtkF3DBitonicSortGlobalDisperseCS.h" -#include "vtkF3DBitonicSortGlobalFlipCS.h" -#include "vtkF3DBitonicSortLocalDisperseCS.h" -#include "vtkF3DBitonicSortLocalSortCS.h" +#if VTK_VERSION_NUMBER >= VTK_VERSION_CHECK(9, 3, 20240914) +#include +#else +#include +#endif #include diff --git a/webassembly/F3DEmscriptenBindings.cxx b/webassembly/F3DEmscriptenBindings.cxx index abf0d77a58..51fe650c18 100644 --- a/webassembly/F3DEmscriptenBindings.cxx +++ b/webassembly/F3DEmscriptenBindings.cxx @@ -2,16 +2,14 @@ #include "engine.h" #include "interactor.h" -#include "loader.h" #include "options.h" +#include "scene.h" #include "window.h" -namespace emscripten -{ -namespace internal +namespace emscripten::internal { template<> -void raw_destructor(f3d::loader* ptr) +void raw_destructor(f3d::scene* ptr) { } @@ -30,90 +28,72 @@ void raw_destructor(f3d::options* ptr) { } } -} -f3d::options* getOptionsPtr(f3d::engine& e) +f3d::options& toggle(f3d::options& o, const std::string& name) { - return &e.getOptions(); + return o.toggle(name); } -f3d::options* toggle(f3d::options& o, const std::string& name) +f3d::options& set_string(f3d::options& o, const std::string& name, const std::string& value) { - return &o.toggle(name); + return o.set(name, value); } -f3d::options* set_string(f3d::options& o, const std::string& name, const std::string& value) +f3d::options& set_integer(f3d::options& o, const std::string& name, int value) { - return &o.set(name, value); + return o.set(name, value); } -f3d::options* set_integer(f3d::options& o, const std::string& name, int value) +f3d::options& set_color(f3d::options& o, const std::string& name, double r, double g, double b) { - return &o.set(name, value); -} -f3d::options* set_color(f3d::options& o, const std::string& name, double r, double g, double b) -{ - return &o.set(name, std::vector{ r, g, b }); + return o.set(name, std::vector{ r, g, b }); } -f3d::loader* getLoaderPtr(f3d::engine& e) -{ - return &e.getLoader(); -} -f3d::loader* loadGeometry(f3d::loader& l, const std::string& p) +f3d::scene& getScenePtr(f3d::engine& e) { - return &l.loadGeometry(p, true); + return e.getScene(); } -f3d::loader* loadScene(f3d::loader& l, const std::string& p) +f3d::scene& add(f3d::scene& l, const std::string& p) { - return &l.loadScene(p); + return l.add(p); } -bool hasGeometryReader(f3d::loader& l, const std::string& p) +bool supports(f3d::scene& l, const std::string& p) { - return l.hasGeometryReader(p); + return l.supports(p); } -bool hasSceneReader(f3d::loader& l, const std::string& p) +f3d::scene* clear(f3d::scene& l) { - return l.hasSceneReader(p); + return &l.clear(); } -f3d::window* getWindowPtr(f3d::engine& e) -{ - return &e.getWindow(); -} -f3d::window* setSize(f3d::window& win, int w, int h) -{ - return &win.setSize(w, h); -} -f3d::window* resetCamera(f3d::window& win) +f3d::window& resetCamera(f3d::window& win) { win.getCamera().resetToBounds(); - return &win; + return win; } -f3d::interactor* getInteractorPtr(f3d::engine& e) +f3d::engine createEngine() { - return &e.getInteractor(); + return f3d::engine::create(); } EMSCRIPTEN_BINDINGS(f3d) { // f3d::options emscripten::class_("Options") - .function("toggle", &toggle, emscripten::allow_raw_pointers()) - .function("set_string", &set_string, emscripten::allow_raw_pointers()) - .function("set_integer", &set_integer, emscripten::allow_raw_pointers()) - .function("set_color", &set_color, emscripten::allow_raw_pointers()); + .function("toggle", &toggle, emscripten::return_value_policy::reference()) + .function("set_string", &set_string, emscripten::return_value_policy::reference()) + .function("set_integer", &set_integer, emscripten::return_value_policy::reference()) + .function("set_color", &set_color, emscripten::return_value_policy::reference()); - // f3d::loader - emscripten::class_("Loader") - .function("loadGeometry", &loadGeometry, emscripten::allow_raw_pointers()) - .function("loadScene", &loadScene, emscripten::allow_raw_pointers()) - .function("hasGeometryReader", &hasGeometryReader, emscripten::allow_raw_pointers()) - .function("hasSceneReader", &hasSceneReader, emscripten::allow_raw_pointers()); + // f3d::scene + emscripten::class_("Scene") + .function("supports", &supports, emscripten::return_value_policy::reference()) + .function("add", &add, emscripten::return_value_policy::reference()) + .function("clear", &clear, emscripten::return_value_policy::reference()); // f3d::window emscripten::class_("Window") - .function("setSize", &setSize, emscripten::allow_raw_pointers()) + .function("setSize", &f3d::window::setSize, emscripten::return_value_policy::reference()) .function("render", &f3d::window::render) - .function("resetCamera", &resetCamera, emscripten::allow_raw_pointers()); + .function("resetCamera", &resetCamera, emscripten::return_value_policy::reference()); // f3d::interactor emscripten::class_("Interactor").function("start", &f3d::interactor::start); @@ -121,10 +101,11 @@ EMSCRIPTEN_BINDINGS(f3d) // f3d::engine emscripten::class_ engine("Engine"); - engine.constructor<>() - .function("getLoader", &getLoaderPtr, emscripten::allow_raw_pointers()) - .function("getWindow", &getWindowPtr, emscripten::allow_raw_pointers()) - .function("getInteractor", &getInteractorPtr, emscripten::allow_raw_pointers()) - .function("getOptions", &getOptionsPtr, emscripten::allow_raw_pointers()) + engine + .class_function("create", &createEngine, emscripten::return_value_policy::take_ownership()) + .function("getScene", &f3d::engine::getScene, emscripten::return_value_policy::reference()) + .function("getWindow", &f3d::engine::getWindow, emscripten::return_value_policy::reference()) + .function("getInteractor", &f3d::engine::getInteractor, emscripten::return_value_policy::reference()) + .function("getOptions", &f3d::engine::getOptions, emscripten::return_value_policy::reference()) .class_function("autoloadPlugins", &f3d::engine::autoloadPlugins); } diff --git a/webassembly/app.html b/webassembly/app.html index 7deb4e5d68..18a2c50c55 100644 --- a/webassembly/app.html +++ b/webassembly/app.html @@ -119,19 +119,16 @@

F3D Web

// automatically load all supported file format readers Module.Engine.autoloadPlugins(); - Module.engineInstance = new Module.Engine(); + Module.engineInstance = Module.Engine.create() const openFile = (name) => { document.getElementById('file-name').innerHTML = name; const filePath = '/' + name; - const loader = Module.engineInstance.getLoader(); - if (loader.hasSceneReader(filePath)) + const scene = Module.engineInstance.getScene(); + if (scene.supports(filePath)) { - loader.loadScene(filePath); - } - else if (loader.hasGeometryReader(filePath)) - { - loader.loadGeometry(filePath); + scene.clear(); + scene.add(filePath); } else { diff --git a/webassembly/build.sh b/webassembly/build.sh index c43698ac89..56fb59ed41 100755 --- a/webassembly/build.sh +++ b/webassembly/build.sh @@ -1,5 +1,7 @@ #!/bin/bash +set -euo pipefail + cmake -S /src -B /build \ -DBUILD_SHARED_LIBS=OFF \ -DF3D_STRICT_BUILD=ON \