diff --git a/lib/mmsolverlibs/.cargo/config.toml b/.cargo/config.toml similarity index 100% rename from lib/mmsolverlibs/.cargo/config.toml rename to .cargo/config.toml diff --git a/.github/workflows/build_and_deploy_docs.yml b/.github/workflows/build_and_deploy_docs.yml index cf1459c65..7b4a8dee3 100644 --- a/.github/workflows/build_and_deploy_docs.yml +++ b/.github/workflows/build_and_deploy_docs.yml @@ -25,7 +25,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r share/requirements-doc.txt + pip install -r share/python_requirements/requirements-github-build-and-deploy-docs.txt - name: Building Sphinx Documentation run: | diff --git a/.github/workflows/lint_code.yml b/.github/workflows/lint_code.yml index 165a63671..768ec35e3 100644 --- a/.github/workflows/lint_code.yml +++ b/.github/workflows/lint_code.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.7"] + python-version: ["3.9"] steps: - uses: actions/checkout@v3 @@ -19,15 +19,11 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install -r share/requirements-dev.txt + pip install -r share/python_requirements/requirements-github-lint-code.txt - - name: Analysing the Python code with flake8 (Hard Error) + - name: Analysing the Python code with ruff (Hard Error) run: | - ./scripts/python_linter_run_flake8_errors_only.bash - - - name: Check Python Formatting - run: | - ./scripts/python_formatter_run_black_check.bash + ./scripts/python_linter_run_ruff.bash - name: Install Clang-Format run: | diff --git a/.gitignore b/.gitignore index ed002761b..bc80187da 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ #* *~ *.bak +*.orig *.pyc *cppcheck-build-* cmake-build-* diff --git a/BUILD.md b/BUILD.md index e3b5c1e8a..a85d42145 100644 --- a/BUILD.md +++ b/BUILD.md @@ -87,7 +87,8 @@ mmSolver v0.4.0 a C++ compiler with at least C++11 is required. - [Visual Studio 2015 update 3 (MSVC 14.0)](https://visualstudio.microsoft.com/downloads/) (Maya 2018 and 2019) - [Visual Studio 2017 (MSVC 15.0)](https://visualstudio.microsoft.com/downloads/) (Maya 2020) - [Visual Studio 2019 (MSVC 16.0)](https://visualstudio.microsoft.com/downloads/) (Maya 2022 and 2023) - - [Visual Studio 2019 (MSVC 19.0)](https://visualstudio.microsoft.com/downloads/) (Maya 2024) + - [Visual Studio 2022 (MSVC 17.0)](https://visualstudio.microsoft.com/downloads/) (Maya 2024) + - [Visual Studio 2022 (MSVC 17.8.3+)](https://visualstudio.microsoft.com/downloads/) (Maya 2025) ## Rust diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e0645923..8b19b6170 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,38 +33,23 @@ cmake_policy(SET CMP0074 NEW) # https://cmake.org/cmake/help/latest/policy/CMP0048.html cmake_policy(SET CMP0048 NEW) -# # Changes how timestamps of downloaded files with -# # ExternalProject_Add() are set. -# # -# # https://cmake.org/cmake/help/latest/policy/CMP0135.html -# cmake_policy(SET CMP0135 NEW) - # Honor visibility properties for all target types. # # https://cmake.org/cmake/help/latest/policy/CMP0063.html cmake_policy(SET CMP0063 NEW) +if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") + # Changes how timestamps of downloaded files with + # ExternalProject_Add() are set. + # + # https://cmake.org/cmake/help/latest/policy/CMP0135.html + cmake_policy(SET CMP0135 NEW) +endif() + # Do not allow using GNU extensions (such as '-std=g++11'), because # it's not compatible with Maya. set(CXX_EXTENSIONS OFF) -# Use the older C++11 ABI for std::string and std::list, to be -# compatible with RHEL/CentOS 7, Maya and the VFX Platform. -# -# https://vfxplatform.com/#footnote-gcc6 -# -# TODO: In VFX Platform CY2023, and the move to RHEL 8 or RHEL 9, -# the new default is to use "_GLIBCXX_USE_CXX11_ABI=1". -# -# https://vfxplatform.com/#footnote-gcc9 -# -# TODO: Add a flag for use in RHEL 8/9 and Rocky Linux, so we can -# enable/disable. -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GLIBCXX_USE_CXX11_ABI=0") -endif() - # For Windows, allow paths of more than 250 to avoid build issues # arrising on Windows with very long file paths. if(NOT DEFINED CMAKE_OBJECT_PATH_MAX) @@ -76,11 +61,10 @@ project(mayaMatchMoveSolver) set(PROJECT_VERSION_MAJOR 0) set(PROJECT_VERSION_MINOR 5) set(PROJECT_VERSION_PATCH 0) -set(PROJECT_VERSION_TWEAK beta4) -set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}.${PROJECT_VERSION_TWEAK}") +set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") set(PROJECT_HOMEPAGE_URL "https://github.com/david-cattermole/mayaMatchMoveSolver") set(PROJECT_DESCRIPTION "Bundle Adjustment solver for MatchMove tasks in Autodesk Maya.") -set(PROJECT_AUTHOR "David Cattermole and others (see AUTHORS.txt file)") +set(PROJECT_AUTHOR "David Cattermole, Patcha Saheb Binginapalli, and others (see AUTHORS.txt file)") set(PROJECT_COPYRIGHT "2018-2024, David Cattermole, Anil Reddy, Kazuma Tonegawa, Patcha Saheb Binginapalli.") enable_testing() diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..18c709cbd --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1207 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anyhow" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" + +[[package]] +name = "approx" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" +dependencies = [ + "num-traits", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bstr" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bytemuck" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d9e0b4957f635b8d3da819d0db5603620467ecf1f692d22a8c2717ce27e6d8" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "cxx" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54ccead7d199d584d139148b04b4a368d1ec7556a1d9ea2548febb1b9d49f9a4" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65777e06cc48f0cb0152024c77d6cf9e4bdb4408e7b48bea993d42fa0f5b02b6" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98532a60dedaebc4848cb2cba5023337cc9ea3af16a5b062633fabfd9f18fb60" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "exr" +version = "1.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fastapprox" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dfa3c0fd35278e839805680f4c2f673ca71eb91068115b4a611e71429bc0c46" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi 0.4.0", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + +[[package]] +name = "libc" +version = "0.2.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d240c6f7e1ba3a28b0249f774e6a9dd0175054b52dfbb61b16eb8505c3785c9" +dependencies = [ + "cc", +] + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "matrixmultiply" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a" +dependencies = [ + "autocfg", + "rawpointer", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "mmcore_cppbind" +version = "0.1.0" +dependencies = [ + "cxx", + "mmcore_rust", +] + +[[package]] +name = "mmcore_rust" +version = "0.1.0" +dependencies = [ + "log", + "shellexpand", +] + +[[package]] +name = "mmimage_cppbind" +version = "0.1.0" +dependencies = [ + "cxx", + "mmimage_rust", +] + +[[package]] +name = "mmimage_rust" +version = "0.1.0" +dependencies = [ + "anyhow", + "exr", + "log", + "num", +] + +[[package]] +name = "mmlens_cppbind" +version = "0.1.0" +dependencies = [ + "anyhow", + "cxx", + "num_cpus", + "rayon", + "rustc-hash", + "smallvec", +] + +[[package]] +name = "mmscenegraph_cppbind" +version = "0.1.0" +dependencies = [ + "cxx", + "mmscenegraph_rust", +] + +[[package]] +name = "mmscenegraph_rust" +version = "0.1.0" +dependencies = [ + "approx", + "criterion", + "fastapprox", + "log", + "nalgebra", + "num-traits", + "petgraph", + "rand", + "rustc-hash", +] + +[[package]] +name = "mmsolverlibs_cppbind" +version = "0.1.0" +dependencies = [ + "mmcore_cppbind", + "mmimage_cppbind", + "mmlens_cppbind", + "mmscenegraph_cppbind", +] + +[[package]] +name = "nalgebra" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c4b5f057b303842cf3262c27e465f4c303572e7f6b0648f60e16248ac3397f4" +dependencies = [ + "approx", + "matrixmultiply", + "num-complex", + "num-rational", + "num-traits", + "simba", + "typenum", +] + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.9", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "oorandom" +version = "11.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" +dependencies = [ + "memchr", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "safe_arch" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3460605018fdc9612bce72735cba0d27efbcd9904780d44c7e3a9948f96148a" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "shellexpand" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" +dependencies = [ + "bstr", + "dirs", + "os_str_bytes", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simba" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a386a501cd104797982c15ae17aafe8b9261315b5d07e3ec803f2ea26be0fa" +dependencies = [ + "approx", + "num-complex", + "num-traits", + "paste", + "wide", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "syn" +version = "2.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wide" +version = "0.7.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b828f995bf1e9622031f8009f8481a85406ce1f4d4588ff746d872043e855690" +dependencies = [ + "bytemuck", + "safe_arch", +] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..ce6635552 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,67 @@ +[workspace] +resolver = "2" +members = [ + "lib/cppbind/mmcore", + "lib/cppbind/mmimage", + "lib/cppbind/mmlens", + "lib/cppbind/mmscenegraph", + "lib/mmsolverlibs", + "lib/rust/mmcore", + "lib/rust/mmimage", + "lib/rust/mmscenegraph", +] + +[workspace.package] +version = "0.1.0" +authors = ["david-cattermole "] +edition = "2018" +publish = false + +[workspace.dependencies] +anyhow = "1.0.89" +approx = "0.5.1" +criterion = { version = "0.5.1", default-features = false, features = ["html_reports"] } +exr = "1.72.0" +fastapprox = "0.3.1" +log = "0.4.22" +nalgebra = { version = "0.33.0", default-features = false, features = ["std", "matrixmultiply"] } +num = "0.4.3" +num-traits = "0.2" +num_cpus = "1.16.0" +petgraph = { version = "0.6", default-features = false, features = ["stable_graph"] } +rand = { version = "0.8.5", default-features = false, features = ["std", "alloc", "small_rng"] } +rayon = "1.10.0" +rustc-hash = "2.0.0" +shellexpand = { version = "3.1", default-features = false, features = ["full"] } +smallvec = "1.13.2" + +# CXX is used to build C++ bindings. +# +# NOTE: When changing this version number ensure to also update the +# installed 'cxxbridge-cmd' version so it stays in sync; Update it +# here: './scripts/internal/build_mmSolverLibs_*.*' +cxx = "=1.0.128" + +# Crates defined inside this project. +mmcore_cppbind = { path = "./lib/cppbind/mmcore" } +mmcore_rust = { path = "./lib/rust/mmcore" } +mmimage_cppbind = { path = "./lib/cppbind/mmimage" } +mmimage_rust = { path = "./lib/rust/mmimage" } +mmlens_cppbind = { path = "./lib/cppbind/mmlens" } +mmlens_rust = { path = "./lib/rust/mmlens" } +mmscenegraph_cppbind = { path = "./lib/cppbind/mmscenegraph" } +mmscenegraph_rust = { path = "./lib/rust/mmscenegraph/" } + +[profile.release] +opt-level = 3 +rpath = false +codegen-units = 1 + +# 'lto = true' Performs "fat" LTO which attempts to perform +# optimizations across all crates within the dependency graph. +lto = true + +# NOTE: If we use 'panic = "abort"' then we are unable to produce tests. +# # https://github.com/rust-lang/cargo/issues/6313 +# +# panic = "abort" diff --git a/LICENSE b/LICENSE index 787cb36bd..6bcb081de 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ Copyright (C) 2018-2024 David Cattermole. Copyright (C) 2019 Anil Reddy. Copyright (C) 2020-2022 Kazuma Tonegawa. -Copyright (C) 2021-2023 Patcha Saheb Binginapalli. +Copyright (C) 2021-2024 Patcha Saheb Binginapalli. This software, mmSolver, is licensed under the GNU LGPL v3 license. diff --git a/README.md b/README.md index f687e1cb4..d15a2748a 100644 --- a/README.md +++ b/README.md @@ -57,6 +57,7 @@ changes. | Releases | Description | | --------------------------------------------------------------------------------------- | -------------------------------------------------- | +| [v0.4.9](https://github.com/david-cattermole/mayaMatchMoveSolver/releases/tag/v0.4.9) | Bug fix for Surface Cluster. | | [v0.4.8](https://github.com/david-cattermole/mayaMatchMoveSolver/releases/tag/v0.4.8) | Add Create Rivet and Surface Cluster tools. | | [v0.4.7](https://github.com/david-cattermole/mayaMatchMoveSolver/releases/tag/v0.4.7) | Bug fix for "Convert to Marker" tool. | | [v0.4.6](https://github.com/david-cattermole/mayaMatchMoveSolver/releases/tag/v0.4.6) | Bug fix for solver and minor features. | diff --git a/docs/source/download.rst b/docs/source/download.rst index 88c6b593c..f17f56c24 100644 --- a/docs/source/download.rst +++ b/docs/source/download.rst @@ -15,47 +15,47 @@ Download the latest release **mmSolver v0.4.9**: * - Linux - Maya 2019 - - `link `_ + - `link `_ * - Linux - Maya 2020 - - `link `_ + - `link `_ * - Linux - Maya 2022 - - `link `_ + - `link `_ * - Linux - Maya 2023 - - `link `_ + - `link `_ * - Linux - Maya 2024 - - `link `_ + - `link `_ * - Windows - Maya 2018 - - `link `_ + - `link `_ * - Windows - Maya 2019 - - `link `_ + - `link `_ * - Windows - Maya 2020 - - `link `_ + - `link `_ * - Windows - Maya 2022 - - `link `_ + - `link `_ * - Windows - Maya 2023 - - `link `_ + - `link `_ * - Windows - Maya 2024 - - `link `_ + - `link `_ Older versions and full release notes can be found on the GitHub releases_ page. diff --git a/docs/source/images/tools_image_cache_preferences_monitor.png b/docs/source/images/tools_image_cache_preferences_monitor.png new file mode 100644 index 000000000..bf5bea179 Binary files /dev/null and b/docs/source/images/tools_image_cache_preferences_monitor.png differ diff --git a/docs/source/images/tools_image_cache_preferences_ui.png b/docs/source/images/tools_image_cache_preferences_ui.png new file mode 100644 index 000000000..c2a1fb13e Binary files /dev/null and b/docs/source/images/tools_image_cache_preferences_ui.png differ diff --git a/docs/source/images/tools_image_plane_attributes_display.png b/docs/source/images/tools_image_plane_attributes_display.png new file mode 100644 index 000000000..6381ff9af Binary files /dev/null and b/docs/source/images/tools_image_plane_attributes_display.png differ diff --git a/docs/source/images/tools_image_plane_attributes_extended_image_details.png b/docs/source/images/tools_image_plane_attributes_extended_image_details.png new file mode 100644 index 000000000..a0cac1e03 Binary files /dev/null and b/docs/source/images/tools_image_plane_attributes_extended_image_details.png differ diff --git a/docs/source/images/tools_image_plane_attributes_hud.png b/docs/source/images/tools_image_plane_attributes_hud.png new file mode 100644 index 000000000..ff5e66fdc Binary files /dev/null and b/docs/source/images/tools_image_plane_attributes_hud.png differ diff --git a/docs/source/images/tools_image_plane_attributes_image_cache.png b/docs/source/images/tools_image_plane_attributes_image_cache.png new file mode 100644 index 000000000..afb5dcfb3 Binary files /dev/null and b/docs/source/images/tools_image_plane_attributes_image_cache.png differ diff --git a/docs/source/images/tools_image_plane_attributes_image_sequence.png b/docs/source/images/tools_image_plane_attributes_image_sequence.png new file mode 100644 index 000000000..a46bcb90e Binary files /dev/null and b/docs/source/images/tools_image_plane_attributes_image_sequence.png differ diff --git a/docs/source/images/tools_image_plane_attributes_misc.png b/docs/source/images/tools_image_plane_attributes_misc.png new file mode 100644 index 000000000..a830e0f81 Binary files /dev/null and b/docs/source/images/tools_image_plane_attributes_misc.png differ diff --git a/docs/source/images/tools_image_plane_attributes_nodes.png b/docs/source/images/tools_image_plane_attributes_nodes.png new file mode 100644 index 000000000..1364b8a46 Binary files /dev/null and b/docs/source/images/tools_image_plane_attributes_nodes.png differ diff --git a/docs/source/images/tools_mesh_from_points_window.png b/docs/source/images/tools_mesh_from_points_window.png new file mode 100644 index 000000000..9659d94db Binary files /dev/null and b/docs/source/images/tools_mesh_from_points_window.png differ diff --git a/docs/source/images/tools_renderer_display_layers.png b/docs/source/images/tools_renderer_display_layers.png deleted file mode 100644 index 7bd977c00..000000000 Binary files a/docs/source/images/tools_renderer_display_layers.png and /dev/null differ diff --git a/docs/source/images/tools_renderer_globals_silhouette.png b/docs/source/images/tools_renderer_globals_silhouette.png new file mode 100644 index 000000000..aece5949c Binary files /dev/null and b/docs/source/images/tools_renderer_globals_silhouette.png differ diff --git a/docs/source/images/tools_renderer_menu.png b/docs/source/images/tools_renderer_menu.png index 659f4cffe..bfc705cb8 100644 Binary files a/docs/source/images/tools_renderer_menu.png and b/docs/source/images/tools_renderer_menu.png differ diff --git a/docs/source/images/tools_renderer_menu_silhouette.png b/docs/source/images/tools_renderer_menu_silhouette.png new file mode 100644 index 000000000..6a289f832 Binary files /dev/null and b/docs/source/images/tools_renderer_menu_silhouette.png differ diff --git a/docs/source/images/tools_renderer_menu_standard.png b/docs/source/images/tools_renderer_menu_standard.png new file mode 100644 index 000000000..4005a4295 Binary files /dev/null and b/docs/source/images/tools_renderer_menu_standard.png differ diff --git a/docs/source/images/tools_renderer_silhouette_viewport.png b/docs/source/images/tools_renderer_silhouette_viewport.png new file mode 100644 index 000000000..0e7eb431f Binary files /dev/null and b/docs/source/images/tools_renderer_silhouette_viewport.png differ diff --git a/docs/source/images/tools_shelf_icons_all.png b/docs/source/images/tools_shelf_icons_all.png index 0b44ff468..77471dae9 100644 Binary files a/docs/source/images/tools_shelf_icons_all.png and b/docs/source/images/tools_shelf_icons_all.png differ diff --git a/docs/source/mmSolver.tools.calibratecamera.rst b/docs/source/mmSolver.tools.calibratecamera.rst new file mode 100644 index 000000000..99a77d0c8 --- /dev/null +++ b/docs/source/mmSolver.tools.calibratecamera.rst @@ -0,0 +1,27 @@ +mmSolver.tools.calibratecamera +============================== + +.. automodule:: mmSolver.tools.calibratecamera + :members: + :undoc-members: + +Tools ++++++ + +.. automodule:: mmSolver.tools.calibratecamera.tool + :members: + :undoc-members: + +Library ++++++++ + +.. automodule:: mmSolver.tools.calibratecamera.lib + :members: + :undoc-members: + +Constants ++++++++++ + +.. automodule:: mmSolver.tools.calibratecamera.constant + :members: + :undoc-members: diff --git a/docs/source/mmSolver.tools.imagecache.rst b/docs/source/mmSolver.tools.imagecache.rst new file mode 100644 index 000000000..a5fc63f5c --- /dev/null +++ b/docs/source/mmSolver.tools.imagecache.rst @@ -0,0 +1,54 @@ +========================= +mmSolver.tools.imagecache +========================= + +.. automodule:: mmSolver.tools.imagecache + :members: + :undoc-members: + +Initialize +++++++++++ + +.. automodule:: mmSolver.tools.imagecache.initialize + :members: + :undoc-members: + +Tools ++++++ + +.. automodule:: mmSolver.tools.imagecache.tool + :members: + :undoc-members: + +Library ++++++++ + +.. automodule:: mmSolver.tools.imagecache.lib + :members: + :undoc-members: + +Configuration ++++++++++++++ + +.. automodule:: mmSolver.tools.imagecache.config_file + :members: + :undoc-members: + +.. automodule:: mmSolver.tools.imagecache.config + :members: + :undoc-members: + +.. automodule:: mmSolver.tools.imagecache.config_scene + :members: + :undoc-members: + +.. automodule:: mmSolver.tools.imagecache.config_utils + :members: + :undoc-members: + +Constants ++++++++++ + +.. automodule:: mmSolver.tools.imagecache.constant + :members: + :undoc-members: diff --git a/docs/source/mmSolver.tools.imagecacheprefs.rst b/docs/source/mmSolver.tools.imagecacheprefs.rst new file mode 100644 index 000000000..998f5f19f --- /dev/null +++ b/docs/source/mmSolver.tools.imagecacheprefs.rst @@ -0,0 +1,35 @@ +============================== +mmSolver.tools.imagecacheprefs +============================== + +.. automodule:: mmSolver.tools.imagecacheprefs + :members: + :undoc-members: + +Tools ++++++ + +.. automodule:: mmSolver.tools.imagecacheprefs.tool + :members: + :undoc-members: + +UI - Layout ++++++++++++ + +.. automodule:: mmSolver.tools.imagecacheprefs.ui.imagecacheprefs_layout + :members: + :undoc-members: + +UI - Window ++++++++++++ + +.. automodule:: mmSolver.tools.imagecacheprefs.ui.imagecacheprefs_window + :members: + :undoc-members: + +Constants ++++++++++ + +.. automodule:: mmSolver.tools.imagecacheprefs.constant + :members: + :undoc-members: diff --git a/docs/source/mmSolver.tools.meshfrompoints.rst b/docs/source/mmSolver.tools.meshfrompoints.rst new file mode 100644 index 000000000..92387f7c0 --- /dev/null +++ b/docs/source/mmSolver.tools.meshfrompoints.rst @@ -0,0 +1,49 @@ +============================= +mmSolver.tools.meshfrompoints +============================= + +.. automodule:: mmSolver.tools.meshfrompoints + :members: + :undoc-members: + +Tools ++++++ + +.. automodule:: mmSolver.tools.meshfrompoints.tool + :members: + :undoc-members: + +Library ++++++++ + +.. automodule:: mmSolver.tools.meshfrompoints.lib + :members: + :undoc-members: + +Delaunator +++++++++++ + +.. automodule:: mmSolver.tools.meshfrompoints.delaunator + :members: + :undoc-members: + +UI - Layout ++++++++++++ + +.. automodule:: mmSolver.tools.meshfrompoints.ui.meshfrompoints_layout + :members: + :undoc-members: + +UI - Window ++++++++++++ + +.. automodule:: mmSolver.tools.meshfrompoints.ui.meshfrompoints_window + :members: + :undoc-members: + +Constants ++++++++++ + +.. automodule:: mmSolver.tools.meshfrompoints.constant + :members: + :undoc-members: diff --git a/docs/source/mmSolver.tools.rst b/docs/source/mmSolver.tools.rst index 9dbd951c6..61d3f0760 100644 --- a/docs/source/mmSolver.tools.rst +++ b/docs/source/mmSolver.tools.rst @@ -11,6 +11,7 @@ mmSolver.tools mmSolver.tools.attachbundletocurve mmSolver.tools.attributebake mmSolver.tools.averagemarker + mmSolver.tools.calibratecamera mmSolver.tools.cameraaim mmSolver.tools.cameracontextmenu mmSolver.tools.cameraobjectscaleadjust @@ -39,6 +40,7 @@ mmSolver.tools mmSolver.tools.loadmarker mmSolver.tools.markerbundlerename mmSolver.tools.markerbundlerenamewithmetadata + mmSolver.tools.meshfrompoints mmSolver.tools.mmhotkeyset mmSolver.tools.mmmenu mmSolver.tools.mmshelf @@ -59,6 +61,7 @@ mmSolver.tools mmSolver.tools.selection mmSolver.tools.setattributedetails mmSolver.tools.setcameraoriginframe + mmSolver.tools.setmeshholdouts mmSolver.tools.setobjectcolour mmSolver.tools.showdeviationcurves mmSolver.tools.smoothkeyframes @@ -71,6 +74,10 @@ mmSolver.tools mmSolver.tools.togglecameradistort mmSolver.tools.togglelinelock mmSolver.tools.togglemarkerlock + mmSolver.tools.toggleobjectmotiontrail + mmSolver.tools.toggleviewportctrls + mmSolver.tools.toggleviewportgeom + mmSolver.tools.toggleviewportimgplns mmSolver.tools.triangulatebundle mmSolver.tools.undoredoscene mmSolver.tools.userpreferences diff --git a/docs/source/mmSolver.tools.setmeshholdouts.rst b/docs/source/mmSolver.tools.setmeshholdouts.rst new file mode 100644 index 000000000..4f274efdb --- /dev/null +++ b/docs/source/mmSolver.tools.setmeshholdouts.rst @@ -0,0 +1,15 @@ +============================== +mmSolver.tools.setmeshholdouts +============================== + +.. automodule:: mmSolver.tools.setmeshholdouts + :members: + :undoc-members: + +Tools ++++++ + +.. automodule:: mmSolver.tools.setmeshholdouts.tool + :members: + :undoc-members: + diff --git a/docs/source/mmSolver.tools.toggleobjectmotiontrail.rst b/docs/source/mmSolver.tools.toggleobjectmotiontrail.rst new file mode 100644 index 000000000..9c2908f95 --- /dev/null +++ b/docs/source/mmSolver.tools.toggleobjectmotiontrail.rst @@ -0,0 +1,21 @@ +====================================== +mmSolver.tools.toggleobjectmotiontrail +====================================== + +.. automodule:: mmSolver.tools.toggleobjectmotiontrail + :members: + :undoc-members: + +Tools ++++++ + +.. automodule:: mmSolver.tools.toggleobjectmotiontrail.tool + :members: + :undoc-members: + +Library ++++++++ + +.. automodule:: mmSolver.tools.toggleobjectmotiontrail.lib + :members: + :undoc-members: diff --git a/docs/source/mmSolver.tools.toggleviewportctrls.rst b/docs/source/mmSolver.tools.toggleviewportctrls.rst new file mode 100644 index 000000000..cee254f67 --- /dev/null +++ b/docs/source/mmSolver.tools.toggleviewportctrls.rst @@ -0,0 +1,21 @@ +================================== +mmSolver.tools.toggleviewportctrls +================================== + +.. automodule:: mmSolver.tools.toggleviewportctrls + :members: + :undoc-members: + +Tools ++++++ + +.. automodule:: mmSolver.tools.toggleviewportctrls.tool + :members: + :undoc-members: + +Library ++++++++ + +.. automodule:: mmSolver.tools.toggleviewportctrls.lib + :members: + :undoc-members: diff --git a/docs/source/mmSolver.tools.toggleviewportgeom.rst b/docs/source/mmSolver.tools.toggleviewportgeom.rst new file mode 100644 index 000000000..15d5d9f08 --- /dev/null +++ b/docs/source/mmSolver.tools.toggleviewportgeom.rst @@ -0,0 +1,21 @@ +================================= +mmSolver.tools.toggleviewportgeom +================================= + +.. automodule:: mmSolver.tools.toggleviewportgeom + :members: + :undoc-members: + +Tools ++++++ + +.. automodule:: mmSolver.tools.toggleviewportgeom.tool + :members: + :undoc-members: + +Library ++++++++ + +.. automodule:: mmSolver.tools.toggleviewportgeom.lib + :members: + :undoc-members: diff --git a/docs/source/mmSolver.tools.toggleviewportimgplns.rst b/docs/source/mmSolver.tools.toggleviewportimgplns.rst new file mode 100644 index 000000000..261b3c95c --- /dev/null +++ b/docs/source/mmSolver.tools.toggleviewportimgplns.rst @@ -0,0 +1,21 @@ +================================== +mmSolver.tools.toggleviewportimgplns +================================== + +.. automodule:: mmSolver.tools.toggleviewportimgplns + :members: + :undoc-members: + +Tools ++++++ + +.. automodule:: mmSolver.tools.toggleviewportimgplns.tool + :members: + :undoc-members: + +Library ++++++++ + +.. automodule:: mmSolver.tools.toggleviewportimgplns.lib + :members: + :undoc-members: diff --git a/docs/source/mmSolver.utils.rst b/docs/source/mmSolver.utils.rst index bc458801c..8c31df4d2 100644 --- a/docs/source/mmSolver.utils.rst +++ b/docs/source/mmSolver.utils.rst @@ -1,3 +1,5 @@ +.. _mmsolver-utils-ref: + mmSolver.utils ============== @@ -145,6 +147,13 @@ Node Affects :members: :undoc-members: +Math +++++ + +.. automodule:: mmSolver.utils.math + :members: + :undoc-members: + Python Compatibility ++++++++++++++++++++ @@ -230,6 +239,8 @@ Undo :members: :undoc-members: +.. _mmsolver-utils-viewport-ref: + Viewport ++++++++ diff --git a/docs/source/nodes_display.rst b/docs/source/nodes_display.rst index 31bfd0a5d..3c75712aa 100644 --- a/docs/source/nodes_display.rst +++ b/docs/source/nodes_display.rst @@ -3,7 +3,12 @@ Display Nodes *To be written.* -``mmRenderGlobals`` Nodes +``mmRenderGlobalsStandard`` Nodes ++++++++++++++++++++++++++ + +*To be written.* + +``mmRenderGlobalsSilhouette`` Nodes +++++++++++++++++++++++++ *To be written.* diff --git a/docs/source/nodes_imageplane.rst b/docs/source/nodes_imageplane.rst index b624f8758..9399f4d4a 100644 --- a/docs/source/nodes_imageplane.rst +++ b/docs/source/nodes_imageplane.rst @@ -9,6 +9,16 @@ Image Plane Nodes *To be written.* ``mmImagePlaneShape`` Node -+++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++ + +*This node type is deprecated.* + +``mmImagePlaneShape2`` Node ++++++++++++++++++++++++++++ + +*To be written.* + +``mmImageSequenceFrameLogic`` Node +++++++++++++++++++++++++++++++++++ *To be written.* diff --git a/docs/source/tools.rst b/docs/source/tools.rst index ca1fb6f36..de69dd183 100644 --- a/docs/source/tools.rst +++ b/docs/source/tools.rst @@ -28,6 +28,7 @@ Pages dedicated to specific tools: tools_linetools tools_displaytools tools_zdepthtools + tools_meshtools tools_generaltools tools_inputoutput tools_hotkeys diff --git a/docs/source/tools_createnode.rst b/docs/source/tools_createnode.rst index c4d9503b4..515f2b0ad 100644 --- a/docs/source/tools_createnode.rst +++ b/docs/source/tools_createnode.rst @@ -121,12 +121,9 @@ Run this Python command: Create ImagePlane ----------------- -Create a MM Solver ImagePlane node, with the chosen image file -(sequence). - -.. note:: The image plane supports any image format supported by - Maya's ``file`` node, but can be buggy when reading image - sequences. +Create a :ref:`MM ImagePlane ` node, with the chosen +image file (sequence) used to display a flat plane with an image +texture in the Maya 3D scene. .. figure:: images/tools_create_mm_image_plane.png :alt: MM Image Plane @@ -145,9 +142,374 @@ Usage: ``file.#.ext``), it will be detected and the full image sequence will be loaded. -Run this Python command: +To create an image plane, you can run this Python command: .. code:: python import mmSolver.tools.createimageplane.tool as tool tool.main() + +.. _imageplane-ref: + +MM ImagePlane +------------- + +The `MM ImagePlane` node is an improved Image Plane, designed for +MatchMove tasks, and can be created with the :ref:`Create ImagePlane +` tool. + +Key features: + +- **Multiple Image Slots**; Switch seamlessly between 4 different + image sequences loaded onto the `MM ImagePlane`. + +- **Memory resource control**; The :ref:`Image Cache + ` is used to limit and detail memory + usage for the `MM ImagePlane` allowing greater control than the + native Maya ImagePlane. + +- **Real-Time Lens Distortion**; Lenses added to the camera (with + :ref:`Create Lens ` tool) will distort the `MM + ImagePlane` in real-time as attributes update. + +- **Frame Range controls and details**; Override the first frame of an + image sequence to any other frame, and see the output frame number + easily for debugging. + +- **Enhanced Display controls**; Adjust the exposure, gamma, + saturation and soft-clip of the input image data, and display + individual colour channels. + +.. _imageplane-display-attributes-ref: + +Display Attributes +~~~~~~~~~~~~~~~~~~ + +`Display Attributes` control how the `MM ImagePlane` looks in the +viewport; use these attributes to make the image easier to see. + +.. figure:: images/tools_image_plane_attributes_display.png + :alt: MM ImagePlane Display attributes. + :align: center + :width: 80% + +.. list-table:: Display Attributes + :widths: auto + :header-rows: 1 + + * - Name + - Description + + * - Visible To Camera Only + - When enabled, this imagePlane node is not visible when viewed + by any other camera (including ``persp`` camera). + + * - Gain + - The color of multiplier of the displayed image. + + * - Exposure + - Relative exposure brightness (measured in `Stops`) applied to + the image. Negative numbers darken the image, positive values + increase the brightness. + + * - Gamma + - Brighten or darken the mid-tones of the image, without + affecting the shadows or highlights as much. Do not use this + slider to approximate a Color Space of 2.2 - use the `Input + Color Space` attribute. + + * - Saturation + - Adjust the color saturation of the image; 0.0 makes the image + black and white, 1.0+ numbers make the image colors very + vibrant. + + * - SoftClip + - Reduce the image highlights to flatten the bright areas of an image. + + * - Alpha Gain + - Alpha channel multiplier. Controls the opacity/transparency of + the image. + + * - Input Color Space + - The color space saved into the image. Left-click to choose + color space from menu. + + * - Image Ignore Alpha + - If the image contains an alpha channel, you can ignore it here, + and treat the image as fully opaque. + + * - Display Channel + - Which channel the image would you like to display? ``RGBA`` + (full), ``RGB`` (color only, disables alpha), ``Red`` only, + ``Green`` only, ``Blue`` only, ``Alpha`` only, or ``Luminance`` + only? + +.. _imageplane-image-sequence-attributes-ref: + +Image Sequence Attributes +~~~~~~~~~~~~~~~~~~~~~~~~~ + +`Image Sequence` attributes define the image that is loaded and the +frame number used for image look-up. + +`MM ImagePlane` is designed to contain multiple image sequence +`Slots`, so that users can easily swap between different images. For +example, you may have an EXR image sequence, a lower-resolution JPEG +image sequence and a color adjusted image sequence to better see +low-contrast image details - you can easily swap between all of these +image sequences by changing the `Image Sequence Slot` attribute. + +.. figure:: images/tools_image_plane_attributes_image_sequence.png + :alt: MM ImagePlane Image Sequence attributes. + :align: center + :width: 80% + +.. list-table:: Image Sequence Attributes + :widths: auto + :header-rows: 1 + + * - Name + - Description + + * - Image Sequence Slot + - Change the `Image Sequence Slot`, between the 4 image sequences + available; ``Main``, ``Alternate 1``, etc. + + * - Image Sequence (Main) + - The "Main" image sequence, and the default. + + * - Image Sequence (Alt 1) + - First alternate image sequence. + + * - Image Sequence (Alt 2) + - Second alternate image sequence. + + * - Image Sequence (Alt 3) + - Third alternate image sequence. + + * - Image Width + - Resolution width of loaded image. + + * - Image Height + - Resolution height of loaded image. + + * - Image Pixel Aspect + - Pixel-aspect ratio loaded image. This changes the way the + physical image plane size is calculated. For example, when + loading raw un-squeezed Anamorphic images, set this value to + ``2.0``. + + * - Start Frame + - The start frame of the loaded image sequence. Will be ``0`` if + an image sequence is not loaded. + + * - End Frame + - The end frame of the loaded image sequence. Will be ``0`` if an + image sequence is not loaded. + + * - Image Sequence Frame + - The current **input** image sequence frame number. + + * - First Frame + - What frame is the first frame in the image sequence? `MM + ImagePlane` Offsets the `Start Frame` of the image sequence so + it is displayed when the Maya timeline is at `First Frame`. + + * - Frame Output + - The current **output** image sequence frame number. + + * - Image Flip (Vertical) + - Flips the image vertically; does *not* flip any asymmetric lens + distortion values. + + * - Image Flop (Horizontal) + - Flops the image horizontally; does *not* flop any asymmetric + lens distortion values. + +.. _imageplane-hud-attributes-ref: + +HUD (Heads-Up Display) Attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +HUD (Heads-Up Display) attributes control the way text is drawn in the +viewport when the `MM ImagePlane` is viewed from it's connected +camera. + +The HUD displays useful information intended to help understanding of +the loaded image and help spot common issues. + +.. figure:: images/tools_image_plane_attributes_hud.png + :alt: MM ImagePlane HUD attributes. + :align: center + :width: 80% + +.. list-table:: HUD Attributes + :widths: auto + :header-rows: 1 + + * - Name + - Description + + * - Draw Hud + - Enable or disable all HUD features. + + * - Draw Camera Size + - Enable or disable the camera size display. + + * - Draw Image Size + - Enable or disable the image size display. + +.. _imageplane-image-cache-attributes-ref: + +Image Cache Attributes +~~~~~~~~~~~~~~~~~~~~~~ + +Provides quick access to see details and control the `Image Cache`, +and clear memory resources. + +See :ref:`Image Cache Preferences ` for +details on how to control the `MM Image Plane` hardware resources +used. + +.. figure:: images/tools_image_plane_attributes_image_cache.png + :alt: MM ImagePlane Image Cache attributes. + :align: center + :width: 80% + +.. list-table:: Image Cache Attributes + :widths: auto + :header-rows: 1 + + * - Name + - Description + + * - Refresh Attribute Editor (button) + - Update the text fields in the attribute editor. + + * - Image Sequence Size + - Breaks down the amount of memory required for the full image + sequence currently loaded; the size of a single frame + multiplied by the image sequence frame count. + + * - GPU Cache Used + - Amount of GPU cache capacity is currently used. + + * - CPU Cache Used + - Amount of CPU cache capacity is currently used. + + * - Total Memory Available + - Amount of memory resources available on the computer. + + * - Clear... (button) + - Left-click to open a menu; clearing all images, clearing memory + from image sequence slots on the current `MM ImagePlane` node. + + * - Image Cache Preferences... (button) + - Left-click to open the :ref:`Image Cache Preferences `. + +.. _imageplane-misc-attributes-ref: + +Miscellaneous Attributes +~~~~~~~~~~~~~~~~~~~~~~~~ + +`Miscellaneous` attributes do not fit into the other categories and +are not commonly adjusted, but provided for rare use cases. + +.. figure:: images/tools_image_plane_attributes_misc.png + :alt: MM ImagePlane Miscellaneous attributes. + :align: center + :width: 80% + +.. list-table:: Miscellaneous Attributes + :widths: auto + :header-rows: 1 + + * - Name + - Description + + * - Mesh Resolution + - Used to adjust the real-time lens distortion quality. You + should adjust the resolution with more polygons when lens + distortion is so extreme that the lens distortion is displayed + is not accurate. Increasing the number also slows-down the + real-time performance. + + * - Output Color Space + - This is the color space that Maya should work with + internally. Left-click to choose color space from menu. + +.. _imageplane-nodes-attributes-ref: + +Nodes Attributes +~~~~~~~~~~~~~~~~ + +These attributes can be used to quickly navigate to the connected +nodes of this `MM ImagePlane` node. + +The node connections cannot be adjusted using this UI. Only adjust +these attributes if you know what you are doing, or want to +experiment. + +.. figure:: images/tools_image_plane_attributes_nodes.png + :alt: MM ImagePlane Nodes attributes. + :align: center + :width: 80% + +.. list-table:: Nodes Attributes + :widths: auto + :header-rows: 1 + + * - Name + - Description + + * - Geometry Node + - Mostly used for debugging. The connected node used to extract + mesh geometry for the `MM ImagePlane` to draw on the + screen. This geometry node may be deformed by lens distortion. + + * - Camera Node + - The image plane is attached to this camera node. + +.. _imageplane-extended-image-details-attributes-ref: + +Extended Image Details Attributes +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`Extended Image Details` explain the internal and low-level details of +the loaded images, and they are used to debug and for scripting tools. + +.. figure:: images/tools_image_plane_attributes_extended_image_details.png + :alt: MM ImagePlane Extended Image Details attributes. + :align: center + :width: 80% + +.. list-table:: Extended Image Details Attributes + :widths: auto + :header-rows: 1 + + * - Name + - Description + + * - Padding + - Image sequence file path frame number padding; For example + ``file.1001.jpg`` (``file.####.jpg``) has a padding of 4. + + * - Image Num Channels + - The number of channels saved in the loaded image; 3 for RGB, 4 + for RGBA. + + * - Image Bytes Per Channel + - The number of bytes that is used for each image channel. 8-bit + is 1-byte; 32-bit is 4-bytes. + + * - Image Size Bytes + - The calculated number of bytes for a single image. This value + is used to make calculations for the :ref:`Image Cache + Attributes ` attributes, + and is mostly intended for debugging and scripting. + + * - Input Color Space + - The string value of the `Input Color Space` of the loaded image. Used for debugging. + + * - Output Color Space + - The string value of the `Output Color Space` of the loaded image. Used for debugging. diff --git a/docs/source/tools_displaytools.rst b/docs/source/tools_displaytools.rst index 6f6f73753..e4e8ada91 100644 --- a/docs/source/tools_displaytools.rst +++ b/docs/source/tools_displaytools.rst @@ -57,6 +57,138 @@ To run the tool, use this Python command: tool.remove() +.. _toggle-viewport-node-types-ref: + +Toggle Viewport Node Types +-------------------------- + +There are a range of `Toggle Viewport *` tools that are used to +hide/show groups of node types. + +Node type groups include: + +- Show/Hide geometry in the current viewport. + +- Show/Hide Locators and NURBS Curves in the current viewport. + +- Show/Hide Image Planes in the current viewport. + +Usage: + +1) Activate a 3D Viewport. + +2) Run tool. + + - The node type visibility will be shown/hidden based on the + current visibility. + +Each different group can be activated with a slightly different Python +command. + + +Toggle Viewport Geometry (Mesh, NURBS, etc): + +.. code:: python + + import mmSolver.tools.toggleviewportgeom.tool as tool + tool.main() + + +Toggle Viewport Controls: (Locators, Curves, etc) + +.. code:: python + + import mmSolver.tools.toggleviewportctrls.tool as tool + tool.main() + + +Toggle Viewport Image Planes (Maya native and MM solver image planes): + +.. code:: python + + import mmSolver.tools.toggleviewportimgplns.tool as tool + tool.main() + + +Alternatively, a user can construct their own custom scripts to +control visibility like so: + +.. code:: python + + import mmSolver.utils.viewport as viewport_utils + model_panel = viewport_utils.get_active_model_panel() + if model_panel: + value = viewport_utils.get_locator_visibility(model_panel) + new_value = not value + viewport_utils.set_locator_visibility(model_panel, new_value) + + +See :ref:`mmSolver.utils.viewport ` +Python module documentation for more help. + +.. _set-mesh-hold-outs-ref: + +Set Mesh Hold-Outs +------------------ + +This tool is used to force mesh nodes to be rendered as hold-out in +the viewport, or not. + +This is similar to assigning a `useBackground` shader, to geometry, +however this tool avoids the need to create a shader, and manage +assignments. + +The tool is split into different individual features which are fairly +self-explanatory: + +- Enable / Disable Hold-Outs on *selected* meshes. + +- Enable / Disable Hold-Outs on *all* Meshes in the scene. + +Usage: + +1) Select meshes (optional) + +2) Run tool. + + - Meshes will be have the Hold-Out attribute enabled / disabled. + + +Each different feature can be activated with a slightly different Python +command. + +Enable Hold-outs on Selected Meshes: + +.. code:: python + + import mmSolver.tools.setmesholdouts.tool as tool + tool.enable_selected_meshes() + + +Disable Hold-outs on Selected Meshes: + +.. code:: python + + import mmSolver.tools.setmesholdouts.tool as tool + tool.disable_selected_meshes() + + +Enable Hold-outs on All Meshes: + +.. code:: python + + import mmSolver.tools.setmesholdouts.tool as tool + tool.enable_all_meshes() + + +Disable Hold-outs on All Meshes: + +.. code:: python + + import mmSolver.tools.setmesholdouts.tool as tool + tool.disable_all_meshes() + + .. _create-sky-dome-tool-ref: Create Horizon / Axis Dome / Sky Dome diff --git a/docs/source/tools_generaltools.rst b/docs/source/tools_generaltools.rst index 3d6a5993f..152b23eb5 100644 --- a/docs/source/tools_generaltools.rst +++ b/docs/source/tools_generaltools.rst @@ -516,6 +516,137 @@ To run the tool, use this Python command: import mmSolver.tools.removesolvernodes.tool as tool tool.main() +.. _image-cache-preferences-ref: + +Image Cache Preferences +----------------------- + +.. figure:: images/tools_image_cache_preferences_ui.png + :alt: Image Cache Preferences window + :align: center + :width: 60% + +The `Image Cache Preferences` control how much memory `MM Solver` is +allowed to use to store image data, and displays real-time information +about memory consumption of the computer hardware resources; both CPU +and GPU memory. + +The :ref:`MM ImagePlane ` uses the Image Cache to +store all image data. + +The `Image Cache Preferences` have *defaults* and *scene override* +options. The *default* options are used when Maya is started, and new +Maya scenes are created. The *scene override* options are used when +the current Maya scene is opened; these only apply to the current Maya +scene and are useful when you wish to adjust a specific scene with +exceptional requirements without adjusting your regular *default* +options. + +.. note:: The `Image Cache Preferences` *only* control the capacity of + images loaded by `MM Solver`. Other types of data in Maya + like geometry and material/surface textures are not + controlled or detailed. + +To run the tool, use this Python command: + +.. code:: python + + import mmSolver.tools.imagecacheprefs.tool as tool + tool.open_window() + +What Image Cache Values Should I Use? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The Image Cache Capacity values are depending on two things; hardware +memory capacity and frame count / image resolution. This section lists +some common wisdom and logic to optimise the use of the `Image Cache`. + +The default `Image Cache Preferences` are intended for a user with a +~200 frame 1080p HD shot, 4 GB of GPU memory and 32 GB CPU memory. The +hardware specifications for a professional workstation in 2024 is +expected to surpass these values, but this assumption is intended to +ensure that lower-spec hardware still performs okay. + +If you don't care about MM ImagePlane playback speed; set the `Image +Cache` capacity to 0%. + +If you want the maximum performance and you have a lot of GPU and CPU +memory, and you only work with one Maya Scene open at once; set the +`Image Cache` capacity to 100% + +If you want to fit the image sequence of an `MM ImagePlane`, look at +the `Image Cache` tab in the Attribute Editor, and set your GPU and +CPU `Image Cache` capacity to just above that number. + +If you are often running out of GPU memory, decrease your GPU memory +capacity to 0% or the lowest possible - you may get a limited playback +speed (depending on the image resolution), however the Image Cache +will only keep 1 image in GPU memory at any one time - at the cost of +slower playback. + +Hardware Resource Monitor +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. figure:: images/tools_image_cache_preferences_monitor.png + :alt: Image Cache Preferences window + :align: center + :width: 60% + +The `Image Cache Preferences` window allows users to monitor the +computer's memory resources similar to the `Microsoft Windows` Task +Manager or System Monitors on `Linux`. Additionally the window +provides details of the `Image Cache` memory usage, detailing how many +individual *Images* are stored and how many *Image Slots* (similar to +image sequences) are stored. + +.. note:: The `Image Cache` *Capacity* and *Used* values only display + statistics of memory of the Image Cache; geometry and + material/shader textures are not counted. + +.. list-table:: GPU/CPU Resource Fields + :widths: auto + :header-rows: 1 + + * - Field + - Description + - Measurement Unit + + * - Memory Total + - Total amount of memory of the hardware resource. + - Gigabytes (GB) + + * - Memory Used + - Amount of memory available for the hardware resource. + - Gigabytes (GB) + + +.. list-table:: GPU/CPU Image Cache Overview Fields + :widths: auto + :header-rows: 1 + + * - Field + - Description + - Measurement Unit + + * - Images + - Number of individual images stored in the `Image Cache` on the + hardware. + - Count + + * - Image Slots + - Number of unique image sequences stored in the `Image Cache` on + the hardware. + - Count + + * - Capacity + - Amount of hardware memory allowed to be used for the `Image + Cache`. + - Gigabytes (GB) + + * - Used + - Amount of `Image Cache` capacity used. + - Gigabytes (GB) / Percentage of Capacity + .. _user-preferences-tool-ref: User Preferences diff --git a/docs/source/tools_hotkeys.rst b/docs/source/tools_hotkeys.rst index e48e504b5..44ff2f25e 100644 --- a/docs/source/tools_hotkeys.rst +++ b/docs/source/tools_hotkeys.rst @@ -86,16 +86,16 @@ viewport. * - Press **ALT + 1** key - Show/Hide geometry in the current viewport. - - N/A + - :ref:`Link ` * - Press **ALT + 2** key - Show/Hide Markers, Bundles, Locators and NURBS Curves in the current viewport. - - N/A + - :ref:`Link ` * - Press **ALT + 3** key - - Show/Hide Maya Image Planes in the current viewport. - - N/A + - Show/Hide Image Planes in the current viewport. + - :ref:`Link ` .. _marking-menu-heading: diff --git a/docs/source/tools_meshtools.rst b/docs/source/tools_meshtools.rst new file mode 100644 index 000000000..af6488b1e --- /dev/null +++ b/docs/source/tools_meshtools.rst @@ -0,0 +1,73 @@ +Mesh Tools +========== + +Mesh tools will create, modify or operate on meshes. These tools are +intended to augment Autodesk Maya's existing modeling tool-set and be +specific to the common use cases for MatchMove tasks. + +.. _mesh-from-points-ref: + +Mesh From Points +---------------- + +The `Mesh From Points` tool is used to generate a triangulated mesh +from a set of 3D points. The generated mesh is extremely helpful to +create a 3D visualisation, or as a starting point for a refined +hand-generated model. + +`Mesh From Points` uses a type of `Delaunay Triangulation +`_ named +`Delaunator `_, which is +optimised to generate meshes for landscape meshes. As a result, the +camera viewing position of the points is used to triangulate the +points in camera-space. + +.. figure:: images/tools_mesh_from_points_window.png + :alt: Mesh from Points Window. + :align: right + :width: 100% + +.. note:: September, 2024; The Mesh From Points tools cannot be undone. + This is intended to be fixed in a future release. + +Usage: + +1) Select a transform nodes. + + - At least 3 transform nodes are required. + +2) Activate a Maya viewport, and adjust the camera view to see the + points. + + - If your points represent a landscape, adjust the camera to view + all nodes from the "sky" point of view (from Y+, looking + downwards). + +3) Run `Create Full Mesh From Points` tool. + + - Optionally, use the `Create Border Mesh From Points` or open the + `Mesh From Points` UI. + + - The `Create Border Edge Strip Mesh` button in the `Mesh From + Points` can be used create a mesh with extra geometry at the + edges. This is intended to be used to create a "patch" of + geometry that can be adjusted further using Maya's modelling + tools. Adjust the `Border Edge Strip Width` slider to adjust the + position of the edges. + +To create a mesh from the selected nodes, use this Python command: + +.. code:: python + + import mmSolver.tools.meshfrompoints.tool as tool + tool.create_full_mesh() + + # Or create the border mesh only. + tool.create_border_mesh() + +To open the window, use this Python command: + +.. code:: python + + import mmSolver.tools.meshfrompoints.tool as tool + tool.main() diff --git a/docs/source/tools_renderer.rst b/docs/source/tools_renderer.rst index bdfc6fae8..21162e6a8 100644 --- a/docs/source/tools_renderer.rst +++ b/docs/source/tools_renderer.rst @@ -1,37 +1,165 @@ .. _renderer-ref: -MM Renderer -=========== +Viewport Renderers +================== -`MM Renderer` is a Viewport 2.0 renderer designed to add helpful -features for use with MatchMove workflows and reviews. - -This tool is currently only in beta, and is not enabled in stable `MM -Solver` releases. +`MM Solver` comes with specialised Viewport 2.0 renderers designed to +add helpful features for use with MatchMove workflows and reviews. .. figure:: images/tools_renderer_menu.png :alt: Viewport Renderer menu :align: center :scale: 80% - Enable `MM Renderer` in the "Renderer" menu on each Viewport. + Viewport 2.0 "Renderer" menu. + +Currently the following Viewport 2.0 renderers are available. + +.. list-table:: MM Solver Renderers + :widths: auto + :header-rows: 1 + + * - Name + - Description + + * - :ref:`MM Standard Renderer ` + - Identical to default "Viewport 2.0", but correctly works with + `MM ImagePlane` nodes in from hold-outs, either using Hold Out + attributes or `Use Background` shaders. + + * - :ref:`MM Silhouette Renderer ` + - Identical to `MM Standard Renderer`, but adds a silhouette + outline edge to all rendered meshes in the viewport. + +.. _renderer-standard-ref: + +MM Standard Renderer +-------------------- + +`MM Standard Renderer` is a Viewport 2.0 renderer designed to add +helpful features for use with MatchMove workflows and reviews. + +`MM Standard Renderer` is different from regular Viewport 2.0 because +it can display `MM Image Plane` nodes with native `useBackground` +shaders (or Hold-Out geometry). + +Usage +~~~~~ + +1) To use `MM Standard Renderer`, load the ``mmSolver`` plug-in (for +example open the Solver UI to ensure the plug-in is loaded), then use +the Viewport's "Renderer" menu to switch to ``MM Standard Renderer``. + +.. figure:: images/tools_renderer_menu_standard.png + :alt: Viewport Renderer menu for `MM Standard Renderer`. + :align: center + :scale: 80% + + Enable `MM Standard Renderer` in the Viewport "Renderer" menu. + +2) Use the Maya viewport and playblast as normal. + +.. _renderer-silhouette-ref: + +MM Silhouette Renderer +---------------------- + +The `MM Silhouette Renderer` is used to add silhouette outline edge to +rendered geometry, allowing the artist to clearly see the edges of a +mesh against an image sequence. + +This rendering effect is particularly effective when the geometry +contains a Hold-Out effect using a `useBackground` shader or hold-out +attributes (see :ref:`Set Mesh Hold-Out ` +tool). + +.. figure:: images/tools_renderer_silhouette_viewport.png + :alt: Example green silhouette effect on shaded objects. + :align: center + :scale: 80% + + Example green silhouette effect on shaded objects. + +.. note:: `MM Silhouette Renderer` requires the use of the OpenGL + graphics API backend; DirectX is not supported. + +Usage +~~~~~ + +1) To use `MM Silhouette Renderer`, load the ``mmSolver`` plug-in (for +example open the Solver UI to ensure the plug-in is loaded), then use +the Viewport's "Renderer" menu to switch to ``MM Silhouette +Renderer``. + +.. figure:: images/tools_renderer_menu_silhouette.png + :alt: Viewport Renderer menu for `MM Silhouette Renderer`. + :align: center + :scale: 80% + + Enable `MM Silhouette Renderer` in the Viewport "Renderer" menu. + +2) Click viewport panel menu "Renderer > MM Silhouette Renderer + [Option Box]" to open the :ref:`renderer settings + ` in the Attribute Editor. + +3) Adjust Silhouette settings as needed for desired effect. + +4) Use the Maya viewport and playblast as normal. + +.. _renderer-silhouette-settings-ref: + +Settings +~~~~~~~~ + +The settings for the `MM Silhouette Renderer` affect all open viewport +panels using the same renderer and allow adjusting the silhouette +effect, including the color and opacity. + +.. figure:: images/tools_renderer_globals_silhouette.png + :alt: The global settings for the `MM Silhouette Renderer` in the + Attribute Editor. + :align: center + :scale: 80% + + The global settings for the `MM Silhouette Renderer` in the + Attribute Editor. + + +.. list-table:: MM Silhouette Renderer Settings + :widths: auto + :header-rows: 1 + + * - Name + - Description + + * - Depth Offset + - The separation between the invisible solid geometry and the + wireframe mesh. Adjust to lower values if small-mesh artifacts + are visible. Set to ``0.0`` to create a shaded wireframe + effect. + + * - Width + - The width of the silhouette lines. -`MM Renderer` is different from regular Viewport 2.0 because it has -the following features: + * - Override Color + - When enabled, all objects will use the silhouette color + below. When disabled, the wireframe color of the object is used + for each object's lines. -- Display of MM Image Plane nodes with native useBackground shaders. + * - Color + - The override color for silhouette lines. -Getting Started -~~~~~~~~~~~~~~~ + * - Alpha + - The global opacity of the silhouette lines. -To use `MM Renderer`, simply load the ``mmSolver`` plug-in (for -example open the Solver UI to ensure the plug-in is loaded), then -use the Viewport's "Renderer" menu to switch to ``MM Renderer``. + * - Cull Face + - Backface culling for solid invisible mesh surfaces. For meshes + with inverted normals values other than ``Back``; Options are + ``Back``, ``Front`` or ``FrontAndBack``. -Known Issues -~~~~~~~~~~~~ + * - (debug) Enable + - Toggle the silhouette effect on/off. -`MM Renderer` is *beta* software and is only released in ``beta`` -versions of `MM Solver`. There are numerous bugs and issues that are -not yet resolved and the viewport renderer is available as a preview -only. + * - (debug) Operation Number + - Used internally to draw only the first N number of rendering + operations inside the renderer. diff --git a/include/mmSolver/nodeTypeIds.h b/include/mmSolver/nodeTypeIds.h index 605d8826f..b8aa0b0b3 100644 --- a/include/mmSolver/nodeTypeIds.h +++ b/include/mmSolver/nodeTypeIds.h @@ -86,9 +86,9 @@ #define MM_MARKER_GROUP_TRANSFORM_TYPE_NAME "mmMarkerGroupTransform" #define MM_MARKER_GROUP_DRAW_CLASSIFY "drawdb/geometry/transform" -#define MM_RENDERER_BASIC_NAME "mmRenderer" -#define MM_RENDER_GLOBALS_BASIC_TYPE_ID 0x0012F194 -#define MM_RENDER_GLOBALS_BASIC_TYPE_NAME "mmRenderGlobals" +#define MM_RENDERER_STANDARD_NAME "mmRendererStandard" +#define MM_RENDER_GLOBALS_STANDARD_TYPE_ID 0x0012F194 +#define MM_RENDER_GLOBALS_STANDARD_TYPE_NAME "mmRenderGlobalsStandard" #define MM_RENDERER_SILHOUETTE_NAME "mmRendererSilhouette" #define MM_RENDER_GLOBALS_SILHOUETTE_TYPE_ID 0x0012F18E @@ -131,11 +131,25 @@ #define MM_IMAGE_PLANE_SHAPE_TYPE_ID 0x0012F187 #define MM_IMAGE_PLANE_SHAPE_TYPE_NAME "mmImagePlaneShape" -#define MM_IMAGE_PLANE_SHAPE_DRAW_CLASSIFY "drawdb/geometry/mmSolver/imagePlane" +#define MM_IMAGE_PLANE_SHAPE_DRAW_CLASSIFY \ + "drawdb/geometry/mmSolver/imagePlane/v1" #define MM_IMAGE_PLANE_SHAPE_DRAW_REGISTRANT_ID "mmImagePlaneShape" #define MM_IMAGE_PLANE_SHAPE_SELECTION_TYPE_NAME "mmImagePlaneShapeSelection" +#define MM_IMAGE_PLANE_SHAPE_DISPLAY_FILTER_DRAW_DB_CLASSIFICATION \ + "drawdb/geometry/mmSolver/imagePlane/v1" #define MM_IMAGE_PLANE_SHAPE_DISPLAY_FILTER_NAME "mmImagePlaneDisplayFilter" -#define MM_IMAGE_PLANE_SHAPE_DISPLAY_FILTER_LABEL "MM ImagePlane" +#define MM_IMAGE_PLANE_SHAPE_DISPLAY_FILTER_LABEL "MM ImagePlane (legacy)" + +#define MM_IMAGE_PLANE_SHAPE_2_TYPE_ID 0x0012F18F +#define MM_IMAGE_PLANE_SHAPE_2_TYPE_NAME "mmImagePlaneShape2" +#define MM_IMAGE_PLANE_SHAPE_2_DRAW_CLASSIFY \ + "drawdb/geometry/mmSolver/imagePlane/v2" +#define MM_IMAGE_PLANE_SHAPE_2_DRAW_REGISTRANT_ID "mmImagePlaneShape2" +#define MM_IMAGE_PLANE_SHAPE_2_SELECTION_TYPE_NAME "mmImagePlaneShape2Selection" +#define MM_IMAGE_PLANE_SHAPE_2_DISPLAY_FILTER_DRAW_DB_CLASSIFICATION \ + "drawdb/geometry/mmSolver/imagePlane/v2" +#define MM_IMAGE_PLANE_SHAPE_2_DISPLAY_FILTER_NAME "mmImagePlane2DisplayFilter" +#define MM_IMAGE_PLANE_SHAPE_2_DISPLAY_FILTER_LABEL "MM ImagePlane" #define CAMERA_INFERNO_TYPE_ID 0x0012F183 // Not used in mmSolver. @@ -154,6 +168,9 @@ #define MM_IMAGE_PLANE_TRANSFORM_TYPE_NAME "mmImagePlaneTransform" #define MM_IMAGE_PLANE_TRANSFORM_DRAW_CLASSIFY "drawdb/geometry/transform" +#define MM_IMAGE_SEQUENCE_FRAME_LOGIC_TYPE_ID 0x0012F190 +#define MM_IMAGE_SEQUENCE_FRAME_LOGIC_TYPE_NAME "mmImageSequenceFrameLogic" + #define OCGM_IMAGE_PLANE_SHAPE_TYPE_ID 0x0012F18B #endif // MM_SOLVER_NODE_TYPE_IDS_H diff --git a/lib/rust/mmimage/.gitignore b/lib/.gitignore similarity index 83% rename from lib/rust/mmimage/.gitignore rename to lib/.gitignore index 06f52c05a..dfe81cdea 100644 --- a/lib/rust/mmimage/.gitignore +++ b/lib/.gitignore @@ -1,5 +1,6 @@ -/target* **/*.rs.bk Cargo.lock *~ +/target* +target/ *.org diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index db5eb9129..f386dbfc4 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2023 David Cattermole. +# Copyright (C) 2023, 2024 David Cattermole. # # This file is part of mmSolver. # @@ -19,6 +19,27 @@ cmake_minimum_required(VERSION 3.15) + +# The "project" command will overwrite the "VERSION" variables. We set +# the VERSION variables after the "project" command, so it should not +# affect us. +# +# https://cmake.org/cmake/help/latest/policy/CMP0048.html +cmake_policy(SET CMP0048 NEW) + +# # Honor visibility properties for all target types. +# # +# # https://cmake.org/cmake/help/latest/policy/CMP0063.html +# cmake_policy(SET CMP0063 NEW) + +if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") + # Changes how timestamps of downloaded files with + # ExternalProject_Add() are set. + # + # https://cmake.org/cmake/help/latest/policy/CMP0135.html + cmake_policy(SET CMP0135 NEW) +endif() + # Do not allow using GNU extensions (such as '-std=g++11'), because # it's not compatible with Maya. set(CXX_EXTENSIONS OFF) @@ -33,7 +54,7 @@ set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT set(PROJECT_HOMEPAGE_URL "https://github.com/david-cattermole/mayaMatchMoveSolver") set(PROJECT_DESCRIPTION "mmSolver libraries.") set(PROJECT_AUTHOR "David Cattermole") -set(PROJECT_COPYRIGHT "2023, David Cattermole.") +set(PROJECT_COPYRIGHT "2023, 2024, David Cattermole.") set(MMSOLVERLIBS_LIB_DIR "/path/to/rust/build/directory/" CACHE PATH "The path to the directory containing the compiled library.") @@ -45,10 +66,8 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} set(cpp_lib_name "mmsolverlibs_cpp") set(rust_lib_name "mmsolverlibs_rust") -set(CMAKE_CXX_STANDARD 11) - -include(MMSolverUtils) -set_global_maya_plugin_compile_options() +include(MMCommonUtils) +mm_common_set_global_compile_options() include(MMRustUtils) set(rust_linktime_file "NOT-FOUND") @@ -71,9 +90,7 @@ add_subdirectory(mmsolverlibs/src) option(MMSOLVERLIBS_BUILD_TESTS "Do you want to build the test files for mmsolverlibs?" OFF) if (MMSOLVERLIBS_BUILD_TESTS) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/cppbind/mmlens/tests) - # # TODO: This does not work on Maya 2024. I suspect this is because - # # of the std::string ABI has changed in CentOS 8.x/9.x. - # add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/cppbind/mmimage/tests) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/cppbind/mmimage/tests) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/cppbind/mmscenegraph/tests) endif() diff --git a/lib/README.md b/lib/README.md new file mode 100644 index 000000000..57ae1ca7e --- /dev/null +++ b/lib/README.md @@ -0,0 +1,8 @@ +# Rust and C++ Libraries + +mmSolver has a number of Rust and C++ libraries, which are abstracted +for general use by the mmSolver Maya Plug-in (but could one day be +reused outside Maya). + +TODO... write more to describe the directory layout and why and how +it's all built with Cargo and CMake. diff --git a/lib/cppbind/.gitignore b/lib/cppbind/.gitignore deleted file mode 100644 index c5ca39258..000000000 --- a/lib/cppbind/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -**/*.rs.bk -Cargo.lock -*~ -target/ diff --git a/lib/cppbind/mmcolorio/README.md b/lib/cppbind/mmcolorio/README.md new file mode 100644 index 000000000..02da6d4f4 --- /dev/null +++ b/lib/cppbind/mmcolorio/README.md @@ -0,0 +1,14 @@ +# MM Color IO + +The 'mmcolorio' library is a wrapper around the OpenColorIO library. + +# Structure + +mmcolorio is split into 2 different directories; 'rust/mmcolorio' and 'cppbind/mmcolorio'. + +'cppbind/mmcolorio' is a Rust crate to define C++ bindings with the help +of the CXX crate. + +# Build Process + +'cppbind/mmcolorio' are built as part of 'mmsolverlibs'. diff --git a/lib/cppbind/mmcolorio/include/mmcolorio/_symbol_export.h b/lib/cppbind/mmcolorio/include/mmcolorio/_symbol_export.h new file mode 100644 index 000000000..47d886315 --- /dev/null +++ b/lib/cppbind/mmcolorio/include/mmcolorio/_symbol_export.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2020, 2021, 2023 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ +#pragma once + +// Cross-platform symbol visibility macro. +#if defined(_MSC_VER) +#define MMCOLORIO_API_EXPORT __declspec(dllexport) +#elif defined(__GNUC__) +#define MMCOLORIO_API_EXPORT __attribute__((visibility("default"))) +#else +#define MMCOLORIO_API_EXPORT +#endif diff --git a/lib/cppbind/mmcolorio/include/mmcolorio/_types.h b/lib/cppbind/mmcolorio/include/mmcolorio/_types.h new file mode 100644 index 000000000..46ada7126 --- /dev/null +++ b/lib/cppbind/mmcolorio/include/mmcolorio/_types.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#pragma once + +#include + +namespace mmcolorio { + +enum class ColorSpaceVisibility : uint8_t { + kActive = 0, + kInactive, + kAll, + + // Must be second to last entry. Used to calculate the full list + // of entries. + kCount, + + // Must be last entry. + kUnknown = 255, +}; + +enum class ColorSpaceRole : uint8_t { + kDefault = 0, + kReference, + kData, + kColorPicking, + kSceneLinear, + kCompositingLog, + kColorTiming, + kTexturePaint, + kMattePaint, + kRendering, + kInterchangeScene, + kInterchangeDisplay, + + // Must be second to last entry. Used to calculate the full list + // of entries. + kCount, + + // Must be last entry. + kUnknown = 255, +}; + +} // namespace mmcolorio diff --git a/lib/cppbind/mmcolorio/include/mmcolorio/lib.h b/lib/cppbind/mmcolorio/include/mmcolorio/lib.h new file mode 100644 index 000000000..31ca967c7 --- /dev/null +++ b/lib/cppbind/mmcolorio/include/mmcolorio/lib.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#ifndef MM_COLOR_IO_LIB_H +#define MM_COLOR_IO_LIB_H + +// STD +#include +#include +#include + +#include "_symbol_export.h" +#include "_types.h" + +namespace mmcolorio { + +const char *get_config_name(); +const char *get_config_description(); +const char *get_config_search_path(); +const char *get_config_working_directory(); + +bool color_space_name_exists(const char *color_space_name); + +const char *guess_color_space_name_from_file_path(const char *file_path); + +const char *get_role_color_space_name(const ColorSpaceRole value); + +std::vector get_color_space_names( + const ColorSpaceVisibility visibility); + +void generate_shader_text(const char *input_color_space_name, + const char *output_color_space_name, + std::string &out_shader_text); + +} // namespace mmcolorio + +#endif // MM_COLOR_IO_LIB_H diff --git a/lib/cppbind/mmcolorio/include/mmcolorio/mmcolorio.h b/lib/cppbind/mmcolorio/include/mmcolorio/mmcolorio.h new file mode 100644 index 000000000..9bf3d3b45 --- /dev/null +++ b/lib/cppbind/mmcolorio/include/mmcolorio/mmcolorio.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2023, 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#ifndef MM_COLOR_IO_MM_COLOR_IO_H +#define MM_COLOR_IO_MM_COLOR_IO_H + +#include "_types.h" +#include "lib.h" + +#endif // MM_COLOR_IO_MM_COLOR_IO_H diff --git a/lib/cppbind/mmcolorio/src/lib.cpp b/lib/cppbind/mmcolorio/src/lib.cpp new file mode 100644 index 000000000..c7b6be329 --- /dev/null +++ b/lib/cppbind/mmcolorio/src/lib.cpp @@ -0,0 +1,691 @@ +/* + * Copyright (C) 2023 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +// Stop OpenColorIO header error on Windows. +// +// https://stackoverflow.com/questions/6884093/warning-c4003-not-enough-actual-parameters-for-macro-max-visual-studio-2010 +#ifndef NOMINMAX +#define NOMINMAX +#endif + +// C++ Standard Library +#include +#include +#include +#include +#include +#include + +// MM Solver +#include +#include + +// OpenColorIO +#include + +namespace OCIO = OCIO_NAMESPACE; + +namespace mmcolorio { + +void print_ocio_config_details(OCIO::ConstConfigRcPtr &config) { + MMSOLVER_CORE_INFO(std::cout, "mmcolorio: ----------------------------"); + MMSOLVER_CORE_INFO(std::cout, + "mmcolorio: OpenColorIO Version=" << OCIO_VERSION); + + if (!config) { + MMSOLVER_CORE_ERR( + std::cerr, "mmcolorio: print_ocio_config_details: config is null."); + return; + } + + try { + MMSOLVER_CORE_INFO( + std::cout, "mmcolorio: OCIO Config:" + << " MajorVersion=" << config->getMajorVersion() + << " MinorVersion=" << config->getMinorVersion()); + + MMSOLVER_CORE_INFO(std::cout, "mmcolorio: OCIO Config:" + << " Name=\"" << config->getName() + << "\""); + + MMSOLVER_CORE_INFO(std::cout, "mmcolorio: OCIO Config:" + << " FamilySeparator=\"" + << config->getFamilySeparator() + << "\""); + + MMSOLVER_CORE_INFO(std::cout, "mmcolorio: OCIO Config:" + << " Description=\"" + << config->getDescription() << "\""); + + MMSOLVER_CORE_INFO(std::cout, "mmcolorio: OCIO Config:" + << " SearchPath=\"" + << config->getSearchPath() << "\""); + MMSOLVER_CORE_INFO(std::cout, "mmcolorio: OCIO Config:" + << " WorkingDir=\"" + << config->getWorkingDir() << "\""); + + const auto searchReferenceType = + OCIO::SearchReferenceSpaceType::SEARCH_REFERENCE_SPACE_SCENE; + const auto visibility = OCIO::ColorSpaceVisibility::COLORSPACE_ACTIVE; + + MMSOLVER_CORE_INFO(std::cout, + "mmcolorio: ----------------------------"); + const int32_t numColorSpaces = + config->getNumColorSpaces(searchReferenceType, visibility); + // const int32_t numColorSpaces = config->getNumColorSpaces(); + for (auto i = 0; i < numColorSpaces; i++) { + const char *colorSpaceName = config->getColorSpaceNameByIndex(i); + OCIO::ConstColorSpaceRcPtr colorSpaceRcPtr = + config->getColorSpace(colorSpaceName); + MMSOLVER_CORE_INFO(std::cout, "mmcolorio: OCIO Config: i=" + << i << " colorSpaceName=\"" + << colorSpaceName << "\""); + } + + MMSOLVER_CORE_INFO(std::cout, + "mmcolorio: ----------------------------"); + const int32_t numRoles = config->getNumRoles(); + for (auto i = 0; i < numRoles; i++) { + const char *roleName = config->getRoleName(i); + const char *roleColorSpaceName = config->getRoleColorSpace(i); + MMSOLVER_CORE_INFO(std::cout, "mmcolorio: OCIO Config: i=" + << i << " roleName=\"" << roleName + << "\" roleColorSpaceName=\"" + << roleColorSpaceName << "\""); + } + + MMSOLVER_CORE_INFO(std::cout, + "mmcolorio: ----------------------------"); + const char *defaultDisplayName = config->getDefaultDisplay(); + MMSOLVER_CORE_INFO(std::cout, + "mmcolorio: OCIO Config: defaultDisplayName=\"" + << defaultDisplayName << "\""); + + const char *activeDisplays = config->getActiveDisplays(); + MMSOLVER_CORE_INFO(std::cout, + "mmcolorio: OCIO Config: activeDisplays=\"" + << activeDisplays << "\""); + + const char *activeViews = config->getActiveViews(); + MMSOLVER_CORE_INFO(std::cout, "mmcolorio: OCIO Config: activeViews=\"" + << activeViews << "\""); + + MMSOLVER_CORE_INFO(std::cout, + "mmcolorio: ----------------------------"); + const int numDisplay = config->getNumDisplays(); + for (auto i = 0; i < numDisplay; i++) { + const char *displayName = config->getDisplay(i); + MMSOLVER_CORE_INFO(std::cout, "mmcolorio: OCIO Config: i=" + << i << " displayName=\"" + << displayName << "\""); + } + } catch (OCIO::Exception &exception) { + MMSOLVER_CORE_ERR( + std::cerr, + "mmcolorio: OpenColorIO Error: print_ocio_config_details: " + << exception.what()); + } + + return; +} + +// This is an example function used to validate OpenColorIO was +// working. This should not be used and can be removed at a later +// date. +void test_opencolorio(uint8_t *pixels, const uint32_t width, + const uint32_t height, const uint8_t number_of_channels) { + const bool verbose = false; + + mmsolver::debug::TimestampBenchmark timer_total; + mmsolver::debug::TimestampBenchmark timer_a; + mmsolver::debug::TimestampBenchmark timer_b; + mmsolver::debug::TimestampBenchmark timer_c; + mmsolver::debug::TimestampBenchmark timer_d; + mmsolver::debug::TimestampBenchmark timer_e; + + timer_total.start(); + try { + OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); + if (verbose) { + timer_c.start(); + print_ocio_config_details(config); + timer_c.stop(); + } + + // Built-in OCIO config (used as a fallback) + if (std::strlen(config->getName()) == 0) { + timer_a.start(); + + MMSOLVER_CORE_WRN(std::cerr, + "mmcolorio: OpenColorIO could not get config, " + "using default config."); + + const char *filename = "ocio://default"; + config = OCIO::Config::CreateFromFile(filename); + OCIO::SetCurrentConfig(config); + timer_a.stop(); + + if (verbose) { + timer_c.start(); + print_ocio_config_details(config); + timer_c.stop(); + } + } + + OCIO::BitDepth inBitDepth = OCIO::BitDepth::BIT_DEPTH_UINT8; + OCIO::BitDepth outBitDepth = OCIO::BitDepth::BIT_DEPTH_UINT8; + + // // TODO: Support 16-bit half or 32-bit float output. + // OCIO::BitDepth outBitDepth = OCIO::BitDepth::BIT_DEPTH_F16; + // OCIO::BitDepth outBitDepth = OCIO::BitDepth::BIT_DEPTH_F32; + + { + timer_d.start(); + + OCIO::ConstProcessorRcPtr processor = config->getProcessor( + OCIO::ROLE_COLOR_PICKING, OCIO::ROLE_SCENE_LINEAR); + + OCIO::OptimizationFlags oFlags = + OCIO::OptimizationFlags::OPTIMIZATION_DEFAULT; + + OCIO::ConstCPUProcessorRcPtr cpu = + processor->getOptimizedCPUProcessor(inBitDepth, outBitDepth, + oFlags); + + timer_d.stop(); + + { + timer_e.start(); + + // Apply the color transform to an existing RGB(A) image. + void *imageData = static_cast(pixels); + const long width_long = static_cast(width); + const long height_long = static_cast(height); + const long numChannels = static_cast(number_of_channels); + const ptrdiff_t chanStrideBytes = sizeof(uint8_t); + const ptrdiff_t xStrideBytes = numChannels * sizeof(uint8_t); + const ptrdiff_t yStrideBytes = + numChannels * width_long * sizeof(uint8_t); + OCIO::PackedImageDesc img( + imageData, width_long, height_long, numChannels, inBitDepth, + chanStrideBytes, xStrideBytes, yStrideBytes); + + // TODO: Allow a different image data type as the output. We + // need to pass another 'OCIO::PackedImageDesc' as a second + // argument here. + cpu->apply(img); + timer_e.stop(); + } + } + } catch (OCIO::Exception &exception) { + MMSOLVER_CORE_ERR(std::cerr, + "mmcolorio: OpenColorIO Error: test_opencolorio: " + << exception.what()); + } + + timer_total.stop(); + + MMSOLVER_CORE_VRB( + std::cout, "mmcolor: timer_a: " << timer_a.get_seconds() << " seconds"); + MMSOLVER_CORE_VRB( + std::cout, "mmcolor: timer_b: " << timer_b.get_seconds() << " seconds"); + MMSOLVER_CORE_VRB( + std::cout, "mmcolor: timer_c: " << timer_c.get_seconds() << " seconds"); + MMSOLVER_CORE_VRB( + std::cout, "mmcolor: timer_d: " << timer_d.get_seconds() << " seconds"); + MMSOLVER_CORE_VRB( + std::cout, "mmcolor: timer_e: " << timer_e.get_seconds() << " seconds"); + MMSOLVER_CORE_VRB( + std::cout, + "mmcolor: timer_total: " << timer_total.get_seconds() << " seconds"); + + return; +} + +// Never make this function public, otherwise we leak the OCIO data +// types. +// +// This function is expected to be used inside a try/catch. +OCIO::ConstConfigRcPtr get_config() { + const bool verbose = false; + + OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig(); + if (verbose) { + print_ocio_config_details(config); + } + + // Built-in OCIO config (used as a fallback) + if (!config || std::strlen(config->getName()) == 0) { + MMSOLVER_CORE_WRN(std::cerr, + "mmcolorio: OpenColorIO could not get config, " + "using default config."); + + const char *filename = "ocio://default"; + config = OCIO::Config::CreateFromFile(filename); + OCIO::SetCurrentConfig(config); + + if (verbose) { + print_ocio_config_details(config); + } + } + + return config; +} + +const char *get_config_name() { + try { + OCIO::ConstConfigRcPtr config = get_config(); + if (!config) { + MMSOLVER_CORE_ERR(std::cerr, + "mmcolorio: get_config_name: config is null."); + return ""; + } + + return config->getName(); + } catch (OCIO::Exception &exception) { + MMSOLVER_CORE_ERR(std::cerr, + "mmcolorio: get_config_name: " + "OpenColorIO Error: " + << exception.what()); + } + + return ""; +} + +const char *get_config_description() { + try { + OCIO::ConstConfigRcPtr config = get_config(); + if (!config) { + MMSOLVER_CORE_ERR( + std::cerr, + "mmcolorio: get_config_description: config is null."); + return ""; + } + + return config->getDescription(); + } catch (OCIO::Exception &exception) { + MMSOLVER_CORE_ERR(std::cerr, + "mmcolorio: get_config_description: " + "OpenColorIO Error: " + << exception.what()); + } + + return ""; +} + +const char *get_config_search_path() { + try { + OCIO::ConstConfigRcPtr config = get_config(); + if (!config) { + MMSOLVER_CORE_ERR( + std::cerr, + "mmcolorio: get_config_search_path: config is null."); + return ""; + } + + return config->getSearchPath(); + } catch (OCIO::Exception &exception) { + MMSOLVER_CORE_ERR(std::cerr, + "mmcolorio: get_config_search_path: " + "OpenColorIO Error: " + << exception.what()); + } + + return ""; +} + +const char *get_config_working_directory() { + try { + OCIO::ConstConfigRcPtr config = get_config(); + if (!config) { + MMSOLVER_CORE_ERR( + std::cerr, + "mmcolorio: config_working_directory: config is null."); + return ""; + } + + return config->getWorkingDir(); + } catch (OCIO::Exception &exception) { + MMSOLVER_CORE_ERR(std::cerr, + "mmcolorio: get_config_working_directory: " + "OpenColorIO Error: " + << exception.what()); + } + + return ""; +} + +bool color_space_name_exists(const char *color_space_name) { + try { + OCIO::ConstConfigRcPtr config = get_config(); + if (!config) { + MMSOLVER_CORE_ERR( + std::cerr, + "mmcolorio: color_space_name_exists: config is null."); + return false; + } + + OCIO::ConstColorSpaceRcPtr color_space = + config->getColorSpace(color_space_name); + if (color_space) { + const char *found_name = color_space->getName(); + if (std::strlen(found_name) > 0) { + return true; + } + } + } catch (OCIO::Exception &exception) { + MMSOLVER_CORE_ERR(std::cerr, + "mmcolorio: guess_color_space_name_from_file_path: " + "OpenColorIO Error: " + << exception.what()); + } + + return false; +} + +const char *guess_color_space_name_from_file_path(const char *file_path) { + try { + OCIO::ConstConfigRcPtr config = get_config(); + if (!config) { + MMSOLVER_CORE_ERR( + std::cerr, + "mmcolorio: guess_color_space_name_from_file_path: config is " + "null."); + return ""; + } + + const char *color_space_name = + config->getColorSpaceFromFilepath(file_path); + return color_space_name; + + } catch (OCIO::Exception &exception) { + MMSOLVER_CORE_ERR(std::cerr, + "mmcolorio: guess_color_space_name_from_file_path: " + "OpenColorIO Error: " + << exception.what()); + } + + return ""; +} + +static const char *color_space_role_convert_mmcolorio_to_ocio( + const ColorSpaceRole value) { + if (value == ColorSpaceRole::kDefault) { + return OCIO::ROLE_DEFAULT; + } else if (value == ColorSpaceRole::kReference) { + return OCIO::ROLE_REFERENCE; + } else if (value == ColorSpaceRole::kData) { + return OCIO::ROLE_DATA; + } else if (value == ColorSpaceRole::kColorPicking) { + return OCIO::ROLE_COLOR_PICKING; + } else if (value == ColorSpaceRole::kSceneLinear) { + return OCIO::ROLE_SCENE_LINEAR; + } else if (value == ColorSpaceRole::kCompositingLog) { + return OCIO::ROLE_COMPOSITING_LOG; + } else if (value == ColorSpaceRole::kColorTiming) { + return OCIO::ROLE_COLOR_TIMING; + } else if (value == ColorSpaceRole::kTexturePaint) { + return OCIO::ROLE_TEXTURE_PAINT; + } else if (value == ColorSpaceRole::kMattePaint) { + return OCIO::ROLE_MATTE_PAINT; + } else if (value == ColorSpaceRole::kRendering) { + return OCIO::ROLE_RENDERING; + } else if (value == ColorSpaceRole::kInterchangeScene) { + return OCIO::ROLE_INTERCHANGE_SCENE; + } else if (value == ColorSpaceRole::kInterchangeDisplay) { + return OCIO::ROLE_INTERCHANGE_DISPLAY; + } else { + MMSOLVER_CORE_ERR( + std::cerr, + "mmcolorio: color_space_role_convert_mmcolorio_to_ocio: " + "Invalid mmcolorio::ColorSpaceRole: " + << static_cast(value)); + } + return OCIO::ROLE_DEFAULT; +} + +const char *get_role_color_space_name(const ColorSpaceRole role) { + const bool verbose = false; + + try { + OCIO::ConstConfigRcPtr config = get_config(); + if (!config) { + MMSOLVER_CORE_ERR( + std::cerr, + "mmcolorio: get_role_color_space_name: config is null."); + return ""; + } + + const char *role_name = + color_space_role_convert_mmcolorio_to_ocio(role); + MMSOLVER_CORE_VRB(std::cout, + "mmcolor: get_role_color_space_name: role_name=\"" + << role_name << "\""); + + OCIO::ConstColorSpaceRcPtr role_color_space = + config->getColorSpace(role_name); + if (role_color_space) { + return role_color_space->getName(); + } + } catch (OCIO::Exception &exception) { + MMSOLVER_CORE_ERR(std::cerr, + "mmcolorio: get_role_color_space_name: " + "OpenColorIO Error: " + << exception.what()); + } + + return ""; +} + +static OCIO::ColorSpaceVisibility +color_space_visibility_convert_mmcolorio_to_ocio( + const ColorSpaceVisibility value) { + if (value == ColorSpaceVisibility::kActive) { + return OCIO::ColorSpaceVisibility::COLORSPACE_ACTIVE; + } else if (value == ColorSpaceVisibility::kInactive) { + return OCIO::ColorSpaceVisibility::COLORSPACE_INACTIVE; + } else if (value == ColorSpaceVisibility::kAll) { + return OCIO::ColorSpaceVisibility::COLORSPACE_ALL; + } else { + MMSOLVER_CORE_ERR( + std::cerr, + "mmcolorio: color_space_visibility_convert_mmcolorio_to_ocio: " + "Invalid mmcolorio::ColorSpaceVisibility: " + << static_cast(value)); + } + return OCIO::ColorSpaceVisibility::COLORSPACE_ALL; +} + +std::vector get_color_space_names( + const ColorSpaceVisibility visibility) { + try { + OCIO::ConstConfigRcPtr config = get_config(); + + auto names = std::vector(); + if (!config) { + MMSOLVER_CORE_ERR( + std::cerr, "mmcolorio: get_color_space_names: config is null."); + return names; + } + + const auto searchReferenceType = + OCIO::SearchReferenceSpaceType::SEARCH_REFERENCE_SPACE_SCENE; + + const auto ocio_visibility = + color_space_visibility_convert_mmcolorio_to_ocio(visibility); + + const int32_t num_color_spaces = + config->getNumColorSpaces(searchReferenceType, ocio_visibility); + + for (auto i = 0; i < num_color_spaces; i++) { + std::string color_space_name = + std::string(config->getColorSpaceNameByIndex(i)); + names.push_back(color_space_name); + } + + return names; + } catch (OCIO::Exception &exception) { + MMSOLVER_CORE_ERR(std::cerr, + "mmcolorio: get_color_space_names: " + "OpenColorIO Error: " + << exception.what()); + } + + auto names = std::vector(); + return names; +} + +void generate_shader_text(const char *input_color_space_name, + const char *output_color_space_name, + std::string &out_shader_text) { + const bool verbose = false; + + MMSOLVER_CORE_VRB( + std::cout, "mmcolorio: generate_shader_text: input_color_space_name=\"" + << input_color_space_name << "\"."); + MMSOLVER_CORE_VRB( + std::cout, "mmcolorio: generate_shader_text: output_color_space_name=\"" + << output_color_space_name << "\"."); + + mmsolver::debug::TimestampBenchmark timer_total; + timer_total.start(); + + out_shader_text.clear(); + try { + OCIO::ConstConfigRcPtr config = get_config(); + if (!config) { + MMSOLVER_CORE_ERR( + std::cerr, "mmcolorio: generate_shader_text: config is null."); + return; + } + + OCIO::ConstColorSpaceRcPtr input_color_space = + config->getColorSpace(input_color_space_name); + OCIO::ConstColorSpaceRcPtr output_color_space = + config->getColorSpace(output_color_space_name); + + OCIO::ConstProcessorRcPtr processor = + config->getProcessor(input_color_space, output_color_space); + + // NOTE: Cache ID will change when the processor values + // change. + MMSOLVER_CORE_VRB( + std::cout, "mmcolor: generate_shader_text: processor cache id: \"" + << processor->getCacheID() << "\""); + + OCIO::OptimizationFlags oFlags = + OCIO::OptimizationFlags::OPTIMIZATION_DEFAULT; + OCIO::ConstGPUProcessorRcPtr gpu_processor = + processor->getOptimizedGPUProcessor(oFlags); + + // unsigned edgelen = 32; + // OCIO::ConstGPUProcessorRcPtr gpu_processor = + // processor->getOptimizedLegacyGPUProcessor(oFlags, edgelen); + + // NOTE: Cache ID will change when the gpu_processor values + // change. + MMSOLVER_CORE_VRB( + std::cout, "mmcolor: generate_shader_text: processor cache id: \"" + << gpu_processor->getCacheID() << "\""); + + // OCIO::GpuShaderDescRcPtr shader_desc = + // OCIO::GpuShaderDesc::Create(); + OCIO::GpuShaderDescRcPtr shader_desc = + OCIO::GpuShaderDesc::CreateShaderDesc(); // ->clone() + // OCIO::GPU_LANGUAGE_GLSL_1_2 + // OCIO::GPU_LANGUAGE_GLSL_1_3 + // OCIO::GPU_LANGUAGE_GLSL_4_0 + // OCIO::GPU_LANGUAGE_GLSL_ES_1_0 + // OCIO::GPU_LANGUAGE_GLSL_ES_3_0 + shader_desc->setLanguage(OCIO::GPU_LANGUAGE_GLSL_4_0); + shader_desc->setFunctionName("OCIODisplay"); + shader_desc->setResourcePrefix("ocio_"); + shader_desc->finalize(); + gpu_processor->extractGpuShaderInfo(shader_desc); + + MMSOLVER_CORE_VRB( + std::cout, + "mmcolor: generate_shader_text: shader desc unique id: \"" + << shader_desc->getUniqueID() << "\""); + MMSOLVER_CORE_VRB( + std::cout, + "mmcolor: generate_shader_text: shader desc pixel name: \"" + << shader_desc->getPixelName() << "\""); + + // NOTE: Cache ID will change when the shader_desc values + // change. + MMSOLVER_CORE_VRB( + std::cout, "mmcolor: generate_shader_text: shader desc cache id: \"" + << shader_desc->getCacheID() << "\""); + MMSOLVER_CORE_VRB(std::cout, + "mmcolor: generate_shader_text: shader desc " + "texture max width: " + << shader_desc->getTextureMaxWidth()); + MMSOLVER_CORE_VRB(std::cout, + "mmcolor: generate_shader_text: shader desc " + "num dynamic properties: " + << shader_desc->getNumDynamicProperties()); + MMSOLVER_CORE_VRB(std::cout, + "mmcolor: generate_shader_text: shader desc " + "num uniform: " + << shader_desc->getNumUniforms()); + MMSOLVER_CORE_VRB(std::cout, + "mmcolor: generate_shader_text: shader desc " + "num textures: " + << shader_desc->getNumTextures()); + MMSOLVER_CORE_VRB(std::cout, + "mmcolor: generate_shader_text: shader desc " + "num 3d textures: " + << shader_desc->getNum3DTextures()); + + const char *shader_text = shader_desc->getShaderText(); + MMSOLVER_CORE_VRB(std::cout, + "mmcolor: generate_shader_text: shader text start:"); + MMSOLVER_CORE_VRB(std::cout, shader_text); + MMSOLVER_CORE_VRB(std::cout, + "mmcolor: generate_shader_text: shader text end."); + out_shader_text += shader_text; + + // TODO: Work out how to get and upload all the shader + // and texture information. + + // const unsigned numUniforms = shader_desc->getNumUniforms(); + // const unsigned numTextures = shader_desc->getNumTextures(); + // const unsigned num3dTextures = shader_desc->getNum3DTextures(); + // shader_desc->get3DTexture(index, textureName, samplerName, + // edgelen, interpolation); + + } catch (OCIO::Exception &exception) { + MMSOLVER_CORE_ERR(std::cerr, + "mmcolorio: generate_shader_text: OpenColorIO Error: " + << exception.what()); + } + + timer_total.stop(); + + MMSOLVER_CORE_VRB(std::cout, "mmcolor: generate_shader_text: timer_total: " + << timer_total.get_seconds() + << " seconds"); + + return; +} + +} // namespace mmcolorio diff --git a/lib/cppbind/mmcore/Cargo.toml b/lib/cppbind/mmcore/Cargo.toml index bd3269d8b..847f16068 100644 --- a/lib/cppbind/mmcore/Cargo.toml +++ b/lib/cppbind/mmcore/Cargo.toml @@ -1,27 +1,14 @@ [package] name = "mmcore_cppbind" -version = "0.1.0" -authors = ["david-cattermole "] -edition = "2018" -publish = false +version.workspace = true +authors.workspace = true +edition.workspace = true +publish.workspace = true [lib] name = "mmcore" path = "./src/lib.rs" [dependencies] -# NOTE: When changing this version number ensure to also update the -# installed 'cxxbridge-cmd' version so it stays in sync; Update it -# here: './scripts/internal/build_rust_library_*.*' -cxx = "=1.0.75" - -[profile.release] -opt-level = 3 -rpath = false -lto = true -codegen-units = 1 - -# NOTE: If we use 'panic = "abort"' then we are unable to produce tests. -# # https://github.com/rust-lang/cargo/issues/6313 -# -# panic = "abort" +cxx = { workspace = true } +mmcore_rust = { workspace = true } diff --git a/lib/cppbind/mmcore/README.md b/lib/cppbind/mmcore/README.md index 56bcd1bc7..8ba79fdab 100644 --- a/lib/cppbind/mmcore/README.md +++ b/lib/cppbind/mmcore/README.md @@ -14,14 +14,15 @@ used in Maya. # Structure -Unlike the other similar sub-libraries 'mmcore' is only defined in -'cppbind/mmcore', the Rust component is not yet needed and has not -been added. +mmcore is split into 2 different directories; 'rust/mmcore' and 'cppbind/mmcore'. + +'rust/mmcore' is the core library written in Rust without the +requirements of C++. 'cppbind/mmcore' is a Rust crate to define C++ bindings with the help of the CXX crate. # Build Process -'cppbind/mmcore' are built as part of +'cppbind/mmcore' and 'rust/mmcore' are built as part of 'mmsolverlibs'. diff --git a/lib/cppbind/mmcore/include/mmcore/_cxx.h b/lib/cppbind/mmcore/include/mmcore/_cxx.h index 41e1f98a3..907ee829f 100644 --- a/lib/cppbind/mmcore/include/mmcore/_cxx.h +++ b/lib/cppbind/mmcore/include/mmcore/_cxx.h @@ -35,122 +35,122 @@ class impl; // https://cxx.rs/binding/string.html class String final { public: - String() noexcept; - String(const String &) noexcept; - String(String &&) noexcept; - ~String() noexcept; - - String(const std::string &); - String(const char *); - String(const char *, std::size_t); - String(const char16_t *); - String(const char16_t *, std::size_t); - - // Replace invalid Unicode data with the replacement character (U+FFFD). - static String lossy(const std::string &) noexcept; - static String lossy(const char *) noexcept; - static String lossy(const char *, std::size_t) noexcept; - static String lossy(const char16_t *) noexcept; - static String lossy(const char16_t *, std::size_t) noexcept; - - String &operator=(const String &) &noexcept; - String &operator=(String &&) &noexcept; - - explicit operator std::string() const; - - // Note: no null terminator. - const char *data() const noexcept; - std::size_t size() const noexcept; - std::size_t length() const noexcept; - bool empty() const noexcept; - - const char *c_str() noexcept; - - std::size_t capacity() const noexcept; - void reserve(size_t new_cap) noexcept; - - using iterator = char *; - iterator begin() noexcept; - iterator end() noexcept; - - using const_iterator = const char *; - const_iterator begin() const noexcept; - const_iterator end() const noexcept; - const_iterator cbegin() const noexcept; - const_iterator cend() const noexcept; - - bool operator==(const String &) const noexcept; - bool operator!=(const String &) const noexcept; - bool operator<(const String &) const noexcept; - bool operator<=(const String &) const noexcept; - bool operator>(const String &) const noexcept; - bool operator>=(const String &) const noexcept; - - void swap(String &) noexcept; - - // Internal API only intended for the cxxbridge code generator. - String(unsafe_bitcopy_t, const String &) noexcept; + String() noexcept; + String(const String &) noexcept; + String(String &&) noexcept; + ~String() noexcept; + + String(const std::string &); + String(const char *); + String(const char *, std::size_t); + String(const char16_t *); + String(const char16_t *, std::size_t); + + // Replace invalid Unicode data with the replacement character (U+FFFD). + static String lossy(const std::string &) noexcept; + static String lossy(const char *) noexcept; + static String lossy(const char *, std::size_t) noexcept; + static String lossy(const char16_t *) noexcept; + static String lossy(const char16_t *, std::size_t) noexcept; + + String &operator=(const String &) &noexcept; + String &operator=(String &&) &noexcept; + + explicit operator std::string() const; + + // Note: no null terminator. + const char *data() const noexcept; + std::size_t size() const noexcept; + std::size_t length() const noexcept; + bool empty() const noexcept; + + const char *c_str() noexcept; + + std::size_t capacity() const noexcept; + void reserve(size_t new_cap) noexcept; + + using iterator = char *; + iterator begin() noexcept; + iterator end() noexcept; + + using const_iterator = const char *; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + bool operator==(const String &) const noexcept; + bool operator!=(const String &) const noexcept; + bool operator<(const String &) const noexcept; + bool operator<=(const String &) const noexcept; + bool operator>(const String &) const noexcept; + bool operator>=(const String &) const noexcept; + + void swap(String &) noexcept; + + // Internal API only intended for the cxxbridge code generator. + String(unsafe_bitcopy_t, const String &) noexcept; private: - struct lossy_t; - String(lossy_t, const char *, std::size_t) noexcept; - String(lossy_t, const char16_t *, std::size_t) noexcept; - friend void swap(String &lhs, String &rhs) noexcept { lhs.swap(rhs); } + struct lossy_t; + String(lossy_t, const char *, std::size_t) noexcept; + String(lossy_t, const char16_t *, std::size_t) noexcept; + friend void swap(String &lhs, String &rhs) noexcept { lhs.swap(rhs); } - // Size and alignment statically verified by rust_string.rs. - std::array repr; + // Size and alignment statically verified by rust_string.rs. + std::array repr; }; -#endif // CXXBRIDGE1_RUST_STRING +#endif // CXXBRIDGE1_RUST_STRING #ifndef CXXBRIDGE1_RUST_STR #define CXXBRIDGE1_RUST_STR // https://cxx.rs/binding/str.html class Str final { public: - Str() noexcept; - Str(const String &) noexcept; - Str(const std::string &); - Str(const char *); - Str(const char *, std::size_t); - - Str &operator=(const Str &) &noexcept = default; - - explicit operator std::string() const; - - // Note: no null terminator. - const char *data() const noexcept; - std::size_t size() const noexcept; - std::size_t length() const noexcept; - bool empty() const noexcept; - - // Important in order for System V ABI to pass in registers. - Str(const Str &) noexcept = default; - ~Str() noexcept = default; - - using iterator = const char *; - using const_iterator = const char *; - const_iterator begin() const noexcept; - const_iterator end() const noexcept; - const_iterator cbegin() const noexcept; - const_iterator cend() const noexcept; - - bool operator==(const Str &) const noexcept; - bool operator!=(const Str &) const noexcept; - bool operator<(const Str &) const noexcept; - bool operator<=(const Str &) const noexcept; - bool operator>(const Str &) const noexcept; - bool operator>=(const Str &) const noexcept; - - void swap(Str &) noexcept; + Str() noexcept; + Str(const String &) noexcept; + Str(const std::string &); + Str(const char *); + Str(const char *, std::size_t); + + Str &operator=(const Str &) &noexcept = default; + + explicit operator std::string() const; + + // Note: no null terminator. + const char *data() const noexcept; + std::size_t size() const noexcept; + std::size_t length() const noexcept; + bool empty() const noexcept; + + // Important in order for System V ABI to pass in registers. + Str(const Str &) noexcept = default; + ~Str() noexcept = default; + + using iterator = const char *; + using const_iterator = const char *; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + bool operator==(const Str &) const noexcept; + bool operator!=(const Str &) const noexcept; + bool operator<(const Str &) const noexcept; + bool operator<=(const Str &) const noexcept; + bool operator>(const Str &) const noexcept; + bool operator>=(const Str &) const noexcept; + + void swap(Str &) noexcept; private: - class uninit; - Str(uninit) noexcept; - friend impl; + class uninit; + Str(uninit) noexcept; + friend impl; - std::array repr; + std::array repr; }; -#endif // CXXBRIDGE1_RUST_STR +#endif // CXXBRIDGE1_RUST_STR #ifndef CXXBRIDGE1_RUST_SLICE namespace detail { @@ -159,211 +159,210 @@ struct copy_assignable_if {}; template <> struct copy_assignable_if { - copy_assignable_if() noexcept = default; - copy_assignable_if(const copy_assignable_if &) noexcept = default; - copy_assignable_if &operator=(const copy_assignable_if &) &noexcept = - delete; - copy_assignable_if &operator=(copy_assignable_if &&) &noexcept = default; + copy_assignable_if() noexcept = default; + copy_assignable_if(const copy_assignable_if &) noexcept = default; + copy_assignable_if &operator=(const copy_assignable_if &) &noexcept = delete; + copy_assignable_if &operator=(copy_assignable_if &&) &noexcept = default; }; -} // namespace detail +} // namespace detail // https://cxx.rs/binding/slice.html template class Slice final : private detail::copy_assignable_if::value> { public: - using value_type = T; + using value_type = T; - Slice() noexcept; - Slice(T *, std::size_t count) noexcept; + Slice() noexcept; + Slice(T *, std::size_t count) noexcept; - Slice &operator=(const Slice &) &noexcept = default; - Slice &operator=(Slice &&) &noexcept = default; + Slice &operator=(const Slice &) &noexcept = default; + Slice &operator=(Slice &&) &noexcept = default; - T *data() const noexcept; - std::size_t size() const noexcept; - std::size_t length() const noexcept; - bool empty() const noexcept; + T *data() const noexcept; + std::size_t size() const noexcept; + std::size_t length() const noexcept; + bool empty() const noexcept; - T &operator[](std::size_t n) const noexcept; - T &at(std::size_t n) const; - T &front() const noexcept; - T &back() const noexcept; + T &operator[](std::size_t n) const noexcept; + T &at(std::size_t n) const; + T &front() const noexcept; + T &back() const noexcept; - // Important in order for System V ABI to pass in registers. - Slice(const Slice &) noexcept = default; - ~Slice() noexcept = default; + // Important in order for System V ABI to pass in registers. + Slice(const Slice &) noexcept = default; + ~Slice() noexcept = default; - class iterator; - iterator begin() const noexcept; - iterator end() const noexcept; + class iterator; + iterator begin() const noexcept; + iterator end() const noexcept; - void swap(Slice &) noexcept; + void swap(Slice &) noexcept; private: - class uninit; - Slice(uninit) noexcept; - friend impl; - friend void sliceInit(void *, const void *, std::size_t) noexcept; - friend void *slicePtr(const void *) noexcept; - friend std::size_t sliceLen(const void *) noexcept; - - std::array repr; + class uninit; + Slice(uninit) noexcept; + friend impl; + friend void sliceInit(void *, const void *, std::size_t) noexcept; + friend void *slicePtr(const void *) noexcept; + friend std::size_t sliceLen(const void *) noexcept; + + std::array repr; }; template class Slice::iterator final { public: - using iterator_category = std::random_access_iterator_tag; - using value_type = T; - using difference_type = std::ptrdiff_t; - using pointer = typename std::add_pointer::type; - using reference = typename std::add_lvalue_reference::type; - - reference operator*() const noexcept; - pointer operator->() const noexcept; - reference operator[](difference_type) const noexcept; - - iterator &operator++() noexcept; - iterator operator++(int) noexcept; - iterator &operator--() noexcept; - iterator operator--(int) noexcept; - - iterator &operator+=(difference_type) noexcept; - iterator &operator-=(difference_type) noexcept; - iterator operator+(difference_type) const noexcept; - iterator operator-(difference_type) const noexcept; - difference_type operator-(const iterator &) const noexcept; - - bool operator==(const iterator &) const noexcept; - bool operator!=(const iterator &) const noexcept; - bool operator<(const iterator &) const noexcept; - bool operator<=(const iterator &) const noexcept; - bool operator>(const iterator &) const noexcept; - bool operator>=(const iterator &) const noexcept; + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = typename std::add_pointer::type; + using reference = typename std::add_lvalue_reference::type; + + reference operator*() const noexcept; + pointer operator->() const noexcept; + reference operator[](difference_type) const noexcept; + + iterator &operator++() noexcept; + iterator operator++(int) noexcept; + iterator &operator--() noexcept; + iterator operator--(int) noexcept; + + iterator &operator+=(difference_type) noexcept; + iterator &operator-=(difference_type) noexcept; + iterator operator+(difference_type) const noexcept; + iterator operator-(difference_type) const noexcept; + difference_type operator-(const iterator &) const noexcept; + + bool operator==(const iterator &) const noexcept; + bool operator!=(const iterator &) const noexcept; + bool operator<(const iterator &) const noexcept; + bool operator<=(const iterator &) const noexcept; + bool operator>(const iterator &) const noexcept; + bool operator>=(const iterator &) const noexcept; private: - friend class Slice; - void *pos; - std::size_t stride; + friend class Slice; + void *pos; + std::size_t stride; }; -#endif // CXXBRIDGE1_RUST_SLICE +#endif // CXXBRIDGE1_RUST_SLICE #ifndef CXXBRIDGE1_RUST_BOX // https://cxx.rs/binding/box.html template class Box final { public: - using element_type = T; - using const_pointer = - typename std::add_pointer::type>::type; - using pointer = typename std::add_pointer::type; + using element_type = T; + using const_pointer = + typename std::add_pointer::type>::type; + using pointer = typename std::add_pointer::type; - Box() = delete; - Box(Box &&) noexcept; - ~Box() noexcept; + Box() = delete; + Box(Box &&) noexcept; + ~Box() noexcept; - explicit Box(const T &); - explicit Box(T &&); + explicit Box(const T &); + explicit Box(T &&); - Box &operator=(Box &&) &noexcept; + Box &operator=(Box &&) &noexcept; - const T *operator->() const noexcept; - const T &operator*() const noexcept; - T *operator->() noexcept; - T &operator*() noexcept; + const T *operator->() const noexcept; + const T &operator*() const noexcept; + T *operator->() noexcept; + T &operator*() noexcept; - template - static Box in_place(Fields &&...); + template + static Box in_place(Fields &&...); - void swap(Box &) noexcept; + void swap(Box &) noexcept; - // Important: requires that `raw` came from an into_raw call. Do not pass a - // pointer from `new` or any other source. - static Box from_raw(T *) noexcept; + // Important: requires that `raw` came from an into_raw call. Do not pass a + // pointer from `new` or any other source. + static Box from_raw(T *) noexcept; - T *into_raw() noexcept; + T *into_raw() noexcept; - /* Deprecated */ using value_type = element_type; + /* Deprecated */ using value_type = element_type; private: - class uninit; - class allocation; - Box(uninit) noexcept; - void drop() noexcept; + class uninit; + class allocation; + Box(uninit) noexcept; + void drop() noexcept; - friend void swap(Box &lhs, Box &rhs) noexcept { lhs.swap(rhs); } + friend void swap(Box &lhs, Box &rhs) noexcept { lhs.swap(rhs); } - T *ptr; + T *ptr; }; -#endif // CXXBRIDGE1_RUST_BOX +#endif // CXXBRIDGE1_RUST_BOX #ifndef CXXBRIDGE1_RUST_VEC // https://cxx.rs/binding/vec.html template class Vec final { public: - using value_type = T; - - Vec() noexcept; - Vec(std::initializer_list); - Vec(const Vec &); - Vec(Vec &&) noexcept; - ~Vec() noexcept; - - Vec &operator=(Vec &&) &noexcept; - Vec &operator=(const Vec &) &; - - std::size_t size() const noexcept; - bool empty() const noexcept; - const T *data() const noexcept; - T *data() noexcept; - std::size_t capacity() const noexcept; - - const T &operator[](std::size_t n) const noexcept; - const T &at(std::size_t n) const; - const T &front() const noexcept; - const T &back() const noexcept; - - T &operator[](std::size_t n) noexcept; - T &at(std::size_t n); - T &front() noexcept; - T &back() noexcept; - - void reserve(std::size_t new_cap); - void push_back(const T &value); - void push_back(T &&value); - template - void emplace_back(Args &&...args); - void truncate(std::size_t len); - void clear(); - - using iterator = typename Slice::iterator; - iterator begin() noexcept; - iterator end() noexcept; - - using const_iterator = typename Slice::iterator; - const_iterator begin() const noexcept; - const_iterator end() const noexcept; - const_iterator cbegin() const noexcept; - const_iterator cend() const noexcept; - - void swap(Vec &) noexcept; - - // Internal API only intended for the cxxbridge code generator. - Vec(unsafe_bitcopy_t, const Vec &) noexcept; + using value_type = T; + + Vec() noexcept; + Vec(std::initializer_list); + Vec(const Vec &); + Vec(Vec &&) noexcept; + ~Vec() noexcept; + + Vec &operator=(Vec &&) &noexcept; + Vec &operator=(const Vec &) &; + + std::size_t size() const noexcept; + bool empty() const noexcept; + const T *data() const noexcept; + T *data() noexcept; + std::size_t capacity() const noexcept; + + const T &operator[](std::size_t n) const noexcept; + const T &at(std::size_t n) const; + const T &front() const noexcept; + const T &back() const noexcept; + + T &operator[](std::size_t n) noexcept; + T &at(std::size_t n); + T &front() noexcept; + T &back() noexcept; + + void reserve(std::size_t new_cap); + void push_back(const T &value); + void push_back(T &&value); + template + void emplace_back(Args &&...args); + void truncate(std::size_t len); + void clear(); + + using iterator = typename Slice::iterator; + iterator begin() noexcept; + iterator end() noexcept; + + using const_iterator = typename Slice::iterator; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + void swap(Vec &) noexcept; + + // Internal API only intended for the cxxbridge code generator. + Vec(unsafe_bitcopy_t, const Vec &) noexcept; private: - void reserve_total(std::size_t new_cap) noexcept; - void set_len(std::size_t len) noexcept; - void drop() noexcept; + void reserve_total(std::size_t new_cap) noexcept; + void set_len(std::size_t len) noexcept; + void drop() noexcept; - friend void swap(Vec &lhs, Vec &rhs) noexcept { lhs.swap(rhs); } + friend void swap(Vec &lhs, Vec &rhs) noexcept { lhs.swap(rhs); } - // Size and alignment statically verified by rust_vec.rs. - std::array repr; + // Size and alignment statically verified by rust_vec.rs. + std::array repr; }; -#endif // CXXBRIDGE1_RUST_VEC +#endif // CXXBRIDGE1_RUST_VEC #ifndef CXXBRIDGE1_RUST_FN // https://cxx.rs/binding/fn.html @@ -373,36 +372,36 @@ class Fn; template class Fn final { public: - Ret operator()(Args... args) const noexcept; - Fn operator*() const noexcept; + Ret operator()(Args... args) const noexcept; + Fn operator*() const noexcept; private: - Ret (*trampoline)(Args..., void *fn) noexcept; - void *fn; + Ret (*trampoline)(Args..., void *fn) noexcept; + void *fn; }; -#endif // CXXBRIDGE1_RUST_FN +#endif // CXXBRIDGE1_RUST_FN #ifndef CXXBRIDGE1_RUST_ERROR #define CXXBRIDGE1_RUST_ERROR // https://cxx.rs/binding/result.html class Error final : public std::exception { public: - Error(const Error &); - Error(Error &&) noexcept; - ~Error() noexcept override; + Error(const Error &); + Error(Error &&) noexcept; + ~Error() noexcept override; - Error &operator=(const Error &) &; - Error &operator=(Error &&) &noexcept; + Error &operator=(const Error &) &; + Error &operator=(Error &&) &noexcept; - const char *what() const noexcept override; + const char *what() const noexcept override; private: - Error() noexcept = default; - friend impl; - const char *msg; - std::size_t len; + Error() noexcept = default; + friend impl; + const char *msg; + std::size_t len; }; -#endif // CXXBRIDGE1_RUST_ERROR +#endif // CXXBRIDGE1_RUST_ERROR #ifndef CXXBRIDGE1_RUST_ISIZE #define CXXBRIDGE1_RUST_ISIZE @@ -411,7 +410,7 @@ using isize = SSIZE_T; #else using isize = ssize_t; #endif -#endif // CXXBRIDGE1_RUST_ISIZE +#endif // CXXBRIDGE1_RUST_ISIZE std::ostream &operator<<(std::ostream &, const String &); std::ostream &operator<<(std::ostream &, const Str &); @@ -421,11 +420,11 @@ std::ostream &operator<<(std::ostream &, const Str &); // Base class of generated opaque Rust types. class Opaque { public: - Opaque() = delete; - Opaque(const Opaque &) = delete; - ~Opaque() = delete; + Opaque() = delete; + Opaque(const Opaque &) = delete; + ~Opaque() = delete; }; -#endif // CXXBRIDGE1_RUST_OPAQUE +#endif // CXXBRIDGE1_RUST_OPAQUE template std::size_t size_of(); @@ -457,7 +456,7 @@ using u8 = std::uint8_t; using u16 = std::uint16_t; using u32 = std::uint32_t; using u64 = std::uint64_t; -using usize = std::size_t; // see static asserts in cxx.cc +using usize = std::size_t; // see static asserts in cxx.cc using i8 = std::int8_t; using i16 = std::int16_t; using i32 = std::int32_t; @@ -480,6 +479,8 @@ using fn = Fn; template using is_relocatable = IsRelocatable; + + //////////////////////////////////////////////////////////////////////////////// /// end public API, begin implementation details @@ -487,230 +488,230 @@ using is_relocatable = IsRelocatable; #define CXXBRIDGE1_PANIC template void panic [[noreturn]] (const char *msg); -#endif // CXXBRIDGE1_PANIC +#endif // CXXBRIDGE1_PANIC #ifndef CXXBRIDGE1_RUST_FN #define CXXBRIDGE1_RUST_FN template Ret Fn::operator()(Args... args) const noexcept { - return (*this->trampoline)(std::forward(args)..., this->fn); + return (*this->trampoline)(std::forward(args)..., this->fn); } template Fn Fn::operator*() const noexcept { - return *this; + return *this; } -#endif // CXXBRIDGE1_RUST_FN +#endif // CXXBRIDGE1_RUST_FN #ifndef CXXBRIDGE1_RUST_BITCOPY_T #define CXXBRIDGE1_RUST_BITCOPY_T struct unsafe_bitcopy_t final { - explicit unsafe_bitcopy_t() = default; + explicit unsafe_bitcopy_t() = default; }; -#endif // CXXBRIDGE1_RUST_BITCOPY_T +#endif // CXXBRIDGE1_RUST_BITCOPY_T #ifndef CXXBRIDGE1_RUST_BITCOPY #define CXXBRIDGE1_RUST_BITCOPY constexpr unsafe_bitcopy_t unsafe_bitcopy{}; -#endif // CXXBRIDGE1_RUST_BITCOPY +#endif // CXXBRIDGE1_RUST_BITCOPY #ifndef CXXBRIDGE1_RUST_SLICE #define CXXBRIDGE1_RUST_SLICE template Slice::Slice() noexcept { - sliceInit(this, reinterpret_cast(align_of()), 0); + sliceInit(this, reinterpret_cast(align_of()), 0); } template Slice::Slice(T *s, std::size_t count) noexcept { - assert(s != nullptr || count == 0); - sliceInit(this, - s == nullptr && count == 0 - ? reinterpret_cast(align_of()) - : const_cast::type *>(s), - count); + assert(s != nullptr || count == 0); + sliceInit(this, + s == nullptr && count == 0 + ? reinterpret_cast(align_of()) + : const_cast::type *>(s), + count); } template T *Slice::data() const noexcept { - return reinterpret_cast(slicePtr(this)); + return reinterpret_cast(slicePtr(this)); } template std::size_t Slice::size() const noexcept { - return sliceLen(this); + return sliceLen(this); } template std::size_t Slice::length() const noexcept { - return this->size(); + return this->size(); } template bool Slice::empty() const noexcept { - return this->size() == 0; + return this->size() == 0; } template T &Slice::operator[](std::size_t n) const noexcept { - assert(n < this->size()); - auto ptr = static_cast(slicePtr(this)) + size_of() * n; - return *reinterpret_cast(ptr); + assert(n < this->size()); + auto ptr = static_cast(slicePtr(this)) + size_of() * n; + return *reinterpret_cast(ptr); } template T &Slice::at(std::size_t n) const { - if (n >= this->size()) { - panic("rust::Slice index out of range"); - } - return (*this)[n]; + if (n >= this->size()) { + panic("rust::Slice index out of range"); + } + return (*this)[n]; } template T &Slice::front() const noexcept { - assert(!this->empty()); - return (*this)[0]; + assert(!this->empty()); + return (*this)[0]; } template T &Slice::back() const noexcept { - assert(!this->empty()); - return (*this)[this->size() - 1]; + assert(!this->empty()); + return (*this)[this->size() - 1]; } template -typename Slice::iterator::reference Slice::iterator::operator*() - const noexcept { - return *static_cast(this->pos); +typename Slice::iterator::reference +Slice::iterator::operator*() const noexcept { + return *static_cast(this->pos); } template -typename Slice::iterator::pointer Slice::iterator::operator->() - const noexcept { - return static_cast(this->pos); +typename Slice::iterator::pointer +Slice::iterator::operator->() const noexcept { + return static_cast(this->pos); } template typename Slice::iterator::reference Slice::iterator::operator[]( typename Slice::iterator::difference_type n) const noexcept { - auto ptr = static_cast(this->pos) + this->stride * n; - return *reinterpret_cast(ptr); + auto ptr = static_cast(this->pos) + this->stride * n; + return *reinterpret_cast(ptr); } template typename Slice::iterator &Slice::iterator::operator++() noexcept { - this->pos = static_cast(this->pos) + this->stride; - return *this; + this->pos = static_cast(this->pos) + this->stride; + return *this; } template typename Slice::iterator Slice::iterator::operator++(int) noexcept { - auto ret = iterator(*this); - this->pos = static_cast(this->pos) + this->stride; - return ret; + auto ret = iterator(*this); + this->pos = static_cast(this->pos) + this->stride; + return ret; } template typename Slice::iterator &Slice::iterator::operator--() noexcept { - this->pos = static_cast(this->pos) - this->stride; - return *this; + this->pos = static_cast(this->pos) - this->stride; + return *this; } template typename Slice::iterator Slice::iterator::operator--(int) noexcept { - auto ret = iterator(*this); - this->pos = static_cast(this->pos) - this->stride; - return ret; + auto ret = iterator(*this); + this->pos = static_cast(this->pos) - this->stride; + return ret; } template typename Slice::iterator &Slice::iterator::operator+=( typename Slice::iterator::difference_type n) noexcept { - this->pos = static_cast(this->pos) + this->stride * n; - return *this; + this->pos = static_cast(this->pos) + this->stride * n; + return *this; } template typename Slice::iterator &Slice::iterator::operator-=( typename Slice::iterator::difference_type n) noexcept { - this->pos = static_cast(this->pos) - this->stride * n; - return *this; + this->pos = static_cast(this->pos) - this->stride * n; + return *this; } template typename Slice::iterator Slice::iterator::operator+( typename Slice::iterator::difference_type n) const noexcept { - auto ret = iterator(*this); - ret.pos = static_cast(this->pos) + this->stride * n; - return ret; + auto ret = iterator(*this); + ret.pos = static_cast(this->pos) + this->stride * n; + return ret; } template typename Slice::iterator Slice::iterator::operator-( typename Slice::iterator::difference_type n) const noexcept { - auto ret = iterator(*this); - ret.pos = static_cast(this->pos) - this->stride * n; - return ret; + auto ret = iterator(*this); + ret.pos = static_cast(this->pos) - this->stride * n; + return ret; } template -typename Slice::iterator::difference_type Slice::iterator::operator-( - const iterator &other) const noexcept { - auto diff = std::distance(static_cast(other.pos), - static_cast(this->pos)); - return diff / this->stride; +typename Slice::iterator::difference_type +Slice::iterator::operator-(const iterator &other) const noexcept { + auto diff = std::distance(static_cast(other.pos), + static_cast(this->pos)); + return diff / this->stride; } template bool Slice::iterator::operator==(const iterator &other) const noexcept { - return this->pos == other.pos; + return this->pos == other.pos; } template bool Slice::iterator::operator!=(const iterator &other) const noexcept { - return this->pos != other.pos; + return this->pos != other.pos; } template bool Slice::iterator::operator<(const iterator &other) const noexcept { - return this->pos < other.pos; + return this->pos < other.pos; } template bool Slice::iterator::operator<=(const iterator &other) const noexcept { - return this->pos <= other.pos; + return this->pos <= other.pos; } template bool Slice::iterator::operator>(const iterator &other) const noexcept { - return this->pos > other.pos; + return this->pos > other.pos; } template bool Slice::iterator::operator>=(const iterator &other) const noexcept { - return this->pos >= other.pos; + return this->pos >= other.pos; } template typename Slice::iterator Slice::begin() const noexcept { - iterator it; - it.pos = slicePtr(this); - it.stride = size_of(); - return it; + iterator it; + it.pos = slicePtr(this); + it.stride = size_of(); + return it; } template typename Slice::iterator Slice::end() const noexcept { - iterator it = this->begin(); - it.pos = static_cast(it.pos) + it.stride * this->size(); - return it; + iterator it = this->begin(); + it.pos = static_cast(it.pos) + it.stride * this->size(); + return it; } template void Slice::swap(Slice &rhs) noexcept { - std::swap(*this, rhs); + std::swap(*this, rhs); } -#endif // CXXBRIDGE1_RUST_SLICE +#endif // CXXBRIDGE1_RUST_SLICE #ifndef CXXBRIDGE1_RUST_BOX #define CXXBRIDGE1_RUST_BOX @@ -719,287 +720,287 @@ class Box::uninit {}; template class Box::allocation { - static T *alloc() noexcept; - static void dealloc(T *) noexcept; + static T *alloc() noexcept; + static void dealloc(T *) noexcept; public: - allocation() noexcept : ptr(alloc()) {} - ~allocation() noexcept { - if (this->ptr) { - dealloc(this->ptr); - } + allocation() noexcept : ptr(alloc()) {} + ~allocation() noexcept { + if (this->ptr) { + dealloc(this->ptr); } - T *ptr; + } + T *ptr; }; template Box::Box(Box &&other) noexcept : ptr(other.ptr) { - other.ptr = nullptr; + other.ptr = nullptr; } template Box::Box(const T &val) { - allocation alloc; - ::new (alloc.ptr) T(val); - this->ptr = alloc.ptr; - alloc.ptr = nullptr; + allocation alloc; + ::new (alloc.ptr) T(val); + this->ptr = alloc.ptr; + alloc.ptr = nullptr; } template Box::Box(T &&val) { - allocation alloc; - ::new (alloc.ptr) T(std::move(val)); - this->ptr = alloc.ptr; - alloc.ptr = nullptr; + allocation alloc; + ::new (alloc.ptr) T(std::move(val)); + this->ptr = alloc.ptr; + alloc.ptr = nullptr; } template Box::~Box() noexcept { - if (this->ptr) { - this->drop(); - } + if (this->ptr) { + this->drop(); + } } template Box &Box::operator=(Box &&other) &noexcept { - if (this->ptr) { - this->drop(); - } - this->ptr = other.ptr; - other.ptr = nullptr; - return *this; + if (this->ptr) { + this->drop(); + } + this->ptr = other.ptr; + other.ptr = nullptr; + return *this; } template const T *Box::operator->() const noexcept { - return this->ptr; + return this->ptr; } template const T &Box::operator*() const noexcept { - return *this->ptr; + return *this->ptr; } template T *Box::operator->() noexcept { - return this->ptr; + return this->ptr; } template T &Box::operator*() noexcept { - return *this->ptr; + return *this->ptr; } template template Box Box::in_place(Fields &&...fields) { - allocation alloc; - auto ptr = alloc.ptr; - ::new (ptr) T{std::forward(fields)...}; - alloc.ptr = nullptr; - return from_raw(ptr); + allocation alloc; + auto ptr = alloc.ptr; + ::new (ptr) T{std::forward(fields)...}; + alloc.ptr = nullptr; + return from_raw(ptr); } template void Box::swap(Box &rhs) noexcept { - using std::swap; - swap(this->ptr, rhs.ptr); + using std::swap; + swap(this->ptr, rhs.ptr); } template Box Box::from_raw(T *raw) noexcept { - Box box = uninit{}; - box.ptr = raw; - return box; + Box box = uninit{}; + box.ptr = raw; + return box; } template T *Box::into_raw() noexcept { - T *raw = this->ptr; - this->ptr = nullptr; - return raw; + T *raw = this->ptr; + this->ptr = nullptr; + return raw; } template Box::Box(uninit) noexcept {} -#endif // CXXBRIDGE1_RUST_BOX +#endif // CXXBRIDGE1_RUST_BOX #ifndef CXXBRIDGE1_RUST_VEC #define CXXBRIDGE1_RUST_VEC template Vec::Vec(std::initializer_list init) : Vec{} { - this->reserve_total(init.size()); - std::move(init.begin(), init.end(), std::back_inserter(*this)); + this->reserve_total(init.size()); + std::move(init.begin(), init.end(), std::back_inserter(*this)); } template Vec::Vec(const Vec &other) : Vec() { - this->reserve_total(other.size()); - std::copy(other.begin(), other.end(), std::back_inserter(*this)); + this->reserve_total(other.size()); + std::copy(other.begin(), other.end(), std::back_inserter(*this)); } template Vec::Vec(Vec &&other) noexcept : repr(other.repr) { - new (&other) Vec(); + new (&other) Vec(); } template Vec::~Vec() noexcept { - this->drop(); + this->drop(); } template Vec &Vec::operator=(Vec &&other) &noexcept { - this->drop(); - this->repr = other.repr; - new (&other) Vec(); - return *this; + this->drop(); + this->repr = other.repr; + new (&other) Vec(); + return *this; } template Vec &Vec::operator=(const Vec &other) & { - if (this != &other) { - this->drop(); - new (this) Vec(other); - } - return *this; + if (this != &other) { + this->drop(); + new (this) Vec(other); + } + return *this; } template bool Vec::empty() const noexcept { - return this->size() == 0; + return this->size() == 0; } template T *Vec::data() noexcept { - return const_cast(const_cast *>(this)->data()); + return const_cast(const_cast *>(this)->data()); } template const T &Vec::operator[](std::size_t n) const noexcept { - assert(n < this->size()); - auto data = reinterpret_cast(this->data()); - return *reinterpret_cast(data + n * size_of()); + assert(n < this->size()); + auto data = reinterpret_cast(this->data()); + return *reinterpret_cast(data + n * size_of()); } template const T &Vec::at(std::size_t n) const { - if (n >= this->size()) { - panic("rust::Vec index out of range"); - } - return (*this)[n]; + if (n >= this->size()) { + panic("rust::Vec index out of range"); + } + return (*this)[n]; } template const T &Vec::front() const noexcept { - assert(!this->empty()); - return (*this)[0]; + assert(!this->empty()); + return (*this)[0]; } template const T &Vec::back() const noexcept { - assert(!this->empty()); - return (*this)[this->size() - 1]; + assert(!this->empty()); + return (*this)[this->size() - 1]; } template T &Vec::operator[](std::size_t n) noexcept { - assert(n < this->size()); - auto data = reinterpret_cast(this->data()); - return *reinterpret_cast(data + n * size_of()); + assert(n < this->size()); + auto data = reinterpret_cast(this->data()); + return *reinterpret_cast(data + n * size_of()); } template T &Vec::at(std::size_t n) { - if (n >= this->size()) { - panic("rust::Vec index out of range"); - } - return (*this)[n]; + if (n >= this->size()) { + panic("rust::Vec index out of range"); + } + return (*this)[n]; } template T &Vec::front() noexcept { - assert(!this->empty()); - return (*this)[0]; + assert(!this->empty()); + return (*this)[0]; } template T &Vec::back() noexcept { - assert(!this->empty()); - return (*this)[this->size() - 1]; + assert(!this->empty()); + return (*this)[this->size() - 1]; } template void Vec::reserve(std::size_t new_cap) { - this->reserve_total(new_cap); + this->reserve_total(new_cap); } template void Vec::push_back(const T &value) { - this->emplace_back(value); + this->emplace_back(value); } template void Vec::push_back(T &&value) { - this->emplace_back(std::move(value)); + this->emplace_back(std::move(value)); } template template void Vec::emplace_back(Args &&...args) { - auto size = this->size(); - this->reserve_total(size + 1); - ::new (reinterpret_cast(reinterpret_cast(this->data()) + - size * size_of())) - T(std::forward(args)...); - this->set_len(size + 1); + auto size = this->size(); + this->reserve_total(size + 1); + ::new (reinterpret_cast(reinterpret_cast(this->data()) + + size * size_of())) + T(std::forward(args)...); + this->set_len(size + 1); } template void Vec::clear() { - this->truncate(0); + this->truncate(0); } template typename Vec::iterator Vec::begin() noexcept { - return Slice(this->data(), this->size()).begin(); + return Slice(this->data(), this->size()).begin(); } template typename Vec::iterator Vec::end() noexcept { - return Slice(this->data(), this->size()).end(); + return Slice(this->data(), this->size()).end(); } template typename Vec::const_iterator Vec::begin() const noexcept { - return this->cbegin(); + return this->cbegin(); } template typename Vec::const_iterator Vec::end() const noexcept { - return this->cend(); + return this->cend(); } template typename Vec::const_iterator Vec::cbegin() const noexcept { - return Slice(this->data(), this->size()).begin(); + return Slice(this->data(), this->size()).begin(); } template typename Vec::const_iterator Vec::cend() const noexcept { - return Slice(this->data(), this->size()).end(); + return Slice(this->data(), this->size()).end(); } template void Vec::swap(Vec &rhs) noexcept { - using std::swap; - swap(this->repr, rhs.repr); + using std::swap; + swap(this->repr, rhs.repr); } // Internal API only intended for the cxxbridge code generator. template Vec::Vec(unsafe_bitcopy_t, const Vec &bits) noexcept : repr(bits.repr) {} -#endif // CXXBRIDGE1_RUST_VEC +#endif // CXXBRIDGE1_RUST_VEC #ifndef CXXBRIDGE1_IS_COMPLETE #define CXXBRIDGE1_IS_COMPLETE @@ -1009,72 +1010,72 @@ template struct is_complete : std::false_type {}; template struct is_complete : std::true_type {}; -} // namespace -} // namespace detail -#endif // CXXBRIDGE1_IS_COMPLETE +} // namespace +} // namespace detail +#endif // CXXBRIDGE1_IS_COMPLETE #ifndef CXXBRIDGE1_LAYOUT #define CXXBRIDGE1_LAYOUT class layout { - template - friend std::size_t size_of(); - template - friend std::size_t align_of(); - template - static typename std::enable_if::value, - std::size_t>::type - do_size_of() { - return T::layout::size(); - } - template - static typename std::enable_if::value, - std::size_t>::type - do_size_of() { - return sizeof(T); - } - template - static typename std::enable_if::value, - std::size_t>::type - size_of() { - return do_size_of(); - } - template - static typename std::enable_if::value, - std::size_t>::type - do_align_of() { - return T::layout::align(); - } - template - static typename std::enable_if::value, - std::size_t>::type - do_align_of() { - return alignof(T); - } - template - static typename std::enable_if::value, - std::size_t>::type - align_of() { - return do_align_of(); - } + template + friend std::size_t size_of(); + template + friend std::size_t align_of(); + template + static typename std::enable_if::value, + std::size_t>::type + do_size_of() { + return T::layout::size(); + } + template + static typename std::enable_if::value, + std::size_t>::type + do_size_of() { + return sizeof(T); + } + template + static + typename std::enable_if::value, std::size_t>::type + size_of() { + return do_size_of(); + } + template + static typename std::enable_if::value, + std::size_t>::type + do_align_of() { + return T::layout::align(); + } + template + static typename std::enable_if::value, + std::size_t>::type + do_align_of() { + return alignof(T); + } + template + static + typename std::enable_if::value, std::size_t>::type + align_of() { + return do_align_of(); + } }; template std::size_t size_of() { - return layout::size_of(); + return layout::size_of(); } template std::size_t align_of() { - return layout::align_of(); + return layout::align_of(); } -#endif // CXXBRIDGE1_LAYOUT +#endif // CXXBRIDGE1_LAYOUT #ifndef CXXBRIDGE1_RELOCATABLE #define CXXBRIDGE1_RELOCATABLE namespace detail { template struct make_void { - using type = void; + using type = void; }; template @@ -1094,7 +1095,7 @@ using detect_IsRelocatable = typename T::IsRelocatable; template struct get_IsRelocatable : std::is_same {}; -} // namespace detail +} // namespace detail template struct IsRelocatable @@ -1104,7 +1105,7 @@ struct IsRelocatable std::integral_constant< bool, std::is_trivially_move_constructible::value && std::is_trivially_destructible::value>>::type {}; -#endif // CXXBRIDGE1_RELOCATABLE +#endif // CXXBRIDGE1_RELOCATABLE -} // namespace cxxbridge1 -} // namespace rust +} // namespace cxxbridge1 +} // namespace rust diff --git a/lib/cppbind/mmcore/include/mmcore/_cxxbridge.h b/lib/cppbind/mmcore/include/mmcore/_cxxbridge.h new file mode 100644 index 000000000..75d9e5d22 --- /dev/null +++ b/lib/cppbind/mmcore/include/mmcore/_cxxbridge.h @@ -0,0 +1,150 @@ +#pragma once +#include "mmcore/_cxx.h" +#include "mmcore/_symbol_export.h" +#include +#include +#include + +namespace rust { +inline namespace cxxbridge1 { +// #include "rust/cxx.h" + +struct unsafe_bitcopy_t; + +namespace { +template +class impl; +} // namespace + +#ifndef CXXBRIDGE1_RUST_STRING +#define CXXBRIDGE1_RUST_STRING +class String final { +public: + String() noexcept; + String(const String &) noexcept; + String(String &&) noexcept; + ~String() noexcept; + + String(const std::string &); + String(const char *); + String(const char *, std::size_t); + String(const char16_t *); + String(const char16_t *, std::size_t); + + static String lossy(const std::string &) noexcept; + static String lossy(const char *) noexcept; + static String lossy(const char *, std::size_t) noexcept; + static String lossy(const char16_t *) noexcept; + static String lossy(const char16_t *, std::size_t) noexcept; + + String &operator=(const String &) &noexcept; + String &operator=(String &&) &noexcept; + + explicit operator std::string() const; + + const char *data() const noexcept; + std::size_t size() const noexcept; + std::size_t length() const noexcept; + bool empty() const noexcept; + + const char *c_str() noexcept; + + std::size_t capacity() const noexcept; + void reserve(size_t new_cap) noexcept; + + using iterator = char *; + iterator begin() noexcept; + iterator end() noexcept; + + using const_iterator = const char *; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + bool operator==(const String &) const noexcept; + bool operator!=(const String &) const noexcept; + bool operator<(const String &) const noexcept; + bool operator<=(const String &) const noexcept; + bool operator>(const String &) const noexcept; + bool operator>=(const String &) const noexcept; + + void swap(String &) noexcept; + + String(unsafe_bitcopy_t, const String &) noexcept; + +private: + struct lossy_t; + String(lossy_t, const char *, std::size_t) noexcept; + String(lossy_t, const char16_t *, std::size_t) noexcept; + friend void swap(String &lhs, String &rhs) noexcept { lhs.swap(rhs); } + + std::array repr; +}; +#endif // CXXBRIDGE1_RUST_STRING + +#ifndef CXXBRIDGE1_RUST_STR +#define CXXBRIDGE1_RUST_STR +class Str final { +public: + Str() noexcept; + Str(const String &) noexcept; + Str(const std::string &); + Str(const char *); + Str(const char *, std::size_t); + + Str &operator=(const Str &) &noexcept = default; + + explicit operator std::string() const; + + const char *data() const noexcept; + std::size_t size() const noexcept; + std::size_t length() const noexcept; + bool empty() const noexcept; + + Str(const Str &) noexcept = default; + ~Str() noexcept = default; + + using iterator = const char *; + using const_iterator = const char *; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + bool operator==(const Str &) const noexcept; + bool operator!=(const Str &) const noexcept; + bool operator<(const Str &) const noexcept; + bool operator<=(const Str &) const noexcept; + bool operator>(const Str &) const noexcept; + bool operator>=(const Str &) const noexcept; + + void swap(Str &) noexcept; + +private: + class uninit; + Str(uninit) noexcept; + friend impl; + + std::array repr; +}; +#endif // CXXBRIDGE1_RUST_STR +} // namespace cxxbridge1 +} // namespace rust + +namespace mmcore { + enum class DistortionDirection : ::std::uint8_t; +} + +namespace mmcore { +#ifndef CXXBRIDGE1_ENUM_mmcore$DistortionDirection +#define CXXBRIDGE1_ENUM_mmcore$DistortionDirection +enum class DistortionDirection : ::std::uint8_t { + kUndistort = 0, + kRedistort = 1, + kNumDistortionDirection = 2, +}; +#endif // CXXBRIDGE1_ENUM_mmcore$DistortionDirection + +MMCORE_API_EXPORT ::rust::String shim_expand_file_path_string(::rust::Str value, ::std::int32_t frame) noexcept; +} // namespace mmcore diff --git a/lib/cppbind/mmcore/include/mmcore/_types.h b/lib/cppbind/mmcore/include/mmcore/_types.h index ca5b63f87..0d14b3ed0 100644 --- a/lib/cppbind/mmcore/include/mmcore/_types.h +++ b/lib/cppbind/mmcore/include/mmcore/_types.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020, 2021, 2023 David Cattermole. + * Copyright (C) 2020, 2021, 2023, 2024 David Cattermole. * * This file is part of mmSolver. * @@ -23,4 +23,8 @@ #include -namespace mmcore {} // namespace mmcore +namespace mmcore { + +using FrameValue = int32_t; + +} // namespace mmcore diff --git a/lib/cppbind/mmcore/include/mmcore/lib.h b/lib/cppbind/mmcore/include/mmcore/lib.h index 0bdd2bf25..546ea682b 100644 --- a/lib/cppbind/mmcore/include/mmcore/lib.h +++ b/lib/cppbind/mmcore/include/mmcore/lib.h @@ -26,6 +26,7 @@ #include #include "_cxx.h" +#include "_cxxbridge.h" #include "_symbol_export.h" #include "_types.h" #include "mmcamera.h" @@ -35,6 +36,10 @@ #include "mmhash.h" #include "mmmath.h" -namespace mmcore {} // namespace mmcore +namespace mmcore { + +rust::String expand_file_path_string(const rust::Str& value, FrameValue frame); + +} // namespace mmcore #endif // MM_CORE_LIB_H diff --git a/lib/cppbind/mmcore/include/mmcore/mmcore.h b/lib/cppbind/mmcore/include/mmcore/mmcore.h index d3a388538..e2aada631 100644 --- a/lib/cppbind/mmcore/include/mmcore/mmcore.h +++ b/lib/cppbind/mmcore/include/mmcore/mmcore.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 David Cattermole. + * Copyright (C) 2023, 2024 David Cattermole. * * This file is part of mmSolver. * @@ -23,6 +23,7 @@ #define MM_CORE_MM_CORE_H #include "_cxx.h" +#include "_cxxbridge.h" #include "_types.h" #include "lib.h" #include "mmcamera.h" diff --git a/lib/cppbind/mmcore/src/_cxxbridge.cpp b/lib/cppbind/mmcore/src/_cxxbridge.cpp new file mode 100644 index 000000000..515d304e7 --- /dev/null +++ b/lib/cppbind/mmcore/src/_cxxbridge.cpp @@ -0,0 +1,180 @@ +#include "mmcore/_cxx.h" +#include "mmcore/_symbol_export.h" +#include +#include +#include +#include +#include +#include + +namespace rust { +inline namespace cxxbridge1 { +// #include "rust/cxx.h" + +struct unsafe_bitcopy_t; + +namespace { +template +class impl; +} // namespace + +#ifndef CXXBRIDGE1_RUST_STRING +#define CXXBRIDGE1_RUST_STRING +class String final { +public: + String() noexcept; + String(const String &) noexcept; + String(String &&) noexcept; + ~String() noexcept; + + String(const std::string &); + String(const char *); + String(const char *, std::size_t); + String(const char16_t *); + String(const char16_t *, std::size_t); + + static String lossy(const std::string &) noexcept; + static String lossy(const char *) noexcept; + static String lossy(const char *, std::size_t) noexcept; + static String lossy(const char16_t *) noexcept; + static String lossy(const char16_t *, std::size_t) noexcept; + + String &operator=(const String &) &noexcept; + String &operator=(String &&) &noexcept; + + explicit operator std::string() const; + + const char *data() const noexcept; + std::size_t size() const noexcept; + std::size_t length() const noexcept; + bool empty() const noexcept; + + const char *c_str() noexcept; + + std::size_t capacity() const noexcept; + void reserve(size_t new_cap) noexcept; + + using iterator = char *; + iterator begin() noexcept; + iterator end() noexcept; + + using const_iterator = const char *; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + bool operator==(const String &) const noexcept; + bool operator!=(const String &) const noexcept; + bool operator<(const String &) const noexcept; + bool operator<=(const String &) const noexcept; + bool operator>(const String &) const noexcept; + bool operator>=(const String &) const noexcept; + + void swap(String &) noexcept; + + String(unsafe_bitcopy_t, const String &) noexcept; + +private: + struct lossy_t; + String(lossy_t, const char *, std::size_t) noexcept; + String(lossy_t, const char16_t *, std::size_t) noexcept; + friend void swap(String &lhs, String &rhs) noexcept { lhs.swap(rhs); } + + std::array repr; +}; +#endif // CXXBRIDGE1_RUST_STRING + +#ifndef CXXBRIDGE1_RUST_STR +#define CXXBRIDGE1_RUST_STR +class Str final { +public: + Str() noexcept; + Str(const String &) noexcept; + Str(const std::string &); + Str(const char *); + Str(const char *, std::size_t); + + Str &operator=(const Str &) &noexcept = default; + + explicit operator std::string() const; + + const char *data() const noexcept; + std::size_t size() const noexcept; + std::size_t length() const noexcept; + bool empty() const noexcept; + + Str(const Str &) noexcept = default; + ~Str() noexcept = default; + + using iterator = const char *; + using const_iterator = const char *; + const_iterator begin() const noexcept; + const_iterator end() const noexcept; + const_iterator cbegin() const noexcept; + const_iterator cend() const noexcept; + + bool operator==(const Str &) const noexcept; + bool operator!=(const Str &) const noexcept; + bool operator<(const Str &) const noexcept; + bool operator<=(const Str &) const noexcept; + bool operator>(const Str &) const noexcept; + bool operator>=(const Str &) const noexcept; + + void swap(Str &) noexcept; + +private: + class uninit; + Str(uninit) noexcept; + friend impl; + + std::array repr; +}; +#endif // CXXBRIDGE1_RUST_STR + +namespace detail { +template +struct operator_new { + void *operator()(::std::size_t sz) { return ::operator new(sz); } +}; + +template +struct operator_new { + void *operator()(::std::size_t sz) { return T::operator new(sz); } +}; +} // namespace detail + +template +union MaybeUninit { + T value; + void *operator new(::std::size_t sz) { return detail::operator_new{}(sz); } + MaybeUninit() {} + ~MaybeUninit() {} +}; +} // namespace cxxbridge1 +} // namespace rust + +namespace mmcore { + enum class DistortionDirection : ::std::uint8_t; +} + +namespace mmcore { +#ifndef CXXBRIDGE1_ENUM_mmcore$DistortionDirection +#define CXXBRIDGE1_ENUM_mmcore$DistortionDirection +enum class DistortionDirection : ::std::uint8_t { + kUndistort = 0, + kRedistort = 1, + kNumDistortionDirection = 2, +}; +#endif // CXXBRIDGE1_ENUM_mmcore$DistortionDirection + +extern "C" { +void mmcore$cxxbridge1$shim_expand_file_path_string(::rust::Str value, ::std::int32_t frame, ::rust::String *return$) noexcept; +} // extern "C" + +MMCORE_API_EXPORT ::rust::String shim_expand_file_path_string(::rust::Str value, ::std::int32_t frame) noexcept { + ::rust::MaybeUninit<::rust::String> return$; + mmcore$cxxbridge1$shim_expand_file_path_string(value, frame, &return$.value); + return ::std::move(return$.value); +} +} // namespace mmcore diff --git a/lib/cppbind/mmcore/src/cxxbridge.rs b/lib/cppbind/mmcore/src/cxxbridge.rs index 19ce90368..ed4c64e8c 100644 --- a/lib/cppbind/mmcore/src/cxxbridge.rs +++ b/lib/cppbind/mmcore/src/cxxbridge.rs @@ -1,5 +1,5 @@ // -// Copyright (C) 2023 David Cattermole. +// Copyright (C) 2023, 2024 David Cattermole. // // This file is part of mmSolver. // @@ -18,6 +18,8 @@ // ==================================================================== // +use crate::shim_expand_file_path_string; + #[cxx::bridge(namespace = "mmcore")] pub mod ffi { unsafe extern "C++" { @@ -38,4 +40,8 @@ pub mod ffi { #[cxx_name = "kNumDistortionDirection"] NumDistortionDirection, } + + extern "Rust" { + pub fn shim_expand_file_path_string(value: &str, frame: i32) -> String; + } } diff --git a/lib/cppbind/mmcore/src/lib.cpp b/lib/cppbind/mmcore/src/lib.cpp index c719927fd..78c0b07a4 100644 --- a/lib/cppbind/mmcore/src/lib.cpp +++ b/lib/cppbind/mmcore/src/lib.cpp @@ -21,4 +21,10 @@ #include -namespace mmcore {} // namespace mmcore +namespace mmcore { + +rust::String expand_file_path_string(const rust::Str& value, FrameValue frame) { + return shim_expand_file_path_string(value, frame); +} + +} // namespace mmcore diff --git a/lib/cppbind/mmcore/src/lib.rs b/lib/cppbind/mmcore/src/lib.rs index 463705b8e..1130a110a 100644 --- a/lib/cppbind/mmcore/src/lib.rs +++ b/lib/cppbind/mmcore/src/lib.rs @@ -19,3 +19,9 @@ // pub mod cxxbridge; + +use mmcore_rust::pathutils::expand_file_path_string as core_expand_file_path_string; + +pub fn shim_expand_file_path_string(value: &str, frame: i32) -> String { + core_expand_file_path_string(value, frame) +} diff --git a/lib/cppbind/mmimage/Cargo.toml b/lib/cppbind/mmimage/Cargo.toml index 4900b6fb3..35aef1a05 100644 --- a/lib/cppbind/mmimage/Cargo.toml +++ b/lib/cppbind/mmimage/Cargo.toml @@ -1,30 +1,14 @@ [package] name = "mmimage_cppbind" -version = "0.1.0" -authors = ["david-cattermole "] -edition = "2018" -publish = false +version.workspace = true +authors.workspace = true +edition.workspace = true +publish.workspace = true [lib] name = "mmimage" path = "./src/lib.rs" [dependencies] -# NOTE: When changing this version number ensure to also update the -# installed 'cxxbridge-cmd' version so it stays in sync; Update it -# here: './scripts/internal/build_rust_library_*.*' -cxx = "=1.0.75" - -[dependencies.mmimage_rust] -path = "../../rust/mmimage" - -[profile.release] -opt-level = 3 -rpath = false -lto = true -codegen-units = 1 - -# NOTE: If we use 'panic = "abort"' then we are unable to produce tests. -# # https://github.com/rust-lang/cargo/issues/6313 -# -# panic = "abort" +cxx = { workspace = true } +mmimage_rust = { workspace = true } diff --git a/lib/cppbind/mmimage/include/mmimage/_cxx.h b/lib/cppbind/mmimage/include/mmimage/_cxx.h index 907ee829f..002282551 100644 --- a/lib/cppbind/mmimage/include/mmimage/_cxx.h +++ b/lib/cppbind/mmimage/include/mmimage/_cxx.h @@ -659,7 +659,8 @@ typename Slice::iterator::difference_type Slice::iterator::operator-(const iterator &other) const noexcept { auto diff = std::distance(static_cast(other.pos), static_cast(this->pos)); - return diff / this->stride; + return diff / static_cast::iterator::difference_type>( + this->stride); } template diff --git a/lib/cppbind/mmimage/include/mmimage/_cxxbridge.h b/lib/cppbind/mmimage/include/mmimage/_cxxbridge.h index 2703ab630..67e1b05ad 100644 --- a/lib/cppbind/mmimage/include/mmimage/_cxxbridge.h +++ b/lib/cppbind/mmimage/include/mmimage/_cxxbridge.h @@ -386,7 +386,8 @@ typename Slice::iterator::difference_type Slice::iterator::operator-(const iterator &other) const noexcept { auto diff = std::distance(static_cast(other.pos), static_cast(this->pos)); - return diff / this->stride; + return diff / static_cast::iterator::difference_type>( + this->stride); } template @@ -989,12 +990,12 @@ struct ExrPixelLayout final { ::std::size_t tile_size_x; ::std::size_t tile_size_y; - bool operator==(const ExrPixelLayout &) const noexcept; - bool operator!=(const ExrPixelLayout &) const noexcept; - bool operator<(const ExrPixelLayout &) const noexcept; - bool operator<=(const ExrPixelLayout &) const noexcept; - bool operator>(const ExrPixelLayout &) const noexcept; - bool operator>=(const ExrPixelLayout &) const noexcept; + bool operator==(ExrPixelLayout const &) const noexcept; + bool operator!=(ExrPixelLayout const &) const noexcept; + bool operator<(ExrPixelLayout const &) const noexcept; + bool operator<=(ExrPixelLayout const &) const noexcept; + bool operator>(ExrPixelLayout const &) const noexcept; + bool operator>=(ExrPixelLayout const &) const noexcept; using IsRelocatable = ::std::true_type; }; #endif // CXXBRIDGE1_STRUCT_mmimage$ExrPixelLayout @@ -1016,12 +1017,12 @@ struct ImageExrEncoder final { ::mmimage::ExrPixelLayout pixel_layout; ::mmimage::ExrLineOrder line_order; - bool operator==(const ImageExrEncoder &) const noexcept; - bool operator!=(const ImageExrEncoder &) const noexcept; - bool operator<(const ImageExrEncoder &) const noexcept; - bool operator<=(const ImageExrEncoder &) const noexcept; - bool operator>(const ImageExrEncoder &) const noexcept; - bool operator>=(const ImageExrEncoder &) const noexcept; + bool operator==(ImageExrEncoder const &) const noexcept; + bool operator!=(ImageExrEncoder const &) const noexcept; + bool operator<(ImageExrEncoder const &) const noexcept; + bool operator<=(ImageExrEncoder const &) const noexcept; + bool operator>(ImageExrEncoder const &) const noexcept; + bool operator>=(ImageExrEncoder const &) const noexcept; using IsRelocatable = ::std::true_type; }; #endif // CXXBRIDGE1_STRUCT_mmimage$ImageExrEncoder @@ -1032,12 +1033,12 @@ struct OptionF32 final { bool exists; float value; - bool operator==(const OptionF32 &) const noexcept; - bool operator!=(const OptionF32 &) const noexcept; - bool operator<(const OptionF32 &) const noexcept; - bool operator<=(const OptionF32 &) const noexcept; - bool operator>(const OptionF32 &) const noexcept; - bool operator>=(const OptionF32 &) const noexcept; + bool operator==(OptionF32 const &) const noexcept; + bool operator!=(OptionF32 const &) const noexcept; + bool operator<(OptionF32 const &) const noexcept; + bool operator<=(OptionF32 const &) const noexcept; + bool operator>(OptionF32 const &) const noexcept; + bool operator>=(OptionF32 const &) const noexcept; using IsRelocatable = ::std::true_type; }; #endif // CXXBRIDGE1_STRUCT_mmimage$OptionF32 @@ -1048,12 +1049,12 @@ struct Vec2F32 final { float x; float y; - bool operator==(const Vec2F32 &) const noexcept; - bool operator!=(const Vec2F32 &) const noexcept; - bool operator<(const Vec2F32 &) const noexcept; - bool operator<=(const Vec2F32 &) const noexcept; - bool operator>(const Vec2F32 &) const noexcept; - bool operator>=(const Vec2F32 &) const noexcept; + bool operator==(Vec2F32 const &) const noexcept; + bool operator!=(Vec2F32 const &) const noexcept; + bool operator<(Vec2F32 const &) const noexcept; + bool operator<=(Vec2F32 const &) const noexcept; + bool operator>(Vec2F32 const &) const noexcept; + bool operator>=(Vec2F32 const &) const noexcept; using IsRelocatable = ::std::true_type; }; #endif // CXXBRIDGE1_STRUCT_mmimage$Vec2F32 @@ -1064,12 +1065,12 @@ struct Vec2I32 final { ::std::int32_t x; ::std::int32_t y; - bool operator==(const Vec2I32 &) const noexcept; - bool operator!=(const Vec2I32 &) const noexcept; - bool operator<(const Vec2I32 &) const noexcept; - bool operator<=(const Vec2I32 &) const noexcept; - bool operator>(const Vec2I32 &) const noexcept; - bool operator>=(const Vec2I32 &) const noexcept; + bool operator==(Vec2I32 const &) const noexcept; + bool operator!=(Vec2I32 const &) const noexcept; + bool operator<(Vec2I32 const &) const noexcept; + bool operator<=(Vec2I32 const &) const noexcept; + bool operator>(Vec2I32 const &) const noexcept; + bool operator>=(Vec2I32 const &) const noexcept; using IsRelocatable = ::std::true_type; }; #endif // CXXBRIDGE1_STRUCT_mmimage$Vec2I32 @@ -1082,12 +1083,12 @@ struct Box2F32 final { float max_x; float max_y; - bool operator==(const Box2F32 &) const noexcept; - bool operator!=(const Box2F32 &) const noexcept; - bool operator<(const Box2F32 &) const noexcept; - bool operator<=(const Box2F32 &) const noexcept; - bool operator>(const Box2F32 &) const noexcept; - bool operator>=(const Box2F32 &) const noexcept; + bool operator==(Box2F32 const &) const noexcept; + bool operator!=(Box2F32 const &) const noexcept; + bool operator<(Box2F32 const &) const noexcept; + bool operator<=(Box2F32 const &) const noexcept; + bool operator>(Box2F32 const &) const noexcept; + bool operator>=(Box2F32 const &) const noexcept; using IsRelocatable = ::std::true_type; }; #endif // CXXBRIDGE1_STRUCT_mmimage$Box2F32 @@ -1100,12 +1101,12 @@ struct ImageRegionRectangle final { ::std::size_t size_x; ::std::size_t size_y; - bool operator==(const ImageRegionRectangle &) const noexcept; - bool operator!=(const ImageRegionRectangle &) const noexcept; - bool operator<(const ImageRegionRectangle &) const noexcept; - bool operator<=(const ImageRegionRectangle &) const noexcept; - bool operator>(const ImageRegionRectangle &) const noexcept; - bool operator>=(const ImageRegionRectangle &) const noexcept; + bool operator==(ImageRegionRectangle const &) const noexcept; + bool operator!=(ImageRegionRectangle const &) const noexcept; + bool operator<(ImageRegionRectangle const &) const noexcept; + bool operator<=(ImageRegionRectangle const &) const noexcept; + bool operator>(ImageRegionRectangle const &) const noexcept; + bool operator>=(ImageRegionRectangle const &) const noexcept; using IsRelocatable = ::std::true_type; }; #endif // CXXBRIDGE1_STRUCT_mmimage$ImageRegionRectangle @@ -1118,12 +1119,12 @@ struct PixelF32x4 final { float b; float a; - bool operator==(const PixelF32x4 &) const noexcept; - bool operator!=(const PixelF32x4 &) const noexcept; - bool operator<(const PixelF32x4 &) const noexcept; - bool operator<=(const PixelF32x4 &) const noexcept; - bool operator>(const PixelF32x4 &) const noexcept; - bool operator>=(const PixelF32x4 &) const noexcept; + bool operator==(PixelF32x4 const &) const noexcept; + bool operator!=(PixelF32x4 const &) const noexcept; + bool operator<(PixelF32x4 const &) const noexcept; + bool operator<=(PixelF32x4 const &) const noexcept; + bool operator>(PixelF32x4 const &) const noexcept; + bool operator>=(PixelF32x4 const &) const noexcept; using IsRelocatable = ::std::true_type; }; #endif // CXXBRIDGE1_STRUCT_mmimage$PixelF32x4 @@ -1134,12 +1135,12 @@ struct PixelF64x2 final { double x; double y; - bool operator==(const PixelF64x2 &) const noexcept; - bool operator!=(const PixelF64x2 &) const noexcept; - bool operator<(const PixelF64x2 &) const noexcept; - bool operator<=(const PixelF64x2 &) const noexcept; - bool operator>(const PixelF64x2 &) const noexcept; - bool operator>=(const PixelF64x2 &) const noexcept; + bool operator==(PixelF64x2 const &) const noexcept; + bool operator!=(PixelF64x2 const &) const noexcept; + bool operator<(PixelF64x2 const &) const noexcept; + bool operator<=(PixelF64x2 const &) const noexcept; + bool operator>(PixelF64x2 const &) const noexcept; + bool operator>=(PixelF64x2 const &) const noexcept; using IsRelocatable = ::std::true_type; }; #endif // CXXBRIDGE1_STRUCT_mmimage$PixelF64x2 @@ -1162,8 +1163,8 @@ struct ShimImagePixelBuffer final : public ::rust::Opaque { MMIMAGE_API_EXPORT ::std::size_t num_channels() const noexcept; MMIMAGE_API_EXPORT ::std::size_t pixel_count() const noexcept; MMIMAGE_API_EXPORT ::std::size_t element_count() const noexcept; - MMIMAGE_API_EXPORT ::rust::Slice as_slice_f32x4() const noexcept; - MMIMAGE_API_EXPORT ::rust::Slice<::mmimage::PixelF32x4> as_slice_f32x4_mut() noexcept; + MMIMAGE_API_EXPORT ::rust::Slice<::mmimage::PixelF32x4 const> as_slice_f32x4() const noexcept; + MMIMAGE_API_EXPORT ::rust::Slice<::mmimage::PixelF32x4 > as_slice_f32x4_mut() noexcept; MMIMAGE_API_EXPORT void resize(::mmimage::BufferDataType data_type, ::std::size_t image_width, ::std::size_t image_height, ::std::size_t num_channels) noexcept; ~ShimImagePixelBuffer() = delete; @@ -1230,9 +1231,9 @@ MMIMAGE_API_EXPORT ::rust::Box<::mmimage::ShimImagePixelBuffer> shim_create_imag MMIMAGE_API_EXPORT ::rust::Box<::mmimage::ShimImageMetaData> shim_create_image_meta_data_box() noexcept; -MMIMAGE_API_EXPORT bool shim_image_read_pixels_exr_f32x4(::rust::Str file_path, ::rust::Box<::mmimage::ShimImageMetaData> &out_meta_data, ::rust::Box<::mmimage::ShimImagePixelBuffer> &out_pixel_buffer) noexcept; +MMIMAGE_API_EXPORT bool shim_image_read_pixels_exr_f32x4(::rust::Str file_path, bool vertical_flip, ::rust::Box<::mmimage::ShimImageMetaData> &out_meta_data, ::rust::Box<::mmimage::ShimImagePixelBuffer> &out_pixel_buffer) noexcept; MMIMAGE_API_EXPORT bool shim_image_read_metadata_exr(::rust::Str file_path, ::rust::Box<::mmimage::ShimImageMetaData> &out_meta_data) noexcept; -MMIMAGE_API_EXPORT bool shim_image_write_pixels_exr_f32x4(::rust::Str file_path, ::mmimage::ImageExrEncoder exr_encoder, const ::rust::Box<::mmimage::ShimImageMetaData> &in_meta_data, const ::rust::Box<::mmimage::ShimImagePixelBuffer> &in_pixel_buffer) noexcept; +MMIMAGE_API_EXPORT bool shim_image_write_pixels_exr_f32x4(::rust::Str file_path, ::mmimage::ImageExrEncoder exr_encoder, ::rust::Box<::mmimage::ShimImageMetaData> const &in_meta_data, ::rust::Box<::mmimage::ShimImagePixelBuffer> const &in_pixel_buffer) noexcept; } // namespace mmimage diff --git a/lib/cppbind/mmimage/include/mmimage/lib.h b/lib/cppbind/mmimage/include/mmimage/lib.h index 211c472d5..cdc999c5f 100644 --- a/lib/cppbind/mmimage/include/mmimage/lib.h +++ b/lib/cppbind/mmimage/include/mmimage/lib.h @@ -42,6 +42,7 @@ bool image_read_metadata_exr(const rust::Str& file_path, ImageMetaData& out_meta_data); bool image_read_pixels_exr_f32x4(const rust::Str& file_path, + const bool vertical_flip, ImageMetaData& out_meta_data, ImagePixelBuffer& out_pixel_data); diff --git a/lib/cppbind/mmimage/src/_cxxbridge.cpp b/lib/cppbind/mmimage/src/_cxxbridge.cpp index 2ce0346d5..745a44c3f 100644 --- a/lib/cppbind/mmimage/src/_cxxbridge.cpp +++ b/lib/cppbind/mmimage/src/_cxxbridge.cpp @@ -386,7 +386,8 @@ typename Slice::iterator::difference_type Slice::iterator::operator-(const iterator &other) const noexcept { auto diff = std::distance(static_cast(other.pos), static_cast(this->pos)); - return diff / this->stride; + return diff / static_cast::iterator::difference_type>( + this->stride); } template @@ -927,6 +928,10 @@ class Slice::uninit {}; template inline Slice::Slice(uninit) noexcept {} +namespace repr { +using Fat = ::std::array<::std::uintptr_t, 2>; +} // namespace repr + namespace detail { template struct operator_new { @@ -948,10 +953,6 @@ union MaybeUninit { }; namespace { -namespace repr { -using Fat = ::std::array<::std::uintptr_t, 2>; -} // namespace repr - template <> class impl final { public: @@ -1043,12 +1044,12 @@ struct ExrPixelLayout final { ::std::size_t tile_size_x; ::std::size_t tile_size_y; - bool operator==(const ExrPixelLayout &) const noexcept; - bool operator!=(const ExrPixelLayout &) const noexcept; - bool operator<(const ExrPixelLayout &) const noexcept; - bool operator<=(const ExrPixelLayout &) const noexcept; - bool operator>(const ExrPixelLayout &) const noexcept; - bool operator>=(const ExrPixelLayout &) const noexcept; + bool operator==(ExrPixelLayout const &) const noexcept; + bool operator!=(ExrPixelLayout const &) const noexcept; + bool operator<(ExrPixelLayout const &) const noexcept; + bool operator<=(ExrPixelLayout const &) const noexcept; + bool operator>(ExrPixelLayout const &) const noexcept; + bool operator>=(ExrPixelLayout const &) const noexcept; using IsRelocatable = ::std::true_type; }; #endif // CXXBRIDGE1_STRUCT_mmimage$ExrPixelLayout @@ -1070,12 +1071,12 @@ struct ImageExrEncoder final { ::mmimage::ExrPixelLayout pixel_layout; ::mmimage::ExrLineOrder line_order; - bool operator==(const ImageExrEncoder &) const noexcept; - bool operator!=(const ImageExrEncoder &) const noexcept; - bool operator<(const ImageExrEncoder &) const noexcept; - bool operator<=(const ImageExrEncoder &) const noexcept; - bool operator>(const ImageExrEncoder &) const noexcept; - bool operator>=(const ImageExrEncoder &) const noexcept; + bool operator==(ImageExrEncoder const &) const noexcept; + bool operator!=(ImageExrEncoder const &) const noexcept; + bool operator<(ImageExrEncoder const &) const noexcept; + bool operator<=(ImageExrEncoder const &) const noexcept; + bool operator>(ImageExrEncoder const &) const noexcept; + bool operator>=(ImageExrEncoder const &) const noexcept; using IsRelocatable = ::std::true_type; }; #endif // CXXBRIDGE1_STRUCT_mmimage$ImageExrEncoder @@ -1086,12 +1087,12 @@ struct OptionF32 final { bool exists; float value; - bool operator==(const OptionF32 &) const noexcept; - bool operator!=(const OptionF32 &) const noexcept; - bool operator<(const OptionF32 &) const noexcept; - bool operator<=(const OptionF32 &) const noexcept; - bool operator>(const OptionF32 &) const noexcept; - bool operator>=(const OptionF32 &) const noexcept; + bool operator==(OptionF32 const &) const noexcept; + bool operator!=(OptionF32 const &) const noexcept; + bool operator<(OptionF32 const &) const noexcept; + bool operator<=(OptionF32 const &) const noexcept; + bool operator>(OptionF32 const &) const noexcept; + bool operator>=(OptionF32 const &) const noexcept; using IsRelocatable = ::std::true_type; }; #endif // CXXBRIDGE1_STRUCT_mmimage$OptionF32 @@ -1102,12 +1103,12 @@ struct Vec2F32 final { float x; float y; - bool operator==(const Vec2F32 &) const noexcept; - bool operator!=(const Vec2F32 &) const noexcept; - bool operator<(const Vec2F32 &) const noexcept; - bool operator<=(const Vec2F32 &) const noexcept; - bool operator>(const Vec2F32 &) const noexcept; - bool operator>=(const Vec2F32 &) const noexcept; + bool operator==(Vec2F32 const &) const noexcept; + bool operator!=(Vec2F32 const &) const noexcept; + bool operator<(Vec2F32 const &) const noexcept; + bool operator<=(Vec2F32 const &) const noexcept; + bool operator>(Vec2F32 const &) const noexcept; + bool operator>=(Vec2F32 const &) const noexcept; using IsRelocatable = ::std::true_type; }; #endif // CXXBRIDGE1_STRUCT_mmimage$Vec2F32 @@ -1118,12 +1119,12 @@ struct Vec2I32 final { ::std::int32_t x; ::std::int32_t y; - bool operator==(const Vec2I32 &) const noexcept; - bool operator!=(const Vec2I32 &) const noexcept; - bool operator<(const Vec2I32 &) const noexcept; - bool operator<=(const Vec2I32 &) const noexcept; - bool operator>(const Vec2I32 &) const noexcept; - bool operator>=(const Vec2I32 &) const noexcept; + bool operator==(Vec2I32 const &) const noexcept; + bool operator!=(Vec2I32 const &) const noexcept; + bool operator<(Vec2I32 const &) const noexcept; + bool operator<=(Vec2I32 const &) const noexcept; + bool operator>(Vec2I32 const &) const noexcept; + bool operator>=(Vec2I32 const &) const noexcept; using IsRelocatable = ::std::true_type; }; #endif // CXXBRIDGE1_STRUCT_mmimage$Vec2I32 @@ -1136,12 +1137,12 @@ struct Box2F32 final { float max_x; float max_y; - bool operator==(const Box2F32 &) const noexcept; - bool operator!=(const Box2F32 &) const noexcept; - bool operator<(const Box2F32 &) const noexcept; - bool operator<=(const Box2F32 &) const noexcept; - bool operator>(const Box2F32 &) const noexcept; - bool operator>=(const Box2F32 &) const noexcept; + bool operator==(Box2F32 const &) const noexcept; + bool operator!=(Box2F32 const &) const noexcept; + bool operator<(Box2F32 const &) const noexcept; + bool operator<=(Box2F32 const &) const noexcept; + bool operator>(Box2F32 const &) const noexcept; + bool operator>=(Box2F32 const &) const noexcept; using IsRelocatable = ::std::true_type; }; #endif // CXXBRIDGE1_STRUCT_mmimage$Box2F32 @@ -1154,12 +1155,12 @@ struct ImageRegionRectangle final { ::std::size_t size_x; ::std::size_t size_y; - bool operator==(const ImageRegionRectangle &) const noexcept; - bool operator!=(const ImageRegionRectangle &) const noexcept; - bool operator<(const ImageRegionRectangle &) const noexcept; - bool operator<=(const ImageRegionRectangle &) const noexcept; - bool operator>(const ImageRegionRectangle &) const noexcept; - bool operator>=(const ImageRegionRectangle &) const noexcept; + bool operator==(ImageRegionRectangle const &) const noexcept; + bool operator!=(ImageRegionRectangle const &) const noexcept; + bool operator<(ImageRegionRectangle const &) const noexcept; + bool operator<=(ImageRegionRectangle const &) const noexcept; + bool operator>(ImageRegionRectangle const &) const noexcept; + bool operator>=(ImageRegionRectangle const &) const noexcept; using IsRelocatable = ::std::true_type; }; #endif // CXXBRIDGE1_STRUCT_mmimage$ImageRegionRectangle @@ -1172,12 +1173,12 @@ struct PixelF32x4 final { float b; float a; - bool operator==(const PixelF32x4 &) const noexcept; - bool operator!=(const PixelF32x4 &) const noexcept; - bool operator<(const PixelF32x4 &) const noexcept; - bool operator<=(const PixelF32x4 &) const noexcept; - bool operator>(const PixelF32x4 &) const noexcept; - bool operator>=(const PixelF32x4 &) const noexcept; + bool operator==(PixelF32x4 const &) const noexcept; + bool operator!=(PixelF32x4 const &) const noexcept; + bool operator<(PixelF32x4 const &) const noexcept; + bool operator<=(PixelF32x4 const &) const noexcept; + bool operator>(PixelF32x4 const &) const noexcept; + bool operator>=(PixelF32x4 const &) const noexcept; using IsRelocatable = ::std::true_type; }; #endif // CXXBRIDGE1_STRUCT_mmimage$PixelF32x4 @@ -1188,12 +1189,12 @@ struct PixelF64x2 final { double x; double y; - bool operator==(const PixelF64x2 &) const noexcept; - bool operator!=(const PixelF64x2 &) const noexcept; - bool operator<(const PixelF64x2 &) const noexcept; - bool operator<=(const PixelF64x2 &) const noexcept; - bool operator>(const PixelF64x2 &) const noexcept; - bool operator>=(const PixelF64x2 &) const noexcept; + bool operator==(PixelF64x2 const &) const noexcept; + bool operator!=(PixelF64x2 const &) const noexcept; + bool operator<(PixelF64x2 const &) const noexcept; + bool operator<=(PixelF64x2 const &) const noexcept; + bool operator>(PixelF64x2 const &) const noexcept; + bool operator>=(PixelF64x2 const &) const noexcept; using IsRelocatable = ::std::true_type; }; #endif // CXXBRIDGE1_STRUCT_mmimage$PixelF64x2 @@ -1216,8 +1217,8 @@ struct ShimImagePixelBuffer final : public ::rust::Opaque { MMIMAGE_API_EXPORT ::std::size_t num_channels() const noexcept; MMIMAGE_API_EXPORT ::std::size_t pixel_count() const noexcept; MMIMAGE_API_EXPORT ::std::size_t element_count() const noexcept; - MMIMAGE_API_EXPORT ::rust::Slice as_slice_f32x4() const noexcept; - MMIMAGE_API_EXPORT ::rust::Slice<::mmimage::PixelF32x4> as_slice_f32x4_mut() noexcept; + MMIMAGE_API_EXPORT ::rust::Slice<::mmimage::PixelF32x4 const> as_slice_f32x4() const noexcept; + MMIMAGE_API_EXPORT ::rust::Slice<::mmimage::PixelF32x4 > as_slice_f32x4_mut() noexcept; MMIMAGE_API_EXPORT void resize(::mmimage::BufferDataType data_type, ::std::size_t image_width, ::std::size_t image_height, ::std::size_t num_channels) noexcept; ~ShimImagePixelBuffer() = delete; @@ -1281,72 +1282,72 @@ struct ShimImageMetaData final : public ::rust::Opaque { #endif // CXXBRIDGE1_STRUCT_mmimage$ShimImageMetaData extern "C" { -bool mmimage$cxxbridge1$ExrPixelLayout$operator$eq(const ExrPixelLayout &, const ExrPixelLayout &) noexcept; -bool mmimage$cxxbridge1$ExrPixelLayout$operator$ne(const ExrPixelLayout &, const ExrPixelLayout &) noexcept; -bool mmimage$cxxbridge1$ExrPixelLayout$operator$lt(const ExrPixelLayout &, const ExrPixelLayout &) noexcept; -bool mmimage$cxxbridge1$ExrPixelLayout$operator$le(const ExrPixelLayout &, const ExrPixelLayout &) noexcept; -bool mmimage$cxxbridge1$ExrPixelLayout$operator$gt(const ExrPixelLayout &, const ExrPixelLayout &) noexcept; -bool mmimage$cxxbridge1$ExrPixelLayout$operator$ge(const ExrPixelLayout &, const ExrPixelLayout &) noexcept; -bool mmimage$cxxbridge1$ImageExrEncoder$operator$eq(const ImageExrEncoder &, const ImageExrEncoder &) noexcept; -bool mmimage$cxxbridge1$ImageExrEncoder$operator$ne(const ImageExrEncoder &, const ImageExrEncoder &) noexcept; -bool mmimage$cxxbridge1$ImageExrEncoder$operator$lt(const ImageExrEncoder &, const ImageExrEncoder &) noexcept; -bool mmimage$cxxbridge1$ImageExrEncoder$operator$le(const ImageExrEncoder &, const ImageExrEncoder &) noexcept; -bool mmimage$cxxbridge1$ImageExrEncoder$operator$gt(const ImageExrEncoder &, const ImageExrEncoder &) noexcept; -bool mmimage$cxxbridge1$ImageExrEncoder$operator$ge(const ImageExrEncoder &, const ImageExrEncoder &) noexcept; -bool mmimage$cxxbridge1$OptionF32$operator$eq(const OptionF32 &, const OptionF32 &) noexcept; -bool mmimage$cxxbridge1$OptionF32$operator$ne(const OptionF32 &, const OptionF32 &) noexcept; -bool mmimage$cxxbridge1$OptionF32$operator$lt(const OptionF32 &, const OptionF32 &) noexcept; -bool mmimage$cxxbridge1$OptionF32$operator$le(const OptionF32 &, const OptionF32 &) noexcept; -bool mmimage$cxxbridge1$OptionF32$operator$gt(const OptionF32 &, const OptionF32 &) noexcept; -bool mmimage$cxxbridge1$OptionF32$operator$ge(const OptionF32 &, const OptionF32 &) noexcept; -bool mmimage$cxxbridge1$Vec2F32$operator$eq(const Vec2F32 &, const Vec2F32 &) noexcept; -bool mmimage$cxxbridge1$Vec2F32$operator$ne(const Vec2F32 &, const Vec2F32 &) noexcept; -bool mmimage$cxxbridge1$Vec2F32$operator$lt(const Vec2F32 &, const Vec2F32 &) noexcept; -bool mmimage$cxxbridge1$Vec2F32$operator$le(const Vec2F32 &, const Vec2F32 &) noexcept; -bool mmimage$cxxbridge1$Vec2F32$operator$gt(const Vec2F32 &, const Vec2F32 &) noexcept; -bool mmimage$cxxbridge1$Vec2F32$operator$ge(const Vec2F32 &, const Vec2F32 &) noexcept; -bool mmimage$cxxbridge1$Vec2I32$operator$eq(const Vec2I32 &, const Vec2I32 &) noexcept; -bool mmimage$cxxbridge1$Vec2I32$operator$lt(const Vec2I32 &, const Vec2I32 &) noexcept; -bool mmimage$cxxbridge1$Vec2I32$operator$le(const Vec2I32 &, const Vec2I32 &) noexcept; -::std::size_t mmimage$cxxbridge1$Vec2I32$operator$hash(const Vec2I32 &) noexcept; -bool mmimage$cxxbridge1$Box2F32$operator$eq(const Box2F32 &, const Box2F32 &) noexcept; -bool mmimage$cxxbridge1$Box2F32$operator$ne(const Box2F32 &, const Box2F32 &) noexcept; -bool mmimage$cxxbridge1$Box2F32$operator$lt(const Box2F32 &, const Box2F32 &) noexcept; -bool mmimage$cxxbridge1$Box2F32$operator$le(const Box2F32 &, const Box2F32 &) noexcept; -bool mmimage$cxxbridge1$Box2F32$operator$gt(const Box2F32 &, const Box2F32 &) noexcept; -bool mmimage$cxxbridge1$Box2F32$operator$ge(const Box2F32 &, const Box2F32 &) noexcept; -bool mmimage$cxxbridge1$ImageRegionRectangle$operator$eq(const ImageRegionRectangle &, const ImageRegionRectangle &) noexcept; -bool mmimage$cxxbridge1$ImageRegionRectangle$operator$lt(const ImageRegionRectangle &, const ImageRegionRectangle &) noexcept; -bool mmimage$cxxbridge1$ImageRegionRectangle$operator$le(const ImageRegionRectangle &, const ImageRegionRectangle &) noexcept; -::std::size_t mmimage$cxxbridge1$ImageRegionRectangle$operator$hash(const ImageRegionRectangle &) noexcept; -bool mmimage$cxxbridge1$PixelF32x4$operator$eq(const PixelF32x4 &, const PixelF32x4 &) noexcept; -bool mmimage$cxxbridge1$PixelF32x4$operator$ne(const PixelF32x4 &, const PixelF32x4 &) noexcept; -bool mmimage$cxxbridge1$PixelF32x4$operator$lt(const PixelF32x4 &, const PixelF32x4 &) noexcept; -bool mmimage$cxxbridge1$PixelF32x4$operator$le(const PixelF32x4 &, const PixelF32x4 &) noexcept; -bool mmimage$cxxbridge1$PixelF32x4$operator$gt(const PixelF32x4 &, const PixelF32x4 &) noexcept; -bool mmimage$cxxbridge1$PixelF32x4$operator$ge(const PixelF32x4 &, const PixelF32x4 &) noexcept; -bool mmimage$cxxbridge1$PixelF64x2$operator$eq(const PixelF64x2 &, const PixelF64x2 &) noexcept; -bool mmimage$cxxbridge1$PixelF64x2$operator$ne(const PixelF64x2 &, const PixelF64x2 &) noexcept; -bool mmimage$cxxbridge1$PixelF64x2$operator$lt(const PixelF64x2 &, const PixelF64x2 &) noexcept; -bool mmimage$cxxbridge1$PixelF64x2$operator$le(const PixelF64x2 &, const PixelF64x2 &) noexcept; -bool mmimage$cxxbridge1$PixelF64x2$operator$gt(const PixelF64x2 &, const PixelF64x2 &) noexcept; -bool mmimage$cxxbridge1$PixelF64x2$operator$ge(const PixelF64x2 &, const PixelF64x2 &) noexcept; +bool mmimage$cxxbridge1$ExrPixelLayout$operator$eq(ExrPixelLayout const &, ExrPixelLayout const &) noexcept; +bool mmimage$cxxbridge1$ExrPixelLayout$operator$ne(ExrPixelLayout const &, ExrPixelLayout const &) noexcept; +bool mmimage$cxxbridge1$ExrPixelLayout$operator$lt(ExrPixelLayout const &, ExrPixelLayout const &) noexcept; +bool mmimage$cxxbridge1$ExrPixelLayout$operator$le(ExrPixelLayout const &, ExrPixelLayout const &) noexcept; +bool mmimage$cxxbridge1$ExrPixelLayout$operator$gt(ExrPixelLayout const &, ExrPixelLayout const &) noexcept; +bool mmimage$cxxbridge1$ExrPixelLayout$operator$ge(ExrPixelLayout const &, ExrPixelLayout const &) noexcept; +bool mmimage$cxxbridge1$ImageExrEncoder$operator$eq(ImageExrEncoder const &, ImageExrEncoder const &) noexcept; +bool mmimage$cxxbridge1$ImageExrEncoder$operator$ne(ImageExrEncoder const &, ImageExrEncoder const &) noexcept; +bool mmimage$cxxbridge1$ImageExrEncoder$operator$lt(ImageExrEncoder const &, ImageExrEncoder const &) noexcept; +bool mmimage$cxxbridge1$ImageExrEncoder$operator$le(ImageExrEncoder const &, ImageExrEncoder const &) noexcept; +bool mmimage$cxxbridge1$ImageExrEncoder$operator$gt(ImageExrEncoder const &, ImageExrEncoder const &) noexcept; +bool mmimage$cxxbridge1$ImageExrEncoder$operator$ge(ImageExrEncoder const &, ImageExrEncoder const &) noexcept; +bool mmimage$cxxbridge1$OptionF32$operator$eq(OptionF32 const &, OptionF32 const &) noexcept; +bool mmimage$cxxbridge1$OptionF32$operator$ne(OptionF32 const &, OptionF32 const &) noexcept; +bool mmimage$cxxbridge1$OptionF32$operator$lt(OptionF32 const &, OptionF32 const &) noexcept; +bool mmimage$cxxbridge1$OptionF32$operator$le(OptionF32 const &, OptionF32 const &) noexcept; +bool mmimage$cxxbridge1$OptionF32$operator$gt(OptionF32 const &, OptionF32 const &) noexcept; +bool mmimage$cxxbridge1$OptionF32$operator$ge(OptionF32 const &, OptionF32 const &) noexcept; +bool mmimage$cxxbridge1$Vec2F32$operator$eq(Vec2F32 const &, Vec2F32 const &) noexcept; +bool mmimage$cxxbridge1$Vec2F32$operator$ne(Vec2F32 const &, Vec2F32 const &) noexcept; +bool mmimage$cxxbridge1$Vec2F32$operator$lt(Vec2F32 const &, Vec2F32 const &) noexcept; +bool mmimage$cxxbridge1$Vec2F32$operator$le(Vec2F32 const &, Vec2F32 const &) noexcept; +bool mmimage$cxxbridge1$Vec2F32$operator$gt(Vec2F32 const &, Vec2F32 const &) noexcept; +bool mmimage$cxxbridge1$Vec2F32$operator$ge(Vec2F32 const &, Vec2F32 const &) noexcept; +bool mmimage$cxxbridge1$Vec2I32$operator$eq(Vec2I32 const &, Vec2I32 const &) noexcept; +bool mmimage$cxxbridge1$Vec2I32$operator$lt(Vec2I32 const &, Vec2I32 const &) noexcept; +bool mmimage$cxxbridge1$Vec2I32$operator$le(Vec2I32 const &, Vec2I32 const &) noexcept; +::std::size_t mmimage$cxxbridge1$Vec2I32$operator$hash(Vec2I32 const &) noexcept; +bool mmimage$cxxbridge1$Box2F32$operator$eq(Box2F32 const &, Box2F32 const &) noexcept; +bool mmimage$cxxbridge1$Box2F32$operator$ne(Box2F32 const &, Box2F32 const &) noexcept; +bool mmimage$cxxbridge1$Box2F32$operator$lt(Box2F32 const &, Box2F32 const &) noexcept; +bool mmimage$cxxbridge1$Box2F32$operator$le(Box2F32 const &, Box2F32 const &) noexcept; +bool mmimage$cxxbridge1$Box2F32$operator$gt(Box2F32 const &, Box2F32 const &) noexcept; +bool mmimage$cxxbridge1$Box2F32$operator$ge(Box2F32 const &, Box2F32 const &) noexcept; +bool mmimage$cxxbridge1$ImageRegionRectangle$operator$eq(ImageRegionRectangle const &, ImageRegionRectangle const &) noexcept; +bool mmimage$cxxbridge1$ImageRegionRectangle$operator$lt(ImageRegionRectangle const &, ImageRegionRectangle const &) noexcept; +bool mmimage$cxxbridge1$ImageRegionRectangle$operator$le(ImageRegionRectangle const &, ImageRegionRectangle const &) noexcept; +::std::size_t mmimage$cxxbridge1$ImageRegionRectangle$operator$hash(ImageRegionRectangle const &) noexcept; +bool mmimage$cxxbridge1$PixelF32x4$operator$eq(PixelF32x4 const &, PixelF32x4 const &) noexcept; +bool mmimage$cxxbridge1$PixelF32x4$operator$ne(PixelF32x4 const &, PixelF32x4 const &) noexcept; +bool mmimage$cxxbridge1$PixelF32x4$operator$lt(PixelF32x4 const &, PixelF32x4 const &) noexcept; +bool mmimage$cxxbridge1$PixelF32x4$operator$le(PixelF32x4 const &, PixelF32x4 const &) noexcept; +bool mmimage$cxxbridge1$PixelF32x4$operator$gt(PixelF32x4 const &, PixelF32x4 const &) noexcept; +bool mmimage$cxxbridge1$PixelF32x4$operator$ge(PixelF32x4 const &, PixelF32x4 const &) noexcept; +bool mmimage$cxxbridge1$PixelF64x2$operator$eq(PixelF64x2 const &, PixelF64x2 const &) noexcept; +bool mmimage$cxxbridge1$PixelF64x2$operator$ne(PixelF64x2 const &, PixelF64x2 const &) noexcept; +bool mmimage$cxxbridge1$PixelF64x2$operator$lt(PixelF64x2 const &, PixelF64x2 const &) noexcept; +bool mmimage$cxxbridge1$PixelF64x2$operator$le(PixelF64x2 const &, PixelF64x2 const &) noexcept; +bool mmimage$cxxbridge1$PixelF64x2$operator$gt(PixelF64x2 const &, PixelF64x2 const &) noexcept; +bool mmimage$cxxbridge1$PixelF64x2$operator$ge(PixelF64x2 const &, PixelF64x2 const &) noexcept; ::std::size_t mmimage$cxxbridge1$ShimImagePixelBuffer$operator$sizeof() noexcept; ::std::size_t mmimage$cxxbridge1$ShimImagePixelBuffer$operator$alignof() noexcept; -::mmimage::BufferDataType mmimage$cxxbridge1$ShimImagePixelBuffer$data_type(const ::mmimage::ShimImagePixelBuffer &self) noexcept; +::mmimage::BufferDataType mmimage$cxxbridge1$ShimImagePixelBuffer$data_type(::mmimage::ShimImagePixelBuffer const &self) noexcept; -::std::size_t mmimage$cxxbridge1$ShimImagePixelBuffer$image_width(const ::mmimage::ShimImagePixelBuffer &self) noexcept; +::std::size_t mmimage$cxxbridge1$ShimImagePixelBuffer$image_width(::mmimage::ShimImagePixelBuffer const &self) noexcept; -::std::size_t mmimage$cxxbridge1$ShimImagePixelBuffer$image_height(const ::mmimage::ShimImagePixelBuffer &self) noexcept; +::std::size_t mmimage$cxxbridge1$ShimImagePixelBuffer$image_height(::mmimage::ShimImagePixelBuffer const &self) noexcept; -::std::size_t mmimage$cxxbridge1$ShimImagePixelBuffer$num_channels(const ::mmimage::ShimImagePixelBuffer &self) noexcept; +::std::size_t mmimage$cxxbridge1$ShimImagePixelBuffer$num_channels(::mmimage::ShimImagePixelBuffer const &self) noexcept; -::std::size_t mmimage$cxxbridge1$ShimImagePixelBuffer$pixel_count(const ::mmimage::ShimImagePixelBuffer &self) noexcept; +::std::size_t mmimage$cxxbridge1$ShimImagePixelBuffer$pixel_count(::mmimage::ShimImagePixelBuffer const &self) noexcept; -::std::size_t mmimage$cxxbridge1$ShimImagePixelBuffer$element_count(const ::mmimage::ShimImagePixelBuffer &self) noexcept; +::std::size_t mmimage$cxxbridge1$ShimImagePixelBuffer$element_count(::mmimage::ShimImagePixelBuffer const &self) noexcept; -::rust::repr::Fat mmimage$cxxbridge1$ShimImagePixelBuffer$as_slice_f32x4(const ::mmimage::ShimImagePixelBuffer &self) noexcept; +::rust::repr::Fat mmimage$cxxbridge1$ShimImagePixelBuffer$as_slice_f32x4(::mmimage::ShimImagePixelBuffer const &self) noexcept; ::rust::repr::Fat mmimage$cxxbridge1$ShimImagePixelBuffer$as_slice_f32x4_mut(::mmimage::ShimImagePixelBuffer &self) noexcept; @@ -1356,316 +1357,316 @@ ::mmimage::ShimImagePixelBuffer *mmimage$cxxbridge1$shim_create_image_pixel_buff ::std::size_t mmimage$cxxbridge1$ShimImageMetaData$operator$sizeof() noexcept; ::std::size_t mmimage$cxxbridge1$ShimImageMetaData$operator$alignof() noexcept; -::mmimage::ImageRegionRectangle mmimage$cxxbridge1$ShimImageMetaData$get_display_window(const ::mmimage::ShimImageMetaData &self) noexcept; +::mmimage::ImageRegionRectangle mmimage$cxxbridge1$ShimImageMetaData$get_display_window(::mmimage::ShimImageMetaData const &self) noexcept; void mmimage$cxxbridge1$ShimImageMetaData$set_display_window(::mmimage::ShimImageMetaData &self, ::mmimage::ImageRegionRectangle value) noexcept; -float mmimage$cxxbridge1$ShimImageMetaData$get_pixel_aspect(const ::mmimage::ShimImageMetaData &self) noexcept; +float mmimage$cxxbridge1$ShimImageMetaData$get_pixel_aspect(::mmimage::ShimImageMetaData const &self) noexcept; void mmimage$cxxbridge1$ShimImageMetaData$set_pixel_aspect(::mmimage::ShimImageMetaData &self, float value) noexcept; -::rust::repr::Fat mmimage$cxxbridge1$ShimImageMetaData$get_layer_name(const ::mmimage::ShimImageMetaData &self) noexcept; +::rust::repr::Fat mmimage$cxxbridge1$ShimImageMetaData$get_layer_name(::mmimage::ShimImageMetaData const &self) noexcept; void mmimage$cxxbridge1$ShimImageMetaData$set_layer_name(::mmimage::ShimImageMetaData &self, ::rust::Str value) noexcept; -::mmimage::Vec2I32 mmimage$cxxbridge1$ShimImageMetaData$get_layer_position(const ::mmimage::ShimImageMetaData &self) noexcept; +::mmimage::Vec2I32 mmimage$cxxbridge1$ShimImageMetaData$get_layer_position(::mmimage::ShimImageMetaData const &self) noexcept; void mmimage$cxxbridge1$ShimImageMetaData$set_layer_position(::mmimage::ShimImageMetaData &self, ::mmimage::Vec2I32 value) noexcept; -::mmimage::Vec2F32 mmimage$cxxbridge1$ShimImageMetaData$get_screen_window_center(const ::mmimage::ShimImageMetaData &self) noexcept; +::mmimage::Vec2F32 mmimage$cxxbridge1$ShimImageMetaData$get_screen_window_center(::mmimage::ShimImageMetaData const &self) noexcept; void mmimage$cxxbridge1$ShimImageMetaData$set_screen_window_center(::mmimage::ShimImageMetaData &self, ::mmimage::Vec2F32 value) noexcept; -float mmimage$cxxbridge1$ShimImageMetaData$get_screen_window_width(const ::mmimage::ShimImageMetaData &self) noexcept; +float mmimage$cxxbridge1$ShimImageMetaData$get_screen_window_width(::mmimage::ShimImageMetaData const &self) noexcept; void mmimage$cxxbridge1$ShimImageMetaData$set_screen_window_width(::mmimage::ShimImageMetaData &self, float value) noexcept; -::rust::repr::Fat mmimage$cxxbridge1$ShimImageMetaData$get_owner(const ::mmimage::ShimImageMetaData &self) noexcept; +::rust::repr::Fat mmimage$cxxbridge1$ShimImageMetaData$get_owner(::mmimage::ShimImageMetaData const &self) noexcept; void mmimage$cxxbridge1$ShimImageMetaData$set_owner(::mmimage::ShimImageMetaData &self, ::rust::Str value) noexcept; -::rust::repr::Fat mmimage$cxxbridge1$ShimImageMetaData$get_comments(const ::mmimage::ShimImageMetaData &self) noexcept; +::rust::repr::Fat mmimage$cxxbridge1$ShimImageMetaData$get_comments(::mmimage::ShimImageMetaData const &self) noexcept; void mmimage$cxxbridge1$ShimImageMetaData$set_comments(::mmimage::ShimImageMetaData &self, ::rust::Str value) noexcept; -::rust::repr::Fat mmimage$cxxbridge1$ShimImageMetaData$get_capture_date(const ::mmimage::ShimImageMetaData &self) noexcept; +::rust::repr::Fat mmimage$cxxbridge1$ShimImageMetaData$get_capture_date(::mmimage::ShimImageMetaData const &self) noexcept; -::mmimage::OptionF32 mmimage$cxxbridge1$ShimImageMetaData$get_utc_offset(const ::mmimage::ShimImageMetaData &self) noexcept; +::mmimage::OptionF32 mmimage$cxxbridge1$ShimImageMetaData$get_utc_offset(::mmimage::ShimImageMetaData const &self) noexcept; -::mmimage::OptionF32 mmimage$cxxbridge1$ShimImageMetaData$get_longitude(const ::mmimage::ShimImageMetaData &self) noexcept; +::mmimage::OptionF32 mmimage$cxxbridge1$ShimImageMetaData$get_longitude(::mmimage::ShimImageMetaData const &self) noexcept; -::mmimage::OptionF32 mmimage$cxxbridge1$ShimImageMetaData$get_latitude(const ::mmimage::ShimImageMetaData &self) noexcept; +::mmimage::OptionF32 mmimage$cxxbridge1$ShimImageMetaData$get_latitude(::mmimage::ShimImageMetaData const &self) noexcept; -::mmimage::OptionF32 mmimage$cxxbridge1$ShimImageMetaData$get_altitude(const ::mmimage::ShimImageMetaData &self) noexcept; +::mmimage::OptionF32 mmimage$cxxbridge1$ShimImageMetaData$get_altitude(::mmimage::ShimImageMetaData const &self) noexcept; -::mmimage::OptionF32 mmimage$cxxbridge1$ShimImageMetaData$get_focus(const ::mmimage::ShimImageMetaData &self) noexcept; +::mmimage::OptionF32 mmimage$cxxbridge1$ShimImageMetaData$get_focus(::mmimage::ShimImageMetaData const &self) noexcept; -::mmimage::OptionF32 mmimage$cxxbridge1$ShimImageMetaData$get_exposure(const ::mmimage::ShimImageMetaData &self) noexcept; +::mmimage::OptionF32 mmimage$cxxbridge1$ShimImageMetaData$get_exposure(::mmimage::ShimImageMetaData const &self) noexcept; -::mmimage::OptionF32 mmimage$cxxbridge1$ShimImageMetaData$get_aperture(const ::mmimage::ShimImageMetaData &self) noexcept; +::mmimage::OptionF32 mmimage$cxxbridge1$ShimImageMetaData$get_aperture(::mmimage::ShimImageMetaData const &self) noexcept; -::mmimage::OptionF32 mmimage$cxxbridge1$ShimImageMetaData$get_iso_speed(const ::mmimage::ShimImageMetaData &self) noexcept; +::mmimage::OptionF32 mmimage$cxxbridge1$ShimImageMetaData$get_iso_speed(::mmimage::ShimImageMetaData const &self) noexcept; -::mmimage::OptionF32 mmimage$cxxbridge1$ShimImageMetaData$get_frames_per_second(const ::mmimage::ShimImageMetaData &self) noexcept; +::mmimage::OptionF32 mmimage$cxxbridge1$ShimImageMetaData$get_frames_per_second(::mmimage::ShimImageMetaData const &self) noexcept; -::rust::repr::Fat mmimage$cxxbridge1$ShimImageMetaData$get_software_name(const ::mmimage::ShimImageMetaData &self) noexcept; +::rust::repr::Fat mmimage$cxxbridge1$ShimImageMetaData$get_software_name(::mmimage::ShimImageMetaData const &self) noexcept; void mmimage$cxxbridge1$ShimImageMetaData$set_software_name(::mmimage::ShimImageMetaData &self, ::rust::Str value) noexcept; -void mmimage$cxxbridge1$ShimImageMetaData$all_named_attribute_names(const ::mmimage::ShimImageMetaData &self, ::rust::Vec<::rust::String> *return$) noexcept; +void mmimage$cxxbridge1$ShimImageMetaData$all_named_attribute_names(::mmimage::ShimImageMetaData const &self, ::rust::Vec<::rust::String> *return$) noexcept; -bool mmimage$cxxbridge1$ShimImageMetaData$has_named_attribute(const ::mmimage::ShimImageMetaData &self, ::rust::Str attribute_name) noexcept; +bool mmimage$cxxbridge1$ShimImageMetaData$has_named_attribute(::mmimage::ShimImageMetaData const &self, ::rust::Str attribute_name) noexcept; -::std::uint8_t mmimage$cxxbridge1$ShimImageMetaData$get_named_attribute_type_index(const ::mmimage::ShimImageMetaData &self, ::rust::Str attribute_name) noexcept; +::std::uint8_t mmimage$cxxbridge1$ShimImageMetaData$get_named_attribute_type_index(::mmimage::ShimImageMetaData const &self, ::rust::Str attribute_name) noexcept; -::std::int32_t mmimage$cxxbridge1$ShimImageMetaData$get_named_attribute_as_i32(const ::mmimage::ShimImageMetaData &self, ::rust::Str attribute_name) noexcept; +::std::int32_t mmimage$cxxbridge1$ShimImageMetaData$get_named_attribute_as_i32(::mmimage::ShimImageMetaData const &self, ::rust::Str attribute_name) noexcept; -float mmimage$cxxbridge1$ShimImageMetaData$get_named_attribute_as_f32(const ::mmimage::ShimImageMetaData &self, ::rust::Str attribute_name) noexcept; +float mmimage$cxxbridge1$ShimImageMetaData$get_named_attribute_as_f32(::mmimage::ShimImageMetaData const &self, ::rust::Str attribute_name) noexcept; -double mmimage$cxxbridge1$ShimImageMetaData$get_named_attribute_as_f64(const ::mmimage::ShimImageMetaData &self, ::rust::Str attribute_name) noexcept; +double mmimage$cxxbridge1$ShimImageMetaData$get_named_attribute_as_f64(::mmimage::ShimImageMetaData const &self, ::rust::Str attribute_name) noexcept; -void mmimage$cxxbridge1$ShimImageMetaData$get_named_attribute_as_string(const ::mmimage::ShimImageMetaData &self, ::rust::Str attribute_name, ::rust::String *return$) noexcept; +void mmimage$cxxbridge1$ShimImageMetaData$get_named_attribute_as_string(::mmimage::ShimImageMetaData const &self, ::rust::Str attribute_name, ::rust::String *return$) noexcept; -void mmimage$cxxbridge1$ShimImageMetaData$as_string(const ::mmimage::ShimImageMetaData &self, ::rust::String *return$) noexcept; +void mmimage$cxxbridge1$ShimImageMetaData$as_string(::mmimage::ShimImageMetaData const &self, ::rust::String *return$) noexcept; ::mmimage::ShimImageMetaData *mmimage$cxxbridge1$shim_create_image_meta_data_box() noexcept; -bool mmimage$cxxbridge1$shim_image_read_pixels_exr_f32x4(::rust::Str file_path, ::rust::Box<::mmimage::ShimImageMetaData> &out_meta_data, ::rust::Box<::mmimage::ShimImagePixelBuffer> &out_pixel_buffer) noexcept; +bool mmimage$cxxbridge1$shim_image_read_pixels_exr_f32x4(::rust::Str file_path, bool vertical_flip, ::rust::Box<::mmimage::ShimImageMetaData> &out_meta_data, ::rust::Box<::mmimage::ShimImagePixelBuffer> &out_pixel_buffer) noexcept; bool mmimage$cxxbridge1$shim_image_read_metadata_exr(::rust::Str file_path, ::rust::Box<::mmimage::ShimImageMetaData> &out_meta_data) noexcept; -bool mmimage$cxxbridge1$shim_image_write_pixels_exr_f32x4(::rust::Str file_path, ::mmimage::ImageExrEncoder exr_encoder, const ::rust::Box<::mmimage::ShimImageMetaData> &in_meta_data, const ::rust::Box<::mmimage::ShimImagePixelBuffer> &in_pixel_buffer) noexcept; +bool mmimage$cxxbridge1$shim_image_write_pixels_exr_f32x4(::rust::Str file_path, ::mmimage::ImageExrEncoder exr_encoder, ::rust::Box<::mmimage::ShimImageMetaData> const &in_meta_data, ::rust::Box<::mmimage::ShimImagePixelBuffer> const &in_pixel_buffer) noexcept; } // extern "C" } // namespace mmimage namespace std { template <> struct hash<::mmimage::Vec2I32> { - ::std::size_t operator()(const ::mmimage::Vec2I32 &self) const noexcept { + ::std::size_t operator()(::mmimage::Vec2I32 const &self) const noexcept { return ::mmimage::mmimage$cxxbridge1$Vec2I32$operator$hash(self); } }; template <> struct hash<::mmimage::ImageRegionRectangle> { - ::std::size_t operator()(const ::mmimage::ImageRegionRectangle &self) const noexcept { + ::std::size_t operator()(::mmimage::ImageRegionRectangle const &self) const noexcept { return ::mmimage::mmimage$cxxbridge1$ImageRegionRectangle$operator$hash(self); } }; } // namespace std namespace mmimage { -bool ExrPixelLayout::operator==(const ExrPixelLayout &rhs) const noexcept { +bool ExrPixelLayout::operator==(ExrPixelLayout const &rhs) const noexcept { return mmimage$cxxbridge1$ExrPixelLayout$operator$eq(*this, rhs); } -bool ExrPixelLayout::operator!=(const ExrPixelLayout &rhs) const noexcept { +bool ExrPixelLayout::operator!=(ExrPixelLayout const &rhs) const noexcept { return mmimage$cxxbridge1$ExrPixelLayout$operator$ne(*this, rhs); } -bool ExrPixelLayout::operator<(const ExrPixelLayout &rhs) const noexcept { +bool ExrPixelLayout::operator<(ExrPixelLayout const &rhs) const noexcept { return mmimage$cxxbridge1$ExrPixelLayout$operator$lt(*this, rhs); } -bool ExrPixelLayout::operator<=(const ExrPixelLayout &rhs) const noexcept { +bool ExrPixelLayout::operator<=(ExrPixelLayout const &rhs) const noexcept { return mmimage$cxxbridge1$ExrPixelLayout$operator$le(*this, rhs); } -bool ExrPixelLayout::operator>(const ExrPixelLayout &rhs) const noexcept { +bool ExrPixelLayout::operator>(ExrPixelLayout const &rhs) const noexcept { return mmimage$cxxbridge1$ExrPixelLayout$operator$gt(*this, rhs); } -bool ExrPixelLayout::operator>=(const ExrPixelLayout &rhs) const noexcept { +bool ExrPixelLayout::operator>=(ExrPixelLayout const &rhs) const noexcept { return mmimage$cxxbridge1$ExrPixelLayout$operator$ge(*this, rhs); } -bool ImageExrEncoder::operator==(const ImageExrEncoder &rhs) const noexcept { +bool ImageExrEncoder::operator==(ImageExrEncoder const &rhs) const noexcept { return mmimage$cxxbridge1$ImageExrEncoder$operator$eq(*this, rhs); } -bool ImageExrEncoder::operator!=(const ImageExrEncoder &rhs) const noexcept { +bool ImageExrEncoder::operator!=(ImageExrEncoder const &rhs) const noexcept { return mmimage$cxxbridge1$ImageExrEncoder$operator$ne(*this, rhs); } -bool ImageExrEncoder::operator<(const ImageExrEncoder &rhs) const noexcept { +bool ImageExrEncoder::operator<(ImageExrEncoder const &rhs) const noexcept { return mmimage$cxxbridge1$ImageExrEncoder$operator$lt(*this, rhs); } -bool ImageExrEncoder::operator<=(const ImageExrEncoder &rhs) const noexcept { +bool ImageExrEncoder::operator<=(ImageExrEncoder const &rhs) const noexcept { return mmimage$cxxbridge1$ImageExrEncoder$operator$le(*this, rhs); } -bool ImageExrEncoder::operator>(const ImageExrEncoder &rhs) const noexcept { +bool ImageExrEncoder::operator>(ImageExrEncoder const &rhs) const noexcept { return mmimage$cxxbridge1$ImageExrEncoder$operator$gt(*this, rhs); } -bool ImageExrEncoder::operator>=(const ImageExrEncoder &rhs) const noexcept { +bool ImageExrEncoder::operator>=(ImageExrEncoder const &rhs) const noexcept { return mmimage$cxxbridge1$ImageExrEncoder$operator$ge(*this, rhs); } -bool OptionF32::operator==(const OptionF32 &rhs) const noexcept { +bool OptionF32::operator==(OptionF32 const &rhs) const noexcept { return mmimage$cxxbridge1$OptionF32$operator$eq(*this, rhs); } -bool OptionF32::operator!=(const OptionF32 &rhs) const noexcept { +bool OptionF32::operator!=(OptionF32 const &rhs) const noexcept { return mmimage$cxxbridge1$OptionF32$operator$ne(*this, rhs); } -bool OptionF32::operator<(const OptionF32 &rhs) const noexcept { +bool OptionF32::operator<(OptionF32 const &rhs) const noexcept { return mmimage$cxxbridge1$OptionF32$operator$lt(*this, rhs); } -bool OptionF32::operator<=(const OptionF32 &rhs) const noexcept { +bool OptionF32::operator<=(OptionF32 const &rhs) const noexcept { return mmimage$cxxbridge1$OptionF32$operator$le(*this, rhs); } -bool OptionF32::operator>(const OptionF32 &rhs) const noexcept { +bool OptionF32::operator>(OptionF32 const &rhs) const noexcept { return mmimage$cxxbridge1$OptionF32$operator$gt(*this, rhs); } -bool OptionF32::operator>=(const OptionF32 &rhs) const noexcept { +bool OptionF32::operator>=(OptionF32 const &rhs) const noexcept { return mmimage$cxxbridge1$OptionF32$operator$ge(*this, rhs); } -bool Vec2F32::operator==(const Vec2F32 &rhs) const noexcept { +bool Vec2F32::operator==(Vec2F32 const &rhs) const noexcept { return mmimage$cxxbridge1$Vec2F32$operator$eq(*this, rhs); } -bool Vec2F32::operator!=(const Vec2F32 &rhs) const noexcept { +bool Vec2F32::operator!=(Vec2F32 const &rhs) const noexcept { return mmimage$cxxbridge1$Vec2F32$operator$ne(*this, rhs); } -bool Vec2F32::operator<(const Vec2F32 &rhs) const noexcept { +bool Vec2F32::operator<(Vec2F32 const &rhs) const noexcept { return mmimage$cxxbridge1$Vec2F32$operator$lt(*this, rhs); } -bool Vec2F32::operator<=(const Vec2F32 &rhs) const noexcept { +bool Vec2F32::operator<=(Vec2F32 const &rhs) const noexcept { return mmimage$cxxbridge1$Vec2F32$operator$le(*this, rhs); } -bool Vec2F32::operator>(const Vec2F32 &rhs) const noexcept { +bool Vec2F32::operator>(Vec2F32 const &rhs) const noexcept { return mmimage$cxxbridge1$Vec2F32$operator$gt(*this, rhs); } -bool Vec2F32::operator>=(const Vec2F32 &rhs) const noexcept { +bool Vec2F32::operator>=(Vec2F32 const &rhs) const noexcept { return mmimage$cxxbridge1$Vec2F32$operator$ge(*this, rhs); } -bool Vec2I32::operator==(const Vec2I32 &rhs) const noexcept { +bool Vec2I32::operator==(Vec2I32 const &rhs) const noexcept { return mmimage$cxxbridge1$Vec2I32$operator$eq(*this, rhs); } -bool Vec2I32::operator!=(const Vec2I32 &rhs) const noexcept { +bool Vec2I32::operator!=(Vec2I32 const &rhs) const noexcept { return !(*this == rhs); } -bool Vec2I32::operator<(const Vec2I32 &rhs) const noexcept { +bool Vec2I32::operator<(Vec2I32 const &rhs) const noexcept { return mmimage$cxxbridge1$Vec2I32$operator$lt(*this, rhs); } -bool Vec2I32::operator<=(const Vec2I32 &rhs) const noexcept { +bool Vec2I32::operator<=(Vec2I32 const &rhs) const noexcept { return mmimage$cxxbridge1$Vec2I32$operator$le(*this, rhs); } -bool Vec2I32::operator>(const Vec2I32 &rhs) const noexcept { +bool Vec2I32::operator>(Vec2I32 const &rhs) const noexcept { return !(*this <= rhs); } -bool Vec2I32::operator>=(const Vec2I32 &rhs) const noexcept { +bool Vec2I32::operator>=(Vec2I32 const &rhs) const noexcept { return !(*this < rhs); } -bool Box2F32::operator==(const Box2F32 &rhs) const noexcept { +bool Box2F32::operator==(Box2F32 const &rhs) const noexcept { return mmimage$cxxbridge1$Box2F32$operator$eq(*this, rhs); } -bool Box2F32::operator!=(const Box2F32 &rhs) const noexcept { +bool Box2F32::operator!=(Box2F32 const &rhs) const noexcept { return mmimage$cxxbridge1$Box2F32$operator$ne(*this, rhs); } -bool Box2F32::operator<(const Box2F32 &rhs) const noexcept { +bool Box2F32::operator<(Box2F32 const &rhs) const noexcept { return mmimage$cxxbridge1$Box2F32$operator$lt(*this, rhs); } -bool Box2F32::operator<=(const Box2F32 &rhs) const noexcept { +bool Box2F32::operator<=(Box2F32 const &rhs) const noexcept { return mmimage$cxxbridge1$Box2F32$operator$le(*this, rhs); } -bool Box2F32::operator>(const Box2F32 &rhs) const noexcept { +bool Box2F32::operator>(Box2F32 const &rhs) const noexcept { return mmimage$cxxbridge1$Box2F32$operator$gt(*this, rhs); } -bool Box2F32::operator>=(const Box2F32 &rhs) const noexcept { +bool Box2F32::operator>=(Box2F32 const &rhs) const noexcept { return mmimage$cxxbridge1$Box2F32$operator$ge(*this, rhs); } -bool ImageRegionRectangle::operator==(const ImageRegionRectangle &rhs) const noexcept { +bool ImageRegionRectangle::operator==(ImageRegionRectangle const &rhs) const noexcept { return mmimage$cxxbridge1$ImageRegionRectangle$operator$eq(*this, rhs); } -bool ImageRegionRectangle::operator!=(const ImageRegionRectangle &rhs) const noexcept { +bool ImageRegionRectangle::operator!=(ImageRegionRectangle const &rhs) const noexcept { return !(*this == rhs); } -bool ImageRegionRectangle::operator<(const ImageRegionRectangle &rhs) const noexcept { +bool ImageRegionRectangle::operator<(ImageRegionRectangle const &rhs) const noexcept { return mmimage$cxxbridge1$ImageRegionRectangle$operator$lt(*this, rhs); } -bool ImageRegionRectangle::operator<=(const ImageRegionRectangle &rhs) const noexcept { +bool ImageRegionRectangle::operator<=(ImageRegionRectangle const &rhs) const noexcept { return mmimage$cxxbridge1$ImageRegionRectangle$operator$le(*this, rhs); } -bool ImageRegionRectangle::operator>(const ImageRegionRectangle &rhs) const noexcept { +bool ImageRegionRectangle::operator>(ImageRegionRectangle const &rhs) const noexcept { return !(*this <= rhs); } -bool ImageRegionRectangle::operator>=(const ImageRegionRectangle &rhs) const noexcept { +bool ImageRegionRectangle::operator>=(ImageRegionRectangle const &rhs) const noexcept { return !(*this < rhs); } -bool PixelF32x4::operator==(const PixelF32x4 &rhs) const noexcept { +bool PixelF32x4::operator==(PixelF32x4 const &rhs) const noexcept { return mmimage$cxxbridge1$PixelF32x4$operator$eq(*this, rhs); } -bool PixelF32x4::operator!=(const PixelF32x4 &rhs) const noexcept { +bool PixelF32x4::operator!=(PixelF32x4 const &rhs) const noexcept { return mmimage$cxxbridge1$PixelF32x4$operator$ne(*this, rhs); } -bool PixelF32x4::operator<(const PixelF32x4 &rhs) const noexcept { +bool PixelF32x4::operator<(PixelF32x4 const &rhs) const noexcept { return mmimage$cxxbridge1$PixelF32x4$operator$lt(*this, rhs); } -bool PixelF32x4::operator<=(const PixelF32x4 &rhs) const noexcept { +bool PixelF32x4::operator<=(PixelF32x4 const &rhs) const noexcept { return mmimage$cxxbridge1$PixelF32x4$operator$le(*this, rhs); } -bool PixelF32x4::operator>(const PixelF32x4 &rhs) const noexcept { +bool PixelF32x4::operator>(PixelF32x4 const &rhs) const noexcept { return mmimage$cxxbridge1$PixelF32x4$operator$gt(*this, rhs); } -bool PixelF32x4::operator>=(const PixelF32x4 &rhs) const noexcept { +bool PixelF32x4::operator>=(PixelF32x4 const &rhs) const noexcept { return mmimage$cxxbridge1$PixelF32x4$operator$ge(*this, rhs); } -bool PixelF64x2::operator==(const PixelF64x2 &rhs) const noexcept { +bool PixelF64x2::operator==(PixelF64x2 const &rhs) const noexcept { return mmimage$cxxbridge1$PixelF64x2$operator$eq(*this, rhs); } -bool PixelF64x2::operator!=(const PixelF64x2 &rhs) const noexcept { +bool PixelF64x2::operator!=(PixelF64x2 const &rhs) const noexcept { return mmimage$cxxbridge1$PixelF64x2$operator$ne(*this, rhs); } -bool PixelF64x2::operator<(const PixelF64x2 &rhs) const noexcept { +bool PixelF64x2::operator<(PixelF64x2 const &rhs) const noexcept { return mmimage$cxxbridge1$PixelF64x2$operator$lt(*this, rhs); } -bool PixelF64x2::operator<=(const PixelF64x2 &rhs) const noexcept { +bool PixelF64x2::operator<=(PixelF64x2 const &rhs) const noexcept { return mmimage$cxxbridge1$PixelF64x2$operator$le(*this, rhs); } -bool PixelF64x2::operator>(const PixelF64x2 &rhs) const noexcept { +bool PixelF64x2::operator>(PixelF64x2 const &rhs) const noexcept { return mmimage$cxxbridge1$PixelF64x2$operator$gt(*this, rhs); } -bool PixelF64x2::operator>=(const PixelF64x2 &rhs) const noexcept { +bool PixelF64x2::operator>=(PixelF64x2 const &rhs) const noexcept { return mmimage$cxxbridge1$PixelF64x2$operator$ge(*this, rhs); } @@ -1701,12 +1702,12 @@ MMIMAGE_API_EXPORT ::std::size_t ShimImagePixelBuffer::element_count() const noe return mmimage$cxxbridge1$ShimImagePixelBuffer$element_count(*this); } -MMIMAGE_API_EXPORT ::rust::Slice ShimImagePixelBuffer::as_slice_f32x4() const noexcept { - return ::rust::impl<::rust::Slice>::slice(mmimage$cxxbridge1$ShimImagePixelBuffer$as_slice_f32x4(*this)); +MMIMAGE_API_EXPORT ::rust::Slice<::mmimage::PixelF32x4 const> ShimImagePixelBuffer::as_slice_f32x4() const noexcept { + return ::rust::impl<::rust::Slice<::mmimage::PixelF32x4 const>>::slice(mmimage$cxxbridge1$ShimImagePixelBuffer$as_slice_f32x4(*this)); } -MMIMAGE_API_EXPORT ::rust::Slice<::mmimage::PixelF32x4> ShimImagePixelBuffer::as_slice_f32x4_mut() noexcept { - return ::rust::impl<::rust::Slice<::mmimage::PixelF32x4>>::slice(mmimage$cxxbridge1$ShimImagePixelBuffer$as_slice_f32x4_mut(*this)); +MMIMAGE_API_EXPORT ::rust::Slice<::mmimage::PixelF32x4 > ShimImagePixelBuffer::as_slice_f32x4_mut() noexcept { + return ::rust::impl<::rust::Slice<::mmimage::PixelF32x4 >>::slice(mmimage$cxxbridge1$ShimImagePixelBuffer$as_slice_f32x4_mut(*this)); } MMIMAGE_API_EXPORT void ShimImagePixelBuffer::resize(::mmimage::BufferDataType data_type, ::std::size_t image_width, ::std::size_t image_height, ::std::size_t num_channels) noexcept { @@ -1879,15 +1880,15 @@ MMIMAGE_API_EXPORT ::rust::Box<::mmimage::ShimImageMetaData> shim_create_image_m return ::rust::Box<::mmimage::ShimImageMetaData>::from_raw(mmimage$cxxbridge1$shim_create_image_meta_data_box()); } -MMIMAGE_API_EXPORT bool shim_image_read_pixels_exr_f32x4(::rust::Str file_path, ::rust::Box<::mmimage::ShimImageMetaData> &out_meta_data, ::rust::Box<::mmimage::ShimImagePixelBuffer> &out_pixel_buffer) noexcept { - return mmimage$cxxbridge1$shim_image_read_pixels_exr_f32x4(file_path, out_meta_data, out_pixel_buffer); +MMIMAGE_API_EXPORT bool shim_image_read_pixels_exr_f32x4(::rust::Str file_path, bool vertical_flip, ::rust::Box<::mmimage::ShimImageMetaData> &out_meta_data, ::rust::Box<::mmimage::ShimImagePixelBuffer> &out_pixel_buffer) noexcept { + return mmimage$cxxbridge1$shim_image_read_pixels_exr_f32x4(file_path, vertical_flip, out_meta_data, out_pixel_buffer); } MMIMAGE_API_EXPORT bool shim_image_read_metadata_exr(::rust::Str file_path, ::rust::Box<::mmimage::ShimImageMetaData> &out_meta_data) noexcept { return mmimage$cxxbridge1$shim_image_read_metadata_exr(file_path, out_meta_data); } -MMIMAGE_API_EXPORT bool shim_image_write_pixels_exr_f32x4(::rust::Str file_path, ::mmimage::ImageExrEncoder exr_encoder, const ::rust::Box<::mmimage::ShimImageMetaData> &in_meta_data, const ::rust::Box<::mmimage::ShimImagePixelBuffer> &in_pixel_buffer) noexcept { +MMIMAGE_API_EXPORT bool shim_image_write_pixels_exr_f32x4(::rust::Str file_path, ::mmimage::ImageExrEncoder exr_encoder, ::rust::Box<::mmimage::ShimImageMetaData> const &in_meta_data, ::rust::Box<::mmimage::ShimImagePixelBuffer> const &in_pixel_buffer) noexcept { return mmimage$cxxbridge1$shim_image_write_pixels_exr_f32x4(file_path, exr_encoder, in_meta_data, in_pixel_buffer); } } // namespace mmimage diff --git a/lib/cppbind/mmimage/src/cxxbridge.rs b/lib/cppbind/mmimage/src/cxxbridge.rs index 1e4cdd179..b7731600d 100644 --- a/lib/cppbind/mmimage/src/cxxbridge.rs +++ b/lib/cppbind/mmimage/src/cxxbridge.rs @@ -281,6 +281,7 @@ pub mod ffi { extern "Rust" { fn shim_image_read_pixels_exr_f32x4( file_path: &str, + vertical_flip: bool, out_meta_data: &mut Box, out_pixel_buffer: &mut Box, ) -> bool; diff --git a/lib/cppbind/mmimage/src/imagemetadata.rs b/lib/cppbind/mmimage/src/imagemetadata.rs index ad59db583..d4d1ac412 100644 --- a/lib/cppbind/mmimage/src/imagemetadata.rs +++ b/lib/cppbind/mmimage/src/imagemetadata.rs @@ -25,7 +25,7 @@ use crate::cxxbridge::ffi::Vec2I32 as BindVec2I32; use mmimage_rust::metadata::ImageMetaData as CoreImageMetaData; -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct ShimImageMetaData { inner: CoreImageMetaData, } diff --git a/lib/cppbind/mmimage/src/imagepixelbuffer.rs b/lib/cppbind/mmimage/src/imagepixelbuffer.rs index 96de7add2..8f10b242c 100644 --- a/lib/cppbind/mmimage/src/imagepixelbuffer.rs +++ b/lib/cppbind/mmimage/src/imagepixelbuffer.rs @@ -44,15 +44,15 @@ fn bind_to_core_buffer_data_type( } } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct ShimImagePixelBuffer { inner: CoreImagePixelBuffer, } impl ShimImagePixelBuffer { - pub fn new() -> Self { + pub fn empty() -> Self { Self { - inner: CoreImagePixelBuffer::new(), + inner: CoreImagePixelBuffer::empty(), } } @@ -129,5 +129,5 @@ impl ShimImagePixelBuffer { } pub fn shim_create_image_pixel_buffer_box() -> Box { - Box::new(ShimImagePixelBuffer::new()) + Box::new(ShimImagePixelBuffer::empty()) } diff --git a/lib/cppbind/mmimage/src/lib.cpp b/lib/cppbind/mmimage/src/lib.cpp index 766012560..fca1d2f51 100644 --- a/lib/cppbind/mmimage/src/lib.cpp +++ b/lib/cppbind/mmimage/src/lib.cpp @@ -42,13 +42,14 @@ bool image_read_metadata_exr(const rust::Str& file_path, } bool image_read_pixels_exr_f32x4(const rust::Str& file_path, + const bool vertical_flip, ImageMetaData& out_meta_data, ImagePixelBuffer& out_pixel_data) { auto pixel_data = out_pixel_data.get_inner(); auto meta_data = out_meta_data.get_inner(); - bool result = - shim_image_read_pixels_exr_f32x4(file_path, meta_data, pixel_data); + bool result = shim_image_read_pixels_exr_f32x4(file_path, vertical_flip, + meta_data, pixel_data); out_pixel_data.set_inner(pixel_data); out_meta_data.set_inner(meta_data); diff --git a/lib/cppbind/mmimage/src/lib.rs b/lib/cppbind/mmimage/src/lib.rs index 414e020eb..c9834787a 100644 --- a/lib/cppbind/mmimage/src/lib.rs +++ b/lib/cppbind/mmimage/src/lib.rs @@ -47,11 +47,12 @@ pub fn shim_image_read_metadata_exr( pub fn shim_image_read_pixels_exr_f32x4( file_path: &str, + vertical_flip: bool, out_meta_data: &mut Box, out_pixel_buffer: &mut Box, ) -> bool { // TODO: How to return errors? An enum perhaps? - let image = core_image_read_pixels_exr_f32x4(file_path); + let image = core_image_read_pixels_exr_f32x4(file_path, vertical_flip); if let Err(_err) = image { return false; } diff --git a/lib/cppbind/mmimage/tests/CMakeLists.txt b/lib/cppbind/mmimage/tests/CMakeLists.txt index 95b964a6e..67cfdab03 100644 --- a/lib/cppbind/mmimage/tests/CMakeLists.txt +++ b/lib/cppbind/mmimage/tests/CMakeLists.txt @@ -22,12 +22,16 @@ set(target_test_exe_name "mmimage_tests") set(test_source_files ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/common.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_a.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_b.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_c.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_d.cpp ) +include(MMCommonUtils) +mm_common_set_global_compile_options() + # Add test executable using the C++ bindings. message(STATUS "target_test_exe_name: ${target_test_exe_name}") message(STATUS "test_source_files: ${test_source_files}") @@ -46,4 +50,9 @@ target_include_directories(${target_test_exe_name} PUBLIC ${RUST_INCLUDE_DIR} ) +# MM Color IO dependencies +include(MMColorIOUtils) +mmcolorio_find_packages() +mmcolorio_target_link_packages(${target_test_exe_name}) + install(TARGETS ${target_test_exe_name}) diff --git a/lib/cppbind/mmimage/tests/common.cpp b/lib/cppbind/mmimage/tests/common.cpp new file mode 100644 index 000000000..0006c1143 --- /dev/null +++ b/lib/cppbind/mmimage/tests/common.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2023 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#include + +#include +#include +#include +#include + +std::string join_path(const char *arg1, const char *arg2) { + std::stringstream stream; + stream << arg1; + stream << arg2; + return stream.str(); +} + +std::string join_path(const char *arg1, const char *arg2, const char *arg3) { + std::stringstream stream; + stream << arg1; + stream << arg2; + stream << arg3; + return stream.str(); +} + +std::string join_path(const char *arg1, const char *arg2, const char *arg3, + const char *arg4) { + std::stringstream stream; + stream << arg1; + stream << arg2; + stream << arg3; + stream << arg4; + return stream.str(); +} + +bool test_print_metadata_named_attributes(const char *test_name, + mmimage::ImageMetaData &meta_data) { + rust::Vec all_attr_names = + meta_data.all_named_attribute_names(); + + std::cout << test_name << " attrs count: " << all_attr_names.size() + << std::endl; + for (auto value : all_attr_names) { + std::cout << test_name << " attr_name: " << value.c_str() << std::endl; + } + return true; +} + +bool test_print_metadata_fields(const char *test_name, + mmimage::ImageMetaData &meta_data) { + float image_pixel_aspect = meta_data.get_pixel_aspect(); + mmimage::ImageRegionRectangle image_display_window = + meta_data.get_display_window(); + rust::Str image_layer_name = meta_data.get_layer_name(); + mmimage::Vec2I32 image_layer_position = meta_data.get_layer_position(); + mmimage::Vec2F32 image_screen_window_center = + meta_data.get_screen_window_center(); + float image_screen_window_width = meta_data.get_screen_window_width(); + rust::Str image_owner = meta_data.get_owner(); + rust::Str image_comments = meta_data.get_comments(); + rust::Str image_capture_date = meta_data.get_capture_date(); + mmimage::OptionF32 image_utc_offset = meta_data.get_utc_offset(); + mmimage::OptionF32 image_longitude = meta_data.get_longitude(); + mmimage::OptionF32 image_latitude = meta_data.get_latitude(); + mmimage::OptionF32 image_altitude = meta_data.get_altitude(); + mmimage::OptionF32 image_focus = meta_data.get_focus(); + mmimage::OptionF32 image_exposure = meta_data.get_exposure(); + mmimage::OptionF32 image_aperture = meta_data.get_aperture(); + mmimage::OptionF32 image_iso_speed = meta_data.get_iso_speed(); + mmimage::OptionF32 image_frames_per_second = + meta_data.get_frames_per_second(); + std::cout << test_name << " image pixel_aspect: " << image_pixel_aspect + << std::endl + << test_name + << " image display_window: " << image_display_window.position_x + << ',' << image_display_window.position_y << ',' + << image_display_window.size_x << ',' + << image_display_window.size_y << std::endl + << test_name << " image owner: " << image_owner << std::endl + << test_name << " image comments: " << image_comments << std::endl + << test_name << " image capture_date: " << image_capture_date + << std::endl + << test_name << " image utc_offset: " << image_utc_offset + << std::endl + << test_name << " image longitude: " << image_longitude + << std::endl + << test_name << " image latitude: " << image_latitude << std::endl + << test_name << " image altitude: " << image_altitude << std::endl + << test_name << " image focus: " << image_focus << std::endl + << test_name << " image exposure: " << image_exposure << std::endl + << test_name << " image aperture: " << image_aperture << std::endl + << test_name << " image iso_speed: " << image_iso_speed + << std::endl + << test_name + << " image frames_per_second: " << image_frames_per_second + << std::endl; + + return true; +} diff --git a/lib/cppbind/mmimage/tests/common.h b/lib/cppbind/mmimage/tests/common.h index ee3d7f7f1..42bd59945 100644 --- a/lib/cppbind/mmimage/tests/common.h +++ b/lib/cppbind/mmimage/tests/common.h @@ -23,98 +23,15 @@ #include -#include -#include -#include #include -static std::string join_path(const char *arg1, const char *arg2) { - std::stringstream stream; - stream << arg1; - stream << arg2; - return stream.str(); -} +std::string join_path(const char *arg1, const char *arg2); +std::string join_path(const char *arg1, const char *arg2, const char *arg3); +std::string join_path(const char *arg1, const char *arg2, const char *arg3, + const char *arg4); -static std::string join_path(const char *arg1, const char *arg2, - const char *arg3) { - std::stringstream stream; - stream << arg1; - stream << arg2; - stream << arg3; - return stream.str(); -} +bool test_print_metadata_named_attributes(const char *test_name, + mmimage::ImageMetaData &meta_data); -static std::string join_path(const char *arg1, const char *arg2, - const char *arg3, const char *arg4) { - std::stringstream stream; - stream << arg1; - stream << arg2; - stream << arg3; - stream << arg4; - return stream.str(); -} - -static bool test_print_metadata_named_attributes( - const char *test_name, mmimage::ImageMetaData &meta_data) { - rust::Vec all_attr_names = - meta_data.all_named_attribute_names(); - - std::cout << test_name << " attrs count: " << all_attr_names.size() - << std::endl; - for (auto value : all_attr_names) { - std::cout << test_name << " attr_name: " << value.c_str() << std::endl; - } - return true; -} - -static bool test_print_metadata_fields(const char *test_name, - mmimage::ImageMetaData &meta_data) { - float image_pixel_aspect = meta_data.get_pixel_aspect(); - mmimage::ImageRegionRectangle image_display_window = - meta_data.get_display_window(); - rust::Str image_layer_name = meta_data.get_layer_name(); - mmimage::Vec2I32 image_layer_position = meta_data.get_layer_position(); - mmimage::Vec2F32 image_screen_window_center = - meta_data.get_screen_window_center(); - float image_screen_window_width = meta_data.get_screen_window_width(); - rust::Str image_owner = meta_data.get_owner(); - rust::Str image_comments = meta_data.get_comments(); - rust::Str image_capture_date = meta_data.get_capture_date(); - mmimage::OptionF32 image_utc_offset = meta_data.get_utc_offset(); - mmimage::OptionF32 image_longitude = meta_data.get_longitude(); - mmimage::OptionF32 image_latitude = meta_data.get_latitude(); - mmimage::OptionF32 image_altitude = meta_data.get_altitude(); - mmimage::OptionF32 image_focus = meta_data.get_focus(); - mmimage::OptionF32 image_exposure = meta_data.get_exposure(); - mmimage::OptionF32 image_aperture = meta_data.get_aperture(); - mmimage::OptionF32 image_iso_speed = meta_data.get_iso_speed(); - mmimage::OptionF32 image_frames_per_second = - meta_data.get_frames_per_second(); - std::cout << test_name << " image pixel_aspect: " << image_pixel_aspect - << std::endl - << test_name - << " image display_window: " << image_display_window.position_x - << ',' << image_display_window.position_y << ',' - << image_display_window.size_x << ',' - << image_display_window.size_y << std::endl - << test_name << " image owner: " << image_owner << std::endl - << test_name << " image comments: " << image_comments << std::endl - << test_name << " image capture_date: " << image_capture_date - << std::endl - << test_name << " image utc_offset: " << image_utc_offset - << std::endl - << test_name << " image longitude: " << image_longitude - << std::endl - << test_name << " image latitude: " << image_latitude << std::endl - << test_name << " image altitude: " << image_altitude << std::endl - << test_name << " image focus: " << image_focus << std::endl - << test_name << " image exposure: " << image_exposure << std::endl - << test_name << " image aperture: " << image_aperture << std::endl - << test_name << " image iso_speed: " << image_iso_speed - << std::endl - << test_name - << " image frames_per_second: " << image_frames_per_second - << std::endl; - - return true; -} +bool test_print_metadata_fields(const char *test_name, + mmimage::ImageMetaData &meta_data); diff --git a/lib/cppbind/mmimage/tests/test_a.cpp b/lib/cppbind/mmimage/tests/test_a.cpp index 751af2f1f..bc3908eb7 100644 --- a/lib/cppbind/mmimage/tests/test_a.cpp +++ b/lib/cppbind/mmimage/tests/test_a.cpp @@ -34,8 +34,9 @@ bool test_a_image_read(const char *test_name, rust::Str file_path) { auto meta_data = mmimg::ImageMetaData(); auto pixel_buffer = mmimg::ImagePixelBuffer(); - bool result = - mmimg::image_read_pixels_exr_f32x4(file_path, meta_data, pixel_buffer); + const bool vertical_flip = false; + bool result = mmimg::image_read_pixels_exr_f32x4(file_path, vertical_flip, + meta_data, pixel_buffer); std::cout << test_name << " image file path: " << file_path << " image read result: " << static_cast(result) << std::endl @@ -87,11 +88,12 @@ bool test_a_image_read(const char *test_name, rust::Str file_path) { } int test_a(const char *test_name, const char *dir_path) { - auto path_string1 = + const std::string path_string1 = join_path(dir_path, "/Beachball/singlepart.0001", ".exr"); - auto path_string2 = join_path(dir_path, "/ScanLines/Tree", ".exr"); - const auto file_path1 = rust::Str(path_string1); - const auto file_path2 = rust::Str(path_string2); + const std::string path_string2 = + join_path(dir_path, "/ScanLines/Tree", ".exr"); + const rust::Str file_path1(path_string1.c_str()); + const rust::Str file_path2(path_string2.c_str()); bool ok = test_a_image_read(test_name, file_path1); if (!ok) { diff --git a/lib/cppbind/mmimage/tests/test_b.cpp b/lib/cppbind/mmimage/tests/test_b.cpp index d976378b7..8ddfe9641 100644 --- a/lib/cppbind/mmimage/tests/test_b.cpp +++ b/lib/cppbind/mmimage/tests/test_b.cpp @@ -58,11 +58,12 @@ bool test_b_load_file_metadata(const char *test_name, rust::Str file_path) { } int test_b(const char *test_name, const char *dir_path) { - auto path_string1 = + const std::string path_string1 = join_path(dir_path, "/Beachball/singlepart.0001", ".exr"); - auto path_string2 = join_path(dir_path, "/ScanLines/Tree", ".exr"); - const auto file_path1 = rust::Str(path_string1); - const auto file_path2 = rust::Str(path_string2); + const std::string path_string2 = + join_path(dir_path, "/ScanLines/Tree", ".exr"); + const rust::Str file_path1(path_string1.c_str()); + const rust::Str file_path2(path_string2.c_str()); bool ok = test_b_load_file_metadata(test_name, file_path1); if (!ok) { diff --git a/lib/cppbind/mmimage/tests/test_c.cpp b/lib/cppbind/mmimage/tests/test_c.cpp index 08629519e..3d14036a5 100644 --- a/lib/cppbind/mmimage/tests/test_c.cpp +++ b/lib/cppbind/mmimage/tests/test_c.cpp @@ -41,8 +41,9 @@ bool test_c_image_write(const char *test_name, rust::Str input_file_path, }; // TODO: Get the EXR Encoding when reading the file. - bool result = mmimg::image_read_pixels_exr_f32x4(input_file_path, meta_data, - pixel_buffer); + const bool vertical_flip = false; + bool result = mmimg::image_read_pixels_exr_f32x4( + input_file_path, vertical_flip, meta_data, pixel_buffer); std::cout << test_name << " image file path: " << input_file_path << " image read result: " << static_cast(result) << std::endl @@ -82,7 +83,7 @@ bool test_c_image_write(const char *test_name, rust::Str input_file_path, meta_data, pixel_buffer); bool reread_result = mmimg::image_read_pixels_exr_f32x4( - output_file_path, meta_data, pixel_buffer); + output_file_path, vertical_flip, meta_data, pixel_buffer); std::cout << test_name << " image file path: " << output_file_path << " image read result: " << static_cast(reread_result) << std::endl @@ -100,16 +101,19 @@ bool test_c_image_write(const char *test_name, rust::Str input_file_path, } int test_c(const char *test_name, const char *dir_path) { - auto path_string1 = + const std::string path_string1 = join_path(dir_path, "/Beachball/singlepart.0001", ".exr"); - auto path_string2 = join_path(dir_path, "/ScanLines/Tree", ".exr"); - auto path_out_string1 = + const std::string path_string2 = + join_path(dir_path, "/ScanLines/Tree", ".exr"); + const std::string path_out_string1 = join_path(dir_path, "/Beachball/singlepart.0001", ".out.exr"); - auto path_out_string2 = join_path(dir_path, "/ScanLines/Tree", ".out.exr"); - const auto file_path1 = rust::Str(path_string1); - const auto file_path2 = rust::Str(path_string2); - const auto file_path_out1 = rust::Str(path_out_string1); - const auto file_path_out2 = rust::Str(path_out_string2); + const std::string path_out_string2 = + join_path(dir_path, "/ScanLines/Tree", ".out.exr"); + + const rust::Str file_path1(path_string1.c_str()); + const rust::Str file_path2(path_string2.c_str()); + const rust::Str file_path_out1(path_out_string1.c_str()); + const rust::Str file_path_out2(path_out_string2.c_str()); bool ok = test_c_image_write(test_name, file_path1, file_path_out1); if (!ok) { diff --git a/lib/cppbind/mmimage/tests/test_d.cpp b/lib/cppbind/mmimage/tests/test_d.cpp index 8558c1db0..384bc43e1 100644 --- a/lib/cppbind/mmimage/tests/test_d.cpp +++ b/lib/cppbind/mmimage/tests/test_d.cpp @@ -72,8 +72,9 @@ bool test_d_image_write(const char *test_name, const size_t image_width, return false; } + const bool vertical_flip = false; bool reread_result = mmimg::image_read_pixels_exr_f32x4( - output_file_path, meta_data, pixel_buffer); + output_file_path, vertical_flip, meta_data, pixel_buffer); std::cout << test_name << " image file path: " << output_file_path << " image read result: " << static_cast(reread_result) << std::endl @@ -91,8 +92,9 @@ bool test_d_image_write(const char *test_name, const size_t image_width, } int test_d(const char *test_name, const char *dir_path) { - auto out_path = join_path(dir_path, "test_identity_st_map.0001.out.exr"); - const auto out_file_path = rust::Str(out_path); + const std::string out_path = + join_path(dir_path, "test_identity_st_map.0001.out.exr"); + rust::Str out_file_path(out_path.c_str()); size_t image_width = 2048; size_t image_height = 1556; diff --git a/lib/cppbind/mmlens/Cargo.toml b/lib/cppbind/mmlens/Cargo.toml index dbcf9543c..a5cd2bd0c 100644 --- a/lib/cppbind/mmlens/Cargo.toml +++ b/lib/cppbind/mmlens/Cargo.toml @@ -1,32 +1,18 @@ [package] name = "mmlens_cppbind" -version = "0.1.0" -authors = ["david-cattermole "] -edition = "2018" -publish = false +version.workspace = true +authors.workspace = true +edition.workspace = true +publish.workspace = true [lib] name = "mmlens" path = "./src/lib.rs" [dependencies] -anyhow = "1.0.71" -# NOTE: When changing this version number ensure to also update the -# installed 'cxxbridge-cmd' version so it stays in sync; Update it -# here: './scripts/internal/build_rust_library_*.*' -cxx = "=1.0.75" -num_cpus = "1.15.0" -rayon = "1.7.0" -smallvec = "1.10.0" -rustc-hash = "1.1.0" - -[profile.release] -opt-level = 3 -rpath = false -lto = true -codegen-units = 1 - -# NOTE: If we use 'panic = "abort"' then we are unable to produce tests. -# # https://github.com/rust-lang/cargo/issues/6313 -# -# panic = "abort" +anyhow = { workspace = true } +cxx = { workspace = true } +num_cpus = { workspace = true } +rayon = { workspace = true } +smallvec = { workspace = true } +rustc-hash = { workspace = true } diff --git a/lib/cppbind/mmlens/tests/CMakeLists.txt b/lib/cppbind/mmlens/tests/CMakeLists.txt index 661ac3f26..249677108 100644 --- a/lib/cppbind/mmlens/tests/CMakeLists.txt +++ b/lib/cppbind/mmlens/tests/CMakeLists.txt @@ -21,6 +21,7 @@ set(target_test_exe_name "mmlens_tests") set(test_source_files + ${CMAKE_CURRENT_SOURCE_DIR}/common.cpp ${CMAKE_CURRENT_SOURCE_DIR}/main.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_batch_3de_anamorphic_std_deg4.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_batch_3de_anamorphic_std_deg4_rescaled.cpp @@ -37,6 +38,9 @@ set(test_source_files ${CMAKE_CURRENT_SOURCE_DIR}/test_once_3de_radial_std_deg4.cpp ) +include(MMCommonUtils) +mm_common_set_global_compile_options() + # Add test executable using the C++ bindings. add_executable(${target_test_exe_name} ${test_source_files}) target_link_libraries(${target_test_exe_name} @@ -54,4 +58,9 @@ target_include_directories(${target_test_exe_name} PUBLIC ${RUST_INCLUDE_DIR} ) +# MM Color IO dependencies +include(MMColorIOUtils) +mmcolorio_find_packages() +mmcolorio_target_link_packages(${target_test_exe_name}) + install(TARGETS ${target_test_exe_name}) diff --git a/lib/cppbind/mmlens/tests/common.cpp b/lib/cppbind/mmlens/tests/common.cpp new file mode 100644 index 000000000..193a9fd97 --- /dev/null +++ b/lib/cppbind/mmlens/tests/common.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2023, 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#include +#include +#include + +std::string join_path(const char* arg1, const char* arg2) { + std::stringstream stream; + stream << arg1; + stream << arg2; + return stream.str(); +} diff --git a/lib/cppbind/mmlens/tests/common.h b/lib/cppbind/mmlens/tests/common.h index 12b4622e4..cf9d93baa 100644 --- a/lib/cppbind/mmlens/tests/common.h +++ b/lib/cppbind/mmlens/tests/common.h @@ -26,12 +26,7 @@ #include #include -static std::string join_path(const char* arg1, const char* arg2) { - std::stringstream stream; - stream << arg1; - stream << arg2; - return stream.str(); -} +std::string join_path(const char* arg1, const char* arg2); const int kCoordinateSystemImage = 0; const int kCoordinateSystemNDC = 1; diff --git a/lib/cppbind/mmlens/tests/test_lens_file_load.cpp b/lib/cppbind/mmlens/tests/test_lens_file_load.cpp index 19a0f626b..2567994e0 100644 --- a/lib/cppbind/mmlens/tests/test_lens_file_load.cpp +++ b/lib/cppbind/mmlens/tests/test_lens_file_load.cpp @@ -71,8 +71,8 @@ int test_lens_file_load(const char* dir_path, const char* file_name) { if (!option_lens_parameters.exists) { continue; } - const mmlens::Parameters3deClassic lens_parameters = - option_lens_parameters.value; + // const mmlens::Parameters3deClassic lens_parameters = + // option_lens_parameters.value; // TODO: Do something with the lens parameters. } else if (lens_model_type == @@ -83,8 +83,8 @@ int test_lens_file_load(const char* dir_path, const char* file_name) { if (!option_lens_parameters.exists) { continue; } - const mmlens::Parameters3deRadialStdDeg4 lens_parameters = - option_lens_parameters.value; + // const mmlens::Parameters3deRadialStdDeg4 lens_parameters = + // option_lens_parameters.value; // TODO: Do something with the lens parameters. } else if (lens_model_type == @@ -97,8 +97,8 @@ int test_lens_file_load(const char* dir_path, const char* file_name) { if (!option_lens_parameters.exists) { continue; } - const mmlens::Parameters3deAnamorphicStdDeg4 lens_parameters = - option_lens_parameters.value; + // const mmlens::Parameters3deAnamorphicStdDeg4 lens_parameters = + // option_lens_parameters.value; // TODO: Do something with the lens parameters. } else if (lens_model_type == @@ -111,8 +111,8 @@ int test_lens_file_load(const char* dir_path, const char* file_name) { if (!option_lens_parameters.exists) { continue; } - const mmlens::Parameters3deAnamorphicStdDeg4Rescaled - lens_parameters = option_lens_parameters.value; + // const mmlens::Parameters3deAnamorphicStdDeg4Rescaled + // lens_parameters = option_lens_parameters.value; // TODO: Do something with the lens parameters. } else { diff --git a/lib/cppbind/mmscenegraph/Cargo.toml b/lib/cppbind/mmscenegraph/Cargo.toml index c2d19524c..c89eaa5f6 100644 --- a/lib/cppbind/mmscenegraph/Cargo.toml +++ b/lib/cppbind/mmscenegraph/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "mmscenegraph_cppbind" -version = "0.1.0" -authors = ["david-cattermole "] -edition = "2018" -publish = false +version.workspace = true +authors.workspace = true +edition.workspace = true +publish.workspace = true [lib] name = "mmscenegraph" @@ -14,21 +14,5 @@ path = "./src/lib.rs" # crate_type = ["staticlib"] [dependencies] -# NOTE: When changing this version number ensure to also update the -# installed 'cxxbridge-cmd' version so it stays in sync; Update it -# here: './scripts/internal/build_rust_library_*.*' -cxx = "=1.0.75" - -[dependencies.mmscenegraph_rust] -path = "../../rust/mmscenegraph/" - -[profile.release] -opt-level = 3 -rpath = false -lto = true -codegen-units = 1 - -# NOTE: If we use 'panic = "abort"' then we are unable to produce tests. -# # https://github.com/rust-lang/cargo/issues/6313 -# -# panic = "abort" +cxx = { workspace = true } +mmscenegraph_rust = { workspace = true } diff --git a/lib/cppbind/mmscenegraph/tests/CMakeLists.txt b/lib/cppbind/mmscenegraph/tests/CMakeLists.txt index 75858457e..da69034a9 100644 --- a/lib/cppbind/mmscenegraph/tests/CMakeLists.txt +++ b/lib/cppbind/mmscenegraph/tests/CMakeLists.txt @@ -27,6 +27,9 @@ set(test_source_files # ${CMAKE_CURRENT_SOURCE_DIR}/test_c.cpp ) +include(MMCommonUtils) +mm_common_set_global_compile_options() + # Add test executable using the C++ bindings. add_executable(${target_test_exe_name} ${test_source_files}) target_link_libraries(${target_test_exe_name} @@ -44,4 +47,9 @@ target_include_directories(${target_test_exe_name} PUBLIC ${RUST_INCLUDE_DIR} ) +# MM Color IO dependencies +include(MMColorIOUtils) +mmcolorio_find_packages() +mmcolorio_target_link_packages(${target_test_exe_name}) + install(TARGETS ${target_test_exe_name}) diff --git a/lib/mmsolverlibs/Cargo.lock b/lib/mmsolverlibs/Cargo.lock deleted file mode 100644 index 761123186..000000000 --- a/lib/mmsolverlibs/Cargo.lock +++ /dev/null @@ -1,839 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "anyhow" -version = "1.0.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" - -[[package]] -name = "approx" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" -dependencies = [ - "num-traits", -] - -[[package]] -name = "approx" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" -dependencies = [ - "num-traits", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bit_field" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" - -[[package]] -name = "bumpalo" -version = "3.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c6ed94e98ecff0c12dd1b04c15ec0d7d9458ca8fe806cea6f12954efe74c63b" - -[[package]] -name = "bytemuck" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" - -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" -dependencies = [ - "autocfg", - "cfg-if", - "crossbeam-utils", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "cxx" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b7df2292959b7e22a5cb39d37b7e72b2c748b12f956cc409b529fddcdc8857b" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2069b1573efd6e5901004e8fdca2e28bc6f47f86dc24da81182851e71cf3208" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d980827d1ec28ea6e0db545fceaa611eb8e43f70eff8c1c33cc2c96ffa0f0476" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "either" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" - -[[package]] -name = "exr" -version = "1.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdd2162b720141a91a054640662d3edce3d50a944a50ffca5313cd951abb35b4" -dependencies = [ - "bit_field", - "flume", - "half", - "lebe", - "miniz_oxide", - "rayon-core", - "smallvec", - "zune-inflate", -] - -[[package]] -name = "fastapprox" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0031c93f37b5d18272de2d932ebff6a7eb32d4bc3bab6751a9af42da7d1a424" - -[[package]] -name = "fixedbitset" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" - -[[package]] -name = "flume" -version = "0.10.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" -dependencies = [ - "futures-core", - "futures-sink", - "nanorand", - "pin-project", - "spin", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "half" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" -dependencies = [ - "crunchy", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "js-sys" -version = "0.3.63" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "lebe" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" - -[[package]] -name = "libc" -version = "0.2.144" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" - -[[package]] -name = "link-cplusplus" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] - -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "matrixmultiply" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090126dc04f95dc0d1c1c91f61bdd474b3930ca064c1edc8a849da2c6cbe1e77" -dependencies = [ - "autocfg", - "rawpointer", -] - -[[package]] -name = "memoffset" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" -dependencies = [ - "autocfg", -] - -[[package]] -name = "miniz_oxide" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" -dependencies = [ - "adler", -] - -[[package]] -name = "mmcore_cppbind" -version = "0.1.0" -dependencies = [ - "cxx", -] - -[[package]] -name = "mmimage_cppbind" -version = "0.1.0" -dependencies = [ - "cxx", - "mmimage_rust", -] - -[[package]] -name = "mmimage_rust" -version = "0.1.0" -dependencies = [ - "anyhow", - "exr", - "log", - "num", -] - -[[package]] -name = "mmlens_cppbind" -version = "0.1.0" -dependencies = [ - "anyhow", - "cxx", - "num_cpus", - "rayon", - "rustc-hash", - "smallvec", -] - -[[package]] -name = "mmscenegraph_cppbind" -version = "0.1.0" -dependencies = [ - "cxx", - "mmscenegraph_rust", -] - -[[package]] -name = "mmscenegraph_rust" -version = "0.1.0" -dependencies = [ - "approx 0.3.2", - "fastapprox", - "log", - "nalgebra", - "num-traits", - "petgraph", - "rand", - "rustc-hash", -] - -[[package]] -name = "mmsolverlibs_cppbind" -version = "0.1.0" -dependencies = [ - "mmcore_cppbind", - "mmimage_cppbind", - "mmlens_cppbind", - "mmscenegraph_cppbind", -] - -[[package]] -name = "nalgebra" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d506eb7e08d6329505faa8a3a00a5dcc6de9f76e0c77e4b75763ae3c770831ff" -dependencies = [ - "approx 0.5.1", - "matrixmultiply", - "num-complex", - "num-rational", - "num-traits", - "simba", - "typenum", -] - -[[package]] -name = "nanorand" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" -dependencies = [ - "getrandom 0.2.9", -] - -[[package]] -name = "num" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" -dependencies = [ - "autocfg", - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "once_cell" -version = "1.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" - -[[package]] -name = "paste" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" - -[[package]] -name = "petgraph" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" -dependencies = [ - "fixedbitset", - "indexmap", -] - -[[package]] -name = "pin-project" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.16", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro2" -version = "1.0.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha", - "rand_core", - "rand_hc", - "rand_pcg", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rawpointer" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" - -[[package]] -name = "rayon" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "num_cpus", -] - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "safe_arch" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62a7484307bd40f8f7ccbacccac730108f2cae119a3b11c74485b48aa9ea650f" -dependencies = [ - "bytemuck", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "simba" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0b7840f121a46d63066ee7a99fc81dcabbc6105e437cae43528cea199b5a05f" -dependencies = [ - "approx 0.5.1", - "num-complex", - "num-traits", - "paste", - "wide", -] - -[[package]] -name = "simd-adler32" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "unicode-ident" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.16", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.16", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" - -[[package]] -name = "wide" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40018623e2dba2602a9790faba8d33f2ebdebf4b86561b83928db735f8784728" -dependencies = [ - "bytemuck", - "safe_arch", -] - -[[package]] -name = "zune-inflate" -version = "0.2.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" -dependencies = [ - "simd-adler32", -] diff --git a/lib/mmsolverlibs/Cargo.toml b/lib/mmsolverlibs/Cargo.toml index 0ab91245f..33efd6932 100644 --- a/lib/mmsolverlibs/Cargo.toml +++ b/lib/mmsolverlibs/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "mmsolverlibs_cppbind" -version = "0.1.0" -authors = ["david-cattermole "] -edition = "2018" -publish = false +version.workspace = true +authors.workspace = true +edition.workspace = true +publish.workspace = true [lib] name = "mmsolverlibs_rust" @@ -12,20 +12,8 @@ path = "./src/lib.rs" # link with C++. crate_type = ["staticlib"] -[dependencies.mmcore_cppbind] -path = "../cppbind/mmcore" - -[dependencies.mmimage_cppbind] -path = "../cppbind/mmimage" - -[dependencies.mmlens_cppbind] -path = "../cppbind/mmlens" - -[dependencies.mmscenegraph_cppbind] -path = "../cppbind/mmscenegraph" - -[profile.release] -opt-level = 3 -rpath = false -lto = true -codegen-units = 1 +[dependencies] +mmcore_cppbind = { workspace = true } +mmimage_cppbind = { workspace = true } +mmlens_cppbind = { workspace = true } +mmscenegraph_cppbind = { workspace = true } diff --git a/lib/mmsolverlibs/include/mmsolverlibs/debug.h b/lib/mmsolverlibs/include/mmsolverlibs/debug.h index 5865508f8..6703b0d2a 100644 --- a/lib/mmsolverlibs/include/mmsolverlibs/debug.h +++ b/lib/mmsolverlibs/include/mmsolverlibs/debug.h @@ -272,11 +272,11 @@ struct TimestampBenchmark { Timestamp timestamp; Timestamp timestampTotal; - void start() { timestamp = get_timestamp(); }; - Timestamp stop() { return timestampTotal += get_timestamp() - timestamp; }; + void start() { TimestampBenchmark::timestamp = get_timestamp(); }; + Timestamp stop() { return TimestampBenchmark::timestampTotal += get_timestamp() - TimestampBenchmark::timestamp; }; double get_seconds(const uint32_t loopNums = 0) const { - double secs = timestamp_as_seconds(timestampTotal); + double secs = timestamp_as_seconds(TimestampBenchmark::timestampTotal); if (loopNums > 0) { secs /= loopNums; } diff --git a/lib/mmsolverlibs/src/CMakeLists.txt b/lib/mmsolverlibs/src/CMakeLists.txt index fe638b5cb..1771ee65a 100644 --- a/lib/mmsolverlibs/src/CMakeLists.txt +++ b/lib/mmsolverlibs/src/CMakeLists.txt @@ -22,6 +22,8 @@ set(MMSOLVERLIBS_CXXBRIDGE_EXE "/path/to/cxxbridge/executable/cxxbridge" CACHE P "The path to the cxxbridge executable file.") set(main_include_dir ${CMAKE_CURRENT_SOURCE_DIR}/../include) +set(mmcolorio_include_dir ${CMAKE_CURRENT_SOURCE_DIR}/../../cppbind/mmcolorio/include) +set(mmcolorio_source_dir ${CMAKE_CURRENT_SOURCE_DIR}/../../cppbind/mmcolorio/src) set(mmcore_include_dir ${CMAKE_CURRENT_SOURCE_DIR}/../../cppbind/mmcore/include) set(mmcore_source_dir ${CMAKE_CURRENT_SOURCE_DIR}/../../cppbind/mmcore/src) set(mmimage_include_dir ${CMAKE_CURRENT_SOURCE_DIR}/../../cppbind/mmimage/include) @@ -33,6 +35,9 @@ set(mmscenegraph_source_dir ${CMAKE_CURRENT_SOURCE_DIR}/../../cppbind/mmscenegra # C++ Source Code set(lib_source_files + ${mmcolorio_source_dir}/lib.cpp + + ${mmcore_source_dir}/_cxxbridge.cpp ${mmcore_source_dir}/lib.cpp ${mmcore_source_dir}/mmcamera.cpp ${mmcore_source_dir}/mmcoord.cpp @@ -41,8 +46,8 @@ set(lib_source_files ${mmcore_source_dir}/mmmath.cpp ${mmlens_source_dir}/_cxxbridge.cpp - ${mmlens_source_dir}/distortion_process.cpp ${mmlens_source_dir}/distortion_layers.cpp + ${mmlens_source_dir}/distortion_process.cpp ${mmlens_source_dir}/lens_model_3de_anamorphic_deg_4_rotate_squeeze_xy.cpp ${mmlens_source_dir}/lens_model_3de_anamorphic_deg_4_rotate_squeeze_xy_rescaled.cpp ${mmlens_source_dir}/lens_model_3de_classic.cpp @@ -57,11 +62,11 @@ set(lib_source_files ${mmscenegraph_source_dir}/_cxxbridge.cpp ${mmscenegraph_source_dir}/attrdatablock.cpp + ${mmscenegraph_source_dir}/evaluationobjects.cpp ${mmscenegraph_source_dir}/flatscene.cpp ${mmscenegraph_source_dir}/line.cpp ${mmscenegraph_source_dir}/scenebake.cpp ${mmscenegraph_source_dir}/scenegraph.cpp - ${mmscenegraph_source_dir}/evaluationobjects.cpp ) include(MMCommonUtils) @@ -97,8 +102,15 @@ target_link_libraries(${cpp_lib_name} # ldpk to be installed so we have the 'ldpk_INCLUDE_DIR' variable. add_dependencies(${cpp_lib_name} ldpk::ldpk) +# MM Color IO dependencies +include(MMColorIOUtils) +mmcolorio_find_packages() +mmcolorio_target_link_packages(${cpp_lib_name}) +mmcolorio_target_include_packages(${cpp_lib_name}) + target_include_directories(${cpp_lib_name} PUBLIC $ + PUBLIC $ PUBLIC $ PUBLIC $ PUBLIC $ @@ -106,13 +118,14 @@ target_include_directories(${cpp_lib_name} PRIVATE $ PRIVATE $ PUBLIC $ -) + ) mm_common_install_target_library("mmsolverlibs_cpp" ${cpp_lib_name}) # Install public headers include(GNUInstallDirs) install(DIRECTORY + "${mmcolorio_include_dir}/" "${mmcore_include_dir}/" "${mmimage_include_dir}/" "${mmlens_include_dir}/" diff --git a/lib/rust/.gitignore b/lib/rust/.gitignore deleted file mode 100644 index 06f52c05a..000000000 --- a/lib/rust/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -/target* -**/*.rs.bk -Cargo.lock -*~ -*.org diff --git a/lib/rust/Cargo.toml b/lib/rust/Cargo.toml deleted file mode 100644 index 7ae7e42dc..000000000 --- a/lib/rust/Cargo.toml +++ /dev/null @@ -1,6 +0,0 @@ -[workspace] - -members = [ - "mmimage", - "mmscenegraph", -] diff --git a/lib/rust/mmcore/Cargo.toml b/lib/rust/mmcore/Cargo.toml new file mode 100644 index 000000000..5d9bb461d --- /dev/null +++ b/lib/rust/mmcore/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "mmcore_rust" +version.workspace = true +authors.workspace = true +edition.workspace = true +publish.workspace = true + +[lib] +name = "mmcore_rust" +path = "./src/lib.rs" +crate_type = ["lib"] + +[dependencies] +log = { workspace = true } +shellexpand = { workspace = true } diff --git a/lib/rust/mmcore/src/lib.rs b/lib/rust/mmcore/src/lib.rs new file mode 100644 index 000000000..d1cfd1186 --- /dev/null +++ b/lib/rust/mmcore/src/lib.rs @@ -0,0 +1,21 @@ +// +// Copyright (C) 2024 David Cattermole. +// +// This file is part of mmSolver. +// +// mmSolver is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// mmSolver is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with mmSolver. If not, see . +// ==================================================================== +// + +pub mod pathutils; diff --git a/lib/rust/mmcore/src/pathutils.rs b/lib/rust/mmcore/src/pathutils.rs new file mode 100644 index 000000000..656d9d24d --- /dev/null +++ b/lib/rust/mmcore/src/pathutils.rs @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2020, 2021, 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +use log::{debug, warn}; +use shellexpand; + +struct FramePaddingIndexRange { + start_index: usize, + end_index: usize, + padding_count: usize, +} + +fn find_frame_padding_index_range(value: &String) -> FramePaddingIndexRange { + debug!("find_frame_padding_index: {}", value); + let mut start_index = 0; + let mut end_index = 0; + let mut last_char_valid = false; + for (i, byte) in value.bytes().enumerate() { + if byte == b'#' { + if last_char_valid == false { + // This is the first time we see a '#' character. + start_index = i; + end_index = i + 1; + } else { + // This might be the last time we see a '#' character. + end_index = i + 1; + } + last_char_valid = true; + } else { + if last_char_valid == true { + // The previous character was the last time we saw + // '#'. + break; + } + } + } + let padding_count = end_index - start_index; + FramePaddingIndexRange { + start_index, + end_index, + padding_count, + } +} + +fn create_expanded_string( + value: &String, + index_range: FramePaddingIndexRange, + frame: i32, +) -> String { + let mut expanded_string = value.clone(); + debug!( + "index: start={} end={} pad={}", + index_range.start_index, + index_range.end_index, + index_range.padding_count + ); + + // FIXME: Create a way to use the padding_count to dynamically + // change the number formatted into the string. "format!" is a + // compile time creation. This is a pretty bad hack. + let frame_string = match index_range.padding_count { + 0 => "".to_string(), + 1 => format!("{:01}", frame), + 2 => format!("{:02}", frame), + 3 => format!("{:03}", frame), + 4 => format!("{:04}", frame), + 5 => format!("{:05}", frame), + 6 => format!("{:06}", frame), + 7 => format!("{:07}", frame), + 8 => format!("{:08}", frame), + 9 => format!("{:09}", frame), + _ => "".to_string(), + }; + expanded_string.replace_range( + index_range.start_index..index_range.end_index, + &frame_string, + ); + expanded_string +} + +fn expand_file_path(value: &str) -> String { + let expanded_path = match shellexpand::full(value) { + Ok(v) => (*v).to_string(), + Err(e) => { + warn!( + "Environment variable unknown: name={} cause={}", + e.var_name, e.cause + ); + value.to_string() + } + }; + expanded_path.to_string() +} + +pub fn expand_file_path_string(value: &str, frame: i32) -> String { + debug!("expand_string: {} frame={}", value, frame); + let expanded_path = expand_file_path(&value); + let index_range = find_frame_padding_index_range(&expanded_path); + if index_range.padding_count > 0 { + create_expanded_string(&expanded_path, index_range, frame) + } else { + // No expansion needed. + expanded_path + } +} diff --git a/lib/rust/mmimage/Cargo.toml b/lib/rust/mmimage/Cargo.toml index 545450a45..00f5fd376 100644 --- a/lib/rust/mmimage/Cargo.toml +++ b/lib/rust/mmimage/Cargo.toml @@ -1,36 +1,17 @@ [package] name = "mmimage_rust" -version = "0.1.0" -authors = ["david-cattermole "] -edition = "2018" -publish = false +version.workspace = true +authors.workspace = true +edition.workspace = true +publish.workspace = true [lib] name = "mmimage_rust" path = "./src/lib.rs" crate_type = ["lib"] -[[bench]] -name = "bench" -harness = false - [dependencies] -log = "0.4.0" -exr = "1.6.3" -anyhow = "1.0.71" -num = "0.4.0" - -[profile.release] -opt-level = 3 -rpath = false -codegen-units = 1 - -# 'lto = true' Performs "fat" LTO which attempts to perform -# optimizations across all crates within the dependency graph. -lto = true - -# NOTE: If we use 'panic = "abort"' then we are unable to produce tests. -# # https://github.com/rust-lang/cargo/issues/6313 -# -# panic = "abort" - +log = { workspace = true } +exr = { workspace = true } +anyhow = { workspace = true } +num = { workspace = true } diff --git a/lib/rust/mmimage/src/lib.rs b/lib/rust/mmimage/src/lib.rs index 35628c6e5..6ffb089fe 100644 --- a/lib/rust/mmimage/src/lib.rs +++ b/lib/rust/mmimage/src/lib.rs @@ -20,12 +20,12 @@ use crate::encoder::ImageExrEncoder; use crate::metadata::ImageMetaData; -use crate::pixelbuffer::BufferDataType; use crate::pixelbuffer::ImagePixelBuffer; use crate::pixeldata::ImagePixelDataF32x4; use anyhow::bail; use anyhow::Result; use exr::prelude::traits::*; +use log::debug; pub mod datatype; pub mod encoder; @@ -58,31 +58,50 @@ pub fn image_read_metadata_exr(file_path: &str) -> Result { } /// Read an EXR image from a file path. +/// +/// Allows vertically flipping the exported pixel data as we read the +/// data. // // https://github.com/johannesvollmer/exrs/blob/master/GUIDE.md // https://github.com/johannesvollmer/exrs/blob/master/examples/0c_read_rgba.rs pub fn image_read_pixels_exr_f32x4( file_path: &str, + vertical_flip: bool, ) -> Result<(ImageMetaData, ImagePixelBuffer)> { + debug!("Opening file: {}", file_path); + let image = exr::image::read::read() .no_deep_data() .largest_resolution_level() .rgba_channels( |resolution, _channels: &exr::image::RgbaChannels| { - // instantiate your image type with the size of the image in - // file. - let default_pixel = (0.0, 0.0, 0.0, 0.0); - let empty_line = vec![default_pixel; resolution.width()]; - let empty_image = vec![empty_line; resolution.height()]; - empty_image + let pixel_buffer = ImagePixelBuffer::new_f32x4( + resolution.width(), + resolution.height(), + ); + pixel_buffer }, - |pixel_vector, position, (r, g, b, a): (f32, f32, f32, f32)| { + move |pixel_buffer, + position, + (r, g, b, a): (f32, f32, f32, f32)| { // transfer the colors from the file to your image type, // requesting all values to be converted to f32 numbers (you // can also directly use f16 instead) and you could also use // `Sample` instead of `f32` to keep the original data type - // from the file - pixel_vector[position.y()][position.x()] = (r, g, b, a) + // from the file. + + let image_width = pixel_buffer.image_width(); + let image_height = pixel_buffer.image_height(); + + let position_x = position.x(); + let position_y = match vertical_flip { + false => position.y(), + true => (image_height - 1) - position.y(), + }; + let index = (position_y * image_width) + position_x; + + let pixel_buffer_slice = pixel_buffer.as_slice_f32x4_mut(); + pixel_buffer_slice[index] = (r, g, b, a) }, ) .first_valid_layer() @@ -91,43 +110,16 @@ pub fn image_read_pixels_exr_f32x4( // printing all pixels might kill the console, so only print some // meta data about the image. - let _data_window = image.layer_data.absolute_bounds(); + let data_window = image.layer_data.absolute_bounds(); let layer_attributes = image.layer_data.attributes; - let image_width = image.layer_data.size.width(); - let image_height = image.layer_data.size.height(); - - // println!("Opened file: {}", file_path); - // println!("Layer Attributes: {:#?}", layer_attributes); - // println!("Data Window: {:?}", data_window); - - let pixel_count = image_width * image_height; - let default_pixel = 0.0; - let mut pixel_data: Vec = vec![default_pixel; pixel_count * 2]; - let pixel_data_slice = unsafe { - std::mem::transmute::<&mut [f64], &mut [(f32, f32, f32, f32)]>( - pixel_data.as_mut_slice(), - ) - }; - - for y in 0..image_height { - for x in 0..image_width { - let position = exr::math::Vec2(x, y); - let pixel = image.layer_data.channel_data.pixels[position.y()] - [position.x()]; - let pixel_index = (image_width * y) + x; - pixel_data_slice[pixel_index] = pixel; - } - } + let _image_width = image.layer_data.size.width(); + let _image_height = image.layer_data.size.height(); - let num_channels = 4; - let image_data = ImagePixelBuffer::from_data( - BufferDataType::F32, - image_width, - image_height, - num_channels, - pixel_data, - ); + debug!("Opened file: {}", file_path); + debug!("Layer Attributes: {:#?}", layer_attributes); + debug!("Data Window: {:?}", data_window); + let image_data = image.layer_data.channel_data.pixels; let image_metadata = ImageMetaData::with_attributes(&image.attributes, &layer_attributes); diff --git a/lib/rust/mmimage/src/metadata.rs b/lib/rust/mmimage/src/metadata.rs index 4b0865bc3..44150d226 100644 --- a/lib/rust/mmimage/src/metadata.rs +++ b/lib/rust/mmimage/src/metadata.rs @@ -139,7 +139,7 @@ fn convert_option_f32_to_option_rational( } } -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum AttributeValue { None, String(String), @@ -226,7 +226,7 @@ fn generate_named_attributes( named_attributes } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct ImageMetaData { // ImageAttributes // https://docs.rs/exr/latest/exr/meta/header/struct.ImageAttributes.html diff --git a/lib/rust/mmimage/src/pixelbuffer.rs b/lib/rust/mmimage/src/pixelbuffer.rs index ca1979d5f..3203b033b 100644 --- a/lib/rust/mmimage/src/pixelbuffer.rs +++ b/lib/rust/mmimage/src/pixelbuffer.rs @@ -18,6 +18,9 @@ // ==================================================================== // +use log::debug; +use std::fmt; + #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum BufferDataType { None = 0, @@ -31,7 +34,6 @@ pub enum BufferDataType { /// /// The stored data is 64-bit aligned, to allow SSE and AVX /// instructions can be used by the compiler. -#[derive(Clone, Debug)] pub struct ImagePixelBuffer { data_type: BufferDataType, image_width: usize, @@ -45,8 +47,25 @@ pub struct ImagePixelBuffer { data: Vec, } +impl fmt::Debug for ImagePixelBuffer { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ImagePixelBuffer") + .field("data_type", &self.data_type) + .field("image_width", &self.image_width) + .field("image_height", &self.image_height) + .field("num_channels", &self.num_channels) + .finish() + } +} + +impl Drop for ImagePixelBuffer { + fn drop(&mut self) { + debug!("Drop {:?}", self) + } +} + impl ImagePixelBuffer { - pub fn new() -> ImagePixelBuffer { + pub fn empty() -> ImagePixelBuffer { ImagePixelBuffer { data_type: BufferDataType::None, image_width: 0, @@ -56,6 +75,25 @@ impl ImagePixelBuffer { } } + pub fn new_f32x4( + image_width: usize, + image_height: usize, + ) -> ImagePixelBuffer { + let pixel_count = image_width * image_height; + let default_pixel = 0.0; + // 'f64' is used to allocate the Vec, so we can ensure the + // underlying memory is 64-bit aligned. + let pixel_data: Vec = vec![default_pixel; pixel_count * 2]; + + ImagePixelBuffer { + data_type: BufferDataType::F32, + image_width, + image_height, + num_channels: 4, + data: pixel_data, + } + } + pub fn from_data( data_type: BufferDataType, image_width: usize, diff --git a/lib/rust/mmimage/src/pixeldata.rs b/lib/rust/mmimage/src/pixeldata.rs index c3654637d..4861ae0cf 100644 --- a/lib/rust/mmimage/src/pixeldata.rs +++ b/lib/rust/mmimage/src/pixeldata.rs @@ -21,7 +21,7 @@ use crate::pixelbuffer::ImagePixelBuffer; use exr::prelude::*; -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct ImagePixelDataF32x4<'a> { buffer_ref: &'a ImagePixelBuffer, } @@ -46,7 +46,7 @@ impl GetPixel for ImagePixelDataF32x4<'_> { } } -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct ImagePixelDataF64x2 { pub width: usize, pub height: usize, diff --git a/lib/rust/mmimage/tests/01_read_image_pixels.rs b/lib/rust/mmimage/tests/01_read_image_pixels.rs index c7be28450..99f3b951d 100644 --- a/lib/rust/mmimage/tests/01_read_image_pixels.rs +++ b/lib/rust/mmimage/tests/01_read_image_pixels.rs @@ -19,6 +19,7 @@ // use anyhow::Result; +use log::{debug, info}; use mmimage_rust::image_read_rgba_pixels_exr_f32; mod common; @@ -50,11 +51,11 @@ fn main() -> Result<()> { let file_path_str = file_path.as_path().to_str(); match file_path_str { Some(value) => { - println!("Reading: {}", value); + info!("Reading: {}", value); let (_pixel_data, _meta_data) = image_read_rgba_pixels_exr_f32(value)?; - // println!("Metadata: {:?}", meta_data); - // println!("Pixel Data: {:?}", pixel_data); + debug!("Metadata: {:?}", meta_data); + debug!("Pixel Data: {:?}", pixel_data); } _ => (), } diff --git a/lib/rust/mmimage/tests/02_read_image_metadata.rs b/lib/rust/mmimage/tests/02_read_image_metadata.rs index f77a26cc1..9f8860f28 100644 --- a/lib/rust/mmimage/tests/02_read_image_metadata.rs +++ b/lib/rust/mmimage/tests/02_read_image_metadata.rs @@ -19,6 +19,7 @@ // use anyhow::Result; +use log::info; use mmimage_rust::image_read_metadata_exr; mod common; @@ -60,21 +61,21 @@ fn main() -> Result<()> { let file_path_str = file_path.as_path().to_str(); match file_path_str { Some(value) => { - println!("Reading: {}", value); + info!("Reading: {}", value); let metadata = image_read_metadata_exr(value)?; - println!("{:#?}", metadata); + info!("{:#?}", metadata); let attr_names = metadata.all_named_attribute_names(); - println!("Attr Name Count: {}", attr_names.len()); + info!("Attr Name Count: {}", attr_names.len()); for attr_name in attr_names { - println!("Attr Name: {:#?}", attr_name); + info!("Attr Name: {:#?}", attr_name); let has_attr = metadata.has_named_attribute(&attr_name); let attr_type = metadata.get_named_attribute_type_index(&attr_name); let value = metadata.get_named_attribute_as_f32(&attr_name); - println!("Attr Name Exists: {:#?}", has_attr); - println!("Attr Type: {:#?}", attr_type); - println!("Attr Value: {:#?}", value); + info!("Attr Name Exists: {:#?}", has_attr); + info!("Attr Type: {:#?}", attr_type); + info!("Attr Value: {:#?}", value); } let attr_name = "focal_length"; @@ -83,9 +84,9 @@ fn main() -> Result<()> { metadata.get_named_attribute_type_index(&attr_name); let focal_length = metadata.get_named_attribute_as_f32(&attr_name); - println!("Focal Length Name Exists: {:#?}", has_focal_length); - println!("Focal Length Type: {:#?}", focal_length_type); - println!("Focal Length Value: {:#?}", focal_length); + info!("Focal Length Name Exists: {:#?}", has_focal_length); + info!("Focal Length Type: {:#?}", focal_length_type); + info!("Focal Length Value: {:#?}", focal_length); } _ => (), } diff --git a/lib/rust/mmimage/tests/common/mod.rs b/lib/rust/mmimage/tests/common/mod.rs index 6222203ae..fe70c5dc4 100644 --- a/lib/rust/mmimage/tests/common/mod.rs +++ b/lib/rust/mmimage/tests/common/mod.rs @@ -1,5 +1,6 @@ use anyhow::bail; use anyhow::Result; +use log::warn; use std::path::Path; use std::path::PathBuf; @@ -15,7 +16,7 @@ pub fn construct_image_file_paths( if file_path.is_file() { file_paths.push(file_path); } else { - println!("Could not find file name {:?}", file_path); + warn!("Could not find file name {:?}", file_path); } } if file_paths.len() == 0 { diff --git a/lib/rust/mmscenegraph/Cargo.toml b/lib/rust/mmscenegraph/Cargo.toml index 9d0347c32..8acd87d6c 100644 --- a/lib/rust/mmscenegraph/Cargo.toml +++ b/lib/rust/mmscenegraph/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "mmscenegraph_rust" -version = "0.1.0" -authors = ["david-cattermole "] -edition = "2018" -publish = false +version.workspace = true +authors.workspace = true +edition.workspace = true +publish.workspace = true [lib] name = "mmscenegraph_rust" @@ -15,40 +15,14 @@ name = "bench" harness = false [dependencies] -approx = "0.3.2" -fastapprox = "0.3.0" -rustc-hash = "1.1.0" -log = "0.4.0" -num-traits = "0.2" - -[dependencies.rand] -version = "0.7" -default-features = false -features = ["std", "alloc", "small_rng"] - -[dependencies.petgraph] -version = "0.5" -default-features = false -features = ["stable_graph"] - -[dependencies.nalgebra] -version = "0.29" -default-features = false -features = ["std", "matrixmultiply"] - -[dev-dependencies.criterion] -version = "0.3.6" -default-features = false -features = ["html_reports"] - -[profile.release] -opt-level = 3 -rpath = false -# 'lto = true' Performs "fat" LTO which attempts to perform -# optimizations across all crates within the dependency graph. -lto = true -# NOTE: If we use 'panic = "abort"' then we are unable to produce tests. -# # https://github.com/rust-lang/cargo/issues/6313 -# -# panic = "abort" -codegen-units = 1 +approx = { workspace = true } +fastapprox = { workspace = true } +rustc-hash = { workspace = true } +log = { workspace = true } +num-traits = { workspace = true } +rand = { workspace = true } +petgraph = { workspace = true } +nalgebra = { workspace = true } + +[dev-dependencies] +criterion = { workspace = true } diff --git a/lib/rust/rustfmt.toml b/lib/rust/rustfmt.toml deleted file mode 100644 index df99c6919..000000000 --- a/lib/rust/rustfmt.toml +++ /dev/null @@ -1 +0,0 @@ -max_width = 80 diff --git a/lib/cppbind/rustfmt.toml b/lib/rustfmt.toml similarity index 100% rename from lib/cppbind/rustfmt.toml rename to lib/rustfmt.toml diff --git a/mel/AETemplates/AEmmImagePlaneShape2Template.mel b/mel/AETemplates/AEmmImagePlaneShape2Template.mel new file mode 100644 index 000000000..09d1277d9 --- /dev/null +++ b/mel/AETemplates/AEmmImagePlaneShape2Template.mel @@ -0,0 +1,655 @@ +// +// Copyright (C) 2022, 2024 David Cattermole. +// +// This file is part of mmSolver. +// +// mmSolver is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// mmSolver is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with mmSolver. If not, see . +// --------------------------------------------------------------------- +// +// Image plane shape node Template file. +// + + +source "AEmmNodeTemplateCommon"; + +global proc string AEmmImagePlaneShape2_slotAttrNameFromInteger(int $value) +{ + string $attr_name = "unknown"; + if ($value == 0) { + $attr_name = "imageSequenceMain"; + } else if ($value == 1) { + $attr_name = "imageSequenceAlternate1"; + } else if ($value == 2) { + $attr_name = "imageSequenceAlternate2"; + } else if ($value == 3) { + $attr_name = "imageSequenceAlternate3"; + } + return $attr_name; +} + + +global proc string AEmmImagePlaneShape2_slotAttrNameFromOptionMenu(string $option_name) +{ + string $attr_name = "unknown"; + if ($option_name == "Main") { + $attr_name = "imageSequenceMain"; + } else if ($option_name == "Alternate 1") { + $attr_name = "imageSequenceAlternate1"; + } else if ($option_name == "Alternate 2") { + $attr_name = "imageSequenceAlternate2"; + } else if ($option_name == "Alternate 3") { + $attr_name = "imageSequenceAlternate3"; + } + return $attr_name; +} + + +global proc int AEmmImagePlaneShape2_slotAttrValueFromOptionMenu(string $option_name) +{ + int $attr_value = -1; + if ($option_name == "Main") { + $attr_value = 0; + } else if ($option_name == "Alternate 1") { + $attr_value = 1; + } else if ($option_name == "Alternate 2") { + $attr_value = 2; + } else if ($option_name == "Alternate 3") { + $attr_value = 3; + } + return $attr_value; +} + + +global proc int AEmmImagePlaneShape2_getSlotValue(string $image_plane_shp) +{ + int $slot = `getAttr ($image_plane_shp + ".imageSequenceSlot")`; + return $slot; +} + + +global proc AEmmImagePlaneShape2_setSlotValue( + string $image_plane_shp, + int $value) +{ + setAttr ($image_plane_shp + ".imageSequenceSlot") $value; +} + + +global proc AEmmImagePlaneShape2_browser( + string $attr_name, + string $image_plane_shp) +{ + int $slot = AEmmImagePlaneShape2_getSlotValue($image_plane_shp); + string $slot_attr_name = + AEmmImagePlaneShape2_slotAttrNameFromInteger($slot); + + string $cmd = ""; + $cmd = $cmd + "import mmSolver.tools.createimageplane.tool as tool;\n"; + $cmd = $cmd + "import mmSolver.tools.createimageplane.lib as lib;\n"; + $cmd = $cmd + "\n"; + $cmd = $cmd + "image_seq = tool.prompt_user_for_image_sequence();\n"; + $cmd = $cmd + "\n"; + $cmd = $cmd + "mm_ip_shp = \"" + $image_plane_shp + "\";\n"; + $cmd = $cmd + "attr_name = \"" + $attr_name + "\";\n"; + $cmd = $cmd + "current_slot_attr = \"" + $slot_attr_name + "\";\n"; + $cmd = $cmd + "\n"; + $cmd = $cmd + "if image_seq:\n"; + $cmd = $cmd + " if attr_name == current_slot_attr:\n"; + $cmd = $cmd + " lib.set_image_sequence(\n"; + $cmd = $cmd + " mm_ip_shp,\n"; + $cmd = $cmd + " image_seq,\n"; + $cmd = $cmd + " attr_name=attr_name\n"; + $cmd = $cmd + " )\n"; + $cmd = $cmd + " else:\n"; + $cmd = $cmd + " maya.cmds.setAttr(\n"; + $cmd = $cmd + " mm_ip_shp + '.' + attr_name,\n"; + $cmd = $cmd + " image_seq,\n"; + $cmd = $cmd + " type='string'\n"; + $cmd = $cmd + " )\n"; + + python($cmd); +} + + +global proc AEmmImagePlaneShape2_sequenceSlotChanged( + string $attr_name, + string $image_plane_shp, + string $slot_option_value) +{ + string $slot_attr_name = + AEmmImagePlaneShape2_slotAttrNameFromOptionMenu($slot_option_value); + int $slot_attr_value = + AEmmImagePlaneShape2_slotAttrValueFromOptionMenu($slot_option_value); + + AEmmImagePlaneShape2_setSlotValue($image_plane_shp, $slot_attr_value); + + string $cmd = ""; + $cmd = $cmd + "import mmSolver.utils.constant as utils_const;\n"; + $cmd = $cmd + "import mmSolver.utils.imageseq as utils_imageseq;\n"; + $cmd = $cmd + "import mmSolver.tools.createimageplane.tool as tool;\n"; + $cmd = $cmd + "import mmSolver.tools.createimageplane.lib as lib;\n"; + $cmd = $cmd + "\n"; + $cmd = $cmd + "mm_ip_shp = \"" + $image_plane_shp + "\";\n"; + $cmd = $cmd + "attr_name = \"" + $attr_name + "\";\n"; + $cmd = $cmd + "current_slot_attr = \"" + $slot_attr_name + "\";\n"; + $cmd = $cmd + "\n"; + $cmd = $cmd + "image_seq = maya.cmds.getAttr(\n"; + $cmd = $cmd + " mm_ip_shp + '.' + current_slot_attr) or ''\n"; + $cmd = $cmd + "if len(image_seq) == 0:\n"; + $cmd = $cmd + " image_seq = lib.get_default_image_path();\n"; + $cmd = $cmd + "\n"; + $cmd = $cmd + "format_style = utils_const.IMAGE_SEQ_FORMAT_STYLE_HASH_PADDED;\n"; + $cmd = $cmd + "real_file_path = utils_imageseq.resolve_file_pattern_to_file_path(image_seq, format_style);\n"; + $cmd = $cmd + "lib.set_image_sequence(\n"; + $cmd = $cmd + " mm_ip_shp,\n"; + $cmd = $cmd + " real_file_path,\n"; + $cmd = $cmd + " attr_name=current_slot_attr\n"; + $cmd = $cmd + ")\n"; + + python($cmd); +} + +global proc AEmmImagePlaneShape2_sequenceSlotNew(string $file_attr) +{ + string $nice_name = `attributeName -nice $file_attr`; + + setUITemplate -pst attributeEditorTemplate; + rowLayout -nc 3 imageSeqSlotLayout; + + text -label $nice_name; + optionMenu -alwaysCallChangeCommand imageSeqSlotField; + + menuItem -label "Main"; + menuItem -label "Alternate 1"; + menuItem -label "Alternate 2"; + menuItem -label "Alternate 3"; + + setParent ..; + setUITemplate -ppt; + + AEmmImagePlaneShape2_sequenceSlotReplace $file_attr; +} + + +global proc AEmmImagePlaneShape2_sequenceSlotReplace(string $file_attr) +{ + string $image_plane_shp[]; + tokenize($file_attr, ".", $image_plane_shp); + if(size($image_plane_shp) < 1) { + return; + } + + string $tokens[]; + tokenize($file_attr, ".", $tokens); + if(size($tokens) < 1) { + return; + } + + // In MEL the '#1' text will be replaced by the chosen value from + // the 'changeCommand'. + string $cmd = + "AEmmImagePlaneShape2_sequenceSlotChanged \"" + $tokens[1] + "\" " + $tokens[0] + " \"#1\";"; + + // Ensure the slot is maintained when the Attribute Editor is + // opened. + int $slot = AEmmImagePlaneShape2_getSlotValue($image_plane_shp[0]); + // The slot value stored is 0-indexed. With '-1' meaning the value + // is undefined and '0' equal to the 'Main' slot. + optionMenu -edit -select ($slot + 1) imageSeqSlotField; + + optionMenu -edit -changeCommand $cmd imageSeqSlotField; +} + + +global proc AEmmImagePlaneShape2_imageSequenceNew(string $file_attr) +{ + string $nice_name = `attributeName -nice $file_attr`; + + setUITemplate -pst attributeEditorTemplate; + + rowLayout -nc 3 textureNameLayout; + text -label $nice_name; + textField textureNameField; + symbolButton -image "navButtonBrowse.png" browser; + setParent ..; + + setUITemplate -ppt; + + AEmmImagePlaneShape2_imageSequenceReplace $file_attr; +} + + +global proc AEmmImagePlaneShape2_imageSequenceReplace(string $file_attr) +{ + string $image_plane_shp[]; + tokenize($file_attr, ".", $image_plane_shp); + if(size($image_plane_shp) < 1) { + return; + } + + string $attr_names[]; + tokenize ($file_attr, ".", $attr_names); + + string $cmd = "AEmmImagePlaneShape2_browser \"" + $attr_names[1] + "\" " + $image_plane_shp[0]; + + connectControl -fileName textureNameField $file_attr; + button -edit -command $cmd browser; +} + + +global proc AEmmImagePlaneShape2_colorSpaceNew(string $node_attr) +{ + setUITemplate -pst attributeEditorTemplate; + + string $cmd = "import mmSolver.tools.createimageplane._lib.mmimageplane_v2 as lib;"; + $cmd += "node_attr = \"" + $node_attr + "\";"; + $cmd += "lib.color_space_attribute_editor_new(node_attr);"; + python($cmd); + + setUITemplate -ppt; + + AEmmImagePlaneShape2_colorSpaceReplace $node_attr; +} + + +global proc AEmmImagePlaneShape2_colorSpaceReplace(string $node_attr) +{ + // Nothing needed here. +} + + +global proc AEmmImagePlaneShape2_imageCache_updateValues( + string $image_plane_shp) +{ + string $image_seq_size_cmd = ""; + $image_seq_size_cmd = "import mmSolver.tools.createimageplane.lib as lib;"; + $image_seq_size_cmd += "image_plane_shp = \"" + $image_plane_shp + "\";"; + $image_seq_size_cmd += "lib.format_image_sequence_size(image_plane_shp);"; + + string $cache_gpu_used_cmd = ""; + $cache_gpu_used_cmd = "import mmSolver.tools.createimageplane.lib as lib;"; + $cache_gpu_used_cmd += "image_plane_shp = \"" + $image_plane_shp + "\";"; + $cache_gpu_used_cmd += "lib.format_cache_gpu_used(image_plane_shp);"; + + string $cache_cpu_used_cmd = ""; + $cache_cpu_used_cmd = "import mmSolver.tools.createimageplane.lib as lib;"; + $cache_cpu_used_cmd += "image_plane_shp = \"" + $image_plane_shp + "\";"; + $cache_cpu_used_cmd += "lib.format_cache_cpu_used(image_plane_shp);"; + + string $memory_gpu_available_cmd = ""; + $memory_gpu_available_cmd = "import mmSolver.tools.createimageplane.lib as lib;"; + $memory_gpu_available_cmd += "image_plane_shp = \"" + $image_plane_shp + "\";"; + $memory_gpu_available_cmd += "lib.format_memory_gpu_available(image_plane_shp);"; + + string $memory_cpu_available_cmd = ""; + $memory_cpu_available_cmd = "import mmSolver.tools.createimageplane.lib as lib;"; + $memory_cpu_available_cmd += "image_plane_shp = \"" + $image_plane_shp + "\";"; + $memory_cpu_available_cmd += "lib.format_memory_cpu_available(image_plane_shp);"; + + string $image_seq_size = python($image_seq_size_cmd); + string $cache_gpu_used = python($cache_gpu_used_cmd); + string $cache_cpu_used = python($cache_cpu_used_cmd); + string $memory_gpu_available = python($memory_gpu_available_cmd); + string $memory_cpu_available = python($memory_cpu_available_cmd); + + text -edit -label $image_seq_size imageCacheImageSequenceSizeText; + text -edit -label $cache_gpu_used imageCacheCacheGpuUsedText; + text -edit -label $cache_cpu_used imageCacheCacheCpuUsedText; + text -edit -label $memory_gpu_available imageCacheMemoryGpuAvailableText; + text -edit -label $memory_cpu_available imageCacheMemoryCpuAvailableText; +} + + +global proc AEmmImagePlaneShape2_imageCacheDisplaySectionNew(string $attr_name) +{ + setUITemplate -pst attributeEditorTemplate; + + // Refresh button + rowLayout -nc 3 imageCacheDisplay_refreshLayout; + { + // Spacer to avoid button on the screen-left of the Attribute + // Editor. + text -label ""; + + button -label "Refresh Attribute Editor" imageCacheDisplay_refreshButton; + } + setParent ..; + + // The total memory size for the image sequence (GB / MB) + rowLayout -nc 2 imageCacheImageSequenceSizeLayout; + { + text -label "Image Sequence Size:"; + text -align "left" imageCacheImageSequenceSizeText; + } + setParent ..; + + text -label ""; // spacer + + rowLayout -nc 2 imageCacheCacheGpuUsedLayout; + { + text -label "GPU Cache Used:"; + text -align "left" imageCacheCacheGpuUsedText; + } + setParent ..; + + rowLayout -nc 2 imageCacheCacheCpuUsedLayout; + { + text -label "CPU Cache Used:"; + text -align "left" imageCacheCacheCpuUsedText; + } + setParent ..; + + text -label ""; // spacer + + rowLayout -nc 2 imageCacheMemoryGpuAvailableLayout; + { + text -label "Total Memory Available:"; + text -align "left" imageCacheMemoryGpuAvailableText; + } + setParent ..; + + rowLayout -nc 2 imageCacheMemoryCpuAvailableLayout; + { + text -label ""; // empty space + text -align "left" imageCacheMemoryCpuAvailableText; + } + setParent ..; + + setUITemplate -ppt; + + AEmmImagePlaneShape2_imageCacheDisplaySectionReplace $attr_name; +} + + +global proc AEmmImagePlaneShape2_imageCacheDisplaySectionReplace(string $attr_name) +{ + string $image_plane_shp[]; + tokenize($attr_name, ".", $image_plane_shp); + if(size($image_plane_shp) < 1) { + return; + } + + AEmmImagePlaneShape2_imageCache_updateValues($image_plane_shp[0]); + + string $update_cmd = "AEmmImagePlaneShape2_imageCache_updateValues " + $image_plane_shp[0]; + button -edit -command $update_cmd imageCacheDisplay_refreshButton; +} + + +global proc AEmmImagePlaneShape2_imageCache_clearAllSlots( + string $image_plane_shp) +{ + string $cmd = "import mmSolver.tools.imagecache.lib as lib;"; + $cmd += "import mmSolver.tools.imagecache.constant as const;"; + $cmd += "node = \"" + $image_plane_shp + "\";"; + $cmd += "lib.erase_all_images_on_image_plane_slots(const.CACHE_TYPE_CPU, node);"; + $cmd += "lib.erase_all_images_on_image_plane_slots(const.CACHE_TYPE_GPU, node);"; + python($cmd); +} + + +global proc AEmmImagePlaneShape2_imageCache_clearActiveSlot( + string $image_plane_shp) +{ + string $cmd = "import mmSolver.tools.imagecache.lib as lib;"; + $cmd += "import mmSolver.tools.imagecache.constant as const;"; + $cmd += "node = \"" + $image_plane_shp + "\";"; + $cmd += "lib.erase_images_in_active_image_plane_slot(const.CACHE_TYPE_CPU, node);"; + $cmd += "lib.erase_images_in_active_image_plane_slot(const.CACHE_TYPE_GPU, node);"; + python($cmd); +} + + +global proc AEmmImagePlaneShape2_imageCache_clearUnusedSlots( + string $image_plane_shp) +{ + string $cmd = "import mmSolver.tools.imagecache.lib as lib;"; + $cmd += "import mmSolver.tools.imagecache.constant as const;"; + $cmd += "node = \"" + $image_plane_shp + "\";"; + $cmd += "lib.erase_images_in_unused_image_plane_slots(const.CACHE_TYPE_CPU, node);"; + $cmd += "lib.erase_images_in_unused_image_plane_slots(const.CACHE_TYPE_GPU, node);"; + python($cmd); +} + + +global proc AEmmImagePlaneShape2_imageCache_clearAllImages() +{ + string $cmd = "import mmSolver.tools.imagecache.lib as lib;"; + $cmd += "import mmSolver.tools.imagecache.constant as const;"; + $cmd += "lib.erase_all_images(const.CACHE_TYPE_CPU);"; + $cmd += "lib.erase_all_images(const.CACHE_TYPE_GPU);"; + python($cmd); +} + + +global proc AEmmImagePlaneShape2_imageCache_openImageCacheUI() +{ + string $cmd = "import mmSolver.tools.imagecacheprefs.tool as tool;"; + $cmd += "tool.open_window();"; + python($cmd); +} + + +global proc AEmmImagePlaneShape2_imageCacheButtonsNew(string $attr_name) +{ + setUITemplate -pst attributeEditorTemplate; + + text -label ""; + + // Clear buttons + rowLayout -nc 3 imageCacheButtons_clearLayout; + { + // Spacer to avoid button on the screen-left of the Attribute + // Editor. + text -label ""; + + button -label "Clear..." imageCacheButtons_clearButton; + + int $left_mouse_button = 1; + popupMenu -button $left_mouse_button; + + menuItem -label "All image slots" imageCacheButtons_clearAllSlotsMenuItem; + menuItem -label "Active image slot" imageCacheButtons_clearActiveSlotMenuItem; + menuItem -label "Unused image slots" imageCacheButtons_clearUnusedImageSlotsMenuItem; + menuItem -divider true; + menuItem -label "All images in cache" imageCacheButtons_clearAllImagesMenuItem; + } + setParent ..; + + // Image Cache Capacity preferences. + rowLayout -nc 3 imageCacheButtons_capacityLayout; + { + // Spacer to avoid button on the screen-left of the Attribute + // Editor. + text -label ""; + + button -label "Image Cache Preferences..." imageCacheButtons_capacityButton; + } + setParent ..; + + setUITemplate -ppt; + + AEmmImagePlaneShape2_imageCacheButtonsReplace $attr_name; +} + + +global proc AEmmImagePlaneShape2_imageCacheButtonsReplace(string $attr_name) +{ + string $image_plane_shp[]; + tokenize($attr_name, ".", $image_plane_shp); + if(size($image_plane_shp) < 1) { + return; + } + + menuItem -edit + -command ("AEmmImagePlaneShape2_imageCache_clearAllSlots " + $image_plane_shp[0]) + imageCacheButtons_clearAllSlotsMenuItem; + menuItem -edit + -command ("AEmmImagePlaneShape2_imageCache_clearActiveSlot " + $image_plane_shp[0]) + imageCacheButtons_clearActiveSlotMenuItem; + menuItem -edit + -command ("AEmmImagePlaneShape2_imageCache_clearUnusedSlots " + $image_plane_shp[0]) + imageCacheButtons_clearUnusedImageSlotsMenuItem; + menuItem -edit + -command ("AEmmImagePlaneShape2_imageCache_clearAllImages;") + imageCacheButtons_clearAllImagesMenuItem; + + button -edit + -command "AEmmImagePlaneShape2_imageCache_openImageCacheUI;" + imageCacheButtons_capacityButton; +} + + +global proc AEmmImagePlaneShape2Template(string $nodeName) +{ + AEmmNodeShapeTemplateCommonBegin($nodeName); + + editorTemplate -beginLayout "Display" -collapse 0; + { + editorTemplate -addControl "visibleToCameraOnly"; + editorTemplate -addSeparator; + editorTemplate -addControl "colorGain"; + editorTemplate -addControl "colorExposure"; + editorTemplate -addControl "colorGamma"; + editorTemplate -addControl "colorSaturation"; + editorTemplate -addControl "colorSoftClip"; + editorTemplate -addControl "alphaGain"; + editorTemplate -addSeparator; + editorTemplate + -callCustom + "AEmmImagePlaneShape2_colorSpaceNew" + "AEmmImagePlaneShape2_colorSpaceReplace" + "inputColorSpace"; + editorTemplate -addSeparator; + editorTemplate -addControl "imageIgnoreAlpha"; + editorTemplate -addControl "displayChannel"; + } + editorTemplate -endLayout; + + editorTemplate -beginLayout "Image Sequence" -collapse 0; + { + editorTemplate + -callCustom + "AEmmImagePlaneShape2_sequenceSlotNew" + "AEmmImagePlaneShape2_sequenceSlotReplace" + "imageSequenceSlot"; + + editorTemplate + -callCustom + "AEmmImagePlaneShape2_imageSequenceNew" + "AEmmImagePlaneShape2_imageSequenceReplace" + "imageSequenceMain"; + editorTemplate + -callCustom + "AEmmImagePlaneShape2_imageSequenceNew" + "AEmmImagePlaneShape2_imageSequenceReplace" + "imageSequenceAlternate1"; + editorTemplate + -callCustom + "AEmmImagePlaneShape2_imageSequenceNew" + "AEmmImagePlaneShape2_imageSequenceReplace" + "imageSequenceAlternate2"; + editorTemplate + -callCustom + "AEmmImagePlaneShape2_imageSequenceNew" + "AEmmImagePlaneShape2_imageSequenceReplace" + "imageSequenceAlternate3"; + + editorTemplate -addSeparator; + editorTemplate -addControl "imageWidth"; + editorTemplate -addControl "imageHeight"; + editorTemplate -addControl "imagePixelAspect"; + editorTemplate -addSeparator; + editorTemplate -addControl "imageSequenceStartFrame"; + editorTemplate -addControl "imageSequenceEndFrame"; + editorTemplate -addSeparator; + // TODO: Add radio button to choose what will connect to the + // 'imageSequenceFrame' value? Options are: + // - Scene Time (time1) + // - Animation Curve + // + editorTemplate -addControl "imageSequenceFrame"; + editorTemplate -addControl "imageSequenceFirstFrame"; + editorTemplate -addControl "imageSequenceFrameOutput"; + editorTemplate -addSeparator; + editorTemplate -addControl "imageFlip"; + editorTemplate -addControl "imageFlop"; + } + editorTemplate -endLayout; + + editorTemplate -beginLayout "HUD" -collapse 0; + { + editorTemplate -addControl "drawHud"; + editorTemplate -addSeparator; + editorTemplate -addControl "drawCameraSize"; + editorTemplate -addControl "drawImageSize"; + // TODO: Add 'hudTextColor' - control the HUD text color. + } + editorTemplate -endLayout; + + editorTemplate -beginLayout "Image Cache" -collapse 1; + { + editorTemplate + -callCustom + "AEmmImagePlaneShape2_imageCacheDisplaySectionNew" + "AEmmImagePlaneShape2_imageCacheDisplaySectionReplace" + ""; + editorTemplate + -callCustom + "AEmmImagePlaneShape2_imageCacheButtonsNew" + "AEmmImagePlaneShape2_imageCacheButtonsReplace" + ""; + } + editorTemplate -endLayout; + + editorTemplate -beginLayout "Miscellaneous" -collapse 1; + { + editorTemplate -addControl "meshResolution"; + editorTemplate + -callCustom + "AEmmImagePlaneShape2_colorSpaceNew" + "AEmmImagePlaneShape2_colorSpaceReplace" + "outputColorSpace"; + } + editorTemplate -endLayout; + + editorTemplate -beginLayout "Nodes" -collapse 1; + { + editorTemplate -addControl "geometryNode"; + editorTemplate -addControl "cameraNode"; + editorTemplate -addControl "imagePlaneShapeNode"; + } + editorTemplate -endLayout; + + + editorTemplate -beginLayout "Extended Image Details" -collapse 1; + { + editorTemplate -addControl "imageNumChannels"; + editorTemplate -addControl "imageBytesPerChannel"; + editorTemplate -addControl "imageSizeBytes"; + editorTemplate -addControl "imageSequencePadding"; + editorTemplate -addControl "inputColorSpace"; + editorTemplate -addControl "outputColorSpace"; + } + editorTemplate -endLayout; + + // Internals that we don't want the user to see. + editorTemplate -suppress "cameraWidthInch"; + editorTemplate -suppress "cameraHeightInch"; + editorTemplate -suppress "lensHashCurrent"; + editorTemplate -suppress "lensHashPrevious"; + editorTemplate -suppress "imageSequenceSlot"; + + AEmmNodeShapeTemplateCommonEnd($nodeName); +} diff --git a/mel/AETemplates/AEmmImagePlaneShapeTemplate.mel b/mel/AETemplates/AEmmImagePlaneShapeTemplate.mel index 075ba7ffe..e72940a58 100644 --- a/mel/AETemplates/AEmmImagePlaneShapeTemplate.mel +++ b/mel/AETemplates/AEmmImagePlaneShapeTemplate.mel @@ -97,6 +97,8 @@ global proc AEmmImagePlaneShape_browser( string $cmd = ""; $cmd = $cmd + "import mmSolver.tools.createimageplane.tool as tool;\n"; $cmd = $cmd + "import mmSolver.tools.createimageplane.lib as lib;\n"; + $cmd = $cmd + "import mmSolver.tools.createimageplane._lib.constant as const;\n"; + $cmd = $cmd + "version = const.MM_IMAGE_PLANE_VERSION_ONE;\n"; $cmd = $cmd + "image_seq = tool.prompt_user_for_image_sequence();\n"; $cmd = $cmd + "if image_seq:\n"; $cmd = $cmd + " mm_ip_shp = \"" + $image_plane_shp + "\";\n"; @@ -106,7 +108,8 @@ global proc AEmmImagePlaneShape_browser( $cmd = $cmd + " lib.set_image_sequence(\n"; $cmd = $cmd + " mm_ip_shp,\n"; $cmd = $cmd + " image_seq,\n"; - $cmd = $cmd + " attr_name=attr_name\n"; + $cmd = $cmd + " attr_name=attr_name,\n"; + $cmd = $cmd + " version=version\n"; $cmd = $cmd + " )\n"; $cmd = $cmd + " else:\n"; $cmd = $cmd + " maya.cmds.setAttr(\n"; @@ -135,6 +138,8 @@ global proc AEmmImagePlaneShape_sequenceSlotChanged( $cmd = $cmd + "import os.path;\n"; $cmd = $cmd + "import mmSolver.tools.createimageplane.tool as tool;\n"; $cmd = $cmd + "import mmSolver.tools.createimageplane.lib as lib;\n"; + $cmd = $cmd + "import mmSolver.tools.createimageplane._lib.constant as const;\n"; + $cmd = $cmd + "version = const.MM_IMAGE_PLANE_VERSION_ONE;\n"; $cmd = $cmd + "mm_ip_shp = \"" + $image_plane_shp + "\";\n"; $cmd = $cmd + "attr_name = \"" + $attr_name + "\";\n"; $cmd = $cmd + "current_slot_attr = \"" + $slot_attr_name + "\";\n"; @@ -145,7 +150,8 @@ global proc AEmmImagePlaneShape_sequenceSlotChanged( $cmd = $cmd + "lib.set_image_sequence(\n"; $cmd = $cmd + " mm_ip_shp,\n"; $cmd = $cmd + " image_seq,\n"; - $cmd = $cmd + " attr_name=current_slot_attr\n"; + $cmd = $cmd + " attr_name=current_slot_attr,\n"; + $cmd = $cmd + " version=version\n"; $cmd = $cmd + ")\n"; python($cmd); diff --git a/mel/AETemplates/AEmmImageSequenceFrameLogicTemplate.mel b/mel/AETemplates/AEmmImageSequenceFrameLogicTemplate.mel new file mode 100644 index 000000000..7a18f7f34 --- /dev/null +++ b/mel/AETemplates/AEmmImageSequenceFrameLogicTemplate.mel @@ -0,0 +1,39 @@ +// +// Copyright (C) 2024 David Cattermole. +// +// This file is part of mmSolver. +// +// mmSolver is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// mmSolver is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with mmSolver. If not, see . +// --------------------------------------------------------------------- +// +// Image Sequence Frame Logic node Template file. +// + +source "AEmmNodeTemplateCommon"; + +global proc AEmmImageSequenceFrameLogicTemplate(string $nodeName) { + AEmmNodeTemplateCommonBegin($nodeName); + + editorTemplate -beginLayout "Common" -collapse 0; + editorTemplate -addControl "inFrame"; + editorTemplate -addSeparator; + editorTemplate -addControl "firstFrame"; + editorTemplate -addControl "startFrame"; + editorTemplate -addControl "endFrame"; + editorTemplate -addSeparator; + editorTemplate -addControl "outFrame"; + editorTemplate -endLayout; + + AEmmNodeTemplateCommonEnd($nodeName); +} diff --git a/mel/AETemplates/AEmmRenderGlobalsSilhouetteTemplate.mel b/mel/AETemplates/AEmmRenderGlobalsSilhouetteTemplate.mel index 93bc3e54f..1b03b899b 100644 --- a/mel/AETemplates/AEmmRenderGlobalsSilhouetteTemplate.mel +++ b/mel/AETemplates/AEmmRenderGlobalsSilhouetteTemplate.mel @@ -17,7 +17,7 @@ // along with mmSolver. If not, see . // --------------------------------------------------------------------- // -// mmRenderer Render Globals AE Template +// Silhouette Renderer Render Globals AE Template // global proc AEmmRenderGlobalsSilhouetteTemplate(string $name) { @@ -31,10 +31,6 @@ global proc AEmmRenderGlobalsSilhouetteTemplate(string $name) { -beginLayout "Attributes" -collapse false; - editorTemplate - -label "Enable" - -addControl "enable"; - editorTemplate -label "Depth Offset" -addControl "depthOffset"; @@ -43,6 +39,10 @@ global proc AEmmRenderGlobalsSilhouetteTemplate(string $name) { -label "Width" -addControl "width"; + editorTemplate + -label "Override Color" + -addControl "overrideColor"; + editorTemplate -label "Color" -addControl "color"; @@ -51,8 +51,27 @@ global proc AEmmRenderGlobalsSilhouetteTemplate(string $name) { -label "Alpha" -addControl "alpha"; + editorTemplate + -label "Cull Face" + -addControl "cullFace"; + + editorTemplate -endLayout; + + editorTemplate + -beginLayout "Debug" + -collapse true; + + editorTemplate + -label "Enable" + -addControl "enable"; + + editorTemplate + -label "Operation Number" + -addControl "operationNum"; + editorTemplate -endLayout; + editorTemplate -endNoOptimize; editorTemplate -addExtraControls; @@ -60,5 +79,9 @@ global proc AEmmRenderGlobalsSilhouetteTemplate(string $name) { // include/call base class/node attributes AEabstractBaseCreateTemplate $name; + // Don't need to show the enable check-box, users will always want + // it enabled, but we leave it here for testing/debugging. + editorTemplate -suppress "enable"; + editorTemplate -endScrollLayout; } diff --git a/mel/AETemplates/AEmmRenderGlobalsTemplate.mel b/mel/AETemplates/AEmmRenderGlobalsStandardTemplate.mel similarity index 92% rename from mel/AETemplates/AEmmRenderGlobalsTemplate.mel rename to mel/AETemplates/AEmmRenderGlobalsStandardTemplate.mel index 04776493b..29c6021fc 100644 --- a/mel/AETemplates/AEmmRenderGlobalsTemplate.mel +++ b/mel/AETemplates/AEmmRenderGlobalsStandardTemplate.mel @@ -17,10 +17,10 @@ // along with mmSolver. If not, see . // --------------------------------------------------------------------- // -// mmRenderer Render Globals AE Template +// Standard Renderer Render Globals AE Template // -global proc AEmmRenderGlobalsTemplate(string $name) { +global proc AEmmRenderGlobalsStandardTemplate(string $name) { string $parent = `setParent -q`; editorTemplate -beginScrollLayout; diff --git a/mel/mmRendererSilhouetteOptionBox.mel b/mel/mmRendererSilhouetteOptionBox.mel index c7277042a..42c884b7c 100644 --- a/mel/mmRendererSilhouetteOptionBox.mel +++ b/mel/mmRendererSilhouetteOptionBox.mel @@ -32,13 +32,18 @@ // global proc mmRendererSilhouetteOptionBox() { - print("mmRendererSilhouetteOptionBox"); - if (!`objExists "mmRenderGlobalsSilhouette"`) { - string $node = `createNode "mmRenderGlobalsSilhouette" - -name "mmRenderGlobalsSilhouette" + // print("mmRendererSilhouetteOptionBox"); + string $node_type = "mmRenderGlobalsSilhouette"; + if (!`objExists $node_type`) { + string $node = `createNode $node_type + -name $node_type -shared -skipSelect`; lockNode -lock on $node; } - select -r "mmRenderGlobalsSilhouette"; + select -r $node_type; + + // Users want to be able to select and change the color swatch, so + // we need the attribute editor open. + AttributeEditor; return; } diff --git a/mel/mmRendererOptionBox.mel b/mel/mmRendererStandardOptionBox.mel similarity index 83% rename from mel/mmRendererOptionBox.mel rename to mel/mmRendererStandardOptionBox.mel index 7290d35b2..4473ec2db 100644 --- a/mel/mmRendererOptionBox.mel +++ b/mel/mmRendererStandardOptionBox.mel @@ -31,14 +31,15 @@ // // -global proc mmRendererOptionBox() { - print("mmRendererOptionBox"); - if (!`objExists "mmRenderGlobals"`) { - string $node = `createNode "mmRenderGlobals" - -name "mmRenderGlobals" +global proc mmRendererStandardOptionBox() { + // print("mmRendererStandardOptionBox"); + string $node_type = "mmRenderGlobalsStandard"; + if (!`objExists $node_type`) { + string $node = `createNode $node_type + -name $node_type -shared -skipSelect`; lockNode -lock on $node; } - select -r "mmRenderGlobals"; + select -r $node_type; return; } diff --git a/pylintrc b/pylintrc index fd4697e8f..7e645132b 100644 --- a/pylintrc +++ b/pylintrc @@ -13,7 +13,8 @@ ignore=CVS,.git # Add files or directories matching the regex patterns to the blacklist. The # regex matches against base names, not paths. -ignore-paths=python/mmSolver/tools/mltools/*.py +ignore-paths=^python/mmSolver/tools/mltools/*.py$, + ^python/mmSolver/tools/meshfrompoints/delaunator.py$ # Pickle collected data for later comparisons. persistent=yes diff --git a/pyproject.toml b/pyproject.toml index 265b16147..53bf1194f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [tool.black] line-length = 88 # The black default is a good compromise. skip-string-normalization = 1 -target-version = ['py34'] +target-version = ['py36', 'py37', 'py38', 'py39', 'py310'] include = '\.py$' extend-exclude = ''' /( @@ -20,9 +20,63 @@ extend-exclude = ''' # Same as Black. line-length = 88 -# Assume Python 3.7 +# Assume Python 3.7+, because that's the minimum supported version by +# Ruff. target-version = "py37" -[tool.ruff.mccabe] +# Exclude a variety of commonly ignored directories. +exclude = [ + ".bzr", + ".direnv", + ".eggs", + ".git", + ".git-rewrite", + ".hg", + ".ipynb_checkpoints", + ".mypy_cache", + ".nox", + ".pants.d", + ".pyenv", + ".pytest_cache", + ".pytype", + ".ruff_cache", + ".svn", + ".tox", + ".venv", + ".vscode", + "__pypackages__", + "_build", + "buck-out", + "build", + "dist", + "node_modules", + "site-packages", + "venv", + + # Custom directories for mmSolver. + "mltools", + "delaunator.py" +] + +[tool.ruff.lint] +# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default. +# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or +# McCabe complexity (`C901`) by default. +select = [ + "E4", + "E7", + "E9", + "F", + # "B" # "Bugbear" lints. Enable at a later date. +] + +ignore = [ + "E402", # Avoid line-length complaints. + "E501", # Avoid module import complaints. + "F401", # Unused module. + "F841" # Local variable `start_frame` is assigned to but never used +] + +[tool.ruff.lint.mccabe] # Unlike Flake8, default to a complexity level of 10. max-complexity = 10 diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 01538bc25..fa38110ad 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -194,6 +194,16 @@ if (MMSOLVER_BUILD_QT_UI) ${CMAKE_CURRENT_BINARY_DIR}/mmSolver/tools/surfacecluster/ui/ui_surfacecluster_layout.py ) + compile_qt_ui_to_python_file("meshfrompoints" + ${CMAKE_CURRENT_SOURCE_DIR}/mmSolver/tools/meshfrompoints/ui/meshfrompoints_layout.ui + ${CMAKE_CURRENT_BINARY_DIR}/mmSolver/tools/meshfrompoints/ui/ui_meshfrompoints_layout.py + ) + + compile_qt_ui_to_python_file("imagecacheprefs" + ${CMAKE_CURRENT_SOURCE_DIR}/mmSolver/tools/imagecacheprefs/ui/imagecacheprefs_layout.ui + ${CMAKE_CURRENT_BINARY_DIR}/mmSolver/tools/imagecacheprefs/ui/ui_imagecacheprefs_layout.py + ) + # Install generated Python UI files install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/" DESTINATION "${MODULE_FULL_NAME}/python" diff --git a/python/mmSolver/_api/_execute/main.py b/python/mmSolver/_api/_execute/main.py index ea6835bc6..e3f861e0f 100644 --- a/python/mmSolver/_api/_execute/main.py +++ b/python/mmSolver/_api/_execute/main.py @@ -279,7 +279,7 @@ def execute( panels = viewport_utils.get_all_model_panels() panel_objs, panel_node_type_vis = executepresolve.preSolve_queryViewportState( - options, panels + options.refresh, options.do_isolate, options.display_node_types, panels ) # Save scene state, to revert to later on. @@ -343,8 +343,16 @@ def execute( marker_relock_nodes |= executepresolve.preSolve_unlockMarkerAttrs(mkr_list) # Prepare frame solve - executepresolve.preSolve_setIsolatedNodes(action_list, options, panels) - executepresolve.preSolve_triggerEvaluation(action_list, cur_frame, options) + executepresolve.preSolve_setIsolatedNodes( + action_list, + options.refresh, + options.do_isolate, + options.display_node_types, + panels, + ) + executepresolve.preSolve_triggerEvaluation( + action_list, cur_frame, options.pre_solve_force_eval, options.force_update + ) # Ensure prediction attributes are created and initialised. collectionutils.set_initial_prediction_attributes(col, attr_list, cur_frame) @@ -506,7 +514,9 @@ def execute( # Refresh the Viewport. if func_is_mmsolver_v1 is True: frame = kwargs.get('frame') - executepostsolve.postSolve_refreshViewport(options, frame) + executepostsolve.postSolve_refreshViewport( + options.refresh, options.force_update, frame + ) finally: # If something has gone wrong, or the user cancels the solver # without finishing, then we make sure to reconnect animcurves @@ -523,7 +533,7 @@ def execute( marker_relock_nodes = set() executepostsolve.postSolve_setViewportState( - options, panel_objs, panel_node_type_vis + options.refresh, options.do_isolate, panel_objs, panel_node_type_vis ) collectionutils.run_status_func(status_fn, 'Solve Ended') collectionutils.run_progress_func(prog_fn, 100) diff --git a/python/mmSolver/_api/_execute/postsolve.py b/python/mmSolver/_api/_execute/postsolve.py index 896ddf5ea..74fc32fd2 100644 --- a/python/mmSolver/_api/_execute/postsolve.py +++ b/python/mmSolver/_api/_execute/postsolve.py @@ -32,37 +32,39 @@ LOG = mmSolver.logger.get_logger() -def postSolve_refreshViewport(options, frame): +def postSolve_refreshViewport(refresh, force_update, frame): """ Refresh the viewport after a solve has finished. - :param options: The execution options for the current solve. - :type options: ExecuteOptions + :type refresh: bool or None + :type do_isolate: bool or None :param frame: The list of frame numbers, first item in list is used to refresh the viewport. :type frame: [int or float, ..] """ - # Refresh the Viewport. - if options.refresh is not True: + assert refresh is None or isinstance(refresh, bool) + assert force_update is None or isinstance(force_update, bool) + + if refresh is not True: return maya.cmds.currentTime( frame[0], edit=True, - update=options.force_update, + update=force_update, ) maya.cmds.refresh() return -def postSolve_setViewportState(options, panel_objs, panel_node_type_vis): +def postSolve_setViewportState(refresh, do_isolate, panel_objs, panel_node_type_vis): """ - Change the viewport state based on the ExecuteOptions given + Change the viewport state based on the values given - :param options: The execution options for the current solve. - :type options: ExecuteOptions + :type refresh: bool or None + :type do_isolate: bool or None :param panel_objs: The panels and object to isolate, in a list of tuples. @@ -72,7 +74,10 @@ def postSolve_setViewportState(options, panel_objs, panel_node_type_vis): The panels and node-type visibility options in a list of tuples. :type panel_node_type_vis: [(str, {str: int or bool or None}), ..] """ - if options.refresh is not True: + assert refresh is None or isinstance(refresh, bool) + assert do_isolate is None or isinstance(do_isolate, bool) + + if refresh is not True: return # Isolate Objects restore. @@ -80,10 +85,10 @@ def postSolve_setViewportState(options, panel_objs, panel_node_type_vis): if objs is None: # No original objects, disable 'isolate # selected' after resetting the objects. - if options.do_isolate is True: + if do_isolate is True: viewport_utils.set_isolated_nodes(panel, [], False) - elif options.do_isolate is True: + elif do_isolate is True: viewport_utils.set_isolated_nodes(panel, list(objs), True) # Show menu restore. diff --git a/python/mmSolver/_api/_execute/presolve.py b/python/mmSolver/_api/_execute/presolve.py index c2b950302..d09cf5ce1 100644 --- a/python/mmSolver/_api/_execute/presolve.py +++ b/python/mmSolver/_api/_execute/presolve.py @@ -57,26 +57,35 @@ def preSolve_updateProgress(prog_fn, status_fn): return -def preSolve_queryViewportState(options, panels): +def preSolve_queryViewportState(refresh, do_isolate, display_node_types, panels): """ + Query the viewport state before solving. + If 'refresh' is 'on' change all viewports to 'isolate selected' on only the markers and bundles being solved. This will speed up computations, especially per-frame solving as it will not re-compute any invisible nodes (such as rigs or image planes). - :param options: - :param panels: - :return: + :type refresh: bool or None + :type do_isolate: bool or None + :type display_node_types: {str: bool, ..} + + :param panels: List of panel names to query details from. + :type panels: [str, ..] + + :rtype: ({str: [str, ..]}, {str: {str: bool}}) """ + assert refresh is None or isinstance(refresh, bool) + assert do_isolate is None or isinstance(do_isolate, bool) + assert display_node_types is None or isinstance(display_node_types, dict) + panel_objs = {} panel_node_type_vis = collections.defaultdict(dict) - if options.refresh is not True: + if refresh is not True: return panel_objs, panel_node_type_vis - display_node_types = options.display_node_types if display_node_types is not None: - assert isinstance(display_node_types, dict) for panel in panels: node_types = display_node_types.keys() node_type_vis = dict() @@ -85,7 +94,7 @@ def preSolve_queryViewportState(options, panels): node_type_vis[node_type] = value panel_node_type_vis[panel] = node_type_vis - if options.do_isolate is True: + if do_isolate is True: for panel in panels: state = maya.cmds.isolateSelect(panel, query=True, state=True) nodes = None @@ -96,17 +105,28 @@ def preSolve_queryViewportState(options, panels): return panel_objs, panel_node_type_vis -def preSolve_setIsolatedNodes(actions_list, options, panels): +def preSolve_setIsolatedNodes( + actions_list, refresh, do_isolate, display_node_types, panels +): """ Prepare frame solve Isolate all nodes used in all of the kwargs to be run. Note; This assumes the isolated objects are visible, but they may actually be hidden. + + :type refresh: bool or None + :type do_isolate: bool or None + :type display_node_types: {str: bool, ..} + + :returns: None """ - if options.refresh is not True: + assert refresh is None or isinstance(refresh, bool) + assert do_isolate is None or isinstance(do_isolate, bool) + assert display_node_types is None or isinstance(display_node_types, dict) + if refresh is not True: return - if options.do_isolate is True: + if do_isolate is True: isolate_nodes = set() for action in actions_list: kwargs = action.kwargs @@ -117,9 +137,7 @@ def preSolve_setIsolatedNodes(actions_list, options, panels): for panel in panels: viewport_utils.set_isolated_nodes(panel, isolate_node_list, True) - display_node_types = options.display_node_types if display_node_types is not None: - assert isinstance(display_node_types, dict) for panel in panels: for node_type, value in display_node_types.items(): if value is None: @@ -129,7 +147,9 @@ def preSolve_setIsolatedNodes(actions_list, options, panels): return -def preSolve_triggerEvaluation(action_list, cur_frame, options): +def preSolve_triggerEvaluation( + action_list, cur_frame, pre_solve_force_eval, force_update +): """ Set the first current time to the frame before current. @@ -144,10 +164,12 @@ def preSolve_triggerEvaluation(action_list, cur_frame, options): :param cur_frame: The current frame number. :type cur_frame: int or float - :param options: The execution options for the solve. - :type options: ExecuteOptions + :type pre_solve_force_eval: None or bool + :type force_update: None or bool + + :rtype: None """ - if options.pre_solve_force_eval is not True: + if pre_solve_force_eval is not True: return frame_list = [] for action in action_list: @@ -160,7 +182,7 @@ def preSolve_triggerEvaluation(action_list, cur_frame, options): maya.cmds.currentTime( cur_frame + 1, edit=True, - update=options.force_update, + update=force_update, ) return diff --git a/python/mmSolver/_api/camera.py b/python/mmSolver/_api/camera.py index 039cca9d2..6706c66a4 100644 --- a/python/mmSolver/_api/camera.py +++ b/python/mmSolver/_api/camera.py @@ -44,6 +44,15 @@ def _create_camera_attributes(cam_shp): """ assert isinstance(cam_shp, pycompat.TEXT_TYPE) assert maya.cmds.nodeType(cam_shp) == 'camera' + + cam_shp_is_locked = maya.cmds.lockNode(cam_shp, query=True)[0] + if cam_shp_is_locked is True: + LOG.warn( + 'Cannot create camera attributes, camera shape is locked; cam_shp=%r', + cam_shp, + ) + return + node_obj = node_utils.get_as_object_apione(cam_shp) dg_node_fn = OpenMaya.MFnDependencyNode(node_obj) @@ -72,9 +81,16 @@ def _create_camera_attributes(cam_shp): def _create_lens_toggle_setup(cam_tfm, cam_shp): - # When linking to a camera, if an attribute 'lens' - # does not already exist, create it. - _create_camera_attributes(cam_shp) + cam_shp_is_locked = maya.cmds.lockNode(cam_shp, query=True)[0] + if cam_shp_is_locked is False: + _create_camera_attributes(cam_shp) + + in_lens_attr_exists = node_utils.attribute_exists('inLens', cam_shp) + if in_lens_attr_exists is False: + return None + + # When linking to a camera, if an attribute 'lens' does not + # already exist, create it. toggle_nodes = ( maya.cmds.listConnections(cam_shp + ".inLens", shapes=False, destination=True) or [] @@ -84,8 +100,13 @@ def _create_lens_toggle_setup(cam_tfm, cam_shp): toggle_node = maya.cmds.createNode( 'mmLensModelToggle', name=const.LENS_TOGGLE_NODE_NAME ) - maya.cmds.connectAttr(cam_shp + '.inLens', toggle_node + '.inLens') - maya.cmds.connectAttr(toggle_node + '.outLens', cam_shp + '.outLens') + + out_lens_attr_exists = node_utils.attribute_exists('outLens', cam_shp) + + if in_lens_attr_exists: + maya.cmds.connectAttr(cam_shp + '.inLens', toggle_node + '.inLens') + if out_lens_attr_exists: + maya.cmds.connectAttr(toggle_node + '.outLens', cam_shp + '.outLens') else: toggle_node = toggle_nodes[0] return toggle_node @@ -96,6 +117,16 @@ def _link_lens_to_camera(cam_tfm, cam_shp, lens): Assumes that no lens is already connected to the camera.""" assert isinstance(lens, lensmodule.Lens) + + in_lens_attr_exists = node_utils.attribute_exists('inLens', cam_shp) + if in_lens_attr_exists is False: + LOG.warn( + 'Cannot link lens to camera, missing "inLens" attribute; cam_shp=%r lens=%r', + cam_shp, + lens, + ) + return + lens_node = lens.get_node() src = lens_node + '.outLens' dst = cam_shp + '.inLens' @@ -106,6 +137,14 @@ def _link_lens_to_camera(cam_tfm, cam_shp, lens): def _unlink_lens_from_camera(cam_tfm, cam_shp): """Disconnect Lens(es) from the Camera attribute.""" + in_lens_attr_exists = node_utils.attribute_exists('inLens', cam_shp) + if in_lens_attr_exists is False: + LOG.warn( + 'Cannot unlink lens from camera, missing "inLens" attribute; cam_shp=%r', + cam_shp, + ) + return + lens_node_connections = ( maya.cmds.listConnections( cam_shp + ".inLens", @@ -519,7 +558,13 @@ def get_lens_enable(self): msg = "Camera object has no transform/shape node: object=%r" LOG.warn(msg, self) return + toggle_node = _create_lens_toggle_setup(cam_tfm, cam_shp) + if toggle_node is None: + msg = "Camera node toggle could not be found or created: object=%r" + LOG.warn(msg, self) + return + return maya.cmds.getAttr(toggle_node + '.enable') def set_lens_enable(self, value): @@ -533,7 +578,13 @@ def set_lens_enable(self, value): msg = "Camera object has no transform/shape node: object=%r" LOG.warn(msg, self) return + toggle_node = _create_lens_toggle_setup(cam_tfm, cam_shp) + if toggle_node is None: + msg = "Camera node toggle could not be found or created: object=%r" + LOG.warn(msg, self) + return + maya.cmds.setAttr(toggle_node + '.enable', value) return @@ -552,7 +603,14 @@ def get_lens(self): msg = "Camera object has no transform/shape node: object=%r" LOG.warn(msg, self) return lens + _create_lens_toggle_setup(cam_tfm, cam_shp) + in_lens_attr_exists = node_utils.attribute_exists('inLens', cam_shp) + if in_lens_attr_exists is not True: + msg = 'Camera node "inLens" attribute not found: object=%r' + LOG.warn(msg, self) + return + nodes = ( maya.cmds.listConnections( cam_shp + '.inLens', source=True, destination=False, shapes=False diff --git a/python/mmSolver/_api/sethelper.py b/python/mmSolver/_api/sethelper.py index 336ea0e66..d4b94a3a7 100644 --- a/python/mmSolver/_api/sethelper.py +++ b/python/mmSolver/_api/sethelper.py @@ -116,7 +116,7 @@ def get_annotation(self): return ret def set_annotation(self, value): - assert isinstance(value, str) + assert isinstance(value, pycompat.TEXT_TYPE) set_node = self.get_node() maya.cmds.sets(set_node, edit=True, text=value) return diff --git a/python/mmSolver/_api/solveresult.py b/python/mmSolver/_api/solveresult.py index 593037388..5eb59aee9 100644 --- a/python/mmSolver/_api/solveresult.py +++ b/python/mmSolver/_api/solveresult.py @@ -715,5 +715,5 @@ def format_timestamp(value): # Remove microseconds from the datetime object. stamp = ts.replace(ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.second, 0) stamp = stamp.isoformat(' ') - assert isinstance(stamp, str) + assert isinstance(stamp, pycompat.TEXT_TYPE) return stamp diff --git a/python/mmSolver/startup.py b/python/mmSolver/startup.py index e932c93f1..9be823eb9 100644 --- a/python/mmSolver/startup.py +++ b/python/mmSolver/startup.py @@ -23,6 +23,10 @@ components of mmSolver. """ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + import os import maya.cmds import maya.utils @@ -69,6 +73,15 @@ def mmsolver_register_events(): mmSolver.tools.registerevents.tool.register_events() +def mmsolver_image_cache_initialise(): + """ + Initialise the mmSolver ImageCache. + """ + import mmSolver.tools.imagecache.initialise + + mmSolver.tools.imagecache.initialise.main() + + def mmsolver_startup(): """ Responsible for starting up mmSolver, including creating shelves, diff --git a/python/mmSolver/tools/attributebake/constant.py b/python/mmSolver/tools/attributebake/constant.py index 49199a7f3..58cc8a3f5 100644 --- a/python/mmSolver/tools/attributebake/constant.py +++ b/python/mmSolver/tools/attributebake/constant.py @@ -53,6 +53,7 @@ DEFAULT_FRAME_RANGE_MODE = FRAME_RANGE_MODE_TIMELINE_INNER_VALUE DEFAULT_SMART_BAKE_STATE = False DEFAULT_FROM_CHANNELBOX_STATE = True +DEFAULT_PRESERVE_OUTSIDE_KEYS_STATE = True # Config files CONFIG_FRAME_RANGE_MODE_KEY = 'mmSolver_attrbake_frame_range_mode' @@ -60,3 +61,4 @@ CONFIG_FRAME_END_KEY = 'mmSolver_attrbake_frame_end' CONFIG_SMART_BAKE_KEY = 'mmSolver_attrbake_smart_bake' CONFIG_FROM_CHANNELBOX_KEY = 'mmSolver_attrbake_from_channel_box' +CONFIG_PRESERVE_OUTSIDE_KEYS_KEY = 'mmSolver_attrbake_preserve_outside_keys' diff --git a/python/mmSolver/tools/attributebake/lib.py b/python/mmSolver/tools/attributebake/lib.py index 2ffaf3d8a..4e512a91c 100644 --- a/python/mmSolver/tools/attributebake/lib.py +++ b/python/mmSolver/tools/attributebake/lib.py @@ -48,7 +48,9 @@ def get_bake_frame_range(frame_range_mode, custom_start_frame, custom_end_frame) return frame_range -def bake_attributes(nodes, attrs, start_frame, end_frame, smart_bake=False): +def bake_attributes( + nodes, attrs, start_frame, end_frame, smart_bake=False, preserve_outside_keys=False +): """ Bake the attributes on nodes. @@ -60,11 +62,14 @@ def bake_attributes(nodes, attrs, start_frame, end_frame, smart_bake=False): :param start_frame: Start frame to bake. :param end_frame: End frame to bake. :param smart_bake: Perform a "smart" bake - do not bake per-frame. + :param preserve_outside_keys: + Delete the keyframes outside the given frame range, or not. """ assert isinstance(nodes, list) assert isinstance(start_frame, pycompat.INT_TYPES) assert isinstance(end_frame, pycompat.INT_TYPES) assert isinstance(smart_bake, bool) + assert isinstance(preserve_outside_keys, bool) assert isinstance(attrs, list) if smart_bake is True: maya.cmds.bakeResults( @@ -72,6 +77,7 @@ def bake_attributes(nodes, attrs, start_frame, end_frame, smart_bake=False): time=(start_frame, end_frame), attribute=attrs, smart=int(smart_bake), + preserveOutsideKeys=preserve_outside_keys, simulation=True, sparseAnimCurveBake=False, minimizeRotation=True, @@ -81,6 +87,7 @@ def bake_attributes(nodes, attrs, start_frame, end_frame, smart_bake=False): nodes, time=(start_frame, end_frame), attribute=attrs, + preserveOutsideKeys=preserve_outside_keys, simulation=True, sparseAnimCurveBake=False, minimizeRotation=True, diff --git a/python/mmSolver/tools/attributebake/tool.py b/python/mmSolver/tools/attributebake/tool.py index 4a4d99eb1..3f779449e 100644 --- a/python/mmSolver/tools/attributebake/tool.py +++ b/python/mmSolver/tools/attributebake/tool.py @@ -66,6 +66,10 @@ def main(): from_channelbox_state = configmaya.get_scene_option( const.CONFIG_FROM_CHANNELBOX_KEY, const.DEFAULT_FROM_CHANNELBOX_STATE ) + preserve_outside_keys_state = configmaya.get_scene_option( + const.CONFIG_PRESERVE_OUTSIDE_KEYS_KEY, + const.DEFAULT_PRESERVE_OUTSIDE_KEYS_STATE, + ) frame_range = lib.get_bake_frame_range( frame_range_mode, custom_start_frame, custom_end_frame @@ -86,15 +90,25 @@ def main(): disable_viewport_mode=const_utils.DISABLE_VIEWPORT_MODE_VP1_VALUE, ) with ctx: + bake_success = False try: lib.bake_attributes( - nodes, attrs, frame_range.start, frame_range.end, smart_bake_state + nodes, + attrs, + frame_range.start, + frame_range.end, + smart_bake=smart_bake_state, + preserve_outside_keys=preserve_outside_keys_state, ) + bake_success = True except Exception: LOG.exception('Bake attributes failed.') finally: e = time.time() - LOG.warn('Bake attribute success. Time elapsed: %r secs', e - s) + if bake_success is True: + LOG.info('Bake attribute success. Time elapsed: %r secs', e - s) + else: + LOG.error('Bake attribute failed. Time elapsed: %r secs', e - s) return diff --git a/python/mmSolver/tools/attributebake/ui/attrbake_layout.py b/python/mmSolver/tools/attributebake/ui/attrbake_layout.py index 2e92c9c7b..30db4f19d 100644 --- a/python/mmSolver/tools/attributebake/ui/attrbake_layout.py +++ b/python/mmSolver/tools/attributebake/ui/attrbake_layout.py @@ -57,6 +57,9 @@ def __init__(self, parent=None, *args, **kwargs): self.end_frame_spinbox.valueChanged.connect(self.endFrameValueChanged) self.smart_bake_cbox.stateChanged.connect(self.smartBakeValueChanged) self.channel_box_cbox.stateChanged.connect(self.fromChannelBoxValueChanged) + self.preserve_outside_keys_cbox.stateChanged.connect( + self.preserveOutsideKeysValueChanged + ) self.populateUi() @@ -127,6 +130,11 @@ def fromChannelBoxValueChanged(self): value = self.channel_box_cbox.isChecked() configmaya.set_scene_option(name, value, add_attr=True) + def preserveOutsideKeysValueChanged(self): + name = const.CONFIG_PRESERVE_OUTSIDE_KEYS_KEY + value = self.preserve_outside_keys_cbox.isChecked() + configmaya.set_scene_option(name, value, add_attr=True) + def reset_options(self): name = const.CONFIG_FRAME_RANGE_MODE_KEY value = const.DEFAULT_FRAME_RANGE_MODE @@ -148,6 +156,10 @@ def reset_options(self): value = const.DEFAULT_FROM_CHANNELBOX_STATE configmaya.set_scene_option(name, value) + name = const.CONFIG_PRESERVE_OUTSIDE_KEYS_KEY + value = const.DEFAULT_PRESERVE_OUTSIDE_KEYS_STATE + configmaya.set_scene_option(name, value) + self.populateUi() return @@ -168,6 +180,10 @@ def populateUi(self): const.CONFIG_FROM_CHANNELBOX_KEY, default=const.DEFAULT_FROM_CHANNELBOX_STATE, ) + preserve_outside_keys_state = configmaya.get_scene_option( + const.CONFIG_PRESERVE_OUTSIDE_KEYS_KEY, + default=const.DEFAULT_PRESERVE_OUTSIDE_KEYS_STATE, + ) label = const.FRAME_RANGE_MODE_VALUE_LABEL_MAP[frame_range_mode] self.frame_range_combo.setCurrentText(label) @@ -180,4 +196,5 @@ def populateUi(self): self.smart_bake_cbox.setChecked(bool(smart_bake_state)) self.channel_box_cbox.setChecked(bool(from_channelbox_state)) + self.preserve_outside_keys_cbox.setChecked(bool(preserve_outside_keys_state)) return diff --git a/python/mmSolver/tools/attributebake/ui/attrbake_layout.ui b/python/mmSolver/tools/attributebake/ui/attrbake_layout.ui index e7b3d33f5..77a5982b2 100644 --- a/python/mmSolver/tools/attributebake/ui/attrbake_layout.ui +++ b/python/mmSolver/tools/attributebake/ui/attrbake_layout.ui @@ -94,6 +94,16 @@ + + + + Preserve Outside Keys + + + false + + + diff --git a/python/mmSolver/tools/cameracontextmenu/constant.py b/python/mmSolver/tools/cameracontextmenu/constant.py index 7c748d23a..ed0667553 100644 --- a/python/mmSolver/tools/cameracontextmenu/constant.py +++ b/python/mmSolver/tools/cameracontextmenu/constant.py @@ -107,7 +107,13 @@ CREATE_CAMERA_TOOLTIP = 'Create a camera.' CREATE_CAMERA_CMD_LANG = 'python' CREATE_CAMERA_CMD = ( - 'import mmSolver.tools.cameracontextmenu.lib as lib;' 'lib.create_camera();' + 'import maya.cmds;' + 'import mmSolver.tools.cameracontextmenu.lib as lib;' + 'cam = lib.create_camera();' + 'cam_tfm = cam.get_transform_node();' + 'cam_shp = cam.get_shape_node();' + 'maya.cmds.select(cam_tfm, replace=True);' + 'lib.open_node_in_attribute_editor(node=cam_shp);' ) @@ -115,8 +121,11 @@ CREATE_IMAGE_PLANE_TOOLTIP = 'Create MM Image Plane on {camera_shape_name}.' CREATE_IMAGE_PLANE_CMD_LANG = 'python' CREATE_IMAGE_PLANE_CMD = ( + 'import maya.cmds;' 'import mmSolver.tools.cameracontextmenu.lib as lib;' - 'lib.create_image_plane(camera_shape_node="{camera_shape_node}");' + 'tfm, shp = lib.create_image_plane(camera_shape_node="{camera_shape_node}");' + 'maya.cmds.select(shp, replace=True);' + 'lib.open_node_in_attribute_editor(node=shp);' ) @@ -124,6 +133,13 @@ CREATE_LENS_TOOLTIP = 'Create Lens Layer on {camera_shape_name}.' CREATE_LENS_CMD_LANG = 'python' CREATE_LENS_CMD = ( - 'import mmSolver.tools.cameracontextmenu.lib as lib;' - 'lib.create_lens(camera_shape_node="{camera_shape_node}");' + 'import maya.cmds\n' + 'import mmSolver.tools.cameracontextmenu.lib as lib\n' + 'lens = lib.create_lens(camera_shape_node="{camera_shape_node}")\n' + 'node = None\n' + 'if lens is not None:\n' + ' node = lens.get_node()\n' + 'if node is not None:\n' + ' maya.cmds.select(node, replace=True)\n' + ' lib.open_node_in_attribute_editor(node=node)\n' ) diff --git a/python/mmSolver/tools/cameracontextmenu/lib/camera.py b/python/mmSolver/tools/cameracontextmenu/lib/camera.py index 449733391..70eee0853 100644 --- a/python/mmSolver/tools/cameracontextmenu/lib/camera.py +++ b/python/mmSolver/tools/cameracontextmenu/lib/camera.py @@ -31,20 +31,21 @@ def create_camera(): cam = createcamera_lib.create_camera() - cam_shp = cam.get_shape_node() - maya.cmds.select(cam_shp, replace=True) return cam def camera_lens_distortion_enabled(camera_shape_node): assert camera_shape_node is not None cam = mmapi.Camera(shape=camera_shape_node) - return cam.get_lens_enable() + return cam.get_lens_enable() is True def toggle_camera_lens_distortion_enabled(camera_shape_node): assert camera_shape_node is not None cam = mmapi.Camera(shape=camera_shape_node) enable = cam.get_lens_enable() + if enable is None: + LOG.warn('Cannot toggle camera lens distortion.') + return cam.set_lens_enable(not enable) return diff --git a/python/mmSolver/tools/centertwodee/lib.py b/python/mmSolver/tools/centertwodee/lib.py index fa9987cdc..460fbee61 100644 --- a/python/mmSolver/tools/centertwodee/lib.py +++ b/python/mmSolver/tools/centertwodee/lib.py @@ -27,6 +27,7 @@ import maya.cmds import mmSolver.logger +import mmSolver.utils.math as math_utils import mmSolver.utils.viewport as viewport_utils import mmSolver.utils.reproject as reproject_utils import mmSolver.tools.centertwodee.constant as const @@ -34,51 +35,6 @@ LOG = mmSolver.logger.get_logger() -def _lerp(min_value, max_value, mix): - """ - Return 'min_value' to 'max_value' linearly, for a 'mix' value - between 0.0 and 1.0. - - :type min_value: float - :type max_value: float - :type mix: float - - :rtype: float - """ - return (1.0 - mix) * min_value + mix * max_value - - -def _inverse_lerp(min_value, max_value, mix): - """ - Return 0.0 to 1.0 linearly, for a 'mix' value between 'min_value' - and 'max_value'. - - :type min_value: float - :type max_value: float - :type mix: float - - :rtype: float - """ - return (mix - min_value) / (max_value - min_value) - - -def _remap(old_min, old_max, new_min, new_max, mix): - """ - Remap from the 'old_*' values to 'new_*' values, using a 'mix' - value between 0.0 and 1.0; - - :type old_min: float - :type old_max: float - :type new_min: float - :type new_max: float - :type mix: float - - :rtype: float - """ - blend = _inverse_lerp(old_min, old_max, mix) - return _lerp(new_min, new_max, blend) - - def set_offset_range(source='slider'): """ Setting in/out ranges for pan offset values. @@ -168,7 +124,7 @@ def process_value(input_value=None, source=None, zoom=None): zoom = True input_range_start, input_range_end, output_range_start, output_range_end = new_range - output = _remap( + output = math_utils.remap( input_range_start, input_range_end, float(output_range_start), diff --git a/python/mmSolver/tools/centertwodee/ui/centertwodee_window.py b/python/mmSolver/tools/centertwodee/ui/centertwodee_window.py index ff28e0017..fd90a6958 100644 --- a/python/mmSolver/tools/centertwodee/ui/centertwodee_window.py +++ b/python/mmSolver/tools/centertwodee/ui/centertwodee_window.py @@ -188,9 +188,9 @@ def main(show=True, auto_raise=True, delete=False): developing the UI in Maya script editor. :type delete: bool - :returns: A new solver window, or None if the window cannot be + :returns: A new center 2D window, or None if the window cannot be opened. - :rtype: SolverWindow or None. + :rtype: CenterTwoDeeWindow or None. """ win = CenterTwoDeeWindow.open_window( show=show, auto_raise=auto_raise, delete=delete diff --git a/python/mmSolver/tools/channelsen/ui/channelsen_window.py b/python/mmSolver/tools/channelsen/ui/channelsen_window.py index 6b3f8ac42..eb6afdcbb 100644 --- a/python/mmSolver/tools/channelsen/ui/channelsen_window.py +++ b/python/mmSolver/tools/channelsen/ui/channelsen_window.py @@ -85,9 +85,9 @@ def main(show=True, auto_raise=True, delete=False): developing the UI in Maya script editor. :type delete: bool - :returns: A new solver window, or None if the window cannot be - opened. - :rtype: SolverWindow or None. + :returns: A new channel sensitivity window, or None if the window + cannot be opened. + :rtype: ChannelSenWindow or None. """ win = ChannelSenWindow.open_window(show=show, auto_raise=auto_raise, delete=delete) return win diff --git a/python/mmSolver/tools/convertmarker/ui/convertmarker_window.py b/python/mmSolver/tools/convertmarker/ui/convertmarker_window.py index 7495592de..f11c0e43c 100644 --- a/python/mmSolver/tools/convertmarker/ui/convertmarker_window.py +++ b/python/mmSolver/tools/convertmarker/ui/convertmarker_window.py @@ -107,9 +107,9 @@ def main(show=True, auto_raise=True, delete=False): developing the UI in Maya script editor. :type delete: bool - :returns: A new solver window, or None if the window cannot be + :returns: A new convert marker window, or None if the window cannot be opened. - :rtype: SolverWindow or None. + :rtype: ConvertMarkerWindow or None. """ win = ConvertMarkerWindow.open_window( show=show, auto_raise=auto_raise, delete=delete diff --git a/python/mmSolver/tools/createcontroller2/tool.py b/python/mmSolver/tools/createcontroller2/tool.py index 415c2bd76..187c809ff 100644 --- a/python/mmSolver/tools/createcontroller2/tool.py +++ b/python/mmSolver/tools/createcontroller2/tool.py @@ -26,6 +26,7 @@ import mmSolver.logger import mmSolver.utils.time as time_utils +import mmSolver.utils.constant as const_utils import mmSolver.utils.tools as tools_utils import mmSolver.tools.createcontroller2.constant as const import mmSolver.tools.createcontroller2.lib as lib @@ -79,6 +80,7 @@ def create_world_controllers(): restore_current_frame=True, use_dg_evaluation_mode=True, disable_viewport=True, + disable_viewport_mode=const_utils.DISABLE_VIEWPORT_MODE_VP1_VALUE, ): for node in nodes: diff --git a/python/mmSolver/tools/createimageplane/_lib/constant.py b/python/mmSolver/tools/createimageplane/_lib/constant.py index c8fa6d3bd..374ec7fa6 100644 --- a/python/mmSolver/tools/createimageplane/_lib/constant.py +++ b/python/mmSolver/tools/createimageplane/_lib/constant.py @@ -1,4 +1,4 @@ -# Copyright (C) 2022 David Cattermole. +# Copyright (C) 2022, 2024 David Cattermole. # # This file is part of mmSolver. # @@ -17,3 +17,37 @@ # DEFAULT_IMAGE_SEQUENCE_ATTR_NAME = 'imageSequenceMain' +ALT_1_IMAGE_SEQUENCE_ATTR_NAME = 'imageSequenceAlternate1' +ALT_2_IMAGE_SEQUENCE_ATTR_NAME = 'imageSequenceAlternate2' +ALT_3_IMAGE_SEQUENCE_ATTR_NAME = 'imageSequenceAlternate3' + +VALID_INPUT_IMAGE_SEQUENCE_ATTR_NAMES = [ + DEFAULT_IMAGE_SEQUENCE_ATTR_NAME, + ALT_1_IMAGE_SEQUENCE_ATTR_NAME, + ALT_2_IMAGE_SEQUENCE_ATTR_NAME, + ALT_3_IMAGE_SEQUENCE_ATTR_NAME, +] + +SHADER_FILE_PATH_ATTR_NAME = 'imageFilePath' +INPUT_COLOR_SPACE_ATTR_NAME = 'inputColorSpace' +OUTPUT_COLOR_SPACE_ATTR_NAME = 'outputColorSpace' + +SCENE_LINEAR_FILE_EXTENSIONS = ['exr', 'sxr'] +SRGB_FILE_EXTENSIONS = ['jpg', 'jpeg', 'png', 'tif', 'tiff', 'tga', 'iff'] + +MM_IMAGE_PLANE_VERSION_ONE = 'mmImagePlaneVersion1' +MM_IMAGE_PLANE_VERSION_TWO = 'mmImagePlaneVersion2' +MM_IMAGE_PLANE_VERSION_LIST = [ + MM_IMAGE_PLANE_VERSION_ONE, + MM_IMAGE_PLANE_VERSION_TWO, +] + +MM_IMAGE_PLANE_SHAPE_V1 = 'mmImagePlaneShape' +MM_IMAGE_PLANE_SHAPE_V2 = 'mmImagePlaneShape2' +MM_IMAGE_PLANE_SHAPE_LIST = [MM_IMAGE_PLANE_SHAPE_V1, MM_IMAGE_PLANE_SHAPE_V2] +MM_IMAGE_PLANE_SHAPE_MAP = { + MM_IMAGE_PLANE_VERSION_ONE: MM_IMAGE_PLANE_SHAPE_V1, + MM_IMAGE_PLANE_VERSION_TWO: MM_IMAGE_PLANE_SHAPE_V2, +} + +MM_IMAGE_PLANE_TRANSFORM = 'mmImagePlaneTransform' diff --git a/python/mmSolver/tools/createimageplane/_lib/format.py b/python/mmSolver/tools/createimageplane/_lib/format.py new file mode 100644 index 000000000..6a2e40681 --- /dev/null +++ b/python/mmSolver/tools/createimageplane/_lib/format.py @@ -0,0 +1,155 @@ +# Copyright (C) 2024 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Functions to control the image cache. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import maya.cmds + +import mmSolver.logger +import mmSolver.utils.constant as const_utils +import mmSolver.utils.python_compat as pycompat +import mmSolver.tools.imagecache.lib as lib +import mmSolver.tools.createimageplane._lib.constant as imageplane_const +import mmSolver.tools.createimageplane._lib.mmimageplane_v2 as imageplane_lib + +LOG = mmSolver.logger.get_logger() + +# Shorter alias. +_MM_IMAGE_PLANE_SHAPE_V2 = imageplane_const.MM_IMAGE_PLANE_SHAPE_V2 + + +def format_image_sequence_size(image_plane_shp): + """ + Look up node values and format as text. + + Example output: + '2,346.1MB (23.7MB x 102 frames)' + """ + assert maya.cmds.nodeType(image_plane_shp) == _MM_IMAGE_PLANE_SHAPE_V2 + + frame_size_bytes = imageplane_lib.get_frame_size_bytes(image_plane_shp) + frame_count = imageplane_lib.get_frame_count(image_plane_shp) + image_sequence_size_bytes = imageplane_lib.get_image_sequence_size_bytes( + image_plane_shp + ) + + frame_size_mb = frame_size_bytes / const_utils.BYTES_TO_MEGABYTES + seq_size_mb = image_sequence_size_bytes / const_utils.BYTES_TO_MEGABYTES + text = '{seq_size_mb:0,.1f} MB ({frame_size_mb:0,.1f} MB x {frame_count} frames)' + return text.format( + seq_size_mb=seq_size_mb, frame_size_mb=frame_size_mb, frame_count=frame_count + ) + + +def _format_cache_used(used_bytes, capacity_bytes): + """ + Look up node values and format as text. + + Example text: + ' 42.1% (3.53 GB) of 8.00 GB' + """ + assert isinstance(used_bytes, pycompat.INT_TYPES) + assert isinstance(capacity_bytes, pycompat.INT_TYPES) + + usage_percent = 0 + usage_gigabytes = 0 + capacity_gigabyte = 0 + if capacity_bytes > 0: + usage_percent = (used_bytes / capacity_bytes) * 100.0 + usage_gigabytes = used_bytes / const_utils.BYTES_TO_GIGABYTES + capacity_gigabyte = capacity_bytes / const_utils.BYTES_TO_GIGABYTES + + text = '{usage_percent:3.1f}% ({usage_gigabytes:0,.2f} GB) of {capacity_gigabyte:0,.2f} GB' + return text.format( + usage_percent=usage_percent, + usage_gigabytes=usage_gigabytes, + capacity_gigabyte=capacity_gigabyte, + ) + + +def format_cache_gpu_used(image_plane_shp): + """ + Look up node values and format text. + + Example text: + ' 42.1% (3.53GB) of 8.00GB' + """ + assert maya.cmds.nodeType(image_plane_shp) == _MM_IMAGE_PLANE_SHAPE_V2 + + used_bytes = lib.get_gpu_cache_used_bytes() + capacity_bytes = lib.get_gpu_cache_capacity_bytes() + return _format_cache_used(int(used_bytes), int(capacity_bytes)) + + +def format_cache_cpu_used(image_plane_shp): + """ + Look up node values and format as text. + + Example text: + ' 23.1% (34.24 GB) of 240.00 GB' + """ + assert maya.cmds.nodeType(image_plane_shp) == _MM_IMAGE_PLANE_SHAPE_V2 + + used_bytes = lib.get_cpu_cache_used_bytes() + capacity_bytes = lib.get_cpu_cache_capacity_bytes() + return _format_cache_used(int(used_bytes), int(capacity_bytes)) + + +def format_memory_gpu_available(image_plane_shp): + """ + Look up node values and format as text. + + Example text: + 'GPU 8.00 GB' + """ + assert maya.cmds.nodeType(image_plane_shp) == _MM_IMAGE_PLANE_SHAPE_V2 + + memory_bytes = lib.get_gpu_memory_total_bytes() + memory_gigabytes = 0.0 + if memory_bytes > 0: + memory_gigabytes = memory_bytes / const_utils.BYTES_TO_GIGABYTES + + text = 'GPU: {memory_gigabytes:0,.2f} GB' + return text.format( + memory_gigabytes=memory_gigabytes, + ) + + +def format_memory_cpu_available(image_plane_shp): + """ + Look up node values and format as text. + + Example text: + 'CPU: 240.00 GB' + """ + assert maya.cmds.nodeType(image_plane_shp) == _MM_IMAGE_PLANE_SHAPE_V2 + + memory_bytes = lib.get_cpu_memory_total_bytes() + memory_gigabytes = 0.0 + if memory_bytes > 0: + memory_gigabytes = memory_bytes / const_utils.BYTES_TO_GIGABYTES + + text = 'CPU: {memory_gigabytes:0,.2f} GB' + return text.format( + memory_gigabytes=memory_gigabytes, + ) diff --git a/python/mmSolver/tools/createimageplane/_lib/main.py b/python/mmSolver/tools/createimageplane/_lib/main.py index 18a8a555f..a2a6424aa 100644 --- a/python/mmSolver/tools/createimageplane/_lib/main.py +++ b/python/mmSolver/tools/createimageplane/_lib/main.py @@ -25,71 +25,59 @@ import mmSolver.logger import mmSolver.api as mmapi import mmSolver.utils.camera as camera_utils +import mmSolver.utils.constant as const_utils +import mmSolver.utils.imageseq as imageseq_utils import mmSolver.utils.python_compat as pycompat - -import mmSolver.tools.createimageplane.constant as const import mmSolver.tools.createimageplane._lib.constant as lib_const -import mmSolver.tools.createimageplane._lib.utilities as lib_utils -import mmSolver.tools.createimageplane._lib.shader as lib_shader import mmSolver.tools.createimageplane._lib.mmimageplane as lib_mmimageplane -import mmSolver.tools.createimageplane._lib.polyplane as lib_polyplane +import mmSolver.tools.createimageplane._lib.mmimageplane_v1 as lib_mmimageplane_v1 import mmSolver.tools.createimageplane._lib.nativeimageplane as lib_nativeimageplane +import mmSolver.tools.createimageplane._lib.polyplane as lib_polyplane +import mmSolver.tools.createimageplane._lib.shader as lib_shader +import mmSolver.tools.createimageplane._lib.utilities as lib_utils LOG = mmSolver.logger.get_logger() -def create_image_plane_on_camera(cam, name=None): +def create_image_plane_on_camera(cam, name=None, version=None): """Create an Image Plane that can be distorted in Maya's viewport (realtime). """ + if version is None: + version = lib_const.MM_IMAGE_PLANE_VERSION_TWO if name is None: name = 'mmImagePlane1' assert isinstance(cam, mmapi.Camera) assert isinstance(name, pycompat.TEXT_TYPE) + assert version in lib_const.MM_IMAGE_PLANE_VERSION_LIST + cam_tfm = cam.get_transform_node() cam_shp = cam.get_shape_node() - mm_ip_tfm = lib_mmimageplane.create_transform_node(name, cam_tfm, cam_shp) + mm_ip_tfm = lib_mmimageplane.create_transform_node( + name, cam_tfm, cam_shp, version=version + ) poly_plane_name = name + 'MeshShape' poly_plane_network = lib_polyplane.create_poly_plane( poly_plane_name, mm_ip_tfm, cam_shp ) - name_shade = name + 'Shader' - shader_network = lib_shader.create_network(name_shade, mm_ip_tfm) + shader_network = None + if version == lib_const.MM_IMAGE_PLANE_VERSION_ONE: + name_shade = name + 'Shader' + shader_network = lib_shader.create_network(name_shade, mm_ip_tfm) name_img_shp = name + 'Shape' mm_ip_shp = lib_mmimageplane.create_shape_node( - name_img_shp, mm_ip_tfm, cam_shp, poly_plane_network, shader_network - ) - - # Shortcut connections to nodes. - lib_utils.force_connect_attr( - shader_network.file_node + '.message', mm_ip_tfm + '.shaderFileNode' + name_img_shp, + mm_ip_tfm, + cam_shp, + poly_plane_network, + shader_network, + version=version, ) - # Logic to calculate the frame number. - frame_expr = const.FRAME_EXPRESSION.format(node=mm_ip_shp) - frame_expr = frame_expr.replace('{{', '{') - frame_expr = frame_expr.replace('}}', '}') - maya.cmds.expression(string=frame_expr) - - # Show the users the final frame number. - shp_node_attr = mm_ip_shp + '.imageSequenceFrameOutput' - maya.cmds.setAttr(shp_node_attr, lock=True) - - # Set useFrameExtension temporarily. Setting useFrameExtension to - # False causes frameOffset to be locked (but we need to edit it). - is_seq = maya.cmds.getAttr(shader_network.file_node + '.useFrameExtension') - maya.cmds.setAttr(shader_network.file_node + '.useFrameExtension', True) - - file_node_attr = shader_network.file_node + '.frameExtension' - lib_utils.force_connect_attr(shp_node_attr, file_node_attr) - maya.cmds.setAttr(file_node_attr, lock=True) - - maya.cmds.setAttr(shader_network.file_node + '.useFrameExtension', is_seq) - # Image sequence. image_sequence_path = lib_utils.get_default_image_path() set_image_sequence(mm_ip_tfm, image_sequence_path) @@ -120,12 +108,12 @@ def convert_image_planes_on_camera(cam): lib_nativeimageplane.copy_depth_value(mm_ip_tfm, native_ip_shp) - name_shader = name + 'Shader' - shader_network = lib_shader.create_network(name_shader, mm_ip_tfm) - name_img_shp = name + 'Shape' mm_ip_shp = lib_mmimageplane.create_shape_node( - name_img_shp, mm_ip_tfm, cam_shp, poly_plane_network, shader_network + name_img_shp, + mm_ip_tfm, + cam_shp, + poly_plane_network, ) # Disable/hide the Maya image plane. @@ -138,20 +126,115 @@ def convert_image_planes_on_camera(cam): return ip_node_pairs -def set_image_sequence(mm_image_plane_node, image_sequence_path, attr_name=None): +def _guess_color_space(file_path): + file_extension = file_path.lower().split('.')[-1] + if file_extension in lib_const.SCENE_LINEAR_FILE_EXTENSIONS: + color_space = maya.cmds.mmColorIO(roleSceneLinear=True) + elif file_extension in lib_const.SRGB_FILE_EXTENSIONS: + color_space = maya.cmds.mmColorIO(roleColorPicking=True) + else: + color_space = maya.cmds.mmColorIO(guessColorSpaceFromFile=file_path) + + if not color_space: + color_space = maya.cmds.mmColorIO(roleData=True) + if not color_space: + color_space = maya.cmds.mmColorIO(roleDefault=True) + + exists = maya.cmds.mmColorIO(colorSpaceExists=color_space) + if exists is False: + color_space = None + + return color_space + + +def _set_image_sequence_v1(mm_image_plane_node, image_sequence_path, attr_name=None): if attr_name is None: attr_name = lib_const.DEFAULT_IMAGE_SEQUENCE_ATTR_NAME + version = lib_const.MM_IMAGE_PLANE_VERSION_ONE - tfm, shp = lib_mmimageplane.get_image_plane_node_pair(mm_image_plane_node) + tfm, shp = lib_mmimageplane.get_image_plane_node_pair( + mm_image_plane_node, version=version + ) if tfm is None or shp is None: LOG.warn('mmImagePlane transform/shape could not be found.') - file_node = lib_mmimageplane.get_file_node(tfm) + file_node = lib_mmimageplane_v1.get_file_node(tfm) if file_node is None: LOG.warn('mmImagePlane shader file node is invalid.') if shp is not None: - lib_mmimageplane.set_image_sequence(shp, image_sequence_path, attr_name) + lib_mmimageplane.set_image_sequence( + shp, image_sequence_path, attr_name, version=version + ) if file_node is not None: lib_shader.set_file_path(file_node, image_sequence_path) return + + +def _set_locked_string_attr(node_attr, value): + maya.cmds.setAttr(node_attr, lock=False) + maya.cmds.setAttr(node_attr, value, type='string') + maya.cmds.setAttr(node_attr, lock=True) + return + + +def _set_image_sequence_v2(mm_image_plane_node, image_sequence_path, attr_name=None): + if attr_name is None: + attr_name = lib_const.DEFAULT_IMAGE_SEQUENCE_ATTR_NAME + assert isinstance(attr_name, pycompat.TEXT_TYPE) + assert attr_name in lib_const.VALID_INPUT_IMAGE_SEQUENCE_ATTR_NAMES + version = lib_const.MM_IMAGE_PLANE_VERSION_TWO + + tfm, shp = lib_mmimageplane.get_image_plane_node_pair( + mm_image_plane_node, version=version + ) + if tfm is None or shp is None: + LOG.warn('mmImagePlane transform/shape could not be found.') + + if shp is not None: + lib_mmimageplane.set_image_sequence( + shp, image_sequence_path, attr_name, version=version + ) + lib_mmimageplane.set_image_sequence( + shp, + image_sequence_path, + lib_const.SHADER_FILE_PATH_ATTR_NAME, + version=version, + ) + + format_style = const_utils.IMAGE_SEQ_FORMAT_STYLE_FIRST_FRAME + ( + file_pattern, + _, + _, + _, + _, + ) = imageseq_utils.expand_image_sequence_path(image_sequence_path, format_style) + first_frame_file_seq = file_pattern.replace('\\', '/') + + input_color_space = _guess_color_space(first_frame_file_seq) + output_color_space = maya.cmds.mmColorIO(roleSceneLinear=True) + + node_attr = shp + '.' + lib_const.INPUT_COLOR_SPACE_ATTR_NAME + _set_locked_string_attr(node_attr, input_color_space) + + node_attr = shp + '.' + lib_const.OUTPUT_COLOR_SPACE_ATTR_NAME + _set_locked_string_attr(node_attr, output_color_space) + + return + + +def set_image_sequence( + mm_image_plane_node, image_sequence_path, attr_name=None, version=None +): + if version is None: + version = lib_const.MM_IMAGE_PLANE_VERSION_TWO + assert version in lib_const.MM_IMAGE_PLANE_VERSION_LIST + if version == lib_const.MM_IMAGE_PLANE_VERSION_ONE: + return _set_image_sequence_v1( + mm_image_plane_node, image_sequence_path, attr_name=attr_name + ) + elif version == lib_const.MM_IMAGE_PLANE_VERSION_TWO: + return _set_image_sequence_v2( + mm_image_plane_node, image_sequence_path, attr_name=attr_name + ) diff --git a/python/mmSolver/tools/createimageplane/_lib/mmimageplane.py b/python/mmSolver/tools/createimageplane/_lib/mmimageplane.py index 93040e4be..42c9e5a9b 100644 --- a/python/mmSolver/tools/createimageplane/_lib/mmimageplane.py +++ b/python/mmSolver/tools/createimageplane/_lib/mmimageplane.py @@ -27,276 +27,37 @@ import mmSolver.logger import mmSolver.api as mmapi -import mmSolver.utils.node as node_utils -import mmSolver.utils.constant as const_utils import mmSolver.utils.python_compat as pycompat -import mmSolver.utils.imageseq as imageseq_utils +import mmSolver.tools.createimageplane.constant as const +import mmSolver.tools.createimageplane._lib.constant as lib_const +import mmSolver.tools.createimageplane._lib.mmimageplane_v1 as lib_mmimageplane_v1 +import mmSolver.tools.createimageplane._lib.mmimageplane_v2 as lib_mmimageplane_v2 import mmSolver.tools.createimageplane._lib.utilities as lib_utils - +import mmSolver.utils.node as node_utils LOG = mmSolver.logger.get_logger() -def _create_transform_attrs(image_plane_tfm): - # Depth attribute - attr = 'depth' - maya.cmds.addAttr( - image_plane_tfm, - longName=attr, - attributeType='double', - minValue=0.0, - defaultValue=1.0, - ) - maya.cmds.setAttr(image_plane_tfm + '.' + attr, keyable=True) - - # Focal Length attribute - attr = 'focalLength' - maya.cmds.addAttr( - image_plane_tfm, - longName=attr, - attributeType='double', - minValue=0.1, - defaultValue=35.0, - ) - maya.cmds.setAttr(image_plane_tfm + '.' + attr, keyable=True) - - # Horizontal Film Aperture attribute - attr = 'horizontalFilmAperture' - value = 36.0 / 25.4 # 35mm film in inches. - maya.cmds.addAttr( - image_plane_tfm, - longName=attr, - attributeType='double', - minValue=0.001, - defaultValue=value, - ) - maya.cmds.setAttr(image_plane_tfm + '.' + attr, keyable=True) - - # Vertical Film Aperture attribute - attr = 'verticalFilmAperture' - value = 24.0 / 25.4 # 35mm film in inches. - maya.cmds.addAttr( - image_plane_tfm, - longName=attr, - attributeType='double', - minValue=0.001, - defaultValue=value, - ) - maya.cmds.setAttr(image_plane_tfm + '.' + attr, keyable=True) - - # Pixel Aspect Ratio attribute - attr = 'pixelAspect' - value = 1.0 - maya.cmds.addAttr( - image_plane_tfm, - longName=attr, - attributeType='double', - minValue=0.001, - defaultValue=value, - ) - maya.cmds.setAttr(image_plane_tfm + '.' + attr, keyable=True) - - # Horizontal Film Offset attribute - attr = 'horizontalFilmOffset' - maya.cmds.addAttr( - image_plane_tfm, - longName=attr, - attributeType='double', - minValue=0.0, - defaultValue=0.0, - ) - maya.cmds.setAttr(image_plane_tfm + '.' + attr, keyable=True) - - # Vertical Film Offset attribute - attr = 'verticalFilmOffset' - maya.cmds.addAttr( - image_plane_tfm, - longName=attr, - attributeType='double', - minValue=0.0, - defaultValue=0.0, - ) - maya.cmds.setAttr(image_plane_tfm + '.' + attr, keyable=True) - return - - -def _create_image_plane_shape_attrs(image_plane_shp): - # Exposure attribute - attr = 'exposure' - maya.cmds.addAttr( - image_plane_shp, - longName=attr, - attributeType='double', - softMinValue=-5.0, - softMaxValue=5.0, - defaultValue=0.0, - ) - node_attr = image_plane_shp + '.' + attr - maya.cmds.setAttr(node_attr, keyable=True) - - # Gamma attribute - attr = 'gamma' - maya.cmds.addAttr( - image_plane_shp, - longName=attr, - attributeType='double', - minValue=0.0, - softMaxValue=3.0, - defaultValue=1.0, - ) - node_attr = image_plane_shp + '.' + attr - maya.cmds.setAttr(node_attr, keyable=True) - - # Color Gain attribute - attr = 'colorGain' - default_value = 1.0 - lib_utils.add_attr_float3_color(image_plane_shp, attr, default_value) - - # Alpha Gain attribute - attr = 'alphaGain' - maya.cmds.addAttr( - image_plane_shp, - longName=attr, - attributeType='double', - minValue=0.0, - softMaxValue=1.0, - defaultValue=1.0, - ) - node_attr = image_plane_shp + '.' + attr - maya.cmds.setAttr(node_attr, keyable=True) - - # Use Image Alpha Channel attribute - attr = 'imageUseAlphaChannel' - maya.cmds.addAttr( - image_plane_shp, longName=attr, attributeType='bool', defaultValue=0 - ) - node_attr = image_plane_shp + '.' + attr - maya.cmds.setAttr(node_attr, keyable=True) - - # Default Image Color attribute, display a dark-red color when an - # image is not found. - attr = 'imageDefaultColor' - default_value = 0.0 - lib_utils.add_attr_float3_color(image_plane_shp, attr, default_value) - node_attr = image_plane_shp + '.' + attr - maya.cmds.setAttr(node_attr + 'R', 0.3) - maya.cmds.setAttr(node_attr + 'G', 0.0) - maya.cmds.setAttr(node_attr + 'B', 0.0) - - # Image Load Enable attribute - attr = 'imageLoadEnable' - maya.cmds.addAttr( - image_plane_shp, longName=attr, attributeType='bool', defaultValue=1 - ) - node_attr = image_plane_shp + '.' + attr - maya.cmds.setAttr(node_attr, keyable=True) - - # Choose which image sequence path to use. This is only visible in - # the Attribute Editor, because this attribute must trigger a - # callback when changed to update the underlying 'file' node. - attr = 'imageSequenceSlot' - maya.cmds.addAttr( - image_plane_shp, - longName=attr, - attributeType='enum', - enumName="main=0:alternate1=1:alternate2=2:alternate3=3", - ) - node_attr = image_plane_shp + '.' + attr - maya.cmds.setAttr(node_attr, keyable=False) - - # Image Sequence attribute - attr = 'imageSequence' - nice_name = 'Image Sequence' - attr_suffix_list = ['Main', 'Alternate1', 'Alternate2', 'Alternate3'] - nice_suffix_list = [' (Main)', ' (Alt 1)', ' (Alt 2)', ' (Alt 3)'] - for attr_suffix, nice_suffix in zip(attr_suffix_list, nice_suffix_list): - maya.cmds.addAttr( - image_plane_shp, - longName=attr + attr_suffix, - niceName=nice_name + nice_suffix, - dataType='string', - ) - - # Image Sequence Frame attribute - attr = 'imageSequenceFrame' - maya.cmds.addAttr( - image_plane_shp, longName=attr, attributeType='double', defaultValue=0.0 - ) - node_attr = image_plane_shp + '.' + attr - maya.cmds.setAttr(node_attr, keyable=True) - lib_utils.force_connect_attr('time1.outTime', node_attr) - - attr = 'imageSequenceFirstFrame' - maya.cmds.addAttr( - image_plane_shp, - longName=attr, - niceName='First Frame', - attributeType='long', - defaultValue=0, - ) - node_attr = image_plane_shp + '.' + attr - maya.cmds.setAttr(node_attr, keyable=False) - maya.cmds.setAttr(node_attr, channelBox=True) - - attr = 'imageSequenceFrameOutput' - maya.cmds.addAttr( - image_plane_shp, - longName=attr, - niceName='Frame Output', - attributeType='double', - defaultValue=0.0, - ) - node_attr = image_plane_shp + '.' + attr - maya.cmds.setAttr(node_attr, keyable=True) - - # Image Sequence details. - maya.cmds.addAttr( - image_plane_shp, - longName='imageSequenceStartFrame', - niceName='Start Frame', - attributeType='long', - defaultValue=0, - ) - maya.cmds.addAttr( - image_plane_shp, - longName='imageSequenceEndFrame', - niceName='End Frame', - attributeType='long', - defaultValue=0, - ) - maya.cmds.addAttr( - image_plane_shp, - longName='imageSequencePadding', - niceName='Padding', - attributeType='long', - defaultValue=0, - ) - - # Mesh Resolution attribute - attr = 'meshResolution' - maya.cmds.addAttr( - image_plane_shp, - longName=attr, - attributeType='long', - minValue=1, - maxValue=256, - defaultValue=32, - ) - node_attr = image_plane_shp + '.' + attr - maya.cmds.setAttr(node_attr, keyable=True) - return - - -def create_transform_node(name_tfm, cam_tfm, cam_shp): +def create_transform_node(name_tfm, cam_tfm, cam_shp, version=None): """ Create a default polygon image plane under camera. """ assert isinstance(name_tfm, pycompat.TEXT_TYPE) + assert maya.cmds.objExists(cam_tfm) + assert maya.cmds.objExists(cam_shp) + assert version in lib_const.MM_IMAGE_PLANE_VERSION_LIST + tfm_node_type = lib_const.MM_IMAGE_PLANE_TRANSFORM + mmapi.load_plugin() - tfm = maya.cmds.createNode('mmImagePlaneTransform', name=name_tfm, parent=cam_tfm) + tfm = maya.cmds.createNode(tfm_node_type, name=name_tfm, parent=cam_tfm) # Create (dynamic) attributes. - _create_transform_attrs(tfm) + if version == lib_const.MM_IMAGE_PLANE_VERSION_ONE: + lib_mmimageplane_v1.create_transform_attrs(tfm) + elif version == lib_const.MM_IMAGE_PLANE_VERSION_TWO: + lib_mmimageplane_v2.create_transform_attrs(tfm) + else: + assert False # Image plane depth be far away, but still visible. far_clip_plane = maya.cmds.getAttr(cam_tfm + '.farClipPlane') @@ -320,26 +81,39 @@ def create_transform_node(name_tfm, cam_tfm, cam_shp): def create_shape_node( - name_img_shp, tfm, cam_shp, poly_plane_node_network, shader_node_network + name_img_shp, + tfm, + cam_shp, + poly_plane_node_network, + shader_node_network, + version=None, ): """ Convert mesh to a mmImagePlaneShape. """ assert isinstance(name_img_shp, pycompat.TEXT_TYPE) + assert version in lib_const.MM_IMAGE_PLANE_VERSION_LIST + shp_node_type = lib_const.MM_IMAGE_PLANE_SHAPE_MAP[version] + assert shp_node_type in lib_const.MM_IMAGE_PLANE_SHAPE_LIST + assert maya.cmds.objExists(tfm) + assert maya.cmds.objExists(cam_shp) img_plane_poly_shp = poly_plane_node_network.mesh_shape img_plane_poly_shp_original = poly_plane_node_network.mesh_shape_original poly_creator = poly_plane_node_network.plane_creator - shd_node = shader_node_network.shd_node - file_node = shader_node_network.file_node - color_gamma_node = shader_node_network.color_gamma_node - alpha_channel_blend_node = shader_node_network.alpha_channel_blend_node - image_load_invert_boolean_node = shader_node_network.image_load_invert_boolean_node - alpha_channel_reverse_node = shader_node_network.alpha_channel_reverse_node + if shader_node_network is not None: + shd_node = shader_node_network.shd_node + file_node = shader_node_network.file_node + color_gamma_node = shader_node_network.color_gamma_node + alpha_channel_blend_node = shader_node_network.alpha_channel_blend_node + image_load_invert_boolean_node = ( + shader_node_network.image_load_invert_boolean_node + ) + alpha_channel_reverse_node = shader_node_network.alpha_channel_reverse_node mmapi.load_plugin() - shp = maya.cmds.createNode('mmImagePlaneShape', name=name_img_shp, parent=tfm) + shp = maya.cmds.createNode(shp_node_type, name=name_img_shp, parent=tfm) maya.cmds.setAttr(shp + '.localPositionX', channelBox=False) maya.cmds.setAttr(shp + '.localPositionY', channelBox=False) @@ -354,68 +128,76 @@ def create_shape_node( # Image plane hash will be used to stop updating the Viewport 2.0 # shape when the lens distortion values stay the same. lens_eval_node = maya.cmds.createNode('mmLensEvaluate') - lib_utils.force_connect_attr(cam_shp + '.outLens', lens_eval_node + '.inLens') + if node_utils.attribute_exists('outLens', cam_shp): + lib_utils.force_connect_attr(cam_shp + '.outLens', lens_eval_node + '.inLens') lib_utils.force_connect_attr(lens_eval_node + '.outHash', shp + '.lensHashCurrent') maya.cmds.reorder(shp, back=True) maya.cmds.reorder(img_plane_poly_shp_original, back=True) maya.cmds.reorder(img_plane_poly_shp, back=True) - _create_image_plane_shape_attrs(shp) + if version == lib_const.MM_IMAGE_PLANE_VERSION_ONE: + lib_mmimageplane_v1.create_image_plane_shape_attrs(shp) + elif version == lib_const.MM_IMAGE_PLANE_VERSION_TWO: + lib_mmimageplane_v2.create_image_plane_shape_attrs(shp) + else: + assert False # Nodes to drive the image plane shape. lib_utils.force_connect_attr(img_plane_poly_shp + '.outMesh', shp + '.geometryNode') - lib_utils.force_connect_attr(shd_node + '.outColor', shp + '.shaderNode') lib_utils.force_connect_attr(cam_shp + '.message', shp + '.cameraNode') + if shader_node_network is not None: + lib_utils.force_connect_attr(shd_node + '.outColor', shp + '.shaderNode') # The image drives the pixel aspect ratio of the image plane. lib_utils.force_connect_attr(shp + '.imagePixelAspect', tfm + '.pixelAspect') - # Use the image alpha channel, or not + # Set the camera size of the image plane shape HUD. lib_utils.force_connect_attr( - shp + '.imageUseAlphaChannel', alpha_channel_blend_node + '.blender' + tfm + '.horizontalFilmAperture', shp + '.cameraWidthInch' ) - - # Allow user to load the image, or not. lib_utils.force_connect_attr( - shp + '.imageLoadEnable', image_load_invert_boolean_node + '.inputX' + tfm + '.verticalFilmAperture', shp + '.cameraHeightInch' ) - # Color Exposure control. - lib_utils.force_connect_attr(shp + '.exposure', file_node + '.exposure') + if shader_node_network is not None: + # Use the image alpha channel, or not + lib_utils.force_connect_attr( + shp + '.imageUseAlphaChannel', alpha_channel_blend_node + '.blender' + ) - # Color Gamma control. - lib_utils.force_connect_attr(shp + '.gamma', color_gamma_node + '.gammaX') - lib_utils.force_connect_attr(shp + '.gamma', color_gamma_node + '.gammaY') - lib_utils.force_connect_attr(shp + '.gamma', color_gamma_node + '.gammaZ') + # Allow user to load the image, or not. + lib_utils.force_connect_attr( + shp + '.imageLoadEnable', image_load_invert_boolean_node + '.inputX' + ) - # Control file color multiplier - lib_utils.force_connect_attr(shp + '.colorGain', file_node + '.colorGain') + # Color Exposure control. + lib_utils.force_connect_attr(shp + '.exposure', file_node + '.exposure') - # Control the alpha gain when 'imageUseAlphaChannel' is disabled. - lib_utils.force_connect_attr(shp + '.alphaGain', file_node + '.alphaGain') - lib_utils.force_connect_attr( - shp + '.alphaGain', alpha_channel_reverse_node + '.inputX' - ) - lib_utils.force_connect_attr( - shp + '.alphaGain', alpha_channel_reverse_node + '.inputY' - ) - lib_utils.force_connect_attr( - shp + '.alphaGain', alpha_channel_reverse_node + '.inputZ' - ) + # Color Gamma control. + lib_utils.force_connect_attr(shp + '.gamma', color_gamma_node + '.gammaX') + lib_utils.force_connect_attr(shp + '.gamma', color_gamma_node + '.gammaY') + lib_utils.force_connect_attr(shp + '.gamma', color_gamma_node + '.gammaZ') - # Set the camera size of the image plane shape HUD. - lib_utils.force_connect_attr( - tfm + '.horizontalFilmAperture', shp + '.cameraWidthInch' - ) - lib_utils.force_connect_attr( - tfm + '.verticalFilmAperture', shp + '.cameraHeightInch' - ) + # Control file color multiplier + lib_utils.force_connect_attr(shp + '.colorGain', file_node + '.colorGain') - # Default color for the image plane, when nothing is loaded. - lib_utils.force_connect_attr( - shp + '.imageDefaultColor', file_node + '.defaultColor' - ) + # Control the alpha gain when 'imageUseAlphaChannel' is disabled. + lib_utils.force_connect_attr(shp + '.alphaGain', file_node + '.alphaGain') + lib_utils.force_connect_attr( + shp + '.alphaGain', alpha_channel_reverse_node + '.inputX' + ) + lib_utils.force_connect_attr( + shp + '.alphaGain', alpha_channel_reverse_node + '.inputY' + ) + lib_utils.force_connect_attr( + shp + '.alphaGain', alpha_channel_reverse_node + '.inputZ' + ) + + # Default color for the image plane, when nothing is loaded. + lib_utils.force_connect_attr( + shp + '.imageDefaultColor', file_node + '.defaultColor' + ) # Mesh Resolution attr drives the plane sub-divisions. node_attr = shp + '.meshResolution' @@ -427,123 +209,80 @@ def create_shape_node( maya.cmds.setAttr(img_plane_poly_shp + '.intermediateObject', 1) # Add extra message attributes for finding nodes during callbacks. - maya.cmds.addAttr(shp, longName='shaderFileNode', attributeType='message') - maya.cmds.addAttr(shp, longName='imagePlaneShapeNode', attributeType='message') - return shp - - -def set_image_sequence(shp, image_sequence_path, attr_name): - assert maya.cmds.nodeType(shp) == 'mmImagePlaneShape' - assert node_utils.attribute_exists(attr_name, shp) is True - - format_style = const_utils.IMAGE_SEQ_FORMAT_STYLE_FIRST_FRAME - ( - file_pattern, - start_frame, - end_frame, - pad_num, - is_seq, - ) = imageseq_utils.expand_image_sequence_path(image_sequence_path, format_style) - first_frame_file_seq = file_pattern + if shader_node_network is not None: + maya.cmds.addAttr(shp, longName='shaderFileNode', attributeType='message') + lib_utils.force_connect_attr(file_node + '.message', shp + '.shaderFileNode') - mmapi.load_plugin() - try: - first_frame_file_seq = first_frame_file_seq.replace('\\', '/') - image_width_height = maya.cmds.mmReadImage( - first_frame_file_seq, query=True, widthHeight=True - ) - except RuntimeError: - image_width_height = None - LOG.warn('Failed to read file: %r', first_frame_file_seq) - - if image_width_height is not None: - image_width = image_width_height[0] - image_height = image_width_height[1] - - if not node_utils.node_is_referenced(shp): - maya.cmds.setAttr(shp + '.imageWidth', lock=False) - maya.cmds.setAttr(shp + '.imageHeight', lock=False) - - maya.cmds.setAttr(shp + '.imageWidth', image_width) - maya.cmds.setAttr(shp + '.imageHeight', image_height) - - if not node_utils.node_is_referenced(shp): - maya.cmds.setAttr(shp + '.imageWidth', lock=True) - maya.cmds.setAttr(shp + '.imageHeight', lock=True) + # Logic to calculate the frame number. + node = maya.cmds.createNode('mmImageSequenceFrameLogic') + lib_utils.force_connect_attr(shp + '.imageSequenceFrame', node + '.inFrame') + lib_utils.force_connect_attr(shp + '.imageSequenceFirstFrame', node + '.firstFrame') + lib_utils.force_connect_attr(shp + '.imageSequenceStartFrame', node + '.startFrame') + lib_utils.force_connect_attr(shp + '.imageSequenceEndFrame', node + '.endFrame') + lib_utils.force_connect_attr(node + '.outFrame', shp + '.imageSequenceFrameOutput') - maya.cmds.setAttr(shp + '.' + attr_name, file_pattern, type='string') + # Only show the users the final frame number, no editing. + maya.cmds.setAttr(shp + '.imageSequenceFrameOutput', lock=True) - if not node_utils.node_is_referenced(shp): - maya.cmds.setAttr(shp + '.imageSequenceStartFrame', lock=False) - maya.cmds.setAttr(shp + '.imageSequenceEndFrame', lock=False) - maya.cmds.setAttr(shp + '.imageSequencePadding', lock=False) - - maya.cmds.setAttr(shp + '.imageSequenceFirstFrame', start_frame) - maya.cmds.setAttr(shp + '.imageSequenceStartFrame', start_frame) - maya.cmds.setAttr(shp + '.imageSequenceEndFrame', end_frame) - maya.cmds.setAttr(shp + '.imageSequencePadding', pad_num) - - if not node_utils.node_is_referenced(shp): - maya.cmds.setAttr(shp + '.imageSequenceStartFrame', lock=True) - maya.cmds.setAttr(shp + '.imageSequenceEndFrame', lock=True) - maya.cmds.setAttr(shp + '.imageSequencePadding', lock=True) - return + return shp -def get_shape_node(image_plane_tfm): - shape = None - shapes = ( - maya.cmds.listRelatives( - image_plane_tfm, shapes=True, fullPath=True, type='mmImagePlaneShape' +def set_image_sequence(shp, image_sequence_path, attr_name, version=None): + assert isinstance(shp, pycompat.TEXT_TYPE) + assert isinstance(image_sequence_path, pycompat.TEXT_TYPE) + assert isinstance(attr_name, pycompat.TEXT_TYPE) + assert version in lib_const.MM_IMAGE_PLANE_VERSION_LIST + result = None + if version == lib_const.MM_IMAGE_PLANE_VERSION_ONE: + result = lib_mmimageplane_v1.set_image_sequence( + shp, image_sequence_path, attr_name ) - or [] - ) - if len(shapes) > 0: - shape = shapes[0] - return shape - - -def get_transform_node(image_plane_shp): - tfm = None - tfms = ( - maya.cmds.listRelatives( - image_plane_shp, parent=True, fullPath=True, type='mmImagePlaneTransform' + elif version == lib_const.MM_IMAGE_PLANE_VERSION_TWO: + result = lib_mmimageplane_v2.set_image_sequence( + shp, image_sequence_path, attr_name ) - or [] - ) - if len(tfms) > 0: - tfm = tfms[0] - return tfm - - -def get_image_plane_node_pair(node): - node_type = maya.cmds.nodeType(node) - assert node_type in ['mmImagePlaneShape', 'mmImagePlaneTransform'] - tfm = None - shp = None - if node_type == 'mmImagePlaneTransform': - shp = get_shape_node(node) - tfm = node else: - # Given a shape, we can look at our parent node to find the - # transform. - tfm = get_transform_node(node) - shp = node - return tfm, shp - - -def get_file_node(image_plane_tfm): - file_node = None - conns = ( - maya.cmds.listConnections( - image_plane_tfm + '.shaderFileNode', - destination=False, - source=True, - plugs=False, - type='file', - ) - or [] - ) - if len(conns) > 0: - file_node = conns[0] - return file_node + assert False + return result + + +def get_shape_node(image_plane_tfm, version=None): + assert isinstance(image_plane_tfm, pycompat.TEXT_TYPE) + assert maya.cmds.objExists(image_plane_tfm) + assert version in lib_const.MM_IMAGE_PLANE_VERSION_LIST + result = None + if version == lib_const.MM_IMAGE_PLANE_VERSION_ONE: + result = lib_mmimageplane_v1.get_shape_node(image_plane_tfm) + elif version == lib_const.MM_IMAGE_PLANE_VERSION_TWO: + result = lib_mmimageplane_v2.get_shape_node(image_plane_tfm) + else: + assert False + return result + + +def get_transform_node(image_plane_shp, version=None): + assert isinstance(image_plane_shp, pycompat.TEXT_TYPE) + assert maya.cmds.objExists(image_plane_shp) + assert version in lib_const.MM_IMAGE_PLANE_VERSION_LIST + result = None + if version == lib_const.MM_IMAGE_PLANE_VERSION_ONE: + result = lib_mmimageplane_v1.get_transform_node(image_plane_shp) + elif version == lib_const.MM_IMAGE_PLANE_VERSION_TWO: + result = lib_mmimageplane_v2.get_transform_node(image_plane_shp) + else: + assert False + return result + + +def get_image_plane_node_pair(node, version=None): + assert isinstance(node, pycompat.TEXT_TYPE) + assert maya.cmds.objExists(node) + assert version in lib_const.MM_IMAGE_PLANE_VERSION_LIST + result = None + if version == lib_const.MM_IMAGE_PLANE_VERSION_ONE: + result = lib_mmimageplane_v1.get_image_plane_node_pair(node) + elif version == lib_const.MM_IMAGE_PLANE_VERSION_TWO: + result = lib_mmimageplane_v2.get_image_plane_node_pair(node) + else: + assert False + return result diff --git a/python/mmSolver/tools/createimageplane/_lib/mmimageplane_v1.py b/python/mmSolver/tools/createimageplane/_lib/mmimageplane_v1.py new file mode 100644 index 000000000..135cbdc3b --- /dev/null +++ b/python/mmSolver/tools/createimageplane/_lib/mmimageplane_v1.py @@ -0,0 +1,424 @@ +# Copyright (C) 2020, 2022 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Library functions for creating and modifying image planes. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import maya.cmds + +import mmSolver.logger +import mmSolver.api as mmapi +import mmSolver.utils.constant as const_utils +import mmSolver.utils.imageseq as imageseq_utils +import mmSolver.utils.node as node_utils +import mmSolver.tools.createimageplane._lib.constant as lib_const +import mmSolver.tools.createimageplane._lib.utilities as lib_utils + + +LOG = mmSolver.logger.get_logger() + + +def create_transform_attrs(image_plane_tfm): + assert maya.cmds.nodeType(image_plane_tfm) == lib_const.MM_IMAGE_PLANE_TRANSFORM + + # Depth attribute + attr = 'depth' + maya.cmds.addAttr( + image_plane_tfm, + longName=attr, + attributeType='double', + minValue=0.0, + defaultValue=1.0, + ) + maya.cmds.setAttr(image_plane_tfm + '.' + attr, keyable=True) + + # Focal Length attribute + attr = 'focalLength' + maya.cmds.addAttr( + image_plane_tfm, + longName=attr, + attributeType='double', + minValue=0.1, + defaultValue=35.0, + ) + maya.cmds.setAttr(image_plane_tfm + '.' + attr, keyable=True) + + # Horizontal Film Aperture attribute + attr = 'horizontalFilmAperture' + value = 36.0 / 25.4 # 35mm film in inches. + maya.cmds.addAttr( + image_plane_tfm, + longName=attr, + attributeType='double', + minValue=0.001, + defaultValue=value, + ) + maya.cmds.setAttr(image_plane_tfm + '.' + attr, keyable=True) + + # Vertical Film Aperture attribute + attr = 'verticalFilmAperture' + value = 24.0 / 25.4 # 35mm film in inches. + maya.cmds.addAttr( + image_plane_tfm, + longName=attr, + attributeType='double', + minValue=0.001, + defaultValue=value, + ) + maya.cmds.setAttr(image_plane_tfm + '.' + attr, keyable=True) + + # Pixel Aspect Ratio attribute + attr = 'pixelAspect' + value = 1.0 + maya.cmds.addAttr( + image_plane_tfm, + longName=attr, + attributeType='double', + minValue=0.001, + defaultValue=value, + ) + maya.cmds.setAttr(image_plane_tfm + '.' + attr, keyable=True) + + # Horizontal Film Offset attribute + attr = 'horizontalFilmOffset' + maya.cmds.addAttr( + image_plane_tfm, + longName=attr, + attributeType='double', + minValue=0.0, + defaultValue=0.0, + ) + maya.cmds.setAttr(image_plane_tfm + '.' + attr, keyable=True) + + # Vertical Film Offset attribute + attr = 'verticalFilmOffset' + maya.cmds.addAttr( + image_plane_tfm, + longName=attr, + attributeType='double', + minValue=0.0, + defaultValue=0.0, + ) + maya.cmds.setAttr(image_plane_tfm + '.' + attr, keyable=True) + return + + +def create_image_plane_shape_attrs(image_plane_shp): + assert maya.cmds.nodeType(image_plane_shp) == lib_const.MM_IMAGE_PLANE_SHAPE_V1 + + # Exposure attribute + attr = 'exposure' + maya.cmds.addAttr( + image_plane_shp, + longName=attr, + attributeType='double', + softMinValue=-5.0, + softMaxValue=5.0, + defaultValue=0.0, + ) + node_attr = image_plane_shp + '.' + attr + maya.cmds.setAttr(node_attr, keyable=True) + + # Gamma attribute + attr = 'gamma' + maya.cmds.addAttr( + image_plane_shp, + longName=attr, + attributeType='double', + minValue=0.0, + softMaxValue=3.0, + defaultValue=1.0, + ) + node_attr = image_plane_shp + '.' + attr + maya.cmds.setAttr(node_attr, keyable=True) + + # Color Gain attribute + attr = 'colorGain' + default_value = 1.0 + lib_utils.add_attr_float3_color(image_plane_shp, attr, default_value) + + # Alpha Gain attribute + attr = 'alphaGain' + maya.cmds.addAttr( + image_plane_shp, + longName=attr, + attributeType='double', + minValue=0.0, + softMaxValue=1.0, + defaultValue=1.0, + ) + node_attr = image_plane_shp + '.' + attr + maya.cmds.setAttr(node_attr, keyable=True) + + # Use Image Alpha Channel attribute + attr = 'imageUseAlphaChannel' + maya.cmds.addAttr( + image_plane_shp, longName=attr, attributeType='bool', defaultValue=0 + ) + node_attr = image_plane_shp + '.' + attr + maya.cmds.setAttr(node_attr, keyable=True) + + # Default Image Color attribute, display a dark-red color when an + # image is not found. + attr = 'imageDefaultColor' + default_value = 0.0 + lib_utils.add_attr_float3_color(image_plane_shp, attr, default_value) + node_attr = image_plane_shp + '.' + attr + maya.cmds.setAttr(node_attr + 'R', 0.3) + maya.cmds.setAttr(node_attr + 'G', 0.0) + maya.cmds.setAttr(node_attr + 'B', 0.0) + + # Image Load Enable attribute + attr = 'imageLoadEnable' + maya.cmds.addAttr( + image_plane_shp, longName=attr, attributeType='bool', defaultValue=1 + ) + node_attr = image_plane_shp + '.' + attr + maya.cmds.setAttr(node_attr, keyable=True) + + # Choose which image sequence path to use. This is only visible in + # the Attribute Editor, because this attribute must trigger a + # callback when changed to update the underlying 'file' node. + attr = 'imageSequenceSlot' + maya.cmds.addAttr( + image_plane_shp, + longName=attr, + attributeType='enum', + enumName="main=0:alternate1=1:alternate2=2:alternate3=3", + ) + node_attr = image_plane_shp + '.' + attr + maya.cmds.setAttr(node_attr, keyable=False) + + # Image Sequence attribute + attr = 'imageSequence' + nice_name = 'Image Sequence' + attr_suffix_list = ['Main', 'Alternate1', 'Alternate2', 'Alternate3'] + nice_suffix_list = [' (Main)', ' (Alt 1)', ' (Alt 2)', ' (Alt 3)'] + for attr_suffix, nice_suffix in zip(attr_suffix_list, nice_suffix_list): + maya.cmds.addAttr( + image_plane_shp, + longName=attr + attr_suffix, + niceName=nice_name + nice_suffix, + dataType='string', + ) + + # Image Sequence Frame attribute + attr = 'imageSequenceFrame' + maya.cmds.addAttr( + image_plane_shp, longName=attr, attributeType='double', defaultValue=0.0 + ) + node_attr = image_plane_shp + '.' + attr + maya.cmds.setAttr(node_attr, keyable=True) + lib_utils.force_connect_attr('time1.outTime', node_attr) + + attr = 'imageSequenceFirstFrame' + maya.cmds.addAttr( + image_plane_shp, + longName=attr, + niceName='First Frame', + attributeType='long', + defaultValue=0, + ) + node_attr = image_plane_shp + '.' + attr + maya.cmds.setAttr(node_attr, keyable=False) + maya.cmds.setAttr(node_attr, channelBox=True) + + attr = 'imageSequenceFrameOutput' + maya.cmds.addAttr( + image_plane_shp, + longName=attr, + niceName='Frame Output', + attributeType='double', + defaultValue=0.0, + ) + node_attr = image_plane_shp + '.' + attr + maya.cmds.setAttr(node_attr, keyable=True) + + # Image Sequence details. + maya.cmds.addAttr( + image_plane_shp, + longName='imageSequenceStartFrame', + niceName='Start Frame', + attributeType='long', + defaultValue=0, + ) + maya.cmds.addAttr( + image_plane_shp, + longName='imageSequenceEndFrame', + niceName='End Frame', + attributeType='long', + defaultValue=0, + ) + maya.cmds.addAttr( + image_plane_shp, + longName='imageSequencePadding', + niceName='Padding', + attributeType='long', + defaultValue=0, + ) + + # Mesh Resolution attribute + attr = 'meshResolution' + maya.cmds.addAttr( + image_plane_shp, + longName=attr, + attributeType='long', + minValue=1, + maxValue=256, + defaultValue=32, + ) + node_attr = image_plane_shp + '.' + attr + maya.cmds.setAttr(node_attr, keyable=True) + return + + +def set_image_sequence(shp, image_sequence_path, attr_name): + assert maya.cmds.nodeType(shp) == lib_const.MM_IMAGE_PLANE_SHAPE_V1 + assert node_utils.attribute_exists(attr_name, shp) is True + + format_style = const_utils.IMAGE_SEQ_FORMAT_STYLE_FIRST_FRAME + ( + file_pattern, + start_frame, + end_frame, + pad_num, + is_seq, + ) = imageseq_utils.expand_image_sequence_path(image_sequence_path, format_style) + first_frame_file_seq = file_pattern + + mmapi.load_plugin() + try: + first_frame_file_seq = first_frame_file_seq.replace('\\', '/') + image_width_height = maya.cmds.mmReadImage( + first_frame_file_seq, query=True, widthHeight=True + ) + except RuntimeError: + image_width_height = None + LOG.warn('Failed to read file: %r', first_frame_file_seq) + + if image_width_height is not None: + image_width = image_width_height[0] + image_height = image_width_height[1] + + if not node_utils.node_is_referenced(shp): + maya.cmds.setAttr(shp + '.imageWidth', lock=False) + maya.cmds.setAttr(shp + '.imageHeight', lock=False) + + maya.cmds.setAttr(shp + '.imageWidth', image_width) + maya.cmds.setAttr(shp + '.imageHeight', image_height) + + if not node_utils.node_is_referenced(shp): + maya.cmds.setAttr(shp + '.imageWidth', lock=True) + maya.cmds.setAttr(shp + '.imageHeight', lock=True) + + maya.cmds.setAttr(shp + '.' + attr_name, file_pattern, type='string') + + if not node_utils.node_is_referenced(shp): + maya.cmds.setAttr(shp + '.imageSequenceStartFrame', lock=False) + maya.cmds.setAttr(shp + '.imageSequenceEndFrame', lock=False) + maya.cmds.setAttr(shp + '.imageSequencePadding', lock=False) + + maya.cmds.setAttr(shp + '.imageSequenceFirstFrame', start_frame) + maya.cmds.setAttr(shp + '.imageSequenceStartFrame', start_frame) + maya.cmds.setAttr(shp + '.imageSequenceEndFrame', end_frame) + maya.cmds.setAttr(shp + '.imageSequencePadding', pad_num) + + if not node_utils.node_is_referenced(shp): + maya.cmds.setAttr(shp + '.imageSequenceStartFrame', lock=True) + maya.cmds.setAttr(shp + '.imageSequenceEndFrame', lock=True) + maya.cmds.setAttr(shp + '.imageSequencePadding', lock=True) + return + + +def get_shape_node(image_plane_tfm): + assert maya.cmds.nodeType(image_plane_tfm) == lib_const.MM_IMAGE_PLANE_TRANSFORM + + shape = None + shapes = ( + maya.cmds.listRelatives( + image_plane_tfm, + shapes=True, + fullPath=True, + type=lib_const.MM_IMAGE_PLANE_SHAPE_V1, + ) + or [] + ) + if len(shapes) > 0: + shape = shapes[0] + return shape + + +def get_transform_node(image_plane_shp): + assert maya.cmds.nodeType(image_plane_shp) == lib_const.MM_IMAGE_PLANE_SHAPE_V1 + + tfm = None + tfms = ( + maya.cmds.listRelatives( + image_plane_shp, + parent=True, + fullPath=True, + type=lib_const.MM_IMAGE_PLANE_TRANSFORM, + ) + or [] + ) + if len(tfms) > 0: + tfm = tfms[0] + return tfm + + +def get_image_plane_node_pair(node): + node_type = maya.cmds.nodeType(node) + assert node_type in [ + lib_const.MM_IMAGE_PLANE_SHAPE_V1, + lib_const.MM_IMAGE_PLANE_TRANSFORM, + ] + + tfm = None + shp = None + if node_type == lib_const.MM_IMAGE_PLANE_TRANSFORM: + shp = get_shape_node(node) + tfm = node + else: + # Given a shape, we can look at our parent node to find the + # transform. + tfm = get_transform_node(node) + shp = node + return tfm, shp + + +def get_file_node(image_plane_tfm): + assert maya.cmds.nodeType(image_plane_tfm) == lib_const.MM_IMAGE_PLANE_TRANSFORM + + file_node = None + conns = ( + maya.cmds.listConnections( + image_plane_tfm + '.shaderFileNode', + destination=False, + source=True, + plugs=False, + type='file', + ) + or [] + ) + if len(conns) > 0: + file_node = conns[0] + return file_node diff --git a/python/mmSolver/tools/createimageplane/_lib/mmimageplane_v2.py b/python/mmSolver/tools/createimageplane/_lib/mmimageplane_v2.py new file mode 100644 index 000000000..04f9e5f68 --- /dev/null +++ b/python/mmSolver/tools/createimageplane/_lib/mmimageplane_v2.py @@ -0,0 +1,547 @@ +# Copyright (C) 2020, 2022, 2024 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Library functions for creating and modifying image planes. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import maya.cmds + +import mmSolver.logger +import mmSolver.api as mmapi +import mmSolver.utils.constant as const_utils +import mmSolver.utils.imageseq as imageseq_utils +import mmSolver.utils.node as node_utils +import mmSolver.utils.python_compat as pycompat +import mmSolver.tools.createimageplane._lib.constant as lib_const +import mmSolver.tools.createimageplane._lib.utilities as lib_utils + + +LOG = mmSolver.logger.get_logger() + + +def create_transform_attrs(image_plane_tfm): + assert maya.cmds.nodeType(image_plane_tfm) == lib_const.MM_IMAGE_PLANE_TRANSFORM + + # Depth attribute + attr = 'depth' + maya.cmds.addAttr( + image_plane_tfm, + longName=attr, + attributeType='double', + minValue=0.0, + defaultValue=1.0, + ) + maya.cmds.setAttr(image_plane_tfm + '.' + attr, keyable=True) + + # Focal Length attribute + attr = 'focalLength' + maya.cmds.addAttr( + image_plane_tfm, + longName=attr, + attributeType='double', + minValue=0.1, + defaultValue=35.0, + ) + maya.cmds.setAttr(image_plane_tfm + '.' + attr, keyable=True) + + # Horizontal Film Aperture attribute + attr = 'horizontalFilmAperture' + value = 36.0 / 25.4 # 35mm film in inches. + maya.cmds.addAttr( + image_plane_tfm, + longName=attr, + attributeType='double', + minValue=0.001, + defaultValue=value, + ) + maya.cmds.setAttr(image_plane_tfm + '.' + attr, keyable=True) + + # Vertical Film Aperture attribute + attr = 'verticalFilmAperture' + value = 24.0 / 25.4 # 35mm film in inches. + maya.cmds.addAttr( + image_plane_tfm, + longName=attr, + attributeType='double', + minValue=0.001, + defaultValue=value, + ) + maya.cmds.setAttr(image_plane_tfm + '.' + attr, keyable=True) + + # Pixel Aspect Ratio attribute + attr = 'pixelAspect' + value = 1.0 + maya.cmds.addAttr( + image_plane_tfm, + longName=attr, + attributeType='double', + minValue=0.001, + defaultValue=value, + ) + maya.cmds.setAttr(image_plane_tfm + '.' + attr, keyable=True) + + # Horizontal Film Offset attribute + attr = 'horizontalFilmOffset' + maya.cmds.addAttr( + image_plane_tfm, + longName=attr, + attributeType='double', + minValue=0.0, + defaultValue=0.0, + ) + maya.cmds.setAttr(image_plane_tfm + '.' + attr, keyable=True) + + # Vertical Film Offset attribute + attr = 'verticalFilmOffset' + maya.cmds.addAttr( + image_plane_tfm, + longName=attr, + attributeType='double', + minValue=0.0, + defaultValue=0.0, + ) + maya.cmds.setAttr(image_plane_tfm + '.' + attr, keyable=True) + return + + +def create_image_plane_shape_attrs(image_plane_shp): + assert maya.cmds.nodeType(image_plane_shp) == lib_const.MM_IMAGE_PLANE_SHAPE_V2 + + # Choose which image sequence path to use. This is only visible in + # the Attribute Editor, because this attribute must trigger a + # callback when changed to update the underlying 'file' node. + attr = 'imageSequenceSlot' + maya.cmds.addAttr( + image_plane_shp, + longName=attr, + attributeType='enum', + enumName="main=0:alternate1=1:alternate2=2:alternate3=3", + ) + node_attr = image_plane_shp + '.' + attr + maya.cmds.setAttr(node_attr, keyable=False) + + # Image Sequence attribute + attr = 'imageSequence' + nice_name = 'Image Sequence' + attr_suffix_list = ['Main', 'Alternate1', 'Alternate2', 'Alternate3'] + nice_suffix_list = [' (Main)', ' (Alt 1)', ' (Alt 2)', ' (Alt 3)'] + for attr_suffix, nice_suffix in zip(attr_suffix_list, nice_suffix_list): + maya.cmds.addAttr( + image_plane_shp, + longName=attr + attr_suffix, + niceName=nice_name + nice_suffix, + dataType='string', + ) + + # Image Sequence Frame attribute + attr = 'imageSequenceFrame' + maya.cmds.addAttr( + image_plane_shp, longName=attr, attributeType='double', defaultValue=0.0 + ) + node_attr = image_plane_shp + '.' + attr + maya.cmds.setAttr(node_attr, keyable=True) + lib_utils.force_connect_attr('time1.outTime', node_attr) + + attr = 'imageSequenceFirstFrame' + maya.cmds.addAttr( + image_plane_shp, + longName=attr, + niceName='First Frame', + attributeType='long', + defaultValue=0, + ) + node_attr = image_plane_shp + '.' + attr + maya.cmds.setAttr(node_attr, keyable=False) + maya.cmds.setAttr(node_attr, channelBox=True) + + attr = 'imageSequenceFrameOutput' + maya.cmds.addAttr( + image_plane_shp, + longName=attr, + niceName='Frame Output', + attributeType='double', + defaultValue=0.0, + ) + node_attr = image_plane_shp + '.' + attr + maya.cmds.setAttr(node_attr, keyable=True) + dst_node_attr = image_plane_shp + '.imageFrameNumber' + lib_utils.force_connect_attr(node_attr, dst_node_attr) + + # Image Sequence details. + maya.cmds.addAttr( + image_plane_shp, + longName='imageSequenceStartFrame', + niceName='Start Frame', + attributeType='long', + defaultValue=0, + ) + maya.cmds.addAttr( + image_plane_shp, + longName='imageSequenceEndFrame', + niceName='End Frame', + attributeType='long', + defaultValue=0, + ) + maya.cmds.addAttr( + image_plane_shp, + longName='imageSequencePadding', + niceName='Padding', + attributeType='long', + defaultValue=0, + ) + + # Mesh Resolution attribute + attr = 'meshResolution' + maya.cmds.addAttr( + image_plane_shp, + longName=attr, + attributeType='long', + minValue=1, + maxValue=256, + defaultValue=32, + ) + node_attr = image_plane_shp + '.' + attr + maya.cmds.setAttr(node_attr, keyable=True) + return + + +SLOT_NUMBER_MAIN = 0 +SLOT_NUMBER_ALT1 = 1 +SLOT_NUMBER_ALT2 = 2 +SLOT_NUMBER_ALT3 = 3 +SLOT_NUMBER_VALUES = [ + SLOT_NUMBER_MAIN, + SLOT_NUMBER_ALT1, + SLOT_NUMBER_ALT2, + SLOT_NUMBER_ALT3, +] +SLOT_ATTR_NAME_MAIN = 'imageSequenceMain' +SLOT_ATTR_NAME_ALT1 = 'imageSequenceAlternate1' +SLOT_ATTR_NAME_ALT2 = 'imageSequenceAlternate2' +SLOT_ATTR_NAME_ALT3 = 'imageSequenceAlternate3' +SLOT_ATTR_NAME_VALUES = [ + SLOT_ATTR_NAME_MAIN, + SLOT_ATTR_NAME_ALT1, + SLOT_ATTR_NAME_ALT2, + SLOT_ATTR_NAME_ALT3, +] +SLOT_NUMBER_TO_ATTR_NAME_MAP = { + SLOT_NUMBER_MAIN: SLOT_ATTR_NAME_MAIN, + SLOT_NUMBER_ALT1: SLOT_ATTR_NAME_ALT1, + SLOT_NUMBER_ALT2: SLOT_ATTR_NAME_ALT2, + SLOT_NUMBER_ALT3: SLOT_ATTR_NAME_ALT3, +} +SLOT_ATTR_NAME_TO_NUMBER_MAP = { + SLOT_ATTR_NAME_MAIN: SLOT_NUMBER_MAIN, + SLOT_ATTR_NAME_ALT1: SLOT_NUMBER_ALT1, + SLOT_ATTR_NAME_ALT2: SLOT_NUMBER_ALT2, + SLOT_ATTR_NAME_ALT3: SLOT_NUMBER_ALT2, +} + + +def _get_image_plane_image_slot_number(shp): + slot_number = maya.cmds.getAttr(shp + '.imageSequenceSlot') + return slot_number + + +def _slot_number_to_attr_name(slot_number): + assert isinstance(slot_number, int) + assert slot_number in SLOT_NUMBER_VALUES + return SLOT_NUMBER_TO_ATTR_NAME_MAP[slot_number] + + +def _slot_attr_name_to_number(attr_name): + assert isinstance(attr_name, pycompat.TEXT_TYPE) + assert attr_name in SLOT_ATTR_NAME_VALUES + return SLOT_ATTR_NAME_TO_NUMBER_MAP[attr_name] + + +def get_file_pattern_for_active_slot(shp): + assert maya.cmds.nodeType(shp) == lib_const.MM_IMAGE_PLANE_SHAPE_V2 + slot_number = _get_image_plane_image_slot_number(shp) + slot_attr_name = _slot_number_to_attr_name(slot_number) + file_pattern = maya.cmds.getAttr('{}.{}'.format(shp, slot_attr_name)) + return file_pattern + + +def get_file_pattern_for_unused_slots(shp): + assert maya.cmds.nodeType(shp) == lib_const.MM_IMAGE_PLANE_SHAPE_V2 + + slot_number = _get_image_plane_image_slot_number(shp) + slot_attr_name = _slot_number_to_attr_name(slot_number) + + all_slot_attr_names = list(SLOT_ATTR_NAME_VALUES) + all_slot_attr_names.remove(slot_attr_name) + + unused_file_patterns = [] + for attr_name in sorted(all_slot_attr_names): + file_pattern = maya.cmds.getAttr('{}.{}'.format(shp, attr_name)) + unused_file_patterns.append(file_pattern) + return unused_file_patterns + + +def get_file_pattern_for_all_slots(shp): + assert maya.cmds.nodeType(shp) == lib_const.MM_IMAGE_PLANE_SHAPE_V2 + + all_file_patterns = [] + for attr_name in SLOT_ATTR_NAME_VALUES: + file_pattern = maya.cmds.getAttr('{}.{}'.format(shp, attr_name)) + all_file_patterns.append(file_pattern) + return all_file_patterns + + +def set_image_sequence(shp, image_sequence_path, attr_name): + assert isinstance(image_sequence_path, pycompat.TEXT_TYPE) + assert maya.cmds.nodeType(shp) == lib_const.MM_IMAGE_PLANE_SHAPE_V2 + assert node_utils.attribute_exists(attr_name, shp) is True + + format_style = const_utils.IMAGE_SEQ_FORMAT_STYLE_FIRST_FRAME + ( + file_pattern, + _, + _, + _, + _, + ) = imageseq_utils.expand_image_sequence_path(image_sequence_path, format_style) + first_frame_file_seq = file_pattern.replace('\\', '/') + + mmapi.load_plugin() + try: + image_data_header = maya.cmds.mmReadImage( + first_frame_file_seq, query=True, dataHeader=True + ) + except RuntimeError: + image_data_header = None + LOG.warn('Failed to read file: %r', first_frame_file_seq) + + image_width = 1 + image_height = 1 + image_num_channels = 0 + image_bytes_per_channel = 0 + image_data_size_as_bytes = 0 + if image_data_header is not None: + image_width = int(image_data_header[0]) + image_height = int(image_data_header[1]) + image_num_channels = int(image_data_header[2]) + image_bytes_per_channel = int(image_data_header[3]) + image_data_size_as_bytes = image_data_header[4] + + if not node_utils.node_is_referenced(shp): + maya.cmds.setAttr(shp + '.imageWidth', lock=False) + maya.cmds.setAttr(shp + '.imageHeight', lock=False) + maya.cmds.setAttr(shp + '.imageNumChannels', lock=False) + maya.cmds.setAttr(shp + '.imageBytesPerChannel', lock=False) + maya.cmds.setAttr(shp + '.imageSizeBytes', lock=False) + + maya.cmds.setAttr(shp + '.imageWidth', image_width) + maya.cmds.setAttr(shp + '.imageHeight', image_height) + maya.cmds.setAttr(shp + '.imageNumChannels', image_num_channels) + maya.cmds.setAttr(shp + '.imageBytesPerChannel', image_bytes_per_channel) + maya.cmds.setAttr(shp + '.imageSizeBytes', image_data_size_as_bytes, type='string') + + if not node_utils.node_is_referenced(shp): + maya.cmds.setAttr(shp + '.imageWidth', lock=True) + maya.cmds.setAttr(shp + '.imageHeight', lock=True) + maya.cmds.setAttr(shp + '.imageNumChannels', lock=True) + maya.cmds.setAttr(shp + '.imageBytesPerChannel', lock=True) + maya.cmds.setAttr(shp + '.imageSizeBytes', lock=True) + + format_style = const_utils.IMAGE_SEQ_FORMAT_STYLE_HASH_PADDED + ( + file_pattern, + start_frame, + end_frame, + pad_num, + is_seq, + ) = imageseq_utils.expand_image_sequence_path(image_sequence_path, format_style) + + maya.cmds.setAttr(shp + '.' + attr_name, file_pattern, type='string') + + if not node_utils.node_is_referenced(shp): + maya.cmds.setAttr(shp + '.imageSequenceStartFrame', lock=False) + maya.cmds.setAttr(shp + '.imageSequenceEndFrame', lock=False) + maya.cmds.setAttr(shp + '.imageSequencePadding', lock=False) + + maya.cmds.setAttr(shp + '.imageSequenceFirstFrame', start_frame) + maya.cmds.setAttr(shp + '.imageSequenceStartFrame', start_frame) + maya.cmds.setAttr(shp + '.imageSequenceEndFrame', end_frame) + maya.cmds.setAttr(shp + '.imageSequencePadding', pad_num) + + if not node_utils.node_is_referenced(shp): + maya.cmds.setAttr(shp + '.imageSequenceStartFrame', lock=True) + maya.cmds.setAttr(shp + '.imageSequenceEndFrame', lock=True) + maya.cmds.setAttr(shp + '.imageSequencePadding', lock=True) + return + + +def get_frame_size_bytes(shp): + assert isinstance(shp, pycompat.TEXT_TYPE) + assert maya.cmds.nodeType(shp) == lib_const.MM_IMAGE_PLANE_SHAPE_V2 + image_size_bytes = maya.cmds.getAttr(shp + '.imageSizeBytes') + return int(image_size_bytes) + + +def get_frame_count(shp): + assert isinstance(shp, pycompat.TEXT_TYPE) + assert maya.cmds.nodeType(shp) == lib_const.MM_IMAGE_PLANE_SHAPE_V2 + start_frame = maya.cmds.getAttr(shp + '.imageSequenceStartFrame') + end_frame = maya.cmds.getAttr(shp + '.imageSequenceEndFrame') + frame_count = end_frame - start_frame + # When the start and end are the same value, that's still one + # frame. + return frame_count + 1 + + +def get_image_sequence_size_bytes(shp): + assert isinstance(shp, pycompat.TEXT_TYPE) + assert maya.cmds.nodeType(shp) == lib_const.MM_IMAGE_PLANE_SHAPE_V2 + image_size_bytes = get_frame_size_bytes(shp) + frame_count = get_frame_count(shp) + return image_size_bytes * frame_count + + +def get_shape_node(image_plane_tfm): + assert maya.cmds.nodeType(image_plane_tfm) == lib_const.MM_IMAGE_PLANE_TRANSFORM + + shape = None + shapes = ( + maya.cmds.listRelatives( + image_plane_tfm, + shapes=True, + fullPath=True, + type=lib_const.MM_IMAGE_PLANE_SHAPE_V2, + ) + or [] + ) + if len(shapes) > 0: + shape = shapes[0] + return shape + + +def get_transform_node(image_plane_shp): + assert maya.cmds.nodeType(image_plane_shp) == lib_const.MM_IMAGE_PLANE_SHAPE_V2 + + tfm = None + tfms = ( + maya.cmds.listRelatives( + image_plane_shp, + parent=True, + fullPath=True, + type=lib_const.MM_IMAGE_PLANE_TRANSFORM, + ) + or [] + ) + if len(tfms) > 0: + tfm = tfms[0] + return tfm + + +def get_image_plane_node_pair(node): + node_type = maya.cmds.nodeType(node) + assert node_type in [ + lib_const.MM_IMAGE_PLANE_SHAPE_V2, + lib_const.MM_IMAGE_PLANE_TRANSFORM, + ] + tfm = None + shp = None + if node_type == lib_const.MM_IMAGE_PLANE_TRANSFORM: + shp = get_shape_node(node) + tfm = node + else: + # Given a shape, we can look at our parent node to find the + # transform. + tfm = get_transform_node(node) + shp = node + return tfm, shp + + +def _set_attribute_editor_color_space(node_attr, control_name, value): + # TODO: Check if the node is editable first? + maya.cmds.setAttr(node_attr, lock=False) + maya.cmds.setAttr(node_attr, value, type='string') + maya.cmds.setAttr(node_attr, lock=True) + maya.cmds.button(control_name, edit=True, label=value) + + +def _maybe_make_menu_item(button_name, label, node_attr, value): + if value and len(value) > 0: + + def func(x): + _set_attribute_editor_color_space(node_attr, button_name, value) + + maya.cmds.menuItem(label=label, command=func) + return + + +def color_space_attribute_editor_new(node_attr): + assert isinstance(node_attr, pycompat.TEXT_TYPE) + split = node_attr.split('.') + if len(split) == 0: + LOG.warn('Could not get attribute name from: %r', node_attr) + return + attr = split[-1] + + current_value = maya.cmds.getAttr(node_attr) + nice_name = maya.cmds.attributeName(node_attr, nice=True) + + layout_name = '{}_colorSpaceLayout'.format(attr) + maya.cmds.rowLayout(layout_name, numberOfColumns=3) + + maya.cmds.text(label=nice_name) + + button_name = "{}_colorSpaceButton".format(attr) + maya.cmds.button(button_name) + if len(current_value) == 0: + maya.cmds.button(button_name, edit=True, label="") + else: + maya.cmds.button(button_name, edit=True, label=current_value) + + left_mouse_button = 1 + maya.cmds.popupMenu(button=left_mouse_button) + + value = maya.cmds.mmColorIO(roleDefault=True) + label = 'Default: {}'.format(value) + _maybe_make_menu_item(button_name, label, node_attr, value) + + value = maya.cmds.mmColorIO(roleSceneLinear=True) + label = 'Scene Linear: {}'.format(value) + _maybe_make_menu_item(button_name, label, node_attr, value) + + value = maya.cmds.mmColorIO(roleMattePaint=True) + label = 'Matte Paint: {}'.format(value) + _maybe_make_menu_item(button_name, label, node_attr, value) + + value = maya.cmds.mmColorIO(roleData=True) + label = 'Data: {}'.format(value) + _maybe_make_menu_item(button_name, label, node_attr, value) + + value = maya.cmds.mmColorIO(roleColorPicking=True) + label = 'Color Picking: {}'.format(value) + _maybe_make_menu_item(button_name, label, node_attr, value) + + maya.cmds.menuItem(divider=True) + + color_space_names = maya.cmds.mmColorIO(listColorSpacesActive=True) + for name in color_space_names: + _maybe_make_menu_item(button_name, name, node_attr, name) + + maya.cmds.setParent('..') diff --git a/python/mmSolver/tools/createimageplane/_lib/polyplane.py b/python/mmSolver/tools/createimageplane/_lib/polyplane.py index d67b31227..b8351a9a6 100644 --- a/python/mmSolver/tools/createimageplane/_lib/polyplane.py +++ b/python/mmSolver/tools/createimageplane/_lib/polyplane.py @@ -29,6 +29,7 @@ import mmSolver.logger import mmSolver.tools.createimageplane._lib.utilities as lib_utils +import mmSolver.utils.node as node_utils LOG = mmSolver.logger.get_logger() @@ -70,7 +71,8 @@ def create_poly_plane(name_mesh_shp, image_plane_tfm, cam_shp): # Drive the Deformer node with the camera lens. src = cam_shp + '.outLens' dst = deform_node + '.inLens' - if not maya.cmds.isConnected(src, dst): + src_attr_exists = node_utils.attribute_exists('outLens', cam_shp) + if src_attr_exists is True and (not maya.cmds.isConnected(src, dst)): lib_utils.force_connect_attr(src, dst) # Get the intermediate mesh shape, so we can re-order the nodes diff --git a/python/mmSolver/tools/createimageplane/_lib/shader.py b/python/mmSolver/tools/createimageplane/_lib/shader.py index 1a4c72525..eee23aecd 100644 --- a/python/mmSolver/tools/createimageplane/_lib/shader.py +++ b/python/mmSolver/tools/createimageplane/_lib/shader.py @@ -156,7 +156,7 @@ def create_network(name_shader, image_plane_tfm): ['outUV', 'uvCoord'], ['outUvFilterSize', 'uvFilterSize'], ] - for (src_attr, dst_attr) in conns: + for src_attr, dst_attr in conns: src = file_place2d + '.' + src_attr dst = file_node + '.' + dst_attr lib_utils.force_connect_attr(src, dst) diff --git a/python/mmSolver/tools/createimageplane/constant.py b/python/mmSolver/tools/createimageplane/constant.py index 14982842c..8d0834d17 100644 --- a/python/mmSolver/tools/createimageplane/constant.py +++ b/python/mmSolver/tools/createimageplane/constant.py @@ -16,6 +16,8 @@ # along with mmSolver. If not, see . # +# NOTE: '{{' and '}}' is used in place of real '{' and '}' characters, +# to allow Python's 'str.format()' to work. DISPLAY_MODE_EXPRESSION = ''' if ({image_plane_tfm}.displayMode == 0) {{ @@ -26,10 +28,3 @@ {live_image_plane_shape}.lodVisibility = 0; }} ''' - -FRAME_EXPRESSION = ''' -int $start_frame = {node}.imageSequenceStartFrame; -int $first_frame = {node}.imageSequenceFirstFrame; -int $user_frame = {node}.imageSequenceFrame; -{node}.imageSequenceFrameOutput = ($start_frame - $first_frame) + $user_frame; -''' diff --git a/python/mmSolver/tools/createimageplane/lib.py b/python/mmSolver/tools/createimageplane/lib.py index f9bc3c3c5..a9ba2b453 100644 --- a/python/mmSolver/tools/createimageplane/lib.py +++ b/python/mmSolver/tools/createimageplane/lib.py @@ -79,6 +79,14 @@ from mmSolver.tools.createimageplane._lib.utilities import get_default_image_path +from mmSolver.tools.createimageplane._lib.format import ( + format_image_sequence_size, + format_cache_gpu_used, + format_cache_cpu_used, + format_memory_gpu_available, + format_memory_cpu_available, +) + # Stop users from accessing the internal functions of this sub-module. __all__ = [ @@ -87,4 +95,9 @@ 'set_image_sequence', 'get_default_image_path', 'DEFAULT_IMAGE_SEQUENCE_ATTR_NAME', + 'format_image_sequence_size', + 'format_cache_gpu_used', + 'format_cache_cpu_used', + 'format_memory_gpu_available', + 'format_memory_cpu_available', ] diff --git a/python/mmSolver/tools/createimageplane/tool.py b/python/mmSolver/tools/createimageplane/tool.py index 1f434b6ad..2632e5c62 100644 --- a/python/mmSolver/tools/createimageplane/tool.py +++ b/python/mmSolver/tools/createimageplane/tool.py @@ -32,25 +32,38 @@ import mmSolver.api as mmapi import mmSolver.utils.viewport as utils_viewport import mmSolver.utils.camera as utils_camera +import mmSolver.tools.createimageplane._lib.constant as const import mmSolver.tools.createimageplane.lib as lib LOG = mmSolver.logger.get_logger() +# Function from +# ./python/mmSolver/tools/cameracontextmenu/lib/utilities.py +def _open_node_in_attribute_editor(node): + mel_cmd = 'showEditorExact "{node}";'.format(node=node) + maya.mel.eval(mel_cmd) + return + + def _get_start_directory(): + fallback_path = os.getcwd() + workspace_path = maya.cmds.workspace(query=True, fullName=True) if workspace_path is None: - return os.getcwd() + return fallback_path workspace_path = os.path.abspath(workspace_path) file_rules = maya.cmds.workspace(query=True, fileRule=True) if file_rules is None: - return os.getcwd() + return fallback_path file_rule_names = file_rules[0::2] file_rule_values = file_rules[1::2] file_rule = 'sourceImages' + if file_rule not in file_rule_names: + return fallback_path file_rule_index = file_rule_names.index(file_rule) dir_name = file_rule_values[file_rule_index] @@ -92,10 +105,11 @@ def prompt_user_for_image_sequence(start_dir=None): return image_sequence_path -def main(): +def _run(version): """ Create a new Image Plane on the selected camera. """ + assert version in const.MM_IMAGE_PLANE_VERSION_LIST mmapi.load_plugin() # Get selected camera(s). @@ -145,11 +159,11 @@ def main(): # user has the transform and shape nodes selected. if cam_shp in created: continue - mm_ip_tfm, mm_ip_shp = lib.create_image_plane_on_camera(cam) + mm_ip_tfm, mm_ip_shp = lib.create_image_plane_on_camera(cam, version=version) image_seq = prompt_user_for_image_sequence() if image_seq: - lib.set_image_sequence(mm_ip_tfm, image_seq) + lib.set_image_sequence(mm_ip_tfm, image_seq, version=version) nodes.append(mm_ip_shp) created.add(cam_shp) @@ -159,7 +173,19 @@ def main(): # Show the last node in the attribute editor. node = nodes[-1] - maya.mel.eval('updateAE("{}");'.format(node)) + _open_node_in_attribute_editor(node) else: maya.cmds.select(sel, replace=True) return + + +def main(): + _run(const.MM_IMAGE_PLANE_VERSION_TWO) + + +def main_version_one(): + _run(const.MM_IMAGE_PLANE_VERSION_ONE) + + +def main_version_two(): + _run(const.MM_IMAGE_PLANE_VERSION_TWO) diff --git a/python/mmSolver/tools/createlens/lib.py b/python/mmSolver/tools/createlens/lib.py index c6b067cdf..a0c419136 100644 --- a/python/mmSolver/tools/createlens/lib.py +++ b/python/mmSolver/tools/createlens/lib.py @@ -50,6 +50,7 @@ import mmSolver.logger import mmSolver.api as mmapi +import mmSolver.utils.node as node_utils LOG = mmSolver.logger.get_logger() @@ -84,10 +85,19 @@ def create_marker_connections(cam): for src, dst in zip(src_list, dst_list): maya.cmds.disconnectAttr(src, dst) - # Ensure Marker have connections to the camera lens. cam_shp = cam.get_shape_node() + in_lens_attr_exists = node_utils.attribute_exists('inLens', cam_shp) + if in_lens_attr_exists is False: + LOG.warn( + 'Cannot create marker connections, camera is missing "inLens" attribute; ' + 'cam_shp=%r', + cam_shp, + ) + return + + # Ensure Marker have connections to the camera lens. + src = cam_shp + '.outLens' for mkr_node in mkr_nodes: - src = cam_shp + '.outLens' dst = mkr_node + '.inLens' if not maya.cmds.isConnected(src, dst): maya.cmds.connectAttr(src, dst) diff --git a/python/mmSolver/tools/imagecache/__init__.py b/python/mmSolver/tools/imagecache/__init__.py new file mode 100644 index 000000000..515b20e61 --- /dev/null +++ b/python/mmSolver/tools/imagecache/__init__.py @@ -0,0 +1,20 @@ +# Copyright (C) 2024 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Image Cache. +""" diff --git a/python/mmSolver/tools/imagecache/_lib/__init__.py b/python/mmSolver/tools/imagecache/_lib/__init__.py new file mode 100644 index 000000000..de016c510 --- /dev/null +++ b/python/mmSolver/tools/imagecache/_lib/__init__.py @@ -0,0 +1,20 @@ +# Copyright (C) 2024 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Image Cache library functions. +""" diff --git a/python/mmSolver/tools/imagecache/_lib/erase.py b/python/mmSolver/tools/imagecache/_lib/erase.py new file mode 100644 index 000000000..007b68506 --- /dev/null +++ b/python/mmSolver/tools/imagecache/_lib/erase.py @@ -0,0 +1,260 @@ +# Copyright (C) 2024 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Functions to control the image cache. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import maya.cmds + +import mmSolver.logger +import mmSolver.utils.imageseq as imageseq_utils +import mmSolver.utils.constant as const_utils +import mmSolver.utils.python_compat as pycompat +import mmSolver.tools.imagecache.constant as const +import mmSolver.tools.imagecache._lib.imagecache_cmd as imagecache_cmd +import mmSolver.tools.createimageplane._lib.constant as imageplane_const +import mmSolver.tools.createimageplane._lib.mmimageplane_v2 as imageplane_lib + +LOG = mmSolver.logger.get_logger() + +# Shorter alias. +_IMAGE_PLANE_SHAPE = imageplane_const.MM_IMAGE_PLANE_SHAPE_V2 + + +def _make_group_name_consistent(group_name): + assert isinstance(group_name, pycompat.TEXT_TYPE) + assert len(group_name) > 0 + + # The ImageCache stores all file paths with UNIX + # forward-slashes convention. + group_name = group_name.replace('\\', '/') + + return group_name + + +def erase_gpu_group_items(group_name): + assert isinstance(group_name, pycompat.TEXT_TYPE) + assert len(group_name) > 0 + group_name = _make_group_name_consistent(group_name) + return maya.cmds.mmImageCache([group_name], edit=True, gpuEraseGroupItems=True) + + +def erase_cpu_group_items(group_name): + assert isinstance(group_name, pycompat.TEXT_TYPE) + assert len(group_name) > 0 + group_name = _make_group_name_consistent(group_name) + return maya.cmds.mmImageCache([group_name], edit=True, cpuEraseGroupItems=True) + + +def erase_gpu_groups_items(group_names): + assert len(group_names) >= 0 + group_names = [_make_group_name_consistent(x) for x in group_names] + return maya.cmds.mmImageCache(group_names, edit=True, gpuEraseGroupItems=True) + + +def erase_cpu_groups_items(group_names): + assert len(group_names) >= 0 + group_names = [_make_group_name_consistent(x) for x in group_names] + return maya.cmds.mmImageCache(group_names, edit=True, cpuEraseGroupItems=True) + + +def erase_gpu_image_items(items): + assert len(items) >= 0 + return maya.cmds.mmImageCache(items, edit=True, gpuEraseItems=True) + + +def erase_cpu_image_items(items): + assert len(items) >= 0 + return maya.cmds.mmImageCache(items, edit=True, cpuEraseItems=True) + + +def erase_all_images_on_image_plane_slots(cache_type, shape_node): + assert cache_type in const.CACHE_TYPE_VALUES + assert maya.cmds.nodeType(shape_node) == _IMAGE_PLANE_SHAPE + + file_patterns = imageplane_lib.get_file_pattern_for_all_slots(shape_node) + file_patterns = [ + x for x in file_patterns if isinstance(x, pycompat.TEXT_TYPE) and len(x) > 0 + ] + if len(file_patterns) == 0: + LOG.warn('mmImagePlane unused slots are all invalid; node=%r', shape_node) + return + + if cache_type == const.CACHE_TYPE_GPU: + return erase_gpu_groups_items(file_patterns) + elif cache_type == const.CACHE_TYPE_CPU: + return erase_cpu_groups_items(file_patterns) + assert False + + +def erase_images_in_active_image_plane_slot(cache_type, shape_node): + assert cache_type in const.CACHE_TYPE_VALUES + assert maya.cmds.nodeType(shape_node) == _IMAGE_PLANE_SHAPE + + file_pattern = imageplane_lib.get_file_pattern_for_active_slot(shape_node) + if file_pattern is None or len(file_pattern) == 0: + LOG.warn('mmImagePlane active slot is invalid=%r', file_pattern) + return + + if cache_type == const.CACHE_TYPE_GPU: + return erase_gpu_group_items(file_pattern) + elif cache_type == const.CACHE_TYPE_CPU: + return erase_cpu_group_items(file_pattern) + raise NotImplementedError + + +def erase_images_in_unused_image_plane_slots(cache_type, shape_node): + assert cache_type in const.CACHE_TYPE_VALUES + assert maya.cmds.nodeType(shape_node) == _IMAGE_PLANE_SHAPE + + file_patterns = imageplane_lib.get_file_pattern_for_unused_slots(shape_node) + file_patterns = [ + x for x in file_patterns if isinstance(x, pycompat.TEXT_TYPE) and len(x) > 0 + ] + if len(file_patterns) == 0: + LOG.warn('mmImagePlane unused slots are all invalid; node=%r', shape_node) + return + + if cache_type == const.CACHE_TYPE_GPU: + return erase_gpu_groups_items(file_patterns) + elif cache_type == const.CACHE_TYPE_CPU: + return erase_cpu_groups_items(file_patterns) + raise NotImplementedError + + +def erase_image_sequence( + cache_type, file_pattern, format_style, start_frame, end_frame +): + assert cache_type in const.CACHE_TYPE_VALUES + assert format_style in const_utils.IMAGE_SEQ_FORMAT_STYLE_VALUES + assert isinstance(file_pattern, pycompat.TEXT_TYPE) + assert isinstance(start_frame, int) + assert isinstance(end_frame, int) + + item_count = None + if cache_type == const.CACHE_TYPE_GPU: + item_count = imagecache_cmd.get_gpu_cache_group_item_count(file_pattern) + elif cache_type == const.CACHE_TYPE_CPU: + item_count = imagecache_cmd.get_cpu_cache_group_item_count(file_pattern) + assert item_count is not None + + if item_count == 0: + LOG.warn('File pattern does not have any items. item_count=%r', item_count) + return + + item_names = set(imagecache_cmd.get_gpu_group_item_names(cache_type, file_pattern)) + assert len(item_names) > 0 + + # Evaluate the file_pattern for start_frame to end_frame. + image_file_paths = set() + for frame in range(start_frame, end_frame + 1): + format_style = const_utils.IMAGE_SEQ_FORMAT_STYLE_EXACT_FRAME + file_path = imageseq_utils.resolve_file_pattern_to_file_path( + file_pattern, format_style, exact_frame=start_frame + ) + image_file_paths.add(file_path) + + # If the evaluated file path is in the image cache, add it to the + # list to be removed. + # + # This assumes both lists use the same representation of the same + # file; the file is absolute and both use forward-slashes (which + # is expected). + common_file_paths = item_names & image_file_paths + + # Remove named items from the cache. + if cache_type == const.CACHE_TYPE_GPU: + return erase_gpu_image_items(list(common_file_paths)) + elif cache_type == const.CACHE_TYPE_CPU: + return erase_cpu_image_items(list(common_file_paths)) + else: + raise NotImplementedError + + +def erase_all_inactive_images(cache_type): + assert cache_type in const.CACHE_TYPE_VALUES + # Removes all the items in the cache that cannot be 'reached' by + # any of the image planes. + LOG.info( + 'erase_all_inactive_images: cache_type=%r', + cache_type, + ) + + # TODO: + # + # 1) Get all image planes. + # + # 2) Get all active slots on all image planes. + # + # 3) Get all groups in the image cache. + # + # 4) For any group that is not in the active slots, remove it. + + raise NotImplementedError + + +def erase_all_images(cache_type): + """ + Clear image cache completely. + """ + assert cache_type in const.CACHE_TYPE_VALUES + + # Remove named items from the cache. + before_item_count = None + after_item_count = None + if cache_type == const.CACHE_TYPE_GPU: + before_item_count = imagecache_cmd.get_gpu_cache_item_count() + capacity_bytes = imagecache_cmd.get_gpu_cache_capacity_bytes() + + # Because setting the capacity to zero will automatically + # evict "all" images (except the minimum number allowed) from + # the cache. + imagecache_cmd.set_gpu_cache_capacity_bytes(0) + + imagecache_cmd.set_gpu_cache_capacity_bytes(capacity_bytes) + after_item_count = imagecache_cmd.get_gpu_cache_item_count() + elif cache_type == const.CACHE_TYPE_CPU: + before_item_count = imagecache_cmd.get_cpu_cache_item_count() + capacity_bytes = imagecache_cmd.get_cpu_cache_capacity_bytes() + + # Because setting the capacity to zero will automatically + # evict "all" images (except the minimum number allowed) from + # the cache. + imagecache_cmd.set_cpu_cache_capacity_bytes(0) + + imagecache_cmd.set_cpu_cache_capacity_bytes(capacity_bytes) + after_item_count = imagecache_cmd.get_cpu_cache_item_count() + else: + raise NotImplementedError + + LOG.debug( + '%s ImageCache items before clearing: %r', cache_type.upper(), before_item_count + ) + LOG.debug( + '%s ImageCache items after clearing: %r', cache_type.upper(), after_item_count + ) + + removed_count = before_item_count - after_item_count + LOG.info( + 'Erased %r images from the %s Image Cache', removed_count, cache_type.upper() + ) + return diff --git a/python/mmSolver/tools/imagecache/_lib/imagecache_cmd.py b/python/mmSolver/tools/imagecache/_lib/imagecache_cmd.py new file mode 100644 index 000000000..899de5980 --- /dev/null +++ b/python/mmSolver/tools/imagecache/_lib/imagecache_cmd.py @@ -0,0 +1,109 @@ +# Copyright (C) 2024 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Functions to control the image cache. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import maya.cmds + +import mmSolver.logger +import mmSolver.utils.python_compat as pycompat + +LOG = mmSolver.logger.get_logger() + + +def get_gpu_cache_item_count(): + return int(maya.cmds.mmImageCache(query=True, gpuItemCount=True)) + + +def get_cpu_cache_item_count(): + return int(maya.cmds.mmImageCache(query=True, cpuItemCount=True)) + + +def get_gpu_cache_group_names(): + return maya.cmds.mmImageCache(query=True, gpuGroupNames=True) + + +def get_cpu_cache_group_names(): + return maya.cmds.mmImageCache(query=True, cpuGroupNames=True) + + +def get_gpu_cache_slot_count(): + # NOTE: The internal mmImageCache name for a "slot" is actually a + # "group" - a group of images. + return int(maya.cmds.mmImageCache(query=True, gpuGroupCount=True)) + + +def get_cpu_cache_slot_count(): + # NOTE: The internal mmImageCache name for a "slot" is actually a + # "group" - a group of images. + return int(maya.cmds.mmImageCache(query=True, cpuGroupCount=True)) + + +def get_gpu_cache_group_item_count(group_name): + assert isinstance(group_name, pycompat.TEXT_TYPE) + assert len(group_name) > 0 + return int(maya.cmds.mmImageCache(group_name, query=True, gpuGroupItemCount=True)) + + +def get_cpu_cache_group_item_count(group_name): + assert isinstance(group_name, pycompat.TEXT_TYPE) + assert len(group_name) > 0 + return int(maya.cmds.mmImageCache(group_name, query=True, cpuGroupItemCount=True)) + + +def get_gpu_cache_group_item_names(group_name): + assert isinstance(group_name, pycompat.TEXT_TYPE) + assert len(group_name) > 0 + return maya.cmds.mmImageCache(group_name, query=True, gpuGroupItemNames=True) + + +def get_cpu_cache_group_item_names(group_name): + assert isinstance(group_name, pycompat.TEXT_TYPE) + assert len(group_name) > 0 + return maya.cmds.mmImageCache(group_name, query=True, cpuGroupItemNames=True) + + +def get_gpu_cache_used_bytes(): + return pycompat.LONG_TYPE(maya.cmds.mmImageCache(query=True, gpuUsed=True)) + + +def get_cpu_cache_used_bytes(): + return pycompat.LONG_TYPE(maya.cmds.mmImageCache(query=True, cpuUsed=True)) + + +def get_gpu_cache_capacity_bytes(): + return pycompat.LONG_TYPE(maya.cmds.mmImageCache(query=True, gpuCapacity=True)) + + +def get_cpu_cache_capacity_bytes(): + return pycompat.LONG_TYPE(maya.cmds.mmImageCache(query=True, cpuCapacity=True)) + + +def set_gpu_cache_capacity_bytes(size_bytes): + assert isinstance(size_bytes, pycompat.INT_TYPES) + return maya.cmds.mmImageCache(edit=True, gpuCapacity=size_bytes) + + +def set_cpu_cache_capacity_bytes(size_bytes): + assert isinstance(size_bytes, pycompat.INT_TYPES) + return maya.cmds.mmImageCache(edit=True, cpuCapacity=size_bytes) diff --git a/python/mmSolver/tools/imagecache/_lib/query_resources.py b/python/mmSolver/tools/imagecache/_lib/query_resources.py new file mode 100644 index 000000000..df4354758 --- /dev/null +++ b/python/mmSolver/tools/imagecache/_lib/query_resources.py @@ -0,0 +1,64 @@ +# Copyright (C) 2024 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Functions to control the image cache. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import maya.cmds + +import mmSolver.logger + +LOG = mmSolver.logger.get_logger() + + +def get_gpu_memory_total_bytes(): + return int(maya.cmds.mmMemoryGPU(query=True, total=True)) + + +def get_gpu_memory_used_bytes(): + return int(maya.cmds.mmMemoryGPU(query=True, used=True)) + + +def get_cpu_memory_total_bytes(): + # The value returned from the command is a 'str', because the + # value may be more than what a 32-bit integer can support, which + # is the largest integer size returned by the Maya API. Therefore + # we simply convert the string to a integer in Python (which + # supports more than 32-bit integers in the 'int' type). + value = maya.cmds.mmMemorySystem( + query=True, + systemPhysicalMemoryTotal=True, + ) + return int(value) + + +def get_cpu_memory_used_bytes(): + # The value returned from the command is a 'str', because the + # value may be more than what a 32-bit integer can support, which + # is the largest integer size returned by the Maya API. Therefore + # we simply convert the string to a integer in Python (which + # supports more than 32-bit integers in the 'int' type). + value = maya.cmds.mmMemorySystem( + query=True, + systemPhysicalMemoryUsed=True, + ) + return int(value) diff --git a/python/mmSolver/tools/imagecache/config.py b/python/mmSolver/tools/imagecache/config.py new file mode 100644 index 000000000..1fbf9c6a9 --- /dev/null +++ b/python/mmSolver/tools/imagecache/config.py @@ -0,0 +1,104 @@ +# Copyright (C) 2024 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Functions to control the image cache. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import collections + +import mmSolver.logger +import mmSolver.tools.imagecache.config_file as config_file +import mmSolver.tools.imagecache.config_scene as config_scene +import mmSolver.tools.createimageplane._lib.constant as imageplane_const + + +# Shorter alias. +_MM_IMAGE_PLANE_SHAPE_V2 = imageplane_const.MM_IMAGE_PLANE_SHAPE_V2 + + +LOG = mmSolver.logger.get_logger() +CapacityData = collections.namedtuple( + 'CapacityData', + [ + 'gpu_default_capacity', + 'cpu_default_capacity', + 'scene_override', + 'gpu_scene_capacity', + 'cpu_scene_capacity', + 'gpu_resolved_capacity', + 'cpu_resolved_capacity', + ], +) + + +def resolve_capacity_data(): + # Open Config file. + default_gpu_capacity = config_file.get_gpu_capacity_percent() + default_cpu_capacity = config_file.get_cpu_capacity_percent() + + # Check Maya scene options for image cache overrides. + scene_override = config_scene.get_cache_scene_override() + scene_gpu_capacity = config_scene.get_gpu_capacity_percent() + scene_cpu_capacity = config_scene.get_cpu_capacity_percent() + + # Apply override to 'resolved' values. + resolved_gpu_capacity = default_gpu_capacity + resolved_cpu_capacity = default_cpu_capacity + if scene_override is True: + resolved_gpu_capacity = scene_gpu_capacity + resolved_cpu_capacity = scene_cpu_capacity + + return CapacityData( + default_gpu_capacity, + default_cpu_capacity, + scene_override, + scene_gpu_capacity, + scene_cpu_capacity, + resolved_gpu_capacity, + resolved_cpu_capacity, + ) + + +def save_capacity_values( + gpu_percent_default, + cpu_percent_default, + scene_override, + gpu_percent_scene, + cpu_percent_scene, +): + assert isinstance(gpu_percent_default, float) + assert isinstance(cpu_percent_default, float) + assert isinstance(scene_override, bool) + assert isinstance(gpu_percent_scene, float) + assert isinstance(cpu_percent_scene, float) + + # Save config values in config file. + config_file.set_gpu_capacity_percent(gpu_percent_default) + config_file.set_cpu_capacity_percent(cpu_percent_default) + config_file.write() + + # Save config values in Maya Scene. + config_scene.set_cache_scene_override(scene_override) + if scene_override is True: + config_scene.set_gpu_capacity_percent(gpu_percent_scene) + config_scene.set_cpu_capacity_percent(cpu_percent_scene) + return diff --git a/python/mmSolver/tools/imagecache/config_file.py b/python/mmSolver/tools/imagecache/config_file.py new file mode 100644 index 000000000..742343016 --- /dev/null +++ b/python/mmSolver/tools/imagecache/config_file.py @@ -0,0 +1,98 @@ +# Copyright (C) 2024 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Functions to control the image cache. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import mmSolver.logger + +import mmSolver.utils.config +import mmSolver.tools.imagecache.config_utils as config_utils +import mmSolver.tools.imagecache.constant as const +import mmSolver.tools.imagecache.lib as lib + +LOG = mmSolver.logger.get_logger() + + +# Initialised without a file path, because we only look up the file in +# the functions below. +# +# Users are not allowed to touch this variable. +# +# Note we never re-assign this variable. The Config object instance is +# always reused. +__CONFIG_PATH = mmSolver.utils.config.get_home_dir_path(const.CONFIG_FILE_NAME) +__CONFIG = mmSolver.utils.config.Config(__CONFIG_PATH) +__CONFIG.autoread = True +__CONFIG.autowrite = False + + +def _get_config(): + global __CONFIG + return __CONFIG + + +def _get_config_value(config, key, fallback): + """Query the attribute from the user's home directory. If the user's + option is saved, use that value instead. + """ + value = fallback + if config is not None: + value = config.get_value(key, fallback) + return value + + +def write(): + global __CONFIG + __CONFIG.write() + + +def get_gpu_capacity_percent(): + default_value = const.CONFIG_GPU_CAPACITY_PERCENT_DEFAULT_VALUE + assert isinstance(default_value, float) + name = const.CONFIG_GPU_CAPACITY_PERCENT_KEY + config = _get_config() + percent = _get_config_value(config, name, default_value) + total_bytes = lib.get_gpu_memory_total_bytes() + return config_utils.convert_to_capacity_value(percent, total_bytes) + + +def set_gpu_capacity_percent(value): + config_file = _get_config() + config_file.set_value(const.CONFIG_GPU_CAPACITY_PERCENT_KEY, value) + return + + +def get_cpu_capacity_percent(): + default_value = const.CONFIG_CPU_CAPACITY_PERCENT_DEFAULT_VALUE + assert isinstance(default_value, float) + name = const.CONFIG_CPU_CAPACITY_PERCENT_KEY + config = _get_config() + percent = _get_config_value(config, name, default_value) + total_bytes = lib.get_cpu_memory_total_bytes() + return config_utils.convert_to_capacity_value(percent, total_bytes) + + +def set_cpu_capacity_percent(value): + config_file = _get_config() + config_file.set_value(const.CONFIG_CPU_CAPACITY_PERCENT_KEY, value) + return diff --git a/python/mmSolver/tools/imagecache/config_scene.py b/python/mmSolver/tools/imagecache/config_scene.py new file mode 100644 index 000000000..c1eea3369 --- /dev/null +++ b/python/mmSolver/tools/imagecache/config_scene.py @@ -0,0 +1,78 @@ +# Copyright (C) 2024 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Functions to control the image cache. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import mmSolver.logger +import mmSolver.utils.configmaya as configmaya +import mmSolver.tools.imagecache.config_utils as config_utils +import mmSolver.tools.imagecache.constant as const +import mmSolver.tools.imagecache._lib.query_resources as query_resources + +LOG = mmSolver.logger.get_logger() + + +def get_cache_scene_override(): + name = const.SCENE_OPTION_CAPACITY_OVERRIDE_KEY + value = configmaya.get_scene_option(name, default=None) + assert value is None or isinstance(value, bool) + return value + + +def set_cache_scene_override(value): + assert value is None or isinstance(value, bool) + name = const.SCENE_OPTION_CAPACITY_OVERRIDE_KEY + configmaya.set_scene_option(name, value, add_attr=True) + return + + +def get_gpu_capacity_percent(): + default_value = const.SCENE_OPTION_GPU_CAPACITY_PERCENT_DEFAULT_VALUE + assert isinstance(default_value, float) + name = const.SCENE_OPTION_GPU_CAPACITY_PERCENT_KEY + percent = configmaya.get_scene_option(name, default=default_value) + total_bytes = query_resources.get_gpu_memory_total_bytes() + return config_utils.convert_to_capacity_value(percent, total_bytes) + + +def set_gpu_capacity_percent(percent): + assert isinstance(percent, float) + name = const.SCENE_OPTION_GPU_CAPACITY_PERCENT_KEY + percent = configmaya.set_scene_option(name, percent, add_attr=True) + return + + +def get_cpu_capacity_percent(): + default_value = const.SCENE_OPTION_CPU_CAPACITY_PERCENT_DEFAULT_VALUE + assert isinstance(default_value, float) + name = const.SCENE_OPTION_CPU_CAPACITY_PERCENT_KEY + percent = configmaya.get_scene_option(name, default=default_value) + total_bytes = query_resources.get_cpu_memory_total_bytes() + return config_utils.convert_to_capacity_value(percent, total_bytes) + + +def set_cpu_capacity_percent(percent): + assert isinstance(percent, float) + name = const.SCENE_OPTION_CPU_CAPACITY_PERCENT_KEY + percent = configmaya.set_scene_option(name, percent, add_attr=True) + return diff --git a/python/mmSolver/tools/imagecache/config_utils.py b/python/mmSolver/tools/imagecache/config_utils.py new file mode 100644 index 000000000..b65e18d37 --- /dev/null +++ b/python/mmSolver/tools/imagecache/config_utils.py @@ -0,0 +1,40 @@ +# Copyright (C) 2024 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Functions to control the image cache. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import collections + +import mmSolver.logger +import mmSolver.utils.python_compat as pycompat + +LOG = mmSolver.logger.get_logger() +CapacityValue = collections.namedtuple('CapacityValue', ['size_bytes', 'percent']) + + +def convert_to_capacity_value(percent, total_bytes): + assert isinstance(percent, float) + assert isinstance(total_bytes, pycompat.INT_TYPES) + ratio = percent / 100.0 + size_bytes = pycompat.LONG_TYPE(total_bytes * ratio) + return CapacityValue(size_bytes, percent) diff --git a/python/mmSolver/tools/imagecache/constant.py b/python/mmSolver/tools/imagecache/constant.py new file mode 100644 index 000000000..f244aca1f --- /dev/null +++ b/python/mmSolver/tools/imagecache/constant.py @@ -0,0 +1,70 @@ +# Copyright (C) 2024 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Image Cache constants. +""" + + +CACHE_TYPE_GPU = 'gpu' +CACHE_TYPE_CPU = 'cpu' +CACHE_TYPE_VALUES = [ + CACHE_TYPE_GPU, + CACHE_TYPE_CPU, +] + +CONFIG_FILE_NAME = 'tools_imagecache.json' + + +# The default values below are based on the following minimum/average data load: +# +# Single image size: +# >>> width = 1920 +# >>> height = 1080 +# >>> num_channels = 3 +# >>> image_byte_size = width * height * num_channels # 6220800 Bytes +# A image is at least 6.2208 MB each frame. +# +# Image Sequence size: +# >>> frame_count = 200 +# >>> image_byte_size * frame_count # 1244160000 Bytes +# The average image sequence will take up about 1.24416 GB of memory. +# +# We also guess that the user has at least 4GB of GPU memory, and 32GB +# of CPU memory, and we assume the user wants to have 3 different Maya +# sessions running at once, each with an image sequence loaded. +# +# 100 MB (2.5% of 4GB) of GPU memory allows ~16 1080p HD images stored +# in memory at once. For large (4K) images this means perhaps only one +# image will be stored in the Image Cache. +CONFIG_GPU_CAPACITY_PERCENT_KEY = 'data/gpu_capacity_percent' +CONFIG_GPU_CAPACITY_PERCENT_DEFAULT_VALUE = 2.5 + +# 3.2 GB (10%) of CPU memory allows ~514 1080p HD images stored in +# memory at once. This is a reasonable amount of CPU memory, but can +# easily be consumed for larger images or longer frame ranges. +CONFIG_CPU_CAPACITY_PERCENT_KEY = 'data/cpu_capacity_percent' +CONFIG_CPU_CAPACITY_PERCENT_DEFAULT_VALUE = 10.0 + +SCENE_OPTION_CAPACITY_OVERRIDE_KEY = 'mmSolver_imagecache_capacity_override' +SCENE_OPTION_CAPACITY_OVERRIDE_DEFAULT_VALUE = False + +SCENE_OPTION_GPU_CAPACITY_PERCENT_KEY = 'mmSolver_imagecache_gpu_capacity_percent' +SCENE_OPTION_GPU_CAPACITY_PERCENT_DEFAULT_VALUE = 0.0 + +SCENE_OPTION_CPU_CAPACITY_PERCENT_KEY = 'mmSolver_imagecache_cpu_capacity_percent' +SCENE_OPTION_CPU_CAPACITY_PERCENT_DEFAULT_VALUE = 0.0 diff --git a/python/mmSolver/tools/imagecache/initialise.py b/python/mmSolver/tools/imagecache/initialise.py new file mode 100644 index 000000000..8da4deab7 --- /dev/null +++ b/python/mmSolver/tools/imagecache/initialise.py @@ -0,0 +1,129 @@ +# Copyright (C) 2024 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Start up and initialize the image cache. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import maya.cmds +import maya.OpenMaya as OpenMaya + +import mmSolver.logger +import mmSolver.api as mmapi +import mmSolver.utils.constant as const_utils +import mmSolver.tools.imagecache.lib as lib +import mmSolver.tools.imagecache.config as config + + +LOG = mmSolver.logger.get_logger() +AFTER_NEW_CALLBACK_ID = None +AFTER_OPEN_CALLBACK_ID = None + + +def _format_gigabytes(memory_bytes): + memory_gigabytes = 0.0 + if memory_bytes > 0: + memory_gigabytes = memory_bytes / const_utils.BYTES_TO_GIGABYTES + + text = '{:0,.2f} GB' + return text.format(memory_gigabytes) + + +def _format_percent(percent): + text = '{:0.1f}'.format(percent) + return text + '%' + + +def _ensure_plugin_loaded(): + """ + Loads all plug-ins required for the ImageCache. + + :raises: mmapi.SolverNotAvailable + """ + mmapi.load_plugin() + command_names = ('mmImageCache', 'mmMemoryGPU', 'mmMemorySystem') + for command_name in command_names: + if command_name not in dir(maya.cmds): + raise mmapi.SolverNotAvailable + return + + +def _dummy_initialize(*args): + """ + This dummy function is used for the callbacks below, but it + contains an extra argument. + """ + main() + + +def main(): + """ + Set Image Cache capacities from preferences. + """ + LOG.info('MM Solver Initialize Image Cache...') + + # Install callbacks to be called each time a new scene is + # opened. We will look for enabled scene override values for the + # cache and set it, otherwise we use the defaults. + global AFTER_NEW_CALLBACK_ID + global AFTER_OPEN_CALLBACK_ID + if AFTER_NEW_CALLBACK_ID is None: + AFTER_NEW_CALLBACK_ID = OpenMaya.MSceneMessage.addCallback( + OpenMaya.MSceneMessage.kAfterNew, _dummy_initialize + ) + if AFTER_OPEN_CALLBACK_ID is None: + AFTER_OPEN_CALLBACK_ID = OpenMaya.MSceneMessage.addCallback( + OpenMaya.MSceneMessage.kAfterOpen, _dummy_initialize + ) + + _ensure_plugin_loaded() + + capacity_data = config.resolve_capacity_data() + gpu_capacity_bytes = capacity_data.gpu_resolved_capacity.size_bytes + cpu_capacity_bytes = capacity_data.cpu_resolved_capacity.size_bytes + gpu_capacity_percent = capacity_data.gpu_resolved_capacity.percent + cpu_capacity_percent = capacity_data.cpu_resolved_capacity.percent + + # Set the image cache capacities. + gpu_capacty_gigabytes = _format_gigabytes(gpu_capacity_bytes) + cpu_capacty_gigabytes = _format_gigabytes(cpu_capacity_bytes) + gpu_capacity_percent = _format_percent(gpu_capacity_percent) + cpu_capacity_percent = _format_percent(cpu_capacity_percent) + + gpu_current_capacity_bytes = lib.get_gpu_cache_capacity_bytes() + cpu_current_capacity_bytes = lib.get_cpu_cache_capacity_bytes() + + if gpu_capacity_bytes != gpu_current_capacity_bytes: + LOG.info( + 'Image Cache - Set GPU Capacity: %s (%s)', + gpu_capacty_gigabytes, + gpu_capacity_percent, + ) + lib.set_gpu_cache_capacity_bytes(gpu_capacity_bytes) + + if cpu_capacity_bytes != cpu_current_capacity_bytes: + LOG.info( + 'Image Cache - Set CPU Capacity: %s (%s)', + cpu_capacty_gigabytes, + cpu_capacity_percent, + ) + lib.set_cpu_cache_capacity_bytes(cpu_capacity_bytes) + return diff --git a/python/mmSolver/tools/imagecache/lib.py b/python/mmSolver/tools/imagecache/lib.py new file mode 100644 index 000000000..70182fa4d --- /dev/null +++ b/python/mmSolver/tools/imagecache/lib.py @@ -0,0 +1,101 @@ +# Copyright (C) 2024 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Functions to control the image cache. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from mmSolver.tools.imagecache._lib.query_resources import ( + get_gpu_memory_total_bytes, + get_gpu_memory_used_bytes, + get_cpu_memory_total_bytes, + get_cpu_memory_used_bytes, +) +from mmSolver.tools.imagecache._lib.imagecache_cmd import ( + get_gpu_cache_item_count, + get_cpu_cache_item_count, + get_gpu_cache_group_names, + get_cpu_cache_group_names, + get_gpu_cache_slot_count, + get_cpu_cache_slot_count, + get_gpu_cache_group_item_count, + get_cpu_cache_group_item_count, + get_gpu_cache_group_item_names, + get_cpu_cache_group_item_names, + get_gpu_cache_used_bytes, + get_cpu_cache_used_bytes, + get_gpu_cache_capacity_bytes, + get_cpu_cache_capacity_bytes, + set_gpu_cache_capacity_bytes, + set_cpu_cache_capacity_bytes, +) +from mmSolver.tools.imagecache._lib.erase import ( + erase_gpu_group_items, + erase_cpu_group_items, + erase_gpu_groups_items, + erase_cpu_groups_items, + erase_gpu_image_items, + erase_cpu_image_items, + erase_all_images_on_image_plane_slots, + erase_images_in_active_image_plane_slot, + erase_images_in_unused_image_plane_slots, + erase_image_sequence, + erase_all_inactive_images, + erase_all_images, +) + +# Stop users from accessing the internal functions of this sub-module. +__all__ = [ + 'get_gpu_memory_total_bytes', + 'get_gpu_memory_used_bytes', + 'get_cpu_memory_total_bytes', + 'get_cpu_memory_used_bytes', + # + 'get_gpu_cache_item_count', + 'get_cpu_cache_item_count', + 'get_gpu_cache_group_names', + 'get_cpu_cache_group_names', + 'get_gpu_cache_slot_count', + 'get_cpu_cache_slot_count', + 'get_gpu_cache_group_item_count', + 'get_cpu_cache_group_item_count', + 'get_gpu_cache_group_item_names', + 'get_cpu_cache_group_item_names', + 'get_gpu_cache_used_bytes', + 'get_cpu_cache_used_bytes', + 'get_gpu_cache_capacity_bytes', + 'get_cpu_cache_capacity_bytes', + 'set_gpu_cache_capacity_bytes', + 'set_cpu_cache_capacity_bytes', + # + 'erase_gpu_group_items', + 'erase_cpu_group_items', + 'erase_gpu_groups_items', + 'erase_cpu_groups_items', + 'erase_gpu_image_items', + 'erase_cpu_image_items', + 'erase_all_images_on_image_plane_slots', + 'erase_images_in_active_image_plane_slot', + 'erase_images_in_unused_image_plane_slots', + 'erase_image_sequence', + 'erase_all_inactive_images', + 'erase_all_images', +] diff --git a/python/mmSolver/tools/imagecacheprefs/__init__.py b/python/mmSolver/tools/imagecacheprefs/__init__.py new file mode 100644 index 000000000..ac8160ee7 --- /dev/null +++ b/python/mmSolver/tools/imagecacheprefs/__init__.py @@ -0,0 +1,20 @@ +# Copyright (C) 2024 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Image Cache tool - Adjust the mmSolver Image Cache +""" diff --git a/python/mmSolver/tools/imagecacheprefs/constant.py b/python/mmSolver/tools/imagecacheprefs/constant.py new file mode 100644 index 000000000..e38753b88 --- /dev/null +++ b/python/mmSolver/tools/imagecacheprefs/constant.py @@ -0,0 +1,27 @@ +# Copyright (C) 2024 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Image Cache constants. +""" + +WINDOW_TITLE = 'Image Cache Preferences' + +CONFIG_FILE_NAME = "tools_imagecacheprefs.json" + +CONFIG_UPDATE_EVERY_N_SECONDS_KEY = 'data/update_every_n_seconds' +CONFIG_UPDATE_EVERY_N_SECONDS_DEFAULT_VALUE = 2 diff --git a/python/mmSolver/tools/imagecacheprefs/tool.py b/python/mmSolver/tools/imagecacheprefs/tool.py new file mode 100644 index 000000000..f4e86b127 --- /dev/null +++ b/python/mmSolver/tools/imagecacheprefs/tool.py @@ -0,0 +1,36 @@ +# Copyright (C) 2024 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Controls the mmSolver Image Cache Preferences. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import mmSolver.logger +import mmSolver.api as mmapi + +LOG = mmSolver.logger.get_logger() + + +def open_window(): + import mmSolver.tools.imagecacheprefs.ui.imagecacheprefs_window as window + + mmapi.load_plugin() + window.main() diff --git a/python/mmSolver/tools/imagecacheprefs/ui/__init__.py b/python/mmSolver/tools/imagecacheprefs/ui/__init__.py new file mode 100644 index 000000000..871b8b123 --- /dev/null +++ b/python/mmSolver/tools/imagecacheprefs/ui/__init__.py @@ -0,0 +1,20 @@ +# Copyright (C) 2024 David Cattermole +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Image Cache user interface. +""" diff --git a/python/mmSolver/tools/imagecacheprefs/ui/imagecacheprefs_layout.py b/python/mmSolver/tools/imagecacheprefs/ui/imagecacheprefs_layout.py new file mode 100644 index 000000000..f8e124902 --- /dev/null +++ b/python/mmSolver/tools/imagecacheprefs/ui/imagecacheprefs_layout.py @@ -0,0 +1,569 @@ +# Copyright (C) 2024 David Cattermole +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +The main component of the user interface for the image cache +window. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import os + +import mmSolver.ui.qtpyutils as qtpyutils + +qtpyutils.override_binding_order() + +import mmSolver.ui.Qt.QtWidgets as QtWidgets +import mmSolver.ui.Qt.QtCore as QtCore + +import mmSolver.logger +import mmSolver.utils.config as config_utils +import mmSolver.utils.constant as const_utils +import mmSolver.utils.math as math_utils +import mmSolver.utils.python_compat as pycompat +import mmSolver.tools.imagecache.config_file as config_file +import mmSolver.tools.imagecache.config_scene as config_scene +import mmSolver.tools.imagecache.config as config +import mmSolver.tools.imagecache.lib as lib +import mmSolver.tools.imagecacheprefs.constant as tool_const +import mmSolver.tools.imagecacheprefs.ui.ui_imagecacheprefs_layout as ui_imagecacheprefs_layout + +LOG = mmSolver.logger.get_logger() + + +def set_memory_total_label( + label, + size_bytes, +): + assert isinstance(label, QtWidgets.QLabel) + assert isinstance(size_bytes, pycompat.INT_TYPES) + gigabytes = 0.0 + if size_bytes > 0: + gigabytes = size_bytes / const_utils.BYTES_TO_GIGABYTES + + text = '{:0,.2f} GB'.format(gigabytes) + label.setText(text) + + +def set_memory_used_label(label, used_size_bytes, total_size_bytes): + assert isinstance(label, QtWidgets.QLabel) + assert isinstance(used_size_bytes, pycompat.INT_TYPES) + assert isinstance(total_size_bytes, pycompat.INT_TYPES) + used_gigabytes = 0.0 + if used_size_bytes > 0: + used_gigabytes = used_size_bytes / const_utils.BYTES_TO_GIGABYTES + + used_percent = 0.0 + if used_size_bytes > 0 and total_size_bytes > 0: + used_percent = (used_size_bytes / total_size_bytes) * 100.0 + + text = '{:0,.2f} GB ({:3.1f}%)'.format(used_gigabytes, used_percent) + label.setText(text) + + +def set_count_label( + label, + value, +): + assert isinstance(label, QtWidgets.QLabel) + assert isinstance(value, int) + text = '{:,}'.format(value) + label.setText(text) + + +def set_capacity_size_label( + label, + size_bytes, +): + assert isinstance(label, QtWidgets.QLabel) + assert isinstance(size_bytes, pycompat.INT_TYPES) + gigabytes = 0.0 + if size_bytes > 0: + gigabytes = size_bytes / const_utils.BYTES_TO_GIGABYTES + + text = '{:0,.2f} GB'.format(gigabytes) + label.setText(text) + + +def set_used_size_label(label, used_size_bytes, capacity_size_bytes): + assert isinstance(label, QtWidgets.QLabel) + assert isinstance(used_size_bytes, pycompat.INT_TYPES) + assert isinstance(capacity_size_bytes, pycompat.INT_TYPES) + used_gigabytes = 0.0 + if used_size_bytes > 0: + used_gigabytes = used_size_bytes / const_utils.BYTES_TO_GIGABYTES + + used_percent = 0.0 + if used_size_bytes > 0 and capacity_size_bytes > 0: + used_percent = (used_size_bytes / capacity_size_bytes) * 100.0 + + text = '{:0,.2f} GB ({:3.1f}%)'.format(used_gigabytes, used_percent) + label.setText(text) + + +def set_percent_slider( + slider, + percent, +): + assert isinstance(slider, QtWidgets.QSlider) + assert isinstance(percent, float) + + old_min = 0.0 + old_max = 100.0 + new_min = float(slider.minimum()) + new_max = float(slider.maximum()) + slider_value = math_utils.remap(old_min, old_max, new_min, new_max, percent) + slider.setValue(int(slider_value)) + + +def set_value_double_spin_box( + doubleSpinBox, + value, +): + assert isinstance(doubleSpinBox, QtWidgets.QDoubleSpinBox) + assert isinstance(value, float) + doubleSpinBox.setValue(value) + + +def set_capacity_widgets( + label, slider, double_spin_box, capacity_bytes, capacity_percent +): + assert isinstance(capacity_bytes, pycompat.INT_TYPES) + assert isinstance(capacity_percent, float) + set_capacity_size_label(label, capacity_bytes) + set_percent_slider(slider, capacity_percent) + set_value_double_spin_box(double_spin_box, capacity_percent) + return + + +def get_tool_config(): + """Get the Image Cache config object or None.""" + file_name = tool_const.CONFIG_FILE_NAME + config_path = config_utils.get_home_dir_path(file_name) + config = config_utils.Config(config_path) + config.set_autoread(False) + config.set_autowrite(False) + if os.path.isfile(config.file_path): + config.read() + return config + + +def _get_tool_config_value(config, key, fallback): + """ + Query the attribute from the user's home directory. If the user's + option is saved, use that value instead. + """ + value = fallback + if config is not None: + value = config.get_value(key, fallback) + return value + + +def get_update_every_n_seconds(config): + default_value = tool_const.CONFIG_UPDATE_EVERY_N_SECONDS_DEFAULT_VALUE + assert isinstance(default_value, int) + name = tool_const.CONFIG_UPDATE_EVERY_N_SECONDS_KEY + seconds = _get_tool_config_value(config, name, default_value) + assert isinstance(seconds, int) + return seconds + + +class ImageCachePrefsLayout(QtWidgets.QWidget, ui_imagecacheprefs_layout.Ui_Form): + def __init__(self, parent=None, *args, **kwargs): + super(ImageCachePrefsLayout, self).__init__(*args, **kwargs) + self.setupUi(self) + + self._config = get_tool_config() + + # Update timer. + self.update_timer = QtCore.QTimer() + milliseconds = int( + tool_const.CONFIG_UPDATE_EVERY_N_SECONDS_DEFAULT_VALUE * 1000 + ) + self.update_timer.setInterval(milliseconds) + self.update_timer.timeout.connect(self.update_resource_values) + self.update_timer.start() + + self._connect_connections() + + # Populate the UI with data + self.reset_options() + + def _connect_connections(self): + self.updateEvery_spinBox.valueChanged[int].connect(self._update_every_changed) + + self.gpuCacheDefaultCapacity_doubleSpinBox.valueChanged[float].connect( + self._gpu_default_capacity_spin_box_changed + ) + self.cpuCacheDefaultCapacity_doubleSpinBox.valueChanged[float].connect( + self._cpu_default_capacity_spin_box_changed + ) + self.gpuCacheSceneCapacity_doubleSpinBox.valueChanged[float].connect( + self._gpu_scene_capacity_spin_box_changed + ) + self.cpuCacheSceneCapacity_doubleSpinBox.valueChanged[float].connect( + self._cpu_scene_capacity_spin_box_changed + ) + + self.gpuCacheDefaultCapacity_horizontalSlider.valueChanged[int].connect( + self._gpu_default_capacity_slider_changed + ) + self.cpuCacheDefaultCapacity_horizontalSlider.valueChanged[int].connect( + self._cpu_default_capacity_slider_changed + ) + self.gpuCacheSceneCapacity_horizontalSlider.valueChanged[int].connect( + self._gpu_scene_capacity_slider_changed + ) + self.cpuCacheSceneCapacity_horizontalSlider.valueChanged[int].connect( + self._cpu_scene_capacity_slider_changed + ) + + def _gpu_default_capacity_spin_box_changed(self, percent): + assert isinstance(percent, float) + LOG.debug('_gpu_default_capacity_spin_box_changed: percent=%r', percent) + + label = self.gpuCacheDefaultCapacityValue_label + slider = self.gpuCacheDefaultCapacity_horizontalSlider + + gpu_memory_total = lib.get_gpu_memory_total_bytes() + ratio = percent / 100.0 + size_bytes = pycompat.LONG_TYPE(ratio * gpu_memory_total) + LOG.debug('_gpu_default_capacity_spin_box_changed: ratio=%r', ratio) + LOG.debug('_gpu_default_capacity_spin_box_changed: size_bytes=%r', size_bytes) + + set_capacity_size_label(label, size_bytes) + + block = slider.blockSignals(True) + set_percent_slider(slider, percent) + slider.blockSignals(block) + + def _cpu_default_capacity_spin_box_changed(self, percent): + assert isinstance(percent, float) + LOG.debug('_cpu_default_capacity_spin_box_changed: percent=%r', percent) + + label = self.cpuCacheDefaultCapacityValue_label + slider = self.cpuCacheDefaultCapacity_horizontalSlider + + cpu_memory_total = lib.get_cpu_memory_total_bytes() + ratio = percent / 100.0 + size_bytes = pycompat.LONG_TYPE(ratio * cpu_memory_total) + LOG.debug('_cpu_default_capacity_spin_box_changed: ratio=%r', ratio) + LOG.debug('_cpu_default_capacity_spin_box_changed: size_bytes=%r', size_bytes) + + set_capacity_size_label(label, size_bytes) + + block = slider.blockSignals(True) + set_percent_slider(slider, percent) + slider.blockSignals(block) + + def _gpu_scene_capacity_spin_box_changed(self, percent): + assert isinstance(percent, float) + LOG.debug('_gpu_scene_capacity_spin_box_changed: percent=%r', percent) + + label = self.gpuCacheSceneCapacityValue_label + slider = self.gpuCacheSceneCapacity_horizontalSlider + + gpu_memory_total = lib.get_gpu_memory_total_bytes() + ratio = percent / 100.0 + size_bytes = pycompat.LONG_TYPE(ratio * gpu_memory_total) + LOG.debug('_gpu_scene_capacity_spin_box_changed: ratio=%r', ratio) + LOG.debug('_gpu_scene_capacity_spin_box_changed: size_bytes=%r', size_bytes) + + set_capacity_size_label(label, size_bytes) + + block = slider.blockSignals(True) + set_percent_slider(slider, percent) + slider.blockSignals(block) + + def _cpu_scene_capacity_spin_box_changed(self, percent): + assert isinstance(percent, float) + LOG.debug('_cpu_scene_capacity_spin_box_changed: percent=%r', percent) + + label = self.cpuCacheSceneCapacityValue_label + slider = self.cpuCacheSceneCapacity_horizontalSlider + + cpu_memory_total = lib.get_cpu_memory_total_bytes() + ratio = percent / 100.0 + size_bytes = pycompat.LONG_TYPE(ratio * cpu_memory_total) + LOG.debug('_cpu_scene_capacity_spin_box_changed: ratio=%r', ratio) + LOG.debug('_cpu_scene_capacity_spin_box_changed: size_bytes=%r', size_bytes) + + set_capacity_size_label(label, size_bytes) + + block = slider.blockSignals(True) + set_percent_slider(slider, percent) + slider.blockSignals(block) + + def _gpu_default_capacity_slider_changed(self, value): + assert isinstance(value, int) + LOG.debug('_gpu_default_capacity_slider_changed: value=%r', value) + + label = self.gpuCacheDefaultCapacityValue_label + spin_box = self.gpuCacheDefaultCapacity_doubleSpinBox + slider = self.gpuCacheDefaultCapacity_horizontalSlider + + old_min = float(slider.minimum()) + old_max = float(slider.maximum()) + new_min = 0.0 + new_max = 100.0 + percent = math_utils.remap(old_min, old_max, new_min, new_max, float(value)) + ratio = percent / new_max + LOG.debug('_gpu_default_capacity_slider_changed: percent=%r', percent) + + gpu_memory_total = lib.get_gpu_memory_total_bytes() + size_bytes = pycompat.LONG_TYPE(ratio * gpu_memory_total) + + set_capacity_size_label(label, size_bytes) + + block = spin_box.blockSignals(True) + set_value_double_spin_box(spin_box, percent) + spin_box.blockSignals(block) + + def _cpu_default_capacity_slider_changed(self, value): + assert isinstance(value, int) + LOG.debug('_cpu_default_capacity_slider_changed: value=%r', value) + + label = self.cpuCacheDefaultCapacityValue_label + spin_box = self.cpuCacheDefaultCapacity_doubleSpinBox + slider = self.cpuCacheDefaultCapacity_horizontalSlider + + old_min = float(slider.minimum()) + old_max = float(slider.maximum()) + new_min = 0.0 + new_max = 100.0 + percent = math_utils.remap(old_min, old_max, new_min, new_max, float(value)) + ratio = percent / new_max + LOG.debug('_cpu_default_capacity_slider_changed: percent=%r', percent) + + cpu_memory_total = lib.get_cpu_memory_total_bytes() + size_bytes = pycompat.LONG_TYPE(ratio * cpu_memory_total) + + set_capacity_size_label(label, size_bytes) + + block = spin_box.blockSignals(True) + set_value_double_spin_box(spin_box, percent) + spin_box.blockSignals(block) + + def _gpu_scene_capacity_slider_changed(self, value): + assert isinstance(value, int) + LOG.debug('_gpu_scene_capacity_slider_changed: value=%r', value) + + label = self.gpuCacheSceneCapacityValue_label + spin_box = self.gpuCacheSceneCapacity_doubleSpinBox + slider = self.gpuCacheSceneCapacity_horizontalSlider + + old_min = float(slider.minimum()) + old_max = float(slider.maximum()) + new_min = 0.0 + new_max = 100.0 + percent = math_utils.remap(old_min, old_max, new_min, new_max, float(value)) + ratio = percent / new_max + LOG.debug('_gpu_scene_capacity_slider_changed: percent=%r', percent) + + gpu_memory_total = lib.get_gpu_memory_total_bytes() + size_bytes = pycompat.LONG_TYPE(ratio * gpu_memory_total) + + set_capacity_size_label(label, size_bytes) + + block = spin_box.blockSignals(True) + set_value_double_spin_box(spin_box, percent) + spin_box.blockSignals(block) + + def _cpu_scene_capacity_slider_changed(self, value): + assert isinstance(value, int) + LOG.debug('_cpu_scene_capacity_slider_changed: value=%r', value) + + label = self.cpuCacheSceneCapacityValue_label + spin_box = self.cpuCacheSceneCapacity_doubleSpinBox + slider = self.cpuCacheSceneCapacity_horizontalSlider + + old_min = float(slider.minimum()) + old_max = float(slider.maximum()) + new_min = 0.0 + new_max = 100.0 + percent = math_utils.remap(old_min, old_max, new_min, new_max, float(value)) + ratio = percent / new_max + LOG.debug('_cpu_scene_capacity_slider_changed: percent=%r', percent) + + cpu_memory_total = lib.get_cpu_memory_total_bytes() + size_bytes = pycompat.LONG_TYPE(ratio * cpu_memory_total) + + set_capacity_size_label(label, size_bytes) + + block = spin_box.blockSignals(True) + set_value_double_spin_box(spin_box, percent) + spin_box.blockSignals(block) + + def _update_every_changed(self, value): + assert isinstance(value, int) + milliseconds = int(value * 1000) + self.update_timer.setInterval(milliseconds) + + if self._config is not None: + self._config.set_value(tool_const.CONFIG_UPDATE_EVERY_N_SECONDS_KEY, value) + self._config.write() + return + + def _get_capacity_bytes( + self, scene_override, default_spin_box, scene_spin_box, memory_total + ): + percent = default_spin_box.value() + if scene_override is True: + percent = scene_spin_box.value() + ratio = percent / 100.0 + + size_bytes = pycompat.LONG_TYPE(ratio * memory_total) + return size_bytes + + def get_gpu_capacity_bytes(self): + scene_override = self.imageCacheSceneSettings_groupBox.isChecked() + default_spin_box = self.gpuCacheDefaultCapacity_doubleSpinBox + scene_spin_box = self.gpuCacheSceneCapacity_doubleSpinBox + gpu_memory_total = lib.get_gpu_memory_total_bytes() + return self._get_capacity_bytes( + scene_override, default_spin_box, scene_spin_box, gpu_memory_total + ) + + def get_cpu_capacity_bytes(self): + scene_override = self.imageCacheSceneSettings_groupBox.isChecked() + default_spin_box = self.cpuCacheDefaultCapacity_doubleSpinBox + scene_spin_box = self.cpuCacheSceneCapacity_doubleSpinBox + cpu_memory_total = lib.get_cpu_memory_total_bytes() + return self._get_capacity_bytes( + scene_override, default_spin_box, scene_spin_box, cpu_memory_total + ) + + def update_resource_values(self): + gpu_cache_item_count = lib.get_gpu_cache_item_count() + cpu_cache_item_count = lib.get_cpu_cache_item_count() + gpu_cache_slot_count = lib.get_gpu_cache_slot_count() + cpu_cache_slot_count = lib.get_cpu_cache_slot_count() + gpu_cache_used = lib.get_gpu_cache_used_bytes() + cpu_cache_used = lib.get_cpu_cache_used_bytes() + gpu_cache_capacity = lib.get_gpu_cache_capacity_bytes() + cpu_cache_capacity = lib.get_cpu_cache_capacity_bytes() + gpu_memory_total = lib.get_gpu_memory_total_bytes() + cpu_memory_total = lib.get_cpu_memory_total_bytes() + gpu_memory_used = lib.get_gpu_memory_used_bytes() + cpu_memory_used = lib.get_cpu_memory_used_bytes() + + set_memory_total_label(self.gpuMemoryTotalValue_label, gpu_memory_total) + set_memory_total_label(self.cpuMemoryTotalValue_label, cpu_memory_total) + + set_memory_used_label( + self.gpuMemoryUsedValue_label, gpu_memory_used, gpu_memory_total + ) + set_memory_used_label( + self.cpuMemoryUsedValue_label, cpu_memory_used, cpu_memory_total + ) + + set_count_label(self.gpuCacheItemCountValue_label, gpu_cache_item_count) + set_count_label(self.cpuCacheItemCountValue_label, cpu_cache_item_count) + + set_count_label(self.gpuCacheSlotCountValue_label, gpu_cache_slot_count) + set_count_label(self.cpuCacheSlotCountValue_label, cpu_cache_slot_count) + + set_capacity_size_label(self.gpuCacheCapacityValue_label, gpu_cache_capacity) + set_capacity_size_label(self.cpuCacheCapacityValue_label, cpu_cache_capacity) + + set_used_size_label( + self.cpuCacheUsedValue_label, cpu_cache_used, cpu_cache_capacity + ) + set_used_size_label( + self.gpuCacheUsedValue_label, gpu_cache_used, gpu_cache_capacity + ) + return + + def reset_options(self): + """ + Reset the UI the values. + + If scene override is enabled, get the scene option values, + otherwise use the default values saved in the config file. + """ + self.update_resource_values() + + update_seconds = get_update_every_n_seconds(self._config) + self.updateEvery_spinBox.setValue(update_seconds) + + capacity_data = config.resolve_capacity_data() + default_gpu_capacity = capacity_data.gpu_default_capacity + default_cpu_capacity = capacity_data.cpu_default_capacity + scene_override = capacity_data.scene_override + scene_gpu_capacity = capacity_data.gpu_scene_capacity + scene_cpu_capacity = capacity_data.cpu_scene_capacity + + # Set default capacities. + set_capacity_widgets( + self.gpuCacheDefaultCapacityValue_label, + self.gpuCacheDefaultCapacity_horizontalSlider, + self.gpuCacheDefaultCapacity_doubleSpinBox, + default_gpu_capacity.size_bytes, + default_gpu_capacity.percent, + ) + set_capacity_widgets( + self.cpuCacheDefaultCapacityValue_label, + self.cpuCacheDefaultCapacity_horizontalSlider, + self.cpuCacheDefaultCapacity_doubleSpinBox, + default_cpu_capacity.size_bytes, + default_cpu_capacity.percent, + ) + + # Scene Override? + self.imageCacheSceneSettings_groupBox.setChecked(scene_override is True) + + # Set scene capacities. + set_capacity_widgets( + self.gpuCacheSceneCapacityValue_label, + self.gpuCacheSceneCapacity_horizontalSlider, + self.gpuCacheSceneCapacity_doubleSpinBox, + scene_gpu_capacity.size_bytes, + scene_gpu_capacity.percent, + ) + set_capacity_widgets( + self.cpuCacheSceneCapacityValue_label, + self.cpuCacheSceneCapacity_horizontalSlider, + self.cpuCacheSceneCapacity_doubleSpinBox, + scene_cpu_capacity.size_bytes, + scene_cpu_capacity.percent, + ) + return + + def save_options(self): + # Save config values in config file. + update_seconds = self.updateEvery_spinBox.value() + self._config.set_value( + tool_const.CONFIG_UPDATE_EVERY_N_SECONDS_KEY, update_seconds + ) + + gpu_percent_default = self.gpuCacheDefaultCapacity_doubleSpinBox.value() + cpu_percent_default = self.cpuCacheDefaultCapacity_doubleSpinBox.value() + scene_override = self.imageCacheSceneSettings_groupBox.isChecked() + gpu_percent_scene = self.gpuCacheSceneCapacity_doubleSpinBox.value() + cpu_percent_scene = self.cpuCacheSceneCapacity_doubleSpinBox.value() + + config.save_capacity_values( + gpu_percent_default, + cpu_percent_default, + scene_override, + gpu_percent_scene, + cpu_percent_scene, + ) + return diff --git a/python/mmSolver/tools/imagecacheprefs/ui/imagecacheprefs_layout.ui b/python/mmSolver/tools/imagecacheprefs/ui/imagecacheprefs_layout.ui new file mode 100644 index 000000000..eeeb19c80 --- /dev/null +++ b/python/mmSolver/tools/imagecacheprefs/ui/imagecacheprefs_layout.ui @@ -0,0 +1,734 @@ + + + Form + + + + 0 + 0 + 547 + 537 + + + + Form + + + + + + + + Update Every + + + + + + + seconds + + + 1 + + + 300 + + + 2 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + GPU Resources + + + + + + + + + + Memory Total: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">2.0 GB</span></p></body></html> + + + + + + + + + + + Memory Used: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">0.125 GB (6.3%)</span></p></body></html> + + + + + + + + + + + + + + CPU Resources + + + + + + + + + + Memory Total: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">128.0 GB</span></p></body></html> + + + + + + + + + + + Memory Used: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">22.5 GB (17.5%)</span></p></body></html> + + + + + + + + + + + + + + + + + + GPU Image Cache Overview + + + + + + + + Images: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">42</span></p></body></html> + + + + + + + + + + + Image Slots: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">2</span></p></body></html> + + + + + + + + + + + Capacity: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">2.0 GB</span></p></body></html> + + + + + + + + + + + Used: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">0.125 GB (6.3%)</span></p></body></html> + + + + + + + + + + + + CPU Image Cache Overview + + + + + + + + Images: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">42</span></p></body></html> + + + + + + + + + + + Image Slots: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">2</span></p></body></html> + + + + + + + + + + + Capacity: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">128.0 GB</span></p></body></html> + + + + + + + + + + + Used: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">22.5 GB (17.5%)</span></p></body></html> + + + + + + + + + + + + + + Image Cache Defaults + + + + + + + + GPU Capacity: + + + + + + + <html><head/><body><p><span style=" font-weight:600;">1 GB</span></p></body></html> + + + + + + + 1000 + + + 100 + + + 500 + + + Qt::Horizontal + + + + + + + % + + + 1 + + + 100.000000000000000 + + + 5.000000000000000 + + + 50.000000000000000 + + + + + + + + + + + CPU Capacity: + + + + + + + <html><head/><body><p><span style=" font-weight:600;">5 GB</span></p></body></html> + + + + + + + 1000 + + + 100 + + + 500 + + + Qt::Horizontal + + + + + + + % + + + 1 + + + 100.000000000000000 + + + 5.000000000000000 + + + 50.000000000000000 + + + + + + + + + + + + Image Cache Scene Override + + + true + + + false + + + + + + + + GPU Capacity: + + + + + + + <html><head/><body><p><span style=" font-weight:600;">1 GB</span></p></body></html> + + + + + + + 1000 + + + 100 + + + 500 + + + Qt::Horizontal + + + + + + + % + + + 1 + + + 100.000000000000000 + + + 5.000000000000000 + + + 50.000000000000000 + + + + + + + + + + + CPU Capacity: + + + + + + + <html><head/><body><p><span style=" font-weight:600;">5 GB</span></p></body></html> + + + + + + + 1000 + + + 100 + + + 500 + + + Qt::Horizontal + + + + + + + % + + + 1 + + + 100.000000000000000 + + + 5.000000000000000 + + + 50.000000000000000 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/python/mmSolver/tools/imagecacheprefs/ui/imagecacheprefs_window.py b/python/mmSolver/tools/imagecacheprefs/ui/imagecacheprefs_window.py new file mode 100644 index 000000000..770c095b3 --- /dev/null +++ b/python/mmSolver/tools/imagecacheprefs/ui/imagecacheprefs_window.py @@ -0,0 +1,134 @@ +# Copyright (C) 2024 David Cattermole +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Window for the Image Cache tool. + +Usage:: + + import mmSolver.tools.imagecacheprefs.ui.imagecacheprefs_window as window + window.main() + +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import mmSolver.ui.qtpyutils as qtpyutils + +qtpyutils.override_binding_order() + +import mmSolver.ui.Qt.QtCore as QtCore +import mmSolver.ui.Qt.QtWidgets as QtWidgets + +import mmSolver.logger +import mmSolver.ui.uiutils as uiutils +import mmSolver.ui.helputils as helputils +import mmSolver.ui.commonmenus as commonmenus +import mmSolver.tools.imagecache.lib as lib +import mmSolver.tools.imagecacheprefs.constant as const +import mmSolver.tools.imagecacheprefs.ui.imagecacheprefs_layout as imagecacheprefs_layout + + +LOG = mmSolver.logger.get_logger() +baseModule, BaseWindow = uiutils.getBaseWindow() + + +def _open_help(): + src = helputils.get_help_source() + page = 'tools_generaltools.html#image-cache-preferences' + helputils.open_help_in_browser(page=page, help_source=src) + return + + +class ImageCachePrefsWindow(BaseWindow): + name = 'ImageCachePrefsWindow' + + def __init__(self, parent=None, name=None): + super(ImageCachePrefsWindow, self).__init__(parent, name=name) + self.setupUi(self) + self.addSubForm(imagecacheprefs_layout.ImageCachePrefsLayout) + + self.setWindowTitle(const.WINDOW_TITLE) + self.setWindowFlags(QtCore.Qt.Tool) + + # Standard Buttons + self.baseHideStandardButtons() + self.applyBtn.show() + self.closeBtn.show() + self.applyBtn.setText('Apply Capacity') + + self.applyBtn.clicked.connect(self._apply) + + # Hide irrelevant stuff + self.baseHideProgressBar() + + self.add_menus(self.menubar) + self.menubar.show() + + def add_menus(self, menubar): + edit_menu = QtWidgets.QMenu('Edit', menubar) + commonmenus.create_edit_menu_items( + edit_menu, reset_settings_func=self.reset_options + ) + menubar.addMenu(edit_menu) + + help_menu = QtWidgets.QMenu('Help', menubar) + commonmenus.create_help_menu_items(help_menu, tool_help_func=_open_help) + menubar.addMenu(help_menu) + + def _apply(self): + form = self.getSubForm() + gpu_capacity_bytes = form.get_gpu_capacity_bytes() + cpu_capacity_bytes = form.get_cpu_capacity_bytes() + + lib.set_gpu_cache_capacity_bytes(gpu_capacity_bytes) + lib.set_cpu_cache_capacity_bytes(cpu_capacity_bytes) + + form.update_resource_values() + form.save_options() + return + + def reset_options(self): + form = self.getSubForm() + form.reset_options() + return + + +def main(show=True, auto_raise=True, delete=False): + """ + Open the Image Cache UI window. + + :param show: Show the UI. + :type show: bool + + :param auto_raise: If the UI is open, raise it to the front? + :type auto_raise: bool + + :param delete: Delete the existing UI and rebuild it? Helpful when + developing the UI in Maya script editor. + :type delete: bool + + :returns: A new image cache window, or None if the window cannot be + opened. + :rtype: ImageCachePrefsWindow or None. + """ + win = ImageCachePrefsWindow.open_window( + show=show, auto_raise=auto_raise, delete=delete + ) + return win diff --git a/python/mmSolver/tools/loadmarker/ui/loadmarker_window.py b/python/mmSolver/tools/loadmarker/ui/loadmarker_window.py index a2596e707..e0641841b 100644 --- a/python/mmSolver/tools/loadmarker/ui/loadmarker_window.py +++ b/python/mmSolver/tools/loadmarker/ui/loadmarker_window.py @@ -259,9 +259,9 @@ def main(show=True, auto_raise=True, delete=False): developing the UI in Maya script editor. :type delete: bool - :returns: A new solver window, or None if the window cannot be + :returns: A new loadMarker window, or None if the window cannot be opened. - :rtype: SolverWindow or None. + :rtype: LoadMarkerWindow or None. """ win = LoadMarkerWindow.open_window(show=show, auto_raise=auto_raise, delete=delete) return win diff --git a/python/mmSolver/tools/meshfrompoints/__init__.py b/python/mmSolver/tools/meshfrompoints/__init__.py new file mode 100644 index 000000000..541b768c3 --- /dev/null +++ b/python/mmSolver/tools/meshfrompoints/__init__.py @@ -0,0 +1,20 @@ +# Copyright (C) 2024 Patcha Saheb Binginapalli. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Mesh From Points tool. +""" diff --git a/python/mmSolver/tools/meshfrompoints/constant.py b/python/mmSolver/tools/meshfrompoints/constant.py new file mode 100644 index 000000000..c200ebe15 --- /dev/null +++ b/python/mmSolver/tools/meshfrompoints/constant.py @@ -0,0 +1,39 @@ +# Copyright (C) 2024 Patcha Saheb Binginapalli. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# + +WINDOW_TITLE = 'Mesh From Points' + +# We must have at least 3 points to make a mesh. +MINIMUM_NUMBER_OF_POINTS = 3 + +# The different types of meshes that can be created. +MESH_TYPE_FULL_MESH_VALUE = 'full_mesh' +MESH_TYPE_BORDER_MESH_VALUE = 'border_mesh' +MESH_TYPE_BORDER_EDGE_STRIP_MESH_VALUE = 'border_edge_strip_mesh' +MESH_TYPE_VALUES = [ + MESH_TYPE_FULL_MESH_VALUE, + MESH_TYPE_BORDER_MESH_VALUE, + MESH_TYPE_BORDER_EDGE_STRIP_MESH_VALUE, +] + +# Default values. +DEFAULT_MESH_NAME = 'meshFromPoints' +DEFAULT_STRIP_WIDTH = 1.0 + +# Config value keys. +CONFIG_STRIP_WIDTH_KEY = 'mmSolver_meshfrompoints_stripWidth' diff --git a/python/mmSolver/tools/meshfrompoints/delaunator.py b/python/mmSolver/tools/meshfrompoints/delaunator.py new file mode 100644 index 000000000..71492d7b0 --- /dev/null +++ b/python/mmSolver/tools/meshfrompoints/delaunator.py @@ -0,0 +1,561 @@ +# Delaunator-Python - Fast Delaunay triangulation of 2D points implemented in Python. +# Copyright (C) 2020 Hakan Seven (https://github.com/HakanSeven12 hakanseven12@gmail.com) +# +# Delaunator-Python is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# Delaunator-Python is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this Delaunator-Python; If not, see . +# +# Code from https://github.com/HakanSeven12/Delaunator-Python/blob/master/Delaunator.py +# + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import math + +import mmSolver.utils.python_compat as pycompat + + +EPSILON = math.pow(2, -52) +EDGE_STACK = [None] * 512 + + +class Delaunator(): + + def __init__(self, points): + n = len(points) + + if (len(points) < 3): + raise ValueError("Need at least 3 points") + coords = [None] * n * 2 + + for i in range(0, n): + p = points[i] + coords[2 * i] = (p[0]) + coords[2 * i + 1] = (p[1]) + triangles = self.constructor(coords) + + def constructor(self, coords): + n = len(coords) >> 1 + + self.coords = coords + + # arrays that will store the triangulation graph + maxTriangles = max(2 * n - 5, 0) + self._triangles = [None] * maxTriangles * 3 + self._halfedges = [None] * maxTriangles * 3 + + # temporary arrays for tracking the edges of the advancing convex hull + self.hashSize = pycompat.LONG_TYPE(math.ceil(math.sqrt(n))) + self.hullPrev = [None] * n # edge to prev edge + self.hullNext = [None] * n # edge to next edge + self.hullTri = [None] * n # edge to adjacent triangle + self.hullHash = [-1] * self.hashSize # angular edge hash + + # temporary arrays for sorting points + self._ids = [None] * n + self._dists = [None] * n + triangles = self.update(coords) + + return triangles + + def update(self, coords): + n = len(coords) >> 1 + + # populate an array of point indices; calculate input data bbox + minX = float('inf') + minY = float('inf') + maxX = float('-inf') + maxY = float('-inf') + + for i in range(0, n): + x = coords[2 * i] + y = coords[2 * i + 1] + if (x < minX): minX = x + if (y < minY): minY = y + if (x > maxX): maxX = x + if (y > maxY): maxY = y + self._ids[i] = i + + cx = (minX + maxX) / 2 + cy = (minY + maxY) / 2 + + minDist = float('inf') + i0 = 0 + i1 = 0 + i2 = 0 + + # pick a seed point close to the center + for i in range(0, n): + d = dist(cx, cy, coords[2 * i], coords[2 * i + 1]) + + if (d < minDist): + i0 = i + minDist = d + + i0x = coords[2 * i0] + i0y = coords[2 * i0 + 1] + minDist = float('inf') + + # find the point closest to the seed + for i in range(0, n): + if (i == i0): continue + d = dist(i0x, i0y, coords[2 * i], coords[2 * i + 1]) + + if (d < minDist and d > 0): + i1 = i + minDist = d + + i1x = coords[2 * i1] + i1y = coords[2 * i1 + 1] + + minRadius = float('inf') + + # find the third point which forms the smallest circumcircle with the first two + for i in range(0, n): + if (i == i0 or i == i1): continue + r = circumradius(i0x, i0y, i1x, i1y, coords[2 * i], + coords[2 * i + 1]) + + if (r < minRadius): + i2 = i + minRadius = r + + i2x = coords[2 * i2] + i2y = coords[2 * i2 + 1] + + if (minRadius == float('inf')): + # order collinear points by dx (or dy if all x are identical) + # and return the list as a hull + for i in range(0, n): + self._dists[i] = (coords[2 * i] - coords[0]) or ( + coords[2 * i + 1] - coords[1]) + + quicksort(self._ids, self._dists, 0, n - 1) + hull = [None] * n + j = 0 + d0 = float('-inf') + + for i in range(0, n): + id = self._ids[i] + + if (self._dists[id] > d0): + hull[j] = id + j += 1 + d0 = self._dists[id] + + self.hull = hull[0:j] + self.triangles = [] + self.halfedges = [] + + # swap the order of the seed points for counter-clockwise orientation + if (orient(i0x, i0y, i1x, i1y, i2x, i2y)): + i = i1 + x = i1x + y = i1y + i1 = i2 + i1x = i2x + i1y = i2y + i2 = i + i2x = x + i2y = y + + center = circumcenter(i0x, i0y, i1x, i1y, i2x, i2y) + self._cx = center[0] + self._cy = center[1] + + for i in range(0, n): + self._dists[i] = dist(coords[2 * i], coords[2 * i + 1], center[0], + center[1]) + + # sort the points by distance from the seed triangle circumcenter + quicksort(self._ids, self._dists, 0, n - 1) + + # set up the seed triangle as the starting hull + self._hullStart = i0 + hullSize = 3 + + self.hullNext[i0] = self.hullPrev[i2] = i1 + self.hullNext[i1] = self.hullPrev[i0] = i2 + self.hullNext[i2] = self.hullPrev[i1] = i0 + + self.hullTri[i0] = 0 + self.hullTri[i1] = 1 + self.hullTri[i2] = 2 + + self.hullHash[self._hashKey(i0x, i0y)] = i0 + self.hullHash[self._hashKey(i1x, i1y)] = i1 + self.hullHash[self._hashKey(i2x, i2y)] = i2 + + self.trianglesLen = 0 + self._addTriangle(i0, i1, i2, -1, -1, -1) + + xp = 0 + yp = 0 + + for k in range(0, len(self._ids)): + i = self._ids[k] + x = coords[2 * i] + y = coords[2 * i + 1] + + # skip near-duplicate points + if (k > 0 and abs(x - xp) <= EPSILON and abs( + y - yp) <= EPSILON): continue + + xp = x + yp = y + + # skip seed triangle points + if (i == i0 or i == i1 or i == i2): continue + + # find a visible edge on the convex hull using edge hash + start = 0 + key = self._hashKey(x, y) + + for j in range(0, self.hashSize): + start = self.hullHash[(key + j) % self.hashSize] + if (start != -1 and start != self.hullNext[start]): break + + start = self.hullPrev[start] + e = start + + while True: + q = self.hullNext[e] + if orient(x, y, coords[2 * e], coords[2 * e + 1], coords[2 * q], + coords[2 * q + 1]): break + e = q + + if (e == start): + e = -1 + break + + if (e == -1): continue # likely a near-duplicate point; skip it + + # add the first triangle from the point + t = self._addTriangle(e, i, self.hullNext[e], -1, -1, + self.hullTri[e]) + + # recursively flip triangles from the point until they satisfy the Delaunay condition + self.hullTri[i] = self._legalize(t + 2, coords) + self.hullTri[e] = t # keep track of boundary triangles on the hull + hullSize += 1 + + # walk forward through the hull, adding more triangles and flipping recursively + n = self.hullNext[e] + + while True: + q = self.hullNext[n] + if not ( + orient(x, y, coords[2 * n], coords[2 * n + 1], + coords[2 * q], + coords[2 * q + 1])): break + t = self._addTriangle(n, i, q, self.hullTri[i], -1, + self.hullTri[n]) + self.hullTri[i] = self._legalize(t + 2, coords) + self.hullNext[n] = n # mark as removed + hullSize -= 1 + n = q + + # walk backward from the other side, adding more triangles and flipping + if (e == start): + while True: + q = self.hullPrev[e] + if not (orient(x, y, coords[2 * q], coords[2 * q + 1], + coords[2 * e], coords[2 * e + 1])): break + t = self._addTriangle(q, i, e, -1, self.hullTri[e], + self.hullTri[q]) + self._legalize(t + 2, coords) + self.hullTri[q] = t + self.hullNext[e] = e # mark as removed + hullSize -= 1 + e = q + + # update the hull indices + self._hullStart = self.hullPrev[i] = e + self.hullNext[e] = self.hullPrev[n] = i + self.hullNext[i] = n + + # save the two new edges in the hash table + self.hullHash[self._hashKey(x, y)] = i + self.hullHash[self._hashKey(coords[2 * e], coords[2 * e + 1])] = e + + self.hull = [None] * hullSize + e = self._hullStart + for i in range(0, hullSize): + self.hull[i] = e + e = self.hullNext[e] + + # trim typed triangle mesh arrays + self.triangles = self._triangles[0:self.trianglesLen] + self.halfedges = self._halfedges[0:self.trianglesLen] + + return self.triangles + + def _hashKey(self, x, y): + angle = pseudoAngle(x - self._cx, + y - self._cy) + key = math.floor(angle * self.hashSize) % self.hashSize + return pycompat.LONG_TYPE(key) + + def _legalize(self, a, coords): + i = 0 + ar = 0 + + # recursion eliminated with a fixed-size stack + while True: + b = self._halfedges[a] + """ + if the pair of triangles doesn't satisfy the Delaunay condition + (p1 is inside the circumcircle of [p0, pl, pr]), flip them, + then do the same check/flip recursively for the new pair of triangles + """ + + # pl pl + # /||\ / \ + # al/ || \bl al/ \a + # / || \ / \ + # / a||b \ flip /___ar___\ + # p0\ || /p1 => p0\---bl---/p1 + # \ || / \ / + # ar\ || /br b\ /br + # \||/ \ / + # pr pr + + a0 = a - a % 3 + ar = a0 + (a + 2) % 3 + + if (b == -1): # convex hull edge + if (i == 0): break + i -= 1 + a = EDGE_STACK[i] + continue + + b0 = b - b % 3 + al = a0 + (a + 1) % 3 + bl = b0 + (b + 2) % 3 + + p0 = self._triangles[ar] + pr = self._triangles[a] + pl = self._triangles[al] + p1 = self._triangles[bl] + + illegal = inCircle( + coords[2 * p0], coords[2 * p0 + 1], + coords[2 * pr], coords[2 * pr + 1], + coords[2 * pl], coords[2 * pl + 1], + coords[2 * p1], coords[2 * p1 + 1]) + + if (illegal): + self._triangles[a] = p1 + self._triangles[b] = p0 + + hbl = self._halfedges[bl] + + # edge swapped on the other side of the hull (rare); fix the halfedge reference + if (hbl == -1): + e = self._hullStart + + while True: + if (self.hullTri[e] == bl): + self.hullTri[e] = a + break + + e = self.hullPrev[e] + if (e == self._hullStart): break + + self._link(a, hbl) + self._link(b, self._halfedges[ar]) + self._link(ar, bl) + + br = b0 + (b + 1) % 3 + + # don't worry about hitting the cap: it can only happen on extremely degenerate input + if (i < len(EDGE_STACK)): + EDGE_STACK[i] = br + i += 1 + + else: + if (i == 0): break + i -= 1 + a = EDGE_STACK[i] + + return ar + + def _link(self, a, b): + self._halfedges[a] = b + if (b != -1): + self._halfedges[b] = a + + # add a new triangle given vertex indices and adjacent half-edge ids + def _addTriangle(self, i0, i1, i2, a, b, c): + t = self.trianglesLen + + self._triangles[t] = i0 + self._triangles[t + 1] = i1 + self._triangles[t + 2] = i2 + + self._link(t, a) + self._link(t + 1, b) + self._link(t + 2, c) + + self.trianglesLen += 3 + + return t + + +# monotonically increases with real angle, but doesn't need expensive trigonometry +def pseudoAngle(dx, dy): + p = dx / (abs(dx) + abs(dy)) + + if (dy > 0): + return (3 - p) / 4 # [0..1] + else: + return (1 + p) / 4 # [0..1] + + +def dist(ax, ay, bx, by): + dx = ax - bx + dy = ay - by + return dx * dx + dy * dy + + +# return 2d orientation sign if we're confident in it through J. Shewchuk's error bound check +def orientIfSure(px, py, rx, ry, qx, qy): + l = (ry - py) * (qx - px) + r = (rx - px) * (qy - py) + + if (abs(l - r) >= 3.3306690738754716e-16 * abs(l + r)): + return l - r + else: + return 0 + + +# a more robust orientation test that's stable in a given triangle (to fix robustness issues) +def orient(rx, ry, qx, qy, px, py): + return (orientIfSure(px, py, rx, ry, qx, qy) or \ + orientIfSure(rx, ry, qx, qy, px, py) or \ + orientIfSure(qx, qy, px, py, rx, ry)) < 0 + + +def inCircle(ax, ay, bx, by, cx, cy, px, py): + dx = ax - px + dy = ay - py + ex = bx - px + ey = by - py + fx = cx - px + fy = cy - py + + ap = dx * dx + dy * dy + bp = ex * ex + ey * ey + cp = fx * fx + fy * fy + + return dx * (ey * cp - bp * fy) - \ + dy * (ex * cp - bp * fx) + \ + ap * (ex * fy - ey * fx) < 0 + + +def circumradius(ax, ay, bx, by, cx, cy): + dx = bx - ax + dy = by - ay + ex = cx - ax + ey = cy - ay + + bl = dx * dx + dy * dy + cl = ex * ex + ey * ey + try: + d = 0.5 / (dx * ey - dy * ex) + except ZeroDivisionError: + d = float('inf') + + x = (ey * bl - dy * cl) * d + y = (dx * cl - ex * bl) * d + + return x * x + y * y + + +def circumcenter(ax, ay, bx, by, cx, cy): + dx = bx - ax + dy = by - ay + ex = cx - ax + ey = cy - ay + + bl = dx * dx + dy * dy + cl = ex * ex + ey * ey + try: + d = 0.5 / (dx * ey - dy * ex) + except ZeroDivisionError: + d = float('inf') + + x = ax + (ey * bl - dy * cl) * d + y = ay + (dx * cl - ex * bl) * d + + return x, y + + +def quicksort(ids, dists, left, right): + if (right - left <= 20): + for i in range(left + 1, right + 1): + temp = ids[i] + tempDist = dists[temp] + j = i - 1 + while (j >= left and dists[ids[j]] > tempDist): + ids[j + 1] = ids[j] + j -= 1 + ids[j + 1] = temp; + + else: + median = (left + right) >> 1 + i = left + 1 + j = right + swap(ids, median, i) + + if (dists[ids[left]] > dists[ids[right]]): + swap(ids, left, right) + + if (dists[ids[i]] > dists[ids[right]]): + swap(ids, i, right) + + if (dists[ids[left]] > dists[ids[i]]): + swap(ids, left, i) + + temp = ids[i] + tempDist = dists[temp] + + while True: + while True: + i += 1 + if (dists[ids[i]] >= tempDist): break + + while True: + j -= 1 + if (dists[ids[j]] <= tempDist): break + + if (j < i): break + swap(ids, i, j); + + ids[left + 1] = ids[j]; + ids[j] = temp; + + if (right - i + 1 >= j - left): + quicksort(ids, dists, i, right) + quicksort(ids, dists, left, j - 1) + + else: + quicksort(ids, dists, left, j - 1) + quicksort(ids, dists, i, right) + + +def swap(arr, i, j): + tmp = arr[i] + arr[i] = arr[j] + arr[j] = tmp diff --git a/python/mmSolver/tools/meshfrompoints/lib.py b/python/mmSolver/tools/meshfrompoints/lib.py new file mode 100644 index 000000000..f39cadc31 --- /dev/null +++ b/python/mmSolver/tools/meshfrompoints/lib.py @@ -0,0 +1,157 @@ +# Copyright (C) 2024 Patcha Saheb Binginapalli. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# + +import collections + +import maya.api.OpenMaya as om +import maya.api.OpenMayaUI as omui +import maya.cmds + +import mmSolver.logger +import mmSolver.utils.node as node_utils +from mmSolver.tools.meshfrompoints.delaunator import Delaunator +import mmSolver.tools.meshfrompoints.constant as const + + +LOG = mmSolver.logger.get_logger() + + +MeshData = collections.namedtuple( + 'MeshData', + ['world_positions', 'full_mesh_indices', 'border_mesh_indices'], +) + + +def _delaunator_indices(transform_nodes, viewport): + """ + Uses transforms and active camera view to compute delaunay + indices in the proper order to create mesh. + + NOTE: This function uses the active view camera! This is hidden + + :param transform_nodes: + :type transform_nodes: list + + """ + assert isinstance(viewport, omui.M3dView) + assert len(transform_nodes) >= const.MINIMUM_NUMBER_OF_POINTS + + world_positions = [] + view_positions = [] + for transform_node in transform_nodes: + transform_pos = maya.cmds.xform( + transform_node, query=True, worldSpace=True, translation=True + ) + transform_mpoint = om.MPoint( + transform_pos[0], transform_pos[1], transform_pos[2] + ) + world_positions.append(transform_mpoint) + + # Convert world position to view space. + view_pos = viewport.worldToView(transform_mpoint) + view_x, view_y = view_pos[0], view_pos[1] + view_positions.append([view_x, view_y]) + + # Compute once, and reuse the result. + computation = Delaunator(view_positions) + mesh_indices = computation.triangles + + # Reverse order + mesh_indices_reverse = [] + for i in range(0, len(mesh_indices), 3): + chunk = mesh_indices[i : i + 3] + mesh_indices_reverse.extend(chunk[::-1]) + full_mesh_indices = mesh_indices_reverse + + # Hull indices + border_mesh_indices = computation.hull + border_mesh_indices.reverse() + + assert len(world_positions) == len(view_positions) + + mesh_data = MeshData( + world_positions=world_positions, + full_mesh_indices=full_mesh_indices, + border_mesh_indices=border_mesh_indices, + ) + return mesh_data + + +def create_mesh_from_transform_nodes( + mesh_type, transform_nodes, offset_value=None, mesh_name=None +): + """ + Creates mesh from transform nodes. + + :param mesh_type: The type of mesh to create. + :type mesh_type: mmSolver.tools.meshfrompoints.constant.MESH_TYPE_*_VALUE. + + :param transform_nodes: List of transform nodes to get 3D positions from. + :type transform_nodes: [str, ..] + + :param offset_value: An offset value for MESH_TYPE_BORDER_EDGE_STRIP_MESH_VALUE. + :type offset_value: float + + :param mesh_name: The name of the mesh node created. + :type mesh_name: str + + :returns: The name of the created mesh node. + :rtype: str + """ + if offset_value is None: + offset_value = const.DEFAULT_STRIP_WIDTH + if mesh_name is None: + mesh_name = const.DEFAULT_MESH_NAME + assert mesh_type in const.MESH_TYPE_VALUES + assert isinstance(mesh_name, str) + assert len(mesh_name) > 0 + assert isinstance(offset_value, float) + assert len(transform_nodes) >= const.MINIMUM_NUMBER_OF_POINTS + + active_viewport = omui.M3dView.active3dView() + mesh_data = _delaunator_indices(transform_nodes, active_viewport) + positions = mesh_data.world_positions + + # Set face count and indices + if mesh_type == const.MESH_TYPE_FULL_MESH_VALUE: + indices = mesh_data.full_mesh_indices + face_counts = [3] * (len(indices) // 3) + else: + indices = mesh_data.border_mesh_indices + face_counts = [len(indices)] + + # Create the mesh. + mesh_fn = om.MFnMesh() + mesh = mesh_fn.create(positions, face_counts, indices) + + # Set mesh name. + dag_node = om.MFnDagNode(mesh) + dag_node.setName(mesh_name) + mesh_node = dag_node.name() + + # Set lambert + maya.cmds.sets(mesh_node, edit=True, forceElement='initialShadingGroup') + + # Create border edge strip mesh + if mesh_type == const.MESH_TYPE_BORDER_EDGE_STRIP_MESH_VALUE: + maya.cmds.polyExtrudeFacet(mesh_node, offset=offset_value) + face_0 = '{}.f[0]'.format(mesh_node) + face_1 = '{}.f[1]'.format(mesh_node) + maya.cmds.delete(face_0, face_1) + + return node_utils.get_long_name(mesh_node) diff --git a/python/mmSolver/tools/meshfrompoints/tool.py b/python/mmSolver/tools/meshfrompoints/tool.py new file mode 100644 index 000000000..8de8332dd --- /dev/null +++ b/python/mmSolver/tools/meshfrompoints/tool.py @@ -0,0 +1,78 @@ +# Copyright (C) 2024 Patcha Saheb Binginapalli. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Mesh From Points main. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import maya.cmds + +import mmSolver.logger + +import mmSolver.tools.meshfrompoints.constant as const +import mmSolver.tools.meshfrompoints.lib as lib + +LOG = mmSolver.logger.get_logger() + + +def _get_selection(): + transform_nodes = maya.cmds.ls(selection=True, transforms=True) or [] + if len(transform_nodes) < 3: + LOG.warn('Please select least three transform nodes.') + return None + return transform_nodes + + +def create_full_mesh(): + transform_nodes = _get_selection() + if transform_nodes is None: + return + + node = lib.create_mesh_from_transform_nodes( + const.MESH_TYPE_FULL_MESH_VALUE, transform_nodes + ) + + if node is not None and maya.cmds.objExists(node): + maya.cmds.select(node, replace=True) + return + + +def create_border_mesh(): + transform_nodes = _get_selection() + if transform_nodes is None: + return + + node = lib.create_mesh_from_transform_nodes( + const.MESH_TYPE_BORDER_MESH_VALUE, transform_nodes + ) + + if node is not None and maya.cmds.objExists(node): + maya.cmds.select(node, replace=True) + return + + +def main(): + """ + Open the 'Mesh From Points' window. + """ + import mmSolver.tools.meshfrompoints.ui.meshfrompoints_window as window + + window.main() diff --git a/python/mmSolver/tools/meshfrompoints/ui/__init__.py b/python/mmSolver/tools/meshfrompoints/ui/__init__.py new file mode 100644 index 000000000..0a51d7170 --- /dev/null +++ b/python/mmSolver/tools/meshfrompoints/ui/__init__.py @@ -0,0 +1,20 @@ +# Copyright (C) 2024 Patcha Saheb Binginapalli. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Mesh From Points user interface. +""" diff --git a/python/mmSolver/tools/meshfrompoints/ui/meshfrompoints_layout.py b/python/mmSolver/tools/meshfrompoints/ui/meshfrompoints_layout.py new file mode 100644 index 000000000..2660de62c --- /dev/null +++ b/python/mmSolver/tools/meshfrompoints/ui/meshfrompoints_layout.py @@ -0,0 +1,116 @@ +# Copyright (C) 2024 Patcha Saheb Binginapalli. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +The main component of the user interface for the mesh from points window. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import maya.cmds + +import mmSolver.ui.qtpyutils as qtpyutils + +qtpyutils.override_binding_order() + +import mmSolver.ui.Qt.QtWidgets as QtWidgets + +import mmSolver.logger +import mmSolver.utils.configmaya as configmaya +import mmSolver.tools.meshfrompoints.ui.ui_meshfrompoints_layout as ui_meshfrompoints_layout +import mmSolver.tools.meshfrompoints.lib as lib +import mmSolver.tools.meshfrompoints.constant as const + + +LOG = mmSolver.logger.get_logger() + + +def _get_selection(): + transform_nodes = maya.cmds.ls(selection=True, transforms=True) or [] + if len(transform_nodes) < 3: + LOG.warn('Please select least three transform nodes.') + return None + return transform_nodes + + +class MeshFromPointsLayout(QtWidgets.QWidget, ui_meshfrompoints_layout.Ui_Form): + def __init__(self, parent=None, *args, **kwargs): + super(MeshFromPointsLayout, self).__init__(*args, **kwargs) + self.setupUi(self) + self.create_connections() + + # TODO: Should the mesh node name be exposed to the user to set? + + # Populate the UI with data + self.populateUi() + + def create_connections(self): + self.createFullMeshBtn.clicked.connect(self.full_mesh_btn_clicked) + self.createBorderMeshBtn.clicked.connect(self.border_mesh_btn_clicked) + self.createEdgeStripMeshBtn.clicked.connect( + self.border_edge_strip_mesh_btn_clicked + ) + + def reset_options(self): + name = const.CONFIG_STRIP_WIDTH_KEY + value = const.DEFAULT_STRIP_WIDTH + configmaya.set_scene_option(name, value) + LOG.debug('key=%r value=%r', name, value) + + self.populateUi() + return + + def populateUi(self): + """ + Update the UI for the first time the class is created. + """ + name = const.CONFIG_STRIP_WIDTH_KEY + value = configmaya.get_scene_option(name, default=const.DEFAULT_STRIP_WIDTH) + LOG.debug('key=%r value=%r', name, value) + self.stripWidthSpinBox.setValue(value) + + def full_mesh_btn_clicked(self): + transform_nodes = _get_selection() + if transform_nodes is None: + return + + lib.create_mesh_from_transform_nodes( + const.MESH_TYPE_FULL_MESH_VALUE, transform_nodes + ) + + def border_mesh_btn_clicked(self): + transform_nodes = _get_selection() + if transform_nodes is None: + return + + lib.create_mesh_from_transform_nodes( + const.MESH_TYPE_BORDER_MESH_VALUE, transform_nodes + ) + + def border_edge_strip_mesh_btn_clicked(self): + transform_nodes = _get_selection() + if transform_nodes is None: + return + + offset = self.stripWidthSpinBox.value() + lib.create_mesh_from_transform_nodes( + const.MESH_TYPE_BORDER_EDGE_STRIP_MESH_VALUE, + transform_nodes, + offset_value=offset, + ) diff --git a/python/mmSolver/tools/meshfrompoints/ui/meshfrompoints_layout.ui b/python/mmSolver/tools/meshfrompoints/ui/meshfrompoints_layout.ui new file mode 100644 index 000000000..ae5fa2362 --- /dev/null +++ b/python/mmSolver/tools/meshfrompoints/ui/meshfrompoints_layout.ui @@ -0,0 +1,100 @@ + + + Form + + + + 0 + 0 + 300 + 117 + + + + Form + + + + 6 + + + 0 + + + + + + + Border Edge Strip Width + + + + + + + Qt::ClickFocus + + + QAbstractSpinBox::UpDownArrows + + + 0.010000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + + + + + + + + + + 280 + 0 + + + + Create Full Mesh + + + + + + + + 280 + 0 + + + + Create Border Mesh + + + + + + + + 280 + 0 + + + + Create Border Edge Strip Mesh + + + + + + + + + + diff --git a/python/mmSolver/tools/meshfrompoints/ui/meshfrompoints_window.py b/python/mmSolver/tools/meshfrompoints/ui/meshfrompoints_window.py new file mode 100644 index 000000000..7514c9a6d --- /dev/null +++ b/python/mmSolver/tools/meshfrompoints/ui/meshfrompoints_window.py @@ -0,0 +1,113 @@ +# Copyright (C) 2024 Patcha Saheb Binginapalli +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Window for the Mesh From Points tool. + +Usage:: + + import mmSolver.tools.meshfrompoints.ui.meshfrompoints_window as meshfrompoints_window + meshfrompoints_window.main() + +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import mmSolver.ui.qtpyutils as qtpyutils + +qtpyutils.override_binding_order() + +import mmSolver.ui.Qt.QtCore as QtCore +import mmSolver.ui.Qt.QtWidgets as QtWidgets + +import mmSolver.logger +import mmSolver.ui.uiutils as uiutils +import mmSolver.ui.commonmenus as commonmenus +import mmSolver.ui.helputils as helputils +import mmSolver.tools.meshfrompoints.ui.meshfrompoints_layout as meshfrompoints_layout +import mmSolver.tools.meshfrompoints.constant as const + +LOG = mmSolver.logger.get_logger() +baseModule, BaseWindow = uiutils.getBaseWindow() + + +def _open_help(): + src = helputils.get_help_source() + page = 'tools_meshtools.html#mesh-from-points' + helputils.open_help_in_browser(page=page, help_source=src) + return + + +class MeshFromPointsWindow(BaseWindow): + name = 'MeshFromPointsWindow' + + def __init__(self, parent=None, name=None): + super(MeshFromPointsWindow, self).__init__(parent, name=name) + self.setupUi(self) + self.addSubForm(meshfrompoints_layout.MeshFromPointsLayout) + + self.setWindowTitle(const.WINDOW_TITLE) + self.setWindowFlags(QtCore.Qt.Tool) + + # Hide irrelevant stuff + self.baseHideStandardButtons() + self.baseHideProgressBar() + + self.add_menus(self.menubar) + self.menubar.show() + + def add_menus(self, menubar): + edit_menu = QtWidgets.QMenu('Edit', menubar) + commonmenus.create_edit_menu_items( + edit_menu, reset_settings_func=self.reset_options + ) + menubar.addMenu(edit_menu) + + help_menu = QtWidgets.QMenu('Help', menubar) + commonmenus.create_help_menu_items(help_menu, tool_help_func=_open_help) + menubar.addMenu(help_menu) + + def reset_options(self): + form = self.getSubForm() + form.reset_options() + return + + +def main(show=True, auto_raise=True, delete=False): + """ + Open the Mesh From Points UI. + + :param show: Show the UI. + :type show: bool + + :param auto_raise: If the UI is open, raise it to the front? + :type auto_raise: bool + + :param delete: Delete the existing UI and rebuild it? Helpful when + developing the UI in Maya script editor. + :type delete: bool + + :returns: A new ui window, or None if the window cannot be + opened. + :rtype: MeshFromPointsWindow or None + """ + win = MeshFromPointsWindow.open_window( + show=show, auto_raise=auto_raise, delete=delete + ) + return win diff --git a/python/mmSolver/tools/raycastmarker/ui/raycastmarker_window.py b/python/mmSolver/tools/raycastmarker/ui/raycastmarker_window.py index 8dd9e4e19..6aa2678ce 100644 --- a/python/mmSolver/tools/raycastmarker/ui/raycastmarker_window.py +++ b/python/mmSolver/tools/raycastmarker/ui/raycastmarker_window.py @@ -106,9 +106,9 @@ def main(show=True, auto_raise=True, delete=False): developing the UI in Maya script editor. :type delete: bool - :returns: A new solver window, or None if the window cannot be - opened. - :rtype: SolverWindow or None. + :returns: A new ray-cast marker window, or None if the window cannot + be opened. + :rtype: RayCastMarkerWindow or None. """ win = RayCastMarkerWindow.open_window( show=show, auto_raise=auto_raise, delete=delete diff --git a/python/mmSolver/tools/reparent2/ui/reparent2_window.py b/python/mmSolver/tools/reparent2/ui/reparent2_window.py index 82f24ad6f..5283a60db 100644 --- a/python/mmSolver/tools/reparent2/ui/reparent2_window.py +++ b/python/mmSolver/tools/reparent2/ui/reparent2_window.py @@ -140,9 +140,9 @@ def main(show=True, auto_raise=True, delete=False): developing the UI in Maya script editor. :type delete: bool - :returns: A new solver window, or None if the window cannot be + :returns: A new reparent2 window, or None if the window cannot be opened. - :rtype: SolverWindow or None. + :rtype: Reparent2Window or None. """ win = Reparent2Window.open_window(show=show, auto_raise=auto_raise, delete=delete) return win diff --git a/python/mmSolver/tools/screenspacerigbake/ui/screenspacerigbake_layout.ui b/python/mmSolver/tools/screenspacerigbake/ui/screenspacerigbake_layout.ui index 01bb88ba5..9cd162398 100644 --- a/python/mmSolver/tools/screenspacerigbake/ui/screenspacerigbake_layout.ui +++ b/python/mmSolver/tools/screenspacerigbake/ui/screenspacerigbake_layout.ui @@ -6,8 +6,8 @@ 0 0 - 289 - 397 + 305 + 444 diff --git a/python/mmSolver/tools/setcameraoriginframe/ui/originframe_window.py b/python/mmSolver/tools/setcameraoriginframe/ui/originframe_window.py index d6c5a80ac..e0a8e62cd 100644 --- a/python/mmSolver/tools/setcameraoriginframe/ui/originframe_window.py +++ b/python/mmSolver/tools/setcameraoriginframe/ui/originframe_window.py @@ -108,9 +108,9 @@ def main(show=True, auto_raise=True, delete=False): developing the UI in Maya script editor. :type delete: bool - :returns: A new solver window, or None if the window cannot be + :returns: A new origin frame window, or None if the window cannot be opened. - :rtype: SolverWindow or None. + :rtype: OriginFrameWindow or None. """ win = OriginFrameWindow.open_window(show=show, auto_raise=auto_raise, delete=delete) return win diff --git a/python/mmSolver/tools/setmeshholdouts/__init__.py b/python/mmSolver/tools/setmeshholdouts/__init__.py new file mode 100644 index 000000000..32de14f1a --- /dev/null +++ b/python/mmSolver/tools/setmeshholdouts/__init__.py @@ -0,0 +1,20 @@ +# Copyright (C) 2024 Patcha Saheb Binginapalli. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Tools to set hold-out attributes on meshes. +""" diff --git a/python/mmSolver/tools/setmeshholdouts/tool.py b/python/mmSolver/tools/setmeshholdouts/tool.py new file mode 100644 index 000000000..1b5c08cbe --- /dev/null +++ b/python/mmSolver/tools/setmeshholdouts/tool.py @@ -0,0 +1,77 @@ +# Copyright (C) 2024 Patcha Saheb Binginapalli. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Tools to set hold-out attributes on meshes. + +These tools can enable or disable the hold-out attribute on mesh nodes. +""" + +import maya.cmds + +import mmSolver.logger + +import mmSolver.utils.node as node_utils + +LOG = mmSolver.logger.get_logger() + +NO_SCENE_MESHES_WARNING_MESSAGE = "No mesh nodes found in the Maya scene." +SELECTED_MESHES_WARNING_MESSAGE = ( + "Please select mesh objects, no mesh nodes were found from the selection." +) + + +def _set_holdout(mesh_nodes, value): + assert isinstance(value, bool) + attr_name = 'holdOut' + for mesh_node in mesh_nodes: + if node_utils.attribute_exists(attr_name, mesh_node): + node_attr = "{}.holdOut".format(mesh_node) + node_utils.set_attr(node_attr, value, relock=True) + return + + +def enable_all_meshes(): + all_meshes = maya.cmds.ls(dag=True, type="mesh") or [] + if len(all_meshes) == 0: + LOG.warn(NO_SCENE_MESHES_WARNING_MESSAGE) + return + _set_holdout(all_meshes, True) + + +def disable_all_meshes(): + all_meshes = maya.cmds.ls(dag=True, type="mesh") or [] + if len(all_meshes) == 0: + LOG.warn(NO_SCENE_MESHES_WARNING_MESSAGE) + return + _set_holdout(all_meshes, False) + + +def enable_selected_meshes(): + selected_meshes = maya.cmds.ls(selection=True, dag=True, type="mesh") or [] + if len(selected_meshes) == 0: + LOG.warn(SELECTED_MESHES_WARNING_MESSAGE) + return + _set_holdout(selected_meshes, True) + + +def disable_selected_meshes(): + selected_meshes = maya.cmds.ls(selection=True, dag=True, type="mesh") or [] + if len(selected_meshes) == 0: + LOG.warn(SELECTED_MESHES_WARNING_MESSAGE) + return + _set_holdout(selected_meshes, False) diff --git a/python/mmSolver/tools/smoothkeyframes/ui/smoothkeys_window.py b/python/mmSolver/tools/smoothkeyframes/ui/smoothkeys_window.py index a4c8a33cf..19af36be1 100644 --- a/python/mmSolver/tools/smoothkeyframes/ui/smoothkeys_window.py +++ b/python/mmSolver/tools/smoothkeyframes/ui/smoothkeys_window.py @@ -108,9 +108,9 @@ def main(show=True, auto_raise=True, delete=False): developing the UI in Maya script editor. :type delete: bool - :returns: A new solver window, or None if the window cannot be + :returns: A new smooth keys window, or None if the window cannot be opened. - :rtype: SolverWindow or None. + :rtype: SmoothKeysWindow or None. """ win = SmoothKeysWindow.open_window(show=show, auto_raise=auto_raise, delete=delete) return win diff --git a/python/mmSolver/tools/solver/lib/collection.py b/python/mmSolver/tools/solver/lib/collection.py index f241e8af5..7ab361895 100644 --- a/python/mmSolver/tools/solver/lib/collection.py +++ b/python/mmSolver/tools/solver/lib/collection.py @@ -681,6 +681,7 @@ def gather_execute_options(): image_plane_state = lib_state.get_display_image_plane_while_solving_state() meshes_state = lib_state.get_display_meshes_while_solving_state() disp_node_types['imagePlane'] = image_plane_state + disp_node_types['mmImagePlaneShape2'] = image_plane_state disp_node_types['mesh'] = meshes_state # Minimal UI from config file. diff --git a/python/mmSolver/tools/solver/ui/solver_window.py b/python/mmSolver/tools/solver/ui/solver_window.py index b6645055f..9ebfa5d02 100644 --- a/python/mmSolver/tools/solver/ui/solver_window.py +++ b/python/mmSolver/tools/solver/ui/solver_window.py @@ -180,83 +180,6 @@ def addMenuBarContents(self, menubar): action.triggered.connect(self.solverPrefWindowTriggeredCB) edit_menu.addAction(action) - # if Qt.IsPySide2 or Qt.IsPyQt5: - # edit_menu.addSection('Undo / Redo') - - # # Undo - # label = 'Undo last command (with disabled viewport)' - # tooltip = ( - # 'Undo the Maya scene state, without updating the viewport or solver UI' - # ) - # action = QtWidgets.QAction(label, edit_menu) - # action.setStatusTip(tooltip) - # action.triggered.connect(self.undoTriggeredCB) - # edit_menu.addAction(action) - - # # Redo - # label = 'Redo last command (with disabled viewport)' - # tooltip = ( - # 'Redo the Maya scene state, without updating the viewport or solver UI' - # ) - # action = QtWidgets.QAction(label, edit_menu) - # action.setStatusTip(tooltip) - # action.triggered.connect(self.redoTriggeredCB) - # edit_menu.addAction(action) - - # if Qt.IsPySide2 or Qt.IsPyQt5: - # edit_menu.addSection('Window Update') - - # # Auto Update Solver Validation - # label = 'Auto-Update Solver Validation' - # tooltip = 'Auto-update details of the solver parameter/error numbers.' - # value = lib_state.get_auto_update_solver_validation_state() - # action = QtWidgets.QAction(label, edit_menu) - # action.setStatusTip(tooltip) - # action.setCheckable(True) - # action.setChecked(value) - # action.toggled.connect( - # self.subForm.solver_settings.autoUpdateSolverValidationChanged - # ) - # edit_menu.addAction(action) - - # if Qt.IsPySide2 or Qt.IsPyQt5: - # edit_menu.addSection('Solver Execution') - - # # Pre-Solve Force Evaluation - # label = 'Pre-Solve Force Evaluation' - # tooltip = 'Before starting a solve, update the scene to force an evaluation.' - # pre_solve_force_eval = lib_state.get_pre_solve_force_eval_state() - # action = QtWidgets.QAction(label, edit_menu) - # action.setStatusTip(tooltip) - # action.setCheckable(True) - # action.setChecked(pre_solve_force_eval) - # action.toggled.connect(type(self).preSolveForceEvalActionToggledCB) - # edit_menu.addAction(action) - - # # Refresh Viewport During Solve - # label = 'Refresh Viewport' - # tooltip = 'Refresh the viewport while Solving.' - # refresh_value = lib_state.get_refresh_viewport_state() - # action = QtWidgets.QAction(label, edit_menu) - # action.setStatusTip(tooltip) - # action.setCheckable(True) - # action.setChecked(refresh_value) - # action.toggled.connect(type(self).refreshActionToggledCB) - # edit_menu.addAction(action) - - # # Force DG evaluation. - # label = 'Force DG Update' - # tooltip = 'Force Maya DG Evaluation while solving.' - # force_dg_update_value = lib_state.get_force_dg_update_state() - # action = QtWidgets.QAction(label, edit_menu) - # action.setStatusTip(tooltip) - # action.setCheckable(True) - # action.setChecked(force_dg_update_value) - # action.toggled.connect(type(self).forceDgUpdateActionToggledCB) - # edit_menu.addAction(action) - - # menubar.addMenu(edit_menu) - # View Menu view_menu = QtWidgets.QMenu('View', menubar) view_menu.setTearOffEnabled(True) @@ -341,45 +264,6 @@ def addMenuBarContents(self, menubar): ) view_menu.addAction(action) - if Qt.IsPySide2 or Qt.IsPyQt5: - view_menu.addSection('During Solve') - - # # Display the Image Planes while solving. - # # - # # TODO: Add other object types to show/hide while solving, - # # such as camera, nurbsCurves, nurbsSurfaces, and locators. - # label = 'Display Image Planes' - # tooltip = 'Display Image Planes while solving.' - # value = lib_state.get_display_image_plane_while_solving_state() - # action = QtWidgets.QAction(label, view_menu) - # action.setStatusTip(tooltip) - # action.setCheckable(True) - # action.setChecked(value) - # action.toggled.connect(type(self).displayImagePlaneWhileSolvingActionToggledCB) - # view_menu.addAction(action) - - # # Display the Meshes while solving. - # label = 'Display Meshes' - # tooltip = 'Display Meshes while solving.' - # value = lib_state.get_display_meshes_while_solving_state() - # action = QtWidgets.QAction(label, view_menu) - # action.setStatusTip(tooltip) - # action.setCheckable(True) - # action.setChecked(value) - # action.toggled.connect(type(self).displayMeshesWhileSolvingActionToggledCB) - # view_menu.addAction(action) - - # # Isolate Objects while solving - # label = 'Isolate Objects' - # tooltip = 'Isolate visibility of all Markers and Bundles while solving.' - # isolate_value = lib_state.get_isolate_object_while_solving_state() - # action = QtWidgets.QAction(label, view_menu) - # action.setStatusTip(tooltip) - # action.setCheckable(True) - # action.setChecked(isolate_value) - # action.toggled.connect(type(self).isolateObjectWhileSolvingActionToggledCB) - # view_menu.addAction(action) - menubar.addMenu(view_menu) # Log Menu @@ -508,38 +392,6 @@ def triggerOutputAttributesUpdate(self): self.subForm.attribute_browser.dataChanged.emit() return - # def undoTriggeredCB(self): - # LOG.debug('undoTriggeredCB') - # import mmSolver.tools.undoredoscene.tool as undoredoscene_tool - - # validation = lib_state.get_auto_update_solver_validation_state() - # with undo_utils.no_undo_context(): - # lib_state.set_auto_update_solver_validation_state(False) - # block = self.blockSignals(True) - # try: - # undoredoscene_tool.main_undo() - # finally: - # with undo_utils.no_undo_context(): - # lib_state.set_auto_update_solver_validation_state(validation) - # self.blockSignals(block) - # return - - # def redoTriggeredCB(self): - # LOG.debug('redoTriggeredCB') - # import mmSolver.tools.undoredoscene.tool as undoredoscene_tool - - # validation = lib_state.get_auto_update_solver_validation_state() - # with undo_utils.no_undo_context(): - # lib_state.set_auto_update_solver_validation_state(False) - # block = self.blockSignals(True) - # try: - # undoredoscene_tool.main_redo() - # finally: - # with undo_utils.no_undo_context(): - # lib_state.set_auto_update_solver_validation_state(validation) - # self.blockSignals(block) - # return - def solverPrefWindowTriggeredCB(self): LOG.debug('solverPrefWindowTriggeredCB') return diff --git a/python/mmSolver/tools/surfacecluster/ui/surfacecluster_window.py b/python/mmSolver/tools/surfacecluster/ui/surfacecluster_window.py index 1608bc87c..509472768 100644 --- a/python/mmSolver/tools/surfacecluster/ui/surfacecluster_window.py +++ b/python/mmSolver/tools/surfacecluster/ui/surfacecluster_window.py @@ -108,9 +108,9 @@ def main(show=True, auto_raise=True, delete=False): developing the UI in Maya script editor. :type delete: bool - :returns: A new solver window, or None if the window cannot be + :returns: A new surface cluster window, or None if the window cannot be opened. - :rtype: SolverWindow or None. + :rtype: SurfaceClusterWindow or None. """ win = SurfaceClusterWindow.open_window( show=show, auto_raise=auto_raise, delete=delete diff --git a/python/mmSolver/tools/togglecameradistort/tool.py b/python/mmSolver/tools/togglecameradistort/tool.py index 284c1ea11..c35928cbc 100644 --- a/python/mmSolver/tools/togglecameradistort/tool.py +++ b/python/mmSolver/tools/togglecameradistort/tool.py @@ -77,6 +77,9 @@ def main(): for cam in cams_to_toggle: # Set lens mode (enabled/disabled) enable = cam.get_lens_enable() + if enable is None: + LOG.error('Cannot toggle lens distortion on camera; cam=%r', cam) + continue cam.set_lens_enable(not enable) maya.cmds.select(sel, replace=True) return diff --git a/python/mmSolver/tools/toggleviewportctrls/lib.py b/python/mmSolver/tools/toggleviewportctrls/lib.py index df7e0396c..a6c1537f8 100644 --- a/python/mmSolver/tools/toggleviewportctrls/lib.py +++ b/python/mmSolver/tools/toggleviewportctrls/lib.py @@ -32,6 +32,9 @@ def toggle_ctrls_visibility(model_panel): value = viewport_utils.get_locator_visibility(model_panel) new_value = not value + viewport_utils.set_mm_marker_visibility(model_panel, new_value) + viewport_utils.set_mm_bundle_visibility(model_panel, new_value) + viewport_utils.set_mm_line_visibility(model_panel, new_value) viewport_utils.set_nurbs_curve_visibility(model_panel, new_value) viewport_utils.set_locator_visibility(model_panel, new_value) viewport_utils.set_joint_visibility(model_panel, new_value) diff --git a/python/mmSolver/tools/toggleviewportimgplns/lib.py b/python/mmSolver/tools/toggleviewportimgplns/lib.py index 11f5db922..622ee083b 100644 --- a/python/mmSolver/tools/toggleviewportimgplns/lib.py +++ b/python/mmSolver/tools/toggleviewportimgplns/lib.py @@ -33,4 +33,5 @@ def toggle_image_plane_visibility(model_panel): value = viewport_utils.get_image_plane_visibility(model_panel) new_value = not value viewport_utils.set_image_plane_visibility(model_panel, new_value) + viewport_utils.set_mm_image_plane_v2_visibility(model_panel, new_value) return diff --git a/python/mmSolver/ui/nodes.py b/python/mmSolver/ui/nodes.py index 262ca146b..48882f5a1 100644 --- a/python/mmSolver/ui/nodes.py +++ b/python/mmSolver/ui/nodes.py @@ -23,6 +23,8 @@ from __future__ import division from __future__ import print_function +import mmSolver.utils.python_compat as pycompat + def get_nodes_recursively(top_node): nodes = [] @@ -151,7 +153,7 @@ def setNeverHasChildren(self, value): self._neverHasChildren = value def icon(self): - assert isinstance(self._iconPath, str) + assert isinstance(self._iconPath, pycompat.TEXT_TYPE) if self._icon is None: import mmSolver.ui.uiutils as uiutils diff --git a/python/mmSolver/ui/uiutils.py b/python/mmSolver/ui/uiutils.py index 44a99cced..688d164fd 100644 --- a/python/mmSolver/ui/uiutils.py +++ b/python/mmSolver/ui/uiutils.py @@ -230,7 +230,7 @@ def getIcon(path): :return: Qt Icon object. :rtype: QIcon """ - assert isinstance(path, str) + assert isinstance(path, pycompat.TEXT_TYPE) icon = QtGui.QIcon(QtGui.QPixmap(path)) return icon diff --git a/python/mmSolver/utils/constant.py b/python/mmSolver/utils/constant.py index 32dbea89c..5c18fcbd2 100644 --- a/python/mmSolver/utils/constant.py +++ b/python/mmSolver/utils/constant.py @@ -88,13 +88,22 @@ # # - Maya Style: "file..png" # - First frame style "file.1001.png" +# - Exact frame style "file.0042.png" # - Hash Padded style: "file.####.png" # - "old school" Nuke style: file.%04d.png # IMAGE_SEQ_FORMAT_STYLE_MAYA = 'maya' IMAGE_SEQ_FORMAT_STYLE_HASH_PADDED = 'hash_padded' IMAGE_SEQ_FORMAT_STYLE_FIRST_FRAME = 'first_frame' +IMAGE_SEQ_FORMAT_STYLE_EXACT_FRAME = 'exact_frame' IMAGE_SEQ_FORMAT_STYLE_PRINTF = 'printf' +IMAGE_SEQ_FORMAT_STYLE_VALUES = [ + IMAGE_SEQ_FORMAT_STYLE_MAYA, + IMAGE_SEQ_FORMAT_STYLE_HASH_PADDED, + IMAGE_SEQ_FORMAT_STYLE_FIRST_FRAME, + IMAGE_SEQ_FORMAT_STYLE_EXACT_FRAME, + IMAGE_SEQ_FORMAT_STYLE_PRINTF, +] # What type of lens distortion mode is used? @@ -109,3 +118,9 @@ DISTORT_MODE_UNDISTORT, DISTORT_MODE_REDISTORT, ) + + +# Memory Conversion +BYTES_TO_KILOBYTES = 1024 ####### int(pow(2, 10)) +BYTES_TO_MEGABYTES = 1048576 #### int(pow(2, 20)) +BYTES_TO_GIGABYTES = 1073741824 # int(pow(2, 30)) diff --git a/python/mmSolver/utils/imageseq.py b/python/mmSolver/utils/imageseq.py index 696d0d79e..8b18c87c9 100644 --- a/python/mmSolver/utils/imageseq.py +++ b/python/mmSolver/utils/imageseq.py @@ -90,7 +90,36 @@ def _get_image_sequence_start_end_frames(base_dir, file_name, file_extension): return start_frame, end_frame, padding_num -def expand_image_sequence_path(image_sequence_path, format_style): +def expand_image_sequence_path(image_sequence_path, format_style, exact_frame=None): + """ + Expand a given image sequence path into tokens. + + Converts 'file.1001.png' into 'file.####.png', when format_style + is IMAGE_SEQ_FORMAT_STYLE_HASH_PADDED. + + :param image_sequence_path: An existing file path. + :type image_sequence_path: str + + :param format_style: What style this function should expand into? + :type format_style: One of mmSolver.utils.constant.IMAGE_SEQ_FORMAT_STYLE_VALUES + + :param exact_frame: The frame number that will be expanded to when + format_style IMAGE_SEQ_FORMAT_STYLE_EXACT_FRAME is used. + :type exact_frame: None or int + + The tokens are: + - file_pattern: str, the pattern of the file path. + - start_frame: int, first frame of the image sequence. + - end_frame: int, last frame of the image sequence. + - padding_num: int, number of padding digits for the frame number. + - is_seq: bool, is this a sequence? Otherwise it's a single frame. + + :returns: + Tuple of (file_pattern, start_frame, end_frame, padding_num, is_seq) + """ + assert os.path.isfile(image_sequence_path) + assert format_style in const.IMAGE_SEQ_FORMAT_STYLE_VALUES + assert exact_frame is None or isinstance(exact_frame, int) image_sequence_path = os.path.abspath(image_sequence_path) ( @@ -124,9 +153,88 @@ def expand_image_sequence_path(image_sequence_path, format_style): elif format_style == const.IMAGE_SEQ_FORMAT_STYLE_FIRST_FRAME: # file.1001.png image_seq_num = str(start_frame).zfill(padding_num) + elif format_style == const.IMAGE_SEQ_FORMAT_STYLE_EXACT_FRAME: + # file.0042.png (exact_frame == 42) + # + # Same as 'IMAGE_SEQ_FORMAT_STYLE_FIRST_FRAME', but allows + # the user to give the frame value that will be used. + frame = start_frame + if exact_frame is not None: + frame = exact_frame + image_seq_num = str(frame).zfill(padding_num) else: raise NotImplementedError file_pattern = '{}{}{}'.format(file_name, image_seq_num, file_extension) file_pattern = os.path.join(base_dir, file_pattern) return file_pattern, start_frame, end_frame, padding_num, is_seq + + +def resolve_file_pattern_to_file_path(file_pattern, format_style, exact_frame=None): + """ + Resolve a file pattern into a valid file path. + + Converts 'file.####.png' into 'file.1001.png', when format_style + is IMAGE_SEQ_FORMAT_STYLE_HASH_PADDED. + + :param file_pattern: The pattern of the file path. + :type file_pattern: str + + :param format_style: The format style of the input file pattern + :type format_style: One of mmSolver.utils.constant.IMAGE_SEQ_FORMAT_STYLE_VALUES + + :param exact_frame: The frame number that will be expanded to. + If None is given, the start_frame of the image sequence is used. + :type exact_frame: None or int + + :returns: Valid file path or None. + :rtype: str or None + """ + assert format_style in const.IMAGE_SEQ_FORMAT_STYLE_VALUES + assert isinstance(file_pattern, str) + + if os.path.isfile(file_pattern): + file_pattern = os.path.abspath(file_pattern) + return file_pattern + + if format_style == const.IMAGE_SEQ_FORMAT_STYLE_MAYA: + # file..png + file_pattern = file_pattern.replace('', '') + elif format_style == const.IMAGE_SEQ_FORMAT_STYLE_HASH_PADDED: + # file.####.png + file_pattern = file_pattern.replace('#', '') + elif format_style == const.IMAGE_SEQ_FORMAT_STYLE_PRINTF: + # file.%04d.png + raise NotImplementedError + elif format_style in [ + const.IMAGE_SEQ_FORMAT_STYLE_FIRST_FRAME, + const.IMAGE_SEQ_FORMAT_STYLE_EXACT_FRAME, + ]: + # file.1001.png + + # Should have already been picked out as a valid file path, so + # it must be wrong. + return None + else: + raise NotImplementedError + + ( + base_dir, + file_name, + seq_num_int, + seq_num_str, + file_extension, + ) = _split_image_sequence_path(file_pattern) + + start_frame, end_frame, padding_num = _get_image_sequence_start_end_frames( + base_dir, file_name, file_extension + ) + + image_seq_num = str(start_frame).zfill(padding_num) + file_pattern = '{}{}{}'.format(file_name, image_seq_num, file_extension) + file_pattern = os.path.join(base_dir, file_pattern) + + file_path, _, _, _, _ = expand_image_sequence_path( + file_pattern, const.IMAGE_SEQ_FORMAT_STYLE_EXACT_FRAME, exact_frame=exact_frame + ) + return file_path diff --git a/python/mmSolver/utils/math.py b/python/mmSolver/utils/math.py new file mode 100644 index 000000000..02041a49d --- /dev/null +++ b/python/mmSolver/utils/math.py @@ -0,0 +1,69 @@ +# Copyright (C) 2021 David Cattermole, Kazuma Tonegawa. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# +""" +Extra mathematical functions. +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + + +def lerp(min_value, max_value, mix): + """ + Return 'min_value' to 'max_value' linearly, for a 'mix' value + between 0.0 and 1.0. + + :type min_value: float + :type max_value: float + :type mix: float + + :rtype: float + """ + return (1.0 - mix) * min_value + mix * max_value + + +def inverse_lerp(min_value, max_value, mix): + """ + Return 0.0 to 1.0 linearly, for a 'mix' value between 'min_value' + and 'max_value'. + + :type min_value: float + :type max_value: float + :type mix: float + + :rtype: float + """ + return (mix - min_value) / (max_value - min_value) + + +def remap(old_min, old_max, new_min, new_max, mix): + """ + Remap from the 'old_*' values to 'new_*' values, using a 'mix' + value between 0.0 and 1.0; + + :type old_min: float + :type old_max: float + :type new_min: float + :type new_max: float + :type mix: float + + :rtype: float + """ + blend = inverse_lerp(old_min, old_max, mix) + return lerp(new_min, new_max, blend) diff --git a/python/mmSolver/utils/node.py b/python/mmSolver/utils/node.py index 8b5b8d899..f785ff2a2 100644 --- a/python/mmSolver/utils/node.py +++ b/python/mmSolver/utils/node.py @@ -60,11 +60,12 @@ def set_attr(plug, value, relock=False): Optionally unlocks and re-locks the plugs. :param plug: Node.Attr to set. - :param value: The ne value to set. + :param value: The new value to set. :param relock: If the plug was already locked, should we set the new value, then re-lock afterward? - :return: + :return: True or False, depending if the value was set or not. + :rtype: bool """ node = plug.partition('.')[0] is_referenced = node_is_referenced(node) @@ -72,13 +73,14 @@ def set_attr(plug, value, relock=False): if is_referenced is True and locked is True: msg = 'Cannot set attr %r, it is locked and the node is referenced.' LOG.warning(msg, plug) + return False if is_referenced is False: # Make sure the plug is unlocked. maya.cmds.setAttr(plug, lock=False) maya.cmds.setAttr(plug, value) if is_referenced is False and relock is True: maya.cmds.setAttr(plug, lock=locked) - return + return True def get_long_name(node): diff --git a/python/mmSolver/utils/tools.py b/python/mmSolver/utils/tools.py index c6677df61..01efc657e 100644 --- a/python/mmSolver/utils/tools.py +++ b/python/mmSolver/utils/tools.py @@ -29,6 +29,7 @@ import maya.cmds import mmSolver.logger +import mmSolver.utils.python_compat as pycompat import mmSolver.utils.viewport as viewport_utils LOG = mmSolver.logger.get_logger() @@ -113,7 +114,7 @@ def tool_context( use_undo_chunk = True if undo_chunk_name is None: undo_chunk_name = str(uuid.uuid4()) - assert isinstance(undo_chunk_name, str) + assert isinstance(undo_chunk_name, pycompat.TEXT_TYPE) if pre_update_frame is None: pre_update_frame = False if post_update_frame is None: diff --git a/python/mmSolver/utils/viewport.py b/python/mmSolver/utils/viewport.py index 827602775..ef779e6b6 100644 --- a/python/mmSolver/utils/viewport.py +++ b/python/mmSolver/utils/viewport.py @@ -30,6 +30,7 @@ import mmSolver.utils.camera as camera_utils import mmSolver.utils.node as node_utils import mmSolver.utils.constant as const +import mmSolver.utils.python_compat as pycompat LOG = mmSolver.logger.get_logger() @@ -56,6 +57,13 @@ 'nRigid', 'texture', 'stroke', + # + # Custom mmSolver node types: + 'mmImagePlaneShape2', + 'mmMarkerShape', + 'mmBundleShape', + 'mmLineShape', + 'mmSkyDomeShape', ] @@ -410,14 +418,16 @@ def set_selection_highlight(model_panel, value): def _get_node_type_visibility(model_panel, node_type): """ - Query the image plane visibility. + Query the node type visibility of the given model panel. :param model_panel: Model panel name to set visibility. :type model_panel: str - :return: The visibility of image planes. + :return: The visibility of the node type. :rtype: bool """ + assert isinstance(model_panel, pycompat.TEXT_TYPE) + assert isinstance(node_type, pycompat.TEXT_TYPE) model_editor = maya.cmds.modelPanel(model_panel, query=True, modelEditor=True) kwargs = { 'query': True, @@ -434,9 +444,12 @@ def _set_node_type_visibility(model_panel, node_type, value): :param model_panel: Model panel name to set visibility. :type model_panel: str - :param value: Visibility of image planes. + :param value: Visibility of the node type. :type value: bool """ + assert isinstance(model_panel, pycompat.TEXT_TYPE) + assert isinstance(node_type, pycompat.TEXT_TYPE) + assert isinstance(value, bool) model_editor = maya.cmds.modelPanel(model_panel, query=True, modelEditor=True) kwargs = { 'edit': True, @@ -446,6 +459,46 @@ def _set_node_type_visibility(model_panel, node_type, value): return +def _get_plugin_display_filter_visibility(model_panel, plugin_display_filter): + """ + Query the display filter visibility of the given model panel. + + :param model_panel: Model panel name to set visibility. + :type model_panel: str + + :return: The visibility of the display filter. + :rtype: bool + """ + assert isinstance(model_panel, pycompat.TEXT_TYPE) + assert isinstance(plugin_display_filter, pycompat.TEXT_TYPE) + model_editor = maya.cmds.modelPanel(model_panel, query=True, modelEditor=True) + value = maya.cmds.modelEditor( + model_editor, query=True, queryPluginObjects=plugin_display_filter + ) + return value + + +def _set_plugin_display_filter_visibility(model_panel, plugin_display_filter, value): + """ + Set the visibility of a plug-in display filter for the given + model panel. + + :param model_panel: Model panel name to set visibility. + :type model_panel: str + + :param value: Visibility of the node type. + :type value: bool + """ + assert isinstance(model_panel, pycompat.TEXT_TYPE) + assert isinstance(plugin_display_filter, pycompat.TEXT_TYPE) + assert isinstance(value, bool) + model_editor = maya.cmds.modelPanel(model_panel, query=True, modelEditor=True) + maya.cmds.modelEditor( + model_editor, edit=True, pluginObjects=[plugin_display_filter, value] + ) + return + + def get_grid_visibility(model_panel): """ Query the grids visibility in given model panel. @@ -754,6 +807,88 @@ def set_stroke_visibility(model_panel, value): return _set_node_type_visibility(model_panel, 'strokes', value) +def get_mm_image_plane_v2_visibility(model_panel): + """ + Query the MM Image Plane visibility in given model panel. + """ + return _get_plugin_display_filter_visibility( + model_panel, 'mmImagePlane2DisplayFilter' + ) + + +def set_mm_image_plane_v2_visibility(model_panel, value): + """ + Set the visibility of MM Image Plane nodes in the given model panel. + """ + return _set_plugin_display_filter_visibility( + model_panel, 'mmImagePlane2DisplayFilter', value + ) + + +def get_mm_marker_visibility(model_panel): + """ + Query the MM Marker visibility in given model panel. + """ + return _get_plugin_display_filter_visibility(model_panel, 'mmMarkerDisplayFilter') + + +def set_mm_marker_visibility(model_panel, value): + """ + Set the visibility of MM Marker nodes in the given model panel. + """ + return _set_plugin_display_filter_visibility( + model_panel, 'mmMarkerDisplayFilter', value + ) + + +def get_mm_bundle_visibility(model_panel): + """ + Query the MM Bundle visibility in given model panel. + """ + return _get_plugin_display_filter_visibility(model_panel, 'mmBundleDisplayFilter') + + +def set_mm_bundle_visibility(model_panel, value): + """ + Set the visibility of MM Bundle nodes in the given model panel. + """ + return _set_plugin_display_filter_visibility( + model_panel, 'mmBundleDisplayFilter', value + ) + + +def get_mm_line_visibility(model_panel): + """ + Query the MM Line visibility in given model panel. + """ + return _get_plugin_display_filter_visibility(model_panel, 'mmLineDisplayFilter') + + +def set_mm_line_visibility(model_panel, value): + """ + Set the visibility of MM Line nodes in the given model panel. + """ + return _set_plugin_display_filter_visibility( + model_panel, 'mmLineDisplayFilter', value + ) + + +def get_mm_sky_dome_visibility(model_panel): + """ + Query the MM Sky Dome visibility in given model panel. + """ + return _get_plugin_display_filter_visibility(model_panel, 'mmSkyDomeDisplayFilter') + + +def set_mm_sky_dome_visibility(model_panel, value): + """ + Set the visibility of MM Sky Dome nodes in the given model panel. + """ + return _set_plugin_display_filter_visibility( + model_panel, 'mmSkyDomeDisplayFilter', value + ) + + NODE_TYPE_TO_GET_VIS_FUNC = { 'mesh': get_mesh_visibility, 'imagePlane': get_image_plane_visibility, @@ -775,6 +910,13 @@ def set_stroke_visibility(model_panel, value): 'nRigid': get_nrigid_visibility, 'texture': get_texture_visibility, 'stroke': get_stroke_visibility, + # + # Custom MM Solver node types. + 'mmImagePlaneShape2': get_mm_image_plane_v2_visibility, + 'mmMarkerShape': get_mm_marker_visibility, + 'mmBundleShape': get_mm_bundle_visibility, + 'mmLineShape': get_mm_line_visibility, + 'mmSkyDomeShape': get_mm_sky_dome_visibility, } @@ -799,6 +941,13 @@ def set_stroke_visibility(model_panel, value): 'nRigid': set_nrigid_visibility, 'texture': set_texture_visibility, 'stroke': set_stroke_visibility, + # + # Custom MM Solver node types. + 'mmImagePlaneShape2': set_mm_image_plane_v2_visibility, + 'mmMarkerShape': set_mm_marker_visibility, + 'mmBundleShape': set_mm_bundle_visibility, + 'mmLineShape': set_mm_line_visibility, + 'mmSkyDomeShape': set_mm_sky_dome_visibility, } diff --git a/scripts/build_mmSolver_linux_maya2016.bash b/scripts/build_mmSolver_linux_maya2016.bash old mode 100644 new mode 100755 diff --git a/scripts/build_mmSolver_linux_maya2017.bash b/scripts/build_mmSolver_linux_maya2017.bash old mode 100644 new mode 100755 diff --git a/scripts/build_mmSolver_linux_maya2018.bash b/scripts/build_mmSolver_linux_maya2018.bash old mode 100644 new mode 100755 index 416fccbaa..f1d82db7f --- a/scripts/build_mmSolver_linux_maya2018.bash +++ b/scripts/build_mmSolver_linux_maya2018.bash @@ -29,6 +29,21 @@ PYTHON_EXE=python CMAKE_EXE=cmake3 RUST_CARGO_EXE=cargo +# OpenColorIO specific options. +OPENCOLORIO_TARBALL_NAME="OpenColorIO-2.2.1.tar.gz" +OPENCOLORIO_TARBALL_EXTRACTED_DIR_NAME="OpenColorIO-2.2.1" +EXPAT_RELATIVE_CMAKE_DIR=lib64/cmake/expat-2.4.1/ +EXPAT_RELATIVE_LIB_PATH=lib64/libexpat.a +# yaml-cpp 0.7.0 +YAML_RELATIVE_CMAKE_DIR=share/cmake/yaml-cpp +YAML_RELATIVE_LIB_PATH=lib64/libyaml-cpp.a +PYSTRING_RELATIVE_LIB_PATH=lib64/libpystring.a +ZLIB_RELATIVE_LIB_PATH=lib/libz.a + +# Which version of the VFX platform are we "using"? (Maya doesn't +# currently conform to the VFX Platform.) +VFX_PLATFORM=2017 + # C++ Standard to use. CXX_STANDARD=11 @@ -40,6 +55,7 @@ CWD=`pwd` # These scripts assume 'RUST_CARGO_EXE' has been set to the Rust # 'cargo' executable. +source "${CWD}/scripts/internal/build_openColorIO_linux.bash" source "${CWD}/scripts/internal/build_mmSolverLibs_linux.bash" source "${CWD}/scripts/internal/build_mmSolver_linux.bash" diff --git a/scripts/build_mmSolver_linux_maya2019.bash b/scripts/build_mmSolver_linux_maya2019.bash old mode 100644 new mode 100755 index 0794dea84..db5b1dd39 --- a/scripts/build_mmSolver_linux_maya2019.bash +++ b/scripts/build_mmSolver_linux_maya2019.bash @@ -29,6 +29,21 @@ PYTHON_EXE=python CMAKE_EXE=cmake3 RUST_CARGO_EXE=cargo +# OpenColorIO specific options. +OPENCOLORIO_TARBALL_NAME="OpenColorIO-2.2.1.tar.gz" +OPENCOLORIO_TARBALL_EXTRACTED_DIR_NAME="OpenColorIO-2.2.1" +EXPAT_RELATIVE_CMAKE_DIR=lib64/cmake/expat-2.4.1/ +EXPAT_RELATIVE_LIB_PATH=lib64/libexpat.a +# yaml-cpp 0.7.0 +YAML_RELATIVE_CMAKE_DIR=share/cmake/yaml-cpp +YAML_RELATIVE_LIB_PATH=lib64/libyaml-cpp.a +PYSTRING_RELATIVE_LIB_PATH=lib64/libpystring.a +ZLIB_RELATIVE_LIB_PATH=lib/libz.a + +# Which version of the VFX platform are we "using"? (Maya doesn't +# currently conform to the VFX Platform.) +VFX_PLATFORM=2018 + # C++ Standard to use. CXX_STANDARD=11 @@ -40,6 +55,7 @@ CWD=`pwd` # These scripts assume 'RUST_CARGO_EXE' has been set to the Rust # 'cargo' executable. +source "${CWD}/scripts/internal/build_openColorIO_linux.bash" source "${CWD}/scripts/internal/build_mmSolverLibs_linux.bash" source "${CWD}/scripts/internal/build_mmSolver_linux.bash" diff --git a/scripts/build_mmSolver_linux_maya2020.bash b/scripts/build_mmSolver_linux_maya2020.bash old mode 100644 new mode 100755 index ed7f6f950..e42a3e5c6 --- a/scripts/build_mmSolver_linux_maya2020.bash +++ b/scripts/build_mmSolver_linux_maya2020.bash @@ -25,10 +25,25 @@ MAYA_VERSION=2020 MAYA_LOCATION=/usr/autodesk/maya2020/ # Executable names/paths used for build process. -PYTHON_EXE=python +PYTHON_EXE=python3 CMAKE_EXE=cmake3 RUST_CARGO_EXE=cargo +# OpenColorIO specific options. +OPENCOLORIO_TARBALL_NAME="OpenColorIO-2.2.1.tar.gz" +OPENCOLORIO_TARBALL_EXTRACTED_DIR_NAME="OpenColorIO-2.2.1" +EXPAT_RELATIVE_CMAKE_DIR=lib64/cmake/expat-2.4.1/ +EXPAT_RELATIVE_LIB_PATH=lib64/libexpat.a +# yaml-cpp 0.7.0 +YAML_RELATIVE_CMAKE_DIR=share/cmake/yaml-cpp +YAML_RELATIVE_LIB_PATH=lib64/libyaml-cpp.a +PYSTRING_RELATIVE_LIB_PATH=lib64/libpystring.a +ZLIB_RELATIVE_LIB_PATH=lib/libz.a + +# Which version of the VFX platform are we "using"? (Maya doesn't +# currently conform to the VFX Platform.) +VFX_PLATFORM=2019 + # C++ Standard to use. CXX_STANDARD=11 @@ -40,6 +55,7 @@ CWD=`pwd` # These scripts assume 'RUST_CARGO_EXE' has been set to the Rust # 'cargo' executable. +source "${CWD}/scripts/internal/build_openColorIO_linux.bash" source "${CWD}/scripts/internal/build_mmSolverLibs_linux.bash" source "${CWD}/scripts/internal/build_mmSolver_linux.bash" diff --git a/scripts/build_mmSolver_linux_maya2022.bash b/scripts/build_mmSolver_linux_maya2022.bash old mode 100644 new mode 100755 index 25ae09807..ed565f07f --- a/scripts/build_mmSolver_linux_maya2022.bash +++ b/scripts/build_mmSolver_linux_maya2022.bash @@ -25,10 +25,25 @@ MAYA_VERSION=2022 MAYA_LOCATION=/usr/autodesk/maya2022/ # Executable names/paths used for build process. -PYTHON_EXE=python +PYTHON_EXE=python3 CMAKE_EXE=cmake3 RUST_CARGO_EXE=cargo +# OpenColorIO specific options. +OPENCOLORIO_TARBALL_NAME="OpenColorIO-2.2.1.tar.gz" +OPENCOLORIO_TARBALL_EXTRACTED_DIR_NAME="OpenColorIO-2.2.1" +EXPAT_RELATIVE_CMAKE_DIR=lib64/cmake/expat-2.4.1/ +EXPAT_RELATIVE_LIB_PATH=lib64/libexpat.a +# yaml-cpp 0.7.0 +YAML_RELATIVE_CMAKE_DIR=share/cmake/yaml-cpp +YAML_RELATIVE_LIB_PATH=lib64/libyaml-cpp.a +PYSTRING_RELATIVE_LIB_PATH=lib64/libpystring.a +ZLIB_RELATIVE_LIB_PATH=lib/libz.a + +# Which version of the VFX platform are we "using"? (Maya doesn't +# currently conform to the VFX Platform.) +VFX_PLATFORM=2021 + # C++ Standard to use. CXX_STANDARD=14 @@ -40,6 +55,7 @@ CWD=`pwd` # This script assumes 'RUST_CARGO_EXE' has been set to the Rust # 'cargo' executable. +source "${CWD}/scripts/internal/build_opencolorio_linux.bash" source "${CWD}/scripts/internal/build_mmSolverLibs_linux.bash" source "${CWD}/scripts/internal/build_mmSolver_linux.bash" diff --git a/scripts/build_mmSolver_linux_maya2023.bash b/scripts/build_mmSolver_linux_maya2023.bash old mode 100644 new mode 100755 index 791888f15..4194668de --- a/scripts/build_mmSolver_linux_maya2023.bash +++ b/scripts/build_mmSolver_linux_maya2023.bash @@ -25,10 +25,25 @@ MAYA_VERSION=2023 MAYA_LOCATION=/usr/autodesk/maya2023/ # Executable names/paths used for build process. -PYTHON_EXE=python +PYTHON_EXE=python3 CMAKE_EXE=cmake3 RUST_CARGO_EXE=cargo +# OpenColorIO specific options. +OPENCOLORIO_TARBALL_NAME="OpenColorIO-2.2.1.tar.gz" +OPENCOLORIO_TARBALL_EXTRACTED_DIR_NAME="OpenColorIO-2.2.1" +EXPAT_RELATIVE_CMAKE_DIR=lib64/cmake/expat-2.4.1/ +EXPAT_RELATIVE_LIB_PATH=lib64/libexpat.a +# yaml-cpp 0.7.0 +YAML_RELATIVE_CMAKE_DIR=share/cmake/yaml-cpp +YAML_RELATIVE_LIB_PATH=lib64/libyaml-cpp.a +PYSTRING_RELATIVE_LIB_PATH=lib64/libpystring.a +ZLIB_RELATIVE_LIB_PATH=lib/libz.a + +# Which version of the VFX platform are we "using"? (Maya doesn't +# currently conform to the VFX Platform.) +VFX_PLATFORM=2022 + # C++ Standard to use. CXX_STANDARD=14 @@ -40,6 +55,7 @@ CWD=`pwd` # These scripts assume 'RUST_CARGO_EXE' has been set to the Rust # 'cargo' executable. +source "${CWD}/scripts/internal/build_openColorIO_linux.bash" source "${CWD}/scripts/internal/build_mmSolverLibs_linux.bash" source "${CWD}/scripts/internal/build_mmSolver_linux.bash" diff --git a/scripts/build_mmSolver_linux_maya2024.bash b/scripts/build_mmSolver_linux_maya2024.bash old mode 100644 new mode 100755 index c02b055ce..74e143a7b --- a/scripts/build_mmSolver_linux_maya2024.bash +++ b/scripts/build_mmSolver_linux_maya2024.bash @@ -25,15 +25,33 @@ MAYA_VERSION=2024 MAYA_LOCATION=/usr/autodesk/maya2024/ # Executable names/paths used for build process. +# +# RockyLinux8 has Python 3.6 by default, but we use Python 3.9 because +# it has better support for tools like 'ruff'. PYTHON_EXE=python3.9 CMAKE_EXE=cmake3 RUST_CARGO_EXE=cargo +# OpenColorIO specific options. +OPENCOLORIO_TARBALL_NAME="OpenColorIO-2.2.1.tar.gz" +OPENCOLORIO_TARBALL_EXTRACTED_DIR_NAME="OpenColorIO-2.2.1" +EXPAT_RELATIVE_CMAKE_DIR=lib64/cmake/expat-2.4.1/ +EXPAT_RELATIVE_LIB_PATH=lib64/libexpat.a +# yaml-cpp 0.7.0 +YAML_RELATIVE_CMAKE_DIR=share/cmake/yaml-cpp +YAML_RELATIVE_LIB_PATH=lib64/libyaml-cpp.a +PYSTRING_RELATIVE_LIB_PATH=lib64/libpystring.a +ZLIB_RELATIVE_LIB_PATH=lib/libz.a + # Manually override OpenGL include headers, because CMake doesn't seem # to automatically find OpenGL headers on RockyLinux8 (which is used # in the Docker containers). OPENGL_INCLUDE_DIR=/usr/include/ +# Which version of the VFX platform are we "using"? (Maya doesn't +# currently conform to the VFX Platform.) +VFX_PLATFORM=2023 + # C++ Standard to use. CXX_STANDARD=14 @@ -45,6 +63,7 @@ CWD=`pwd` # These scripts assume 'RUST_CARGO_EXE' has been set to the Rust # 'cargo' executable. +source "${CWD}/scripts/internal/build_openColorIO_linux.bash" source "${CWD}/scripts/internal/build_mmSolverLibs_linux.bash" source "${CWD}/scripts/internal/build_mmSolver_linux.bash" diff --git a/scripts/build_mmSolver_linux_maya2025.bash b/scripts/build_mmSolver_linux_maya2025.bash new file mode 100755 index 000000000..3f8af5925 --- /dev/null +++ b/scripts/build_mmSolver_linux_maya2025.bash @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +# +# Copyright (C) 2019, 2022, 2023, 2024 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# --------------------------------------------------------------------- +# +# Builds the Maya MatchMove Solver project. + +# Maya +MAYA_VERSION=2025 +MAYA_LOCATION=/usr/autodesk/maya2025/ + +# Executable names/paths used for build process. +PYTHON_EXE=python3.9 +CMAKE_EXE=cmake3 +RUST_CARGO_EXE=cargo + +# OpenColorIO specific options. +OPENCOLORIO_TARBALL_NAME="OpenColorIO-2.3.2.tar.gz" +OPENCOLORIO_TARBALL_EXTRACTED_DIR_NAME="OpenColorIO-2.3.2" +EXPAT_RELATIVE_CMAKE_DIR=lib64/cmake/expat-2.4.1/ +EXPAT_RELATIVE_LIB_PATH=lib64/libexpat.a +# yaml-cpp 0.7.0 +YAML_RELATIVE_CMAKE_DIR=share/cmake/yaml-cpp +YAML_RELATIVE_LIB_PATH=lib64/libyaml-cpp.a +PYSTRING_RELATIVE_LIB_PATH=lib64/libpystring.a +ZLIB_RELATIVE_LIB_PATH=lib/libz.a + +# Manually override OpenGL include headers, because CMake doesn't seem +# to automatically find OpenGL headers on RockyLinux8 (which is used +# in the Docker containers). +OPENGL_INCLUDE_DIR=/usr/include/ + +# Which version of the VFX platform are we "using"? (Maya doesn't +# currently conform to the VFX Platform.) +VFX_PLATFORM=2024 + +# C++ Standard to use. +CXX_STANDARD=14 + +# The -e flag causes the script to exit as soon as one command returns +# a non-zero exit code. +set -ev + +CWD=`pwd` + +# These scripts assume 'RUST_CARGO_EXE' has been set to the Rust +# 'cargo' executable. +source "${CWD}/scripts/internal/build_openColorIO_linux.bash" +source "${CWD}/scripts/internal/build_mmSolverLibs_linux.bash" +source "${CWD}/scripts/internal/build_mmSolver_linux.bash" + +cd ${CWD} diff --git a/scripts/build_mmSolver_mac_maya2018.bash b/scripts/build_mmSolver_mac_maya2018.bash old mode 100644 new mode 100755 diff --git a/scripts/build_mmSolver_mac_maya2019.bash b/scripts/build_mmSolver_mac_maya2019.bash old mode 100644 new mode 100755 diff --git a/scripts/build_mmSolver_windows64_maya2016.bat b/scripts/build_mmSolver_windows64_maya2016.bat index d5c4caf5b..d8c9bcb55 100644 --- a/scripts/build_mmSolver_windows64_maya2016.bat +++ b/scripts/build_mmSolver_windows64_maya2016.bat @@ -26,7 +26,7 @@ SETLOCAL :: Note: Do not enclose the MAYA_VERSION in quotes, it will :: lead to tears. SET MAYA_VERSION=2016 -SET MAYA_LOCATION="C:\Program Files\Autodesk\Maya2016" +SET MAYA_LOCATION="%PROGRAMFILES%\Autodesk\Maya2016" :: Executable names/paths used for build process. SET PYTHON_EXE=python @@ -45,6 +45,8 @@ if errorlevel 1 goto failed_to_build_mmsolverlibs CALL scripts\internal\build_mmSolver_windows64.bat if errorlevel 1 goto failed_to_build_mmsolver + +:: Successful return. exit /b 0 :failed_to_build_mmsolverlibs diff --git a/scripts/build_mmSolver_windows64_maya2016_extension2.bat b/scripts/build_mmSolver_windows64_maya2016_extension2.bat index 22027ed76..755e26307 100644 --- a/scripts/build_mmSolver_windows64_maya2016_extension2.bat +++ b/scripts/build_mmSolver_windows64_maya2016_extension2.bat @@ -26,7 +26,7 @@ SETLOCAL :: Note: Do not enclose the MAYA_VERSION in quotes, it will :: lead to tears. SET MAYA_VERSION=2016.5 -SET MAYA_LOCATION="C:\Program Files\Autodesk\Maya2016.5" +SET MAYA_LOCATION="%PROGRAMFILES%\Autodesk\Maya2016.5" :: Executable names/paths used for build process. SET PYTHON_EXE=python @@ -45,6 +45,8 @@ if errorlevel 1 goto failed_to_build_mmsolverlibs CALL scripts\internal\build_mmSolver_windows64.bat if errorlevel 1 goto failed_to_build_mmsolver + +:: Successful return. exit /b 0 :failed_to_build_mmsolverlibs diff --git a/scripts/build_mmSolver_windows64_maya2017.bat b/scripts/build_mmSolver_windows64_maya2017.bat index a53325b38..9aab313ab 100644 --- a/scripts/build_mmSolver_windows64_maya2017.bat +++ b/scripts/build_mmSolver_windows64_maya2017.bat @@ -26,7 +26,7 @@ SETLOCAL :: Note: Do not enclose the MAYA_VERSION in quotes, it will :: lead to tears. SET MAYA_VERSION=2017 -SET MAYA_LOCATION="C:\Program Files\Autodesk\Maya2017" +SET MAYA_LOCATION="%PROGRAMFILES%\Autodesk\Maya2017" :: Executable names/paths used for build process. SET PYTHON_EXE=python @@ -45,6 +45,8 @@ if errorlevel 1 goto failed_to_build_mmsolverlibs CALL scripts\internal\build_mmSolver_windows64.bat if errorlevel 1 goto failed_to_build_mmsolver + +:: Successful return. exit /b 0 :failed_to_build_mmsolverlibs diff --git a/scripts/build_mmSolver_windows64_maya2018.bat b/scripts/build_mmSolver_windows64_maya2018.bat index a840fa9ca..9751665b6 100644 --- a/scripts/build_mmSolver_windows64_maya2018.bat +++ b/scripts/build_mmSolver_windows64_maya2018.bat @@ -26,19 +26,36 @@ SETLOCAL :: Note: Do not enclose the MAYA_VERSION in quotes, it will :: lead to tears. SET MAYA_VERSION=2018 -SET MAYA_LOCATION="C:\Program Files\Autodesk\Maya2018" +SET MAYA_LOCATION="%PROGRAMFILES%\Autodesk\Maya2018" :: Executable names/paths used for build process. SET PYTHON_EXE=python SET CMAKE_EXE=cmake SET RUST_CARGO_EXE=cargo +:: OpenColorIO specific options. +:: +:: https://github.com/AcademySoftwareFoundation/OpenColorIO/releases/tag/v2.2.1 +:: https://github.com/AcademySoftwareFoundation/OpenColorIO/archive/refs/tags/v2.2.1.tar.gz +SET OPENCOLORIO_TARBALL_NAME=OpenColorIO-2.2.1.tar.gz +SET OPENCOLORIO_TARBALL_EXTRACTED_DIR_NAME=OpenColorIO-2.2.1 +SET EXPAT_RELATIVE_LIB_PATH=lib\cmake\expat-2.2.8\ +:: yaml-cpp 0.6.3 +SET YAML_RELATIVE_CMAKE_DIR=CMake\ + +:: Which version of the VFX platform are we "using"? (Maya doesn't +:: currently conform to the VFX Platform.) +SET VFX_PLATFORM=2017 + :: C++ Standard to use. SET CXX_STANDARD=11 :: Setup Compiler environment. Change for your install path as needed. CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 +CALL scripts\internal\build_opencolorio_windows64.bat +if errorlevel 1 goto failed_to_build_opencolorio + :: This script assumes 'RUST_CARGO_EXE' has been set to the Rust :: 'cargo' executable. CALL scripts\internal\build_mmSolverLibs_windows64.bat @@ -46,8 +63,14 @@ if errorlevel 1 goto failed_to_build_mmsolverlibs CALL scripts\internal\build_mmSolver_windows64.bat if errorlevel 1 goto failed_to_build_mmsolver + +:: Successful return. exit /b 0 +:failed_to_build_opencolorio +echo Failed to build OpenColorIO dependency. +exit /b 1 + :failed_to_build_mmsolverlibs echo Failed to build MM Solver Library entry point. exit /b 1 diff --git a/scripts/build_mmSolver_windows64_maya2019.bat b/scripts/build_mmSolver_windows64_maya2019.bat index 81c55e387..9d48c8d60 100644 --- a/scripts/build_mmSolver_windows64_maya2019.bat +++ b/scripts/build_mmSolver_windows64_maya2019.bat @@ -26,19 +26,36 @@ SETLOCAL :: Note: Do not enclose the MAYA_VERSION in quotes, it will :: lead to tears. SET MAYA_VERSION=2019 -SET MAYA_LOCATION="C:\Program Files\Autodesk\Maya2019" +SET MAYA_LOCATION="%PROGRAMFILES%\Autodesk\Maya2019" :: Executable names/paths used for build process. SET PYTHON_EXE=python SET CMAKE_EXE=cmake SET RUST_CARGO_EXE=cargo +:: OpenColorIO specific options. +:: +:: https://github.com/AcademySoftwareFoundation/OpenColorIO/releases/tag/v2.2.1 +:: https://github.com/AcademySoftwareFoundation/OpenColorIO/archive/refs/tags/v2.2.1.tar.gz +SET OPENCOLORIO_TARBALL_NAME=OpenColorIO-2.2.1.tar.gz +SET OPENCOLORIO_TARBALL_EXTRACTED_DIR_NAME=OpenColorIO-2.2.1 +SET EXPAT_RELATIVE_LIB_PATH=lib\cmake\expat-2.2.8\ +:: yaml-cpp 0.6.3 +SET YAML_RELATIVE_CMAKE_DIR=CMake\ + +:: Which version of the VFX platform are we "using"? (Maya doesn't +:: currently conform to the VFX Platform.) +SET VFX_PLATFORM=2018 + :: C++ Standard to use. SET CXX_STANDARD=11 :: Setup Compiler environment. Change for your install path as needed. CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" amd64 +CALL scripts\internal\build_opencolorio_windows64.bat +if errorlevel 1 goto failed_to_build_opencolorio + :: This script assumes 'RUST_CARGO_EXE' has been set to the Rust :: 'cargo' executable. CALL scripts\internal\build_mmSolverLibs_windows64.bat @@ -46,8 +63,14 @@ if errorlevel 1 goto failed_to_build_mmsolverlibs CALL scripts\internal\build_mmSolver_windows64.bat if errorlevel 1 goto failed_to_build_mmsolver + +:: Successful return. exit /b 0 +:failed_to_build_opencolorio +echo Failed to build OpenColorIO dependency. +exit /b 1 + :failed_to_build_mmsolverlibs echo Failed to build MM Solver Library entry point. exit /b 1 diff --git a/scripts/build_mmSolver_windows64_maya2020.bat b/scripts/build_mmSolver_windows64_maya2020.bat index 426656711..e0ed5f49f 100644 --- a/scripts/build_mmSolver_windows64_maya2020.bat +++ b/scripts/build_mmSolver_windows64_maya2020.bat @@ -26,19 +26,41 @@ SETLOCAL :: Note: Do not enclose the MAYA_VERSION in quotes, it will :: lead to tears. SET MAYA_VERSION=2020 -SET MAYA_LOCATION="C:\Program Files\Autodesk\Maya2020" +SET MAYA_LOCATION="%PROGRAMFILES%\Autodesk\Maya2020" :: Executable names/paths used for build process. SET PYTHON_EXE=python SET CMAKE_EXE=cmake SET RUST_CARGO_EXE=cargo +:: OpenColorIO specific options. +:: +:: https://github.com/AcademySoftwareFoundation/OpenColorIO/releases/tag/v2.2.1 +:: https://github.com/AcademySoftwareFoundation/OpenColorIO/archive/refs/tags/v2.2.1.tar.gz +SET OPENCOLORIO_TARBALL_NAME=OpenColorIO-2.2.1.tar.gz +SET OPENCOLORIO_TARBALL_EXTRACTED_DIR_NAME=OpenColorIO-2.2.1 +SET EXPAT_RELATIVE_CMAKE_DIR=lib\cmake\expat-2.4.1\ +SET EXPAT_RELATIVE_LIB_PATH=lib\expatMD.lib +SET ZLIB_RELATIVE_LIB_PATH=lib\zlibstatic.lib +SET MINIZIP_RELATIVE_CMAKE_DIR=lib\cmake\minizip-ng +:: yaml-cpp 0.7.0 +SET YAML_RELATIVE_CMAKE_DIR=share\cmake\yaml-cpp\ +SET YAML_RELATIVE_LIB_PATH=lib\yaml-cpp.lib +SET PYSTRING_RELATIVE_LIB_PATH=lib\pystring.lib + +:: Which version of the VFX platform are we "using"? (Maya doesn't +:: currently conform to the VFX Platform.) +SET VFX_PLATFORM=2019 + :: C++ Standard to use. SET CXX_STANDARD=11 :: Setup Compiler environment. Change for your install path as needed. CALL "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" amd64 +CALL scripts\internal\build_opencolorio_windows64.bat +if errorlevel 1 goto failed_to_build_opencolorio + :: This script assumes 'RUST_CARGO_EXE' has been set to the Rust :: 'cargo' executable. CALL scripts\internal\build_mmSolverLibs_windows64.bat @@ -46,8 +68,14 @@ if errorlevel 1 goto failed_to_build_mmsolverlibs CALL scripts\internal\build_mmSolver_windows64.bat if errorlevel 1 goto failed_to_build_mmsolver + +:: Successful return. exit /b 0 +:failed_to_build_opencolorio +echo Failed to build OpenColorIO dependency. +exit /b 1 + :failed_to_build_mmsolverlibs echo Failed to build MM Solver Library entry point. exit /b 1 diff --git a/scripts/build_mmSolver_windows64_maya2022.bat b/scripts/build_mmSolver_windows64_maya2022.bat index 5cd4a1a99..82a09cebc 100644 --- a/scripts/build_mmSolver_windows64_maya2022.bat +++ b/scripts/build_mmSolver_windows64_maya2022.bat @@ -26,19 +26,41 @@ SETLOCAL :: Note: Do not enclose the MAYA_VERSION in quotes, it will :: lead to tears. SET MAYA_VERSION=2022 -SET MAYA_LOCATION="C:\Program Files\Autodesk\Maya2022" +SET MAYA_LOCATION="%PROGRAMFILES%\Autodesk\Maya2022" :: Executable names/paths used for build process. SET PYTHON_EXE=python SET CMAKE_EXE=cmake SET RUST_CARGO_EXE=cargo +:: OpenColorIO specific options. +:: +:: https://github.com/AcademySoftwareFoundation/OpenColorIO/releases/tag/v2.2.1 +:: https://github.com/AcademySoftwareFoundation/OpenColorIO/archive/refs/tags/v2.2.1.tar.gz +SET OPENCOLORIO_TARBALL_NAME=OpenColorIO-2.2.1.tar.gz +SET OPENCOLORIO_TARBALL_EXTRACTED_DIR_NAME=OpenColorIO-2.2.1 +SET EXPAT_RELATIVE_CMAKE_DIR=lib\cmake\expat-2.4.1\ +SET EXPAT_RELATIVE_LIB_PATH=lib\expatMD.lib +SET ZLIB_RELATIVE_LIB_PATH=lib\zlibstatic.lib +SET MINIZIP_RELATIVE_CMAKE_DIR=lib\cmake\minizip-ng +:: yaml-cpp 0.7.0 +SET YAML_RELATIVE_CMAKE_DIR=share\cmake\yaml-cpp\ +SET YAML_RELATIVE_LIB_PATH=lib\yaml-cpp.lib +SET PYSTRING_RELATIVE_LIB_PATH=lib\pystring.lib + +:: Which version of the VFX platform are we "using"? (Maya doesn't +:: currently conform to the VFX Platform.) +SET VFX_PLATFORM=2021 + :: C++ Standard to use. SET CXX_STANDARD=14 :: Setup Compiler environment. Change for your install path as needed. CALL "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" amd64 +CALL scripts\internal\build_opencolorio_windows64.bat +if errorlevel 1 goto failed_to_build_opencolorio + :: This script assumes 'RUST_CARGO_EXE' has been set to the Rust :: 'cargo' executable. CALL scripts\internal\build_mmSolverLibs_windows64.bat @@ -46,8 +68,14 @@ if errorlevel 1 goto failed_to_build_mmsolverlibs CALL scripts\internal\build_mmSolver_windows64.bat if errorlevel 1 goto failed_to_build_mmsolver + +:: Successful return. exit /b 0 +:failed_to_build_opencolorio +echo Failed to build OpenColorIO dependency. +exit /b 1 + :failed_to_build_mmsolverlibs echo Failed to build MM Solver Library entry point. exit /b 1 diff --git a/scripts/build_mmSolver_windows64_maya2023.bat b/scripts/build_mmSolver_windows64_maya2023.bat index ddcabf684..38d2a0d3d 100644 --- a/scripts/build_mmSolver_windows64_maya2023.bat +++ b/scripts/build_mmSolver_windows64_maya2023.bat @@ -26,19 +26,41 @@ SETLOCAL :: Note: Do not enclose the MAYA_VERSION in quotes, it will :: lead to tears. SET MAYA_VERSION=2023 -SET MAYA_LOCATION="C:\Program Files\Autodesk\Maya2023" +SET MAYA_LOCATION="%PROGRAMFILES%\Autodesk\Maya2023" :: Executable names/paths used for build process. SET PYTHON_EXE=python SET CMAKE_EXE=cmake SET RUST_CARGO_EXE=cargo +:: OpenColorIO specific options. +:: +:: https://github.com/AcademySoftwareFoundation/OpenColorIO/releases/tag/v2.2.1 +:: https://github.com/AcademySoftwareFoundation/OpenColorIO/archive/refs/tags/v2.2.1.tar.gz +SET OPENCOLORIO_TARBALL_NAME=OpenColorIO-2.2.1.tar.gz +SET OPENCOLORIO_TARBALL_EXTRACTED_DIR_NAME=OpenColorIO-2.2.1 +SET EXPAT_RELATIVE_CMAKE_DIR=lib\cmake\expat-2.4.1\ +SET EXPAT_RELATIVE_LIB_PATH=lib\expatMD.lib +SET ZLIB_RELATIVE_LIB_PATH=lib\zlibstatic.lib +SET MINIZIP_RELATIVE_CMAKE_DIR=lib\cmake\minizip-ng +:: yaml-cpp 0.7.0 +SET YAML_RELATIVE_CMAKE_DIR=share\cmake\yaml-cpp\ +SET YAML_RELATIVE_LIB_PATH=lib\yaml-cpp.lib +SET PYSTRING_RELATIVE_LIB_PATH=lib\pystring.lib + +:: Which version of the VFX platform are we "using"? (Maya doesn't +:: currently conform to the VFX Platform.) +SET VFX_PLATFORM=2022 + :: C++ Standard to use. SET CXX_STANDARD=14 :: Setup Compiler environment. Change for your install path as needed. CALL "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvarsall.bat" amd64 +CALL scripts\internal\build_opencolorio_windows64.bat +if errorlevel 1 goto failed_to_build_opencolorio + :: This script assumes 'RUST_CARGO_EXE' has been set to the Rust :: 'cargo' executable. CALL scripts\internal\build_mmSolverLibs_windows64.bat @@ -46,8 +68,14 @@ if errorlevel 1 goto failed_to_build_mmsolverlibs CALL scripts\internal\build_mmSolver_windows64.bat if errorlevel 1 goto failed_to_build_mmsolver + +:: Successful return. exit /b 0 +:failed_to_build_opencolorio +echo Failed to build OpenColorIO dependency. +exit /b 1 + :failed_to_build_mmsolverlibs echo Failed to build MM Solver Library entry point. exit /b 1 diff --git a/scripts/build_mmSolver_windows64_maya2024.bat b/scripts/build_mmSolver_windows64_maya2024.bat index dbd4e3814..df60950c7 100644 --- a/scripts/build_mmSolver_windows64_maya2024.bat +++ b/scripts/build_mmSolver_windows64_maya2024.bat @@ -1,7 +1,7 @@ @ECHO OFF SETLOCAL :: -:: Copyright (C) 2019 David Cattermole. +:: Copyright (C) 2019, 2024 David Cattermole. :: :: This file is part of mmSolver. :: @@ -26,18 +26,40 @@ SETLOCAL :: Note: Do not enclose the MAYA_VERSION in quotes, it will :: lead to tears. SET MAYA_VERSION=2024 -SET MAYA_LOCATION="C:\Program Files\Autodesk\Maya2024" +SET MAYA_LOCATION="%PROGRAMFILES%\Autodesk\Maya2024" :: Executable names/paths used for build process. SET PYTHON_EXE=python SET CMAKE_EXE=cmake SET RUST_CARGO_EXE=cargo +:: OpenColorIO specific options. +:: +:: https://github.com/AcademySoftwareFoundation/OpenColorIO/releases/tag/v2.2.1 +:: https://github.com/AcademySoftwareFoundation/OpenColorIO/archive/refs/tags/v2.2.1.tar.gz +SET OPENCOLORIO_TARBALL_NAME=OpenColorIO-2.2.1.tar.gz +SET OPENCOLORIO_TARBALL_EXTRACTED_DIR_NAME=OpenColorIO-2.2.1 +SET EXPAT_RELATIVE_CMAKE_DIR=lib\cmake\expat-2.4.1\ +SET EXPAT_RELATIVE_LIB_PATH=lib\expatMD.lib +SET ZLIB_RELATIVE_LIB_PATH=lib\zlibstatic.lib +SET MINIZIP_RELATIVE_CMAKE_DIR=lib\cmake\minizip-ng +:: yaml-cpp 0.7.0 +SET YAML_RELATIVE_CMAKE_DIR=share\cmake\yaml-cpp\ +SET YAML_RELATIVE_LIB_PATH=lib\yaml-cpp.lib +SET PYSTRING_RELATIVE_LIB_PATH=lib\pystring.lib + +:: Which version of the VFX platform are we "using"? (Maya doesn't +:: currently conform to the VFX Platform.) +SET VFX_PLATFORM=2023 + :: C++ Standard to use. SET CXX_STANDARD=14 :: Setup Compiler environment. Change for your install path as needed. -CALL "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" amd64 +CALL "%PROGRAMFILES%\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" amd64 + +CALL scripts\internal\build_opencolorio_windows64.bat +if errorlevel 1 goto failed_to_build_opencolorio :: This script assumes 'RUST_CARGO_EXE' has been set to the Rust :: 'cargo' executable. @@ -46,8 +68,14 @@ if errorlevel 1 goto failed_to_build_mmsolverlibs CALL scripts\internal\build_mmSolver_windows64.bat if errorlevel 1 goto failed_to_build_mmsolver + +:: Successful return. exit /b 0 +:failed_to_build_opencolorio +echo Failed to build OpenColorIO dependency. +exit /b 1 + :failed_to_build_mmsolverlibs echo Failed to build MM Solver Library entry point. exit /b 1 diff --git a/scripts/internal/build_mmSolverLibs_linux.bash b/scripts/internal/build_mmSolverLibs_linux.bash index 351f9251b..615120c1a 100644 --- a/scripts/internal/build_mmSolverLibs_linux.bash +++ b/scripts/internal/build_mmSolverLibs_linux.bash @@ -1,6 +1,6 @@ #!/usr/bin/env bash # -# Copyright (C) 2022, 2023 David Cattermole. +# Copyright (C) 2022, 2023, 2024 David Cattermole. # # This file is part of mmSolver. # @@ -52,6 +52,10 @@ if [ ${BUILD_TYPE}=="Release" ]; then BUILD_TYPE_DIR="release" fi +# Allows you to see the build command lines, to help debugging build +# problems. Set to ON to enable, and OFF to disable. +MMSOLVER_BUILD_VERBOSE=OFF + # Where to find the mmsolverlibs Rust libraries and headers. MMSOLVERLIBS_INSTALL_PATH="${BUILD_DIR_BASE}/build_mmsolverlibs/install/maya${MAYA_VERSION}_linux/" MMSOLVERLIBS_ROOT="${PROJECT_ROOT}/lib" @@ -60,6 +64,30 @@ MMSOLVERLIBS_CPP_TARGET_DIR="${BUILD_DIR_BASE}/build_mmsolverlibs/rust_linux_may MMSOLVERLIBS_LIB_DIR="${MMSOLVERLIBS_CPP_TARGET_DIR}/${BUILD_TYPE_DIR}" MMSOLVERLIBS_INCLUDE_DIR="${MMSOLVERLIBS_ROOT}/include" +# Paths for dependencies. +EXTERNAL_BUILD_DIR="${BUILD_DIR_BASE}/build_opencolorio/cmake_linux_maya${MAYA_VERSION}_${BUILD_TYPE}/ext/dist" +OPENCOLORIO_INSTALL_DIR="${BUILD_DIR_BASE}/build_opencolorio/install/maya${MAYA_VERSION}_linux/" +OPENCOLORIO_CMAKE_CONFIG_DIR="${OPENCOLORIO_INSTALL_DIR}/lib64/cmake/OpenColorIO/" +OPENCOLORIO_CMAKE_FIND_MODULES_DIR="${PROJECT_ROOT}/external/working/maya${MAYA_VERSION}_linux/${OPENCOLORIO_TARBALL_EXTRACTED_DIR_NAME}/share/cmake/modules" + +expat_DIR="${EXTERNAL_BUILD_DIR}/${EXPAT_RELATIVE_CMAKE_DIR}" +expat_INCLUDE_DIR="${EXTERNAL_BUILD_DIR}/include/" +expat_LIBRARY="${EXTERNAL_BUILD_DIR}/${EXPAT_RELATIVE_LIB_PATH}" + +pystring_LIBRARY="${EXTERNAL_BUILD_DIR}/${PYSTRING_RELATIVE_LIB_PATH}" +pystring_INCLUDE_DIR="${EXTERNAL_BUILD_DIR}/include" + +yaml_DIR="${EXTERNAL_BUILD_DIR}/${YAML_RELATIVE_CMAKE_DIR}" +yaml_LIBRARY="${EXTERNAL_BUILD_DIR}/${YAML_RELATIVE_LIB_PATH}" +yaml_INCLUDE_DIR="${EXTERNAL_BUILD_DIR}/include/" + +Imath_DIR="${EXTERNAL_BUILD_DIR}/lib64/cmake/Imath" + +ZLIB_LIBRARY="${EXTERNAL_BUILD_DIR}/${ZLIB_RELATIVE_LIB_PATH}" +ZLIB_INCLUDE_DIR="${EXTERNAL_BUILD_DIR}/include/" + +minizip_DIR="${EXTERNAL_BUILD_DIR}/lib64/cmake/minizip-ng" + MMSOLVERLIBS_BUILD_TESTS=1 echo "Building mmsolverlibs... (${MMSOLVERLIBS_ROOT})" @@ -81,9 +109,9 @@ then # './lib/cppbind/mmlens/Cargo.toml' # './lib/cppbind/mmcore/Cargo.toml' # './scripts/internal/build_mmSolverLibs_windows64.bat' - ${RUST_CARGO_EXE} install cxxbridge-cmd --version 1.0.75 + ${RUST_CARGO_EXE} install cxxbridge-cmd --version 1.0.128 fi -MMSOLVERLIBS_CXXBRIDGE_EXE="${HOME}\.cargo\bin\cxxbridge" +MMSOLVERLIBS_CXXBRIDGE_EXE="${HOME}/.cargo/bin/cxxbridge" cd ${MMSOLVERLIBS_RUST_ROOT} ${RUST_CARGO_EXE} build ${RELEASE_FLAG} --target-dir ${MMSOLVERLIBS_CPP_TARGET_DIR} @@ -101,15 +129,34 @@ cd ${BUILD_DIR} export MAYA_VERSION=${MAYA_VERSION} ${CMAKE_EXE} \ + -DBUILD_SHARED_LIBS=OFF \ -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ -DCMAKE_INSTALL_PREFIX=${MMSOLVERLIBS_INSTALL_PATH} \ -DCMAKE_POSITION_INDEPENDENT_CODE=1 \ -DCMAKE_CXX_STANDARD=${CXX_STANDARD} \ + -DCMAKE_MODULE_PATH=${OPENCOLORIO_CMAKE_FIND_MODULES_DIR} \ + -DCMAKE_VERBOSE_MAKEFILE=${MMSOLVER_BUILD_VERBOSE} \ + -DMMSOLVER_VFX_PLATFORM=${VFX_PLATFORM} \ -DMMSOLVERLIBS_CXXBRIDGE_EXE=${MMSOLVERLIBS_CXXBRIDGE_EXE} \ -DMMSOLVERLIBS_BUILD_TESTS=${MMSOLVERLIBS_BUILD_TESTS} \ -DMMSOLVERLIBS_LIB_DIR=${MMSOLVERLIBS_LIB_DIR} \ -Dldpk_URL=${LDPK_URL} \ - -DBUILD_SHARED_LIBS=OFF \ + -DOpenColorIO_DIR=${OPENCOLORIO_CMAKE_CONFIG_DIR} \ + -DOCIO_INSTALL_EXT_PACKAGES=NONE \ + -Dexpat_DIR=${expat_DIR} \ + -Dexpat_LIBRARY=${expat_LIBRARY} \ + -Dexpat_INCLUDE_DIR=${expat_INCLUDE_DIR} \ + -Dexpat_USE_STATIC_LIBS=TRUE \ + -Dpystring_LIBRARY=${pystring_LIBRARY} \ + -Dpystring_INCLUDE_DIR=${pystring_INCLUDE_DIR} \ + -Dyaml-cpp_DIR=${yaml_DIR} \ + -Dyaml-cpp_LIBRARY=${yaml_LIBRARY} \ + -Dyaml-cpp_INCLUDE_DIR=${yaml_INCLUDE_DIR} \ + -DImath_DIR=${Imath_DIR} \ + -DZLIB_LIBRARY=${ZLIB_LIBRARY} \ + -DZLIB_INCLUDE_DIR=${ZLIB_INCLUDE_DIR} \ + -DZLIB_STATIC_LIBRARY=ON \ + -Dminizip-ng_DIR=${minizip_DIR} \ ${MMSOLVERLIBS_ROOT} ${CMAKE_EXE} --build . --parallel diff --git a/scripts/internal/build_mmSolverLibs_windows64.bat b/scripts/internal/build_mmSolverLibs_windows64.bat index c6d5302ac..267b3cdb8 100644 --- a/scripts/internal/build_mmSolverLibs_windows64.bat +++ b/scripts/internal/build_mmSolverLibs_windows64.bat @@ -1,6 +1,6 @@ @ECHO OFF :: -:: Copyright (C) 2022, 2023 David Cattermole. +:: Copyright (C) 2022, 2023, 2024 David Cattermole. :: :: This file is part of mmSolver. :: @@ -18,7 +18,7 @@ :: along with mmSolver. If not, see . :: --------------------------------------------------------------------- :: -:: Build the mmscenegraph library. +:: Build the mmSolverLibs library. :: :: NOTE: Do not call this script directly! This file should be called by :: the build_mmSolver_windows64_mayaXXXX.bat files. @@ -43,16 +43,47 @@ IF "%BUILD_TYPE%"=="Release" ( SET BUILD_TYPE_DIR=release ) +:: Allows you to see the build command lines, to help debugging build +:: problems. Set to ON to enable, and OFF to disable. +SET MMSOLVER_BUILD_VERBOSE=OFF + :: Where to find the mmsolverlibs Rust libraries and headers. SET MMSOLVERLIBS_INSTALL_PATH=%BUILD_DIR_BASE%\build_mmsolverlibs\install\maya%MAYA_VERSION%_windows64\ SET MMSOLVERLIBS_ROOT=%PROJECT_ROOT%\lib SET MMSOLVERLIBS_RUST_ROOT=%MMSOLVERLIBS_ROOT%\mmsolverlibs -SET MMSOLVERLIBS_CPP_TARGET_DIR="%BUILD_DIR_BASE%\build_mmsolverlibs\rust_windows64_maya%MAYA_VERSION%" -SET MMSOLVERLIBS_LIB_DIR="%MMSOLVERLIBS_CPP_TARGET_DIR%\%BUILD_TYPE_DIR%" -SET MMSOLVERLIBS_INCLUDE_DIR="%MMSOLVERLIBS_ROOT%\include" +SET MMSOLVERLIBS_CPP_TARGET_DIR=%BUILD_DIR_BASE%\build_mmsolverlibs\rust_windows64_maya%MAYA_VERSION% +SET MMSOLVERLIBS_LIB_DIR=%MMSOLVERLIBS_CPP_TARGET_DIR%\%BUILD_TYPE_DIR% +SET MMSOLVERLIBS_INCLUDE_DIR=%MMSOLVERLIBS_ROOT%\include SET MMSOLVERLIBS_BUILD_TESTS=1 +:: Paths for dependencies. +SET EXTERNAL_BUILD_DIR=%BUILD_DIR_BASE%\build_opencolorio\cmake_win64_maya%MAYA_VERSION%_%BUILD_TYPE%\ext\dist +SET OPENCOLORIO_INSTALL_DIR=%BUILD_DIR_BASE%\build_opencolorio\install\maya%MAYA_VERSION%_windows64\ +SET OPENCOLORIO_CMAKE_CONFIG_DIR=%OPENCOLORIO_INSTALL_DIR%\lib\cmake\OpenColorIO\ +SET OPENCOLORIO_CMAKE_FIND_MODULES_DIR=%PROJECT_ROOT%\external\working\maya%MAYA_VERSION%_windows64\%OPENCOLORIO_TARBALL_EXTRACTED_DIR_NAME%\share\cmake\modules +:: Convert back-slashes to forward-slashes. +:: https://stackoverflow.com/questions/23542453/change-backslash-to-forward-slash-in-windows-batch-file +SET "OPENCOLORIO_CMAKE_FIND_MODULES_DIR=%OPENCOLORIO_CMAKE_FIND_MODULES_DIR:\=/%" + +SET expat_DIR=%EXTERNAL_BUILD_DIR%\%EXPAT_RELATIVE_CMAKE_DIR% +SET expat_INCLUDE_DIR=%EXTERNAL_BUILD_DIR%\include\ +SET expat_LIBRARY=%EXTERNAL_BUILD_DIR%\%EXPAT_RELATIVE_LIB_PATH% + +SET pystring_INCLUDE_DIR=%EXTERNAL_BUILD_DIR%\include +SET pystring_LIBRARY=%EXTERNAL_BUILD_DIR%\%PYSTRING_RELATIVE_LIB_PATH% + +SET yaml_DIR=%EXTERNAL_BUILD_DIR%\%YAML_RELATIVE_CMAKE_DIR% +SET yaml_LIBRARY=%EXTERNAL_BUILD_DIR%\%YAML_RELATIVE_LIB_PATH% +SET yaml_INCLUDE_DIR=%EXTERNAL_BUILD_DIR%\include\ + +SET Imath_DIR=%EXTERNAL_BUILD_DIR%\lib\cmake\Imath + +SET ZLIB_INCLUDE_DIR=%EXTERNAL_BUILD_DIR%\include\ +SET ZLIB_LIBRARY=%EXTERNAL_BUILD_DIR%\%ZLIB_RELATIVE_LIB_PATH% + +SET minizip_DIR=%EXTERNAL_BUILD_DIR%\%MINIZIP_RELATIVE_CMAKE_DIR% + ECHO Building mmsolverlibs... (%MMSOLVERLIBS_ROOT%) :: Check if 'cxxbridge.exe' is installed or not, and then install it if @@ -72,8 +103,8 @@ IF ERRORLEVEL 1 ( :: './lib/cppbind/mmlens/Cargo.toml' :: './lib/cppbind/mmcore/Cargo.toml' :: './scripts/internal/build_mmSolverLibs_windows64.bat' - :: './scripts/internal/build_rust_library_linux.bash' - %RUST_CARGO_EXE% install cxxbridge-cmd --version 1.0.75 + :: './scripts/internal/build_mmSolverLibs_linux.bash' + %RUST_CARGO_EXE% install cxxbridge-cmd --version 1.0.128 ) SET MMSOLVERLIBS_CXXBRIDGE_EXE="%USERPROFILE%\.cargo\bin\cxxbridge.exe" :: Convert back-slashes to forward-slashes. @@ -121,17 +152,36 @@ MKDIR "%BUILD_DIR_NAME%" CHDIR "%BUILD_DIR%" %CMAKE_EXE% -G %CMAKE_GENERATOR% ^ + -DBUILD_SHARED_LIBS=OFF ^ -DCMAKE_BUILD_TYPE=%BUILD_TYPE% ^ -DCMAKE_INSTALL_PREFIX=%MMSOLVERLIBS_INSTALL_PATH% ^ -DCMAKE_IGNORE_PATH=%IGNORE_INCLUDE_DIRECTORIES% ^ -DCMAKE_CXX_STANDARD=%CXX_STANDARD% ^ -DCMAKE_C_COMPILER=%CMAKE_C_COMPILER% ^ -DCMAKE_CXX_COMPILER=%CMAKE_CXX_COMPILER% ^ + -DCMAKE_MODULE_PATH=%OPENCOLORIO_CMAKE_FIND_MODULES_DIR% ^ + -DCMAKE_VERBOSE_MAKEFILE=%MMSOLVER_BUILD_VERBOSE% ^ + -DMMSOLVER_VFX_PLATFORM=%VFX_PLATFORM% ^ -DMMSOLVERLIBS_CXXBRIDGE_EXE=%MMSOLVERLIBS_CXXBRIDGE_EXE% ^ -DMMSOLVERLIBS_BUILD_TESTS=%MMSOLVERLIBS_BUILD_TESTS% ^ -DMMSOLVERLIBS_LIB_DIR=%MMSOLVERLIBS_LIB_DIR% ^ -Dldpk_URL=%LDPK_URL% ^ - -DBUILD_SHARED_LIBS=OFF ^ + -DOpenColorIO_DIR=%OPENCOLORIO_CMAKE_CONFIG_DIR% ^ + -DOCIO_INSTALL_EXT_PACKAGES=NONE ^ + -Dexpat_DIR=%expat_DIR% ^ + -Dexpat_LIBRARY=%expat_LIBRARY% ^ + -Dexpat_INCLUDE_DIR=%expat_INCLUDE_DIR% ^ + -Dexpat_USE_STATIC_LIBS=TRUE ^ + -Dpystring_LIBRARY=%pystring_LIBRARY% ^ + -Dpystring_INCLUDE_DIR=%pystring_INCLUDE_DIR% ^ + -Dyaml-cpp_DIR=%yaml_DIR% ^ + -Dyaml-cpp_LIBRARY=%yaml_LIBRARY% ^ + -Dyaml-cpp_INCLUDE_DIR=%yaml_INCLUDE_DIR% ^ + -DImath_DIR=%Imath_DIR% ^ + -DZLIB_LIBRARY=%ZLIB_LIBRARY% ^ + -DZLIB_INCLUDE_DIR=%ZLIB_INCLUDE_DIR% ^ + -DZLIB_STATIC_LIBRARY=ON ^ + -Dminizip-ng_DIR=%minizip_DIR% ^ %MMSOLVERLIBS_ROOT% if errorlevel 1 goto failed_to_generate_cpp diff --git a/scripts/internal/build_mmSolver_linux.bash b/scripts/internal/build_mmSolver_linux.bash index 9a91b4762..c321d3728 100644 --- a/scripts/internal/build_mmSolver_linux.bash +++ b/scripts/internal/build_mmSolver_linux.bash @@ -1,6 +1,6 @@ #!/usr/bin/env bash # -# Copyright (C) 2019, 2022 David Cattermole. +# Copyright (C) 2019, 2022, 2024 David Cattermole. # # This file is part of mmSolver. # @@ -76,6 +76,10 @@ MMSOLVER_BUILD_ICONS=1 MMSOLVER_BUILD_CONFIG=1 MMSOLVER_BUILD_TESTS=0 +# Allows you to see the build command lines, to help debugging build +# problems. Set to ON to enable, and OFF to disable. +MMSOLVER_BUILD_VERBOSE=OFF + # Path to this script. DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" # The root of this project. @@ -86,11 +90,35 @@ echo "Project Root: ${PROJECT_ROOT}" PYTHON_VIRTUAL_ENV_DIR_NAME="python_venv_linux_maya${MAYA_VERSION}" source "${PROJECT_ROOT}/scripts/internal/python_venv_activate.bash" -# Paths for dependencies. +# Where to find the mmsolverlibs Rust libraries and headers. MMSOLVERLIBS_INSTALL_DIR="${BUILD_DIR_BASE}/build_mmsolverlibs/install/maya${MAYA_VERSION}_linux/" MMSOLVERLIBS_CMAKE_CONFIG_DIR="${MMSOLVERLIBS_INSTALL_DIR}/lib64/cmake/mmsolverlibs_cpp" MMSOLVERLIBS_RUST_DIR="${BUILD_DIR_BASE}/build_mmsolverlibs/rust_linux_maya${MAYA_VERSION}/${BUILD_TYPE_DIR}" +# Paths for dependencies. +EXTERNAL_BUILD_DIR="${BUILD_DIR_BASE}/build_opencolorio/cmake_linux_maya${MAYA_VERSION}_${BUILD_TYPE}/ext/dist" +OPENCOLORIO_INSTALL_DIR="${BUILD_DIR_BASE}/build_opencolorio/install/maya${MAYA_VERSION}_linux/" +OPENCOLORIO_CMAKE_CONFIG_DIR="${OPENCOLORIO_INSTALL_DIR}/lib64/cmake/OpenColorIO/" +OPENCOLORIO_CMAKE_FIND_MODULES_DIR="${PROJECT_ROOT}/external/working/maya${MAYA_VERSION}_linux/${OPENCOLORIO_TARBALL_EXTRACTED_DIR_NAME}/share/cmake/modules" + +expat_DIR="${EXTERNAL_BUILD_DIR}/${EXPAT_RELATIVE_CMAKE_DIR}" +expat_INCLUDE_DIR="${EXTERNAL_BUILD_DIR}/include/" +expat_LIBRARY="${EXTERNAL_BUILD_DIR}/${EXPAT_RELATIVE_LIB_PATH}" + +pystring_LIBRARY="${EXTERNAL_BUILD_DIR}/${PYSTRING_RELATIVE_LIB_PATH}" +pystring_INCLUDE_DIR="${EXTERNAL_BUILD_DIR}/include" + +yaml_DIR="${EXTERNAL_BUILD_DIR}/${YAML_RELATIVE_CMAKE_DIR}" +yaml_LIBRARY="${EXTERNAL_BUILD_DIR}/${YAML_RELATIVE_LIB_PATH}" +yaml_INCLUDE_DIR="${EXTERNAL_BUILD_DIR}/include/" + +Imath_DIR="${EXTERNAL_BUILD_DIR}/lib64/cmake/Imath" + +ZLIB_LIBRARY="${EXTERNAL_BUILD_DIR}/${ZLIB_RELATIVE_LIB_PATH}" +ZLIB_INCLUDE_DIR="${EXTERNAL_BUILD_DIR}/include/" + +minizip_DIR="${EXTERNAL_BUILD_DIR}/lib64/cmake/minizip-ng" + # We don't want to find system packages. CMAKE_IGNORE_PATH="/lib;/lib64;/usr;/usr/lib;/usr/lib64;/usr/local;/usr/local/lib;/usr/local/lib64;" @@ -108,7 +136,10 @@ ${CMAKE_EXE} \ -DCMAKE_IGNORE_PATH=${CMAKE_IGNORE_PATH} \ -DCMAKE_POSITION_INDEPENDENT_CODE=1 \ -DCMAKE_CXX_STANDARD=${CXX_STANDARD} \ + -DCMAKE_MODULE_PATH=${OPENCOLORIO_CMAKE_FIND_MODULES_DIR} \ + -DCMAKE_VERBOSE_MAKEFILE=${MMSOLVER_BUILD_VERBOSE} \ -DOPENGL_INCLUDE_DIR=${OPENGL_INCLUDE_DIR} \ + -DMMSOLVER_VFX_PLATFORM=${VFX_PLATFORM} \ -DMMSOLVER_BUILD_PLUGIN=${MMSOLVER_BUILD_PLUGIN} \ -DMMSOLVER_BUILD_TOOLS=${MMSOLVER_BUILD_TOOLS} \ -DMMSOLVER_BUILD_PYTHON=${MMSOLVER_BUILD_PYTHON} \ @@ -126,6 +157,22 @@ ${CMAKE_EXE} \ -DMAYA_VERSION=${MAYA_VERSION} \ -Dmmsolverlibs_rust_DIR=${MMSOLVERLIBS_RUST_DIR} \ -Dmmsolverlibs_cpp_DIR=${MMSOLVERLIBS_CMAKE_CONFIG_DIR} \ + -DOpenColorIO_DIR=${OPENCOLORIO_CMAKE_CONFIG_DIR} \ + -DOCIO_INSTALL_EXT_PACKAGES=NONE \ + -Dexpat_DIR=${expat_DIR} \ + -Dexpat_LIBRARY=${expat_LIBRARY} \ + -Dexpat_INCLUDE_DIR=${expat_INCLUDE_DIR} \ + -Dexpat_USE_STATIC_LIBS=TRUE \ + -Dpystring_LIBRARY=${pystring_LIBRARY} \ + -Dpystring_INCLUDE_DIR=${pystring_INCLUDE_DIR} \ + -Dyaml-cpp_DIR=${yaml_DIR} \ + -Dyaml-cpp_LIBRARY=${yaml_LIBRARY} \ + -Dyaml-cpp_INCLUDE_DIR=${yaml_INCLUDE_DIR} \ + -DImath_DIR=${Imath_DIR} \ + -DZLIB_LIBRARY=${ZLIB_LIBRARY} \ + -DZLIB_INCLUDE_DIR=${ZLIB_INCLUDE_DIR} \ + -DZLIB_STATIC_LIBRARY=ON \ + -Dminizip-ng_DIR=${minizip_DIR} \ ${PROJECT_ROOT} ${CMAKE_EXE} --build . --parallel diff --git a/scripts/internal/build_mmSolver_windows64.bat b/scripts/internal/build_mmSolver_windows64.bat index 62fa41203..d364d8ac8 100644 --- a/scripts/internal/build_mmSolver_windows64.bat +++ b/scripts/internal/build_mmSolver_windows64.bat @@ -1,7 +1,7 @@ @ECHO OFF SETLOCAL :: -:: Copyright (C) 2019 David Cattermole. +:: Copyright (C) 2019, 2024 David Cattermole. :: :: This file is part of mmSolver. :: @@ -76,6 +76,10 @@ SET MMSOLVER_BUILD_ICONS=1 SET MMSOLVER_BUILD_CONFIG=1 SET MMSOLVER_BUILD_TESTS=0 +:: Allows you to see the build command lines, to help debugging build +:: problems. Set to ON to enable, and OFF to disable. +SET MMSOLVER_BUILD_VERBOSE=OFF + SET PYTHON_VIRTUAL_ENV_DIR_NAME=python_venv_windows64_maya%MAYA_VERSION% :: Note: There is no need to deactivate the virtual environment because @@ -88,6 +92,33 @@ SET MMSOLVERLIBS_INSTALL_DIR="%BUILD_DIR_BASE%\build_mmsolverlibs\install\maya%M SET MMSOLVERLIBS_CMAKE_CONFIG_DIR="%MMSOLVERLIBS_INSTALL_DIR%\lib\cmake\mmsolverlibs_cpp" SET MMSOLVERLIBS_RUST_DIR="%BUILD_DIR_BASE%\build_mmsolverlibs\rust_windows64_maya%MAYA_VERSION%\%BUILD_TYPE_DIR%" +:: Paths for dependencies. +SET EXTERNAL_BUILD_DIR=%BUILD_DIR_BASE%\build_opencolorio\cmake_win64_maya%MAYA_VERSION%_%BUILD_TYPE%\ext\dist +SET OPENCOLORIO_INSTALL_DIR=%BUILD_DIR_BASE%\build_opencolorio\install\maya%MAYA_VERSION%_windows64\ +SET OPENCOLORIO_CMAKE_CONFIG_DIR=%OPENCOLORIO_INSTALL_DIR%\lib\cmake\OpenColorIO\ +SET OPENCOLORIO_CMAKE_FIND_MODULES_DIR=%PROJECT_ROOT%\external\working\maya%MAYA_VERSION%_windows64\%OPENCOLORIO_TARBALL_EXTRACTED_DIR_NAME%\share\cmake\modules +:: Convert back-slashes to forward-slashes. +:: https://stackoverflow.com/questions/23542453/change-backslash-to-forward-slash-in-windows-batch-file +SET "OPENCOLORIO_CMAKE_FIND_MODULES_DIR=%OPENCOLORIO_CMAKE_FIND_MODULES_DIR:\=/%" + +SET expat_DIR=%EXTERNAL_BUILD_DIR%\%EXPAT_RELATIVE_CMAKE_DIR% +SET expat_INCLUDE_DIR=%EXTERNAL_BUILD_DIR%\include\ +SET expat_LIBRARY=%EXTERNAL_BUILD_DIR%\%EXPAT_RELATIVE_LIB_PATH% + +SET pystring_INCLUDE_DIR=%EXTERNAL_BUILD_DIR%\include +SET pystring_LIBRARY=%EXTERNAL_BUILD_DIR%\%PYSTRING_RELATIVE_LIB_PATH% + +SET yaml_DIR=%EXTERNAL_BUILD_DIR%\%YAML_RELATIVE_CMAKE_DIR% +SET yaml_LIBRARY=%EXTERNAL_BUILD_DIR%\%YAML_RELATIVE_LIB_PATH% +SET yaml_INCLUDE_DIR=%EXTERNAL_BUILD_DIR%\include\ + +SET Imath_DIR=%EXTERNAL_BUILD_DIR%\lib\cmake\Imath + +SET ZLIB_INCLUDE_DIR=%EXTERNAL_BUILD_DIR%\include\ +SET ZLIB_LIBRARY=%EXTERNAL_BUILD_DIR%\%ZLIB_RELATIVE_LIB_PATH% + +SET minizip_DIR=%EXTERNAL_BUILD_DIR%\%MINIZIP_RELATIVE_CMAKE_DIR% + :: MinGW is a common install for developers on Windows and :: if installed and used it will cause build conflicts and :: errors, so we disable it. @@ -124,6 +155,9 @@ CHDIR "%BUILD_DIR%" -DCMAKE_CXX_STANDARD=%CXX_STANDARD% ^ -DCMAKE_C_COMPILER=%CMAKE_C_COMPILER% ^ -DCMAKE_CXX_COMPILER=%CMAKE_CXX_COMPILER% ^ + -DCMAKE_MODULE_PATH=%OPENCOLORIO_CMAKE_FIND_MODULES_DIR% ^ + -DCMAKE_VERBOSE_MAKEFILE=%MMSOLVER_BUILD_VERBOSE% ^ + -DMMSOLVER_VFX_PLATFORM=%VFX_PLATFORM% ^ -DMMSOLVER_BUILD_PLUGIN=%MMSOLVER_BUILD_PLUGIN% ^ -DMMSOLVER_BUILD_TOOLS=%MMSOLVER_BUILD_TOOLS% ^ -DMMSOLVER_BUILD_PYTHON=%MMSOLVER_BUILD_PYTHON% ^ @@ -141,6 +175,22 @@ CHDIR "%BUILD_DIR%" -DMAYA_VERSION=%MAYA_VERSION% ^ -Dmmsolverlibs_rust_DIR=%MMSOLVERLIBS_RUST_DIR% ^ -Dmmsolverlibs_cpp_DIR=%MMSOLVERLIBS_CMAKE_CONFIG_DIR% ^ + -DOpenColorIO_DIR=%OPENCOLORIO_CMAKE_CONFIG_DIR% ^ + -DOCIO_INSTALL_EXT_PACKAGES=NONE ^ + -DZLIB_LIBRARY=%ZLIB_LIBRARY% ^ + -DZLIB_INCLUDE_DIR=%ZLIB_INCLUDE_DIR% ^ + -DZLIB_STATIC_LIBRARY=ON ^ + -Dexpat_DIR=%expat_DIR% ^ + -Dexpat_LIBRARY=%expat_LIBRARY% ^ + -Dexpat_INCLUDE_DIR=%expat_INCLUDE_DIR% ^ + -Dexpat_USE_STATIC_LIBS=TRUE ^ + -DImath_DIR=%Imath_DIR% ^ + -Dminizip-ng_DIR=%minizip_DIR% ^ + -Dpystring_LIBRARY=%pystring_LIBRARY% ^ + -Dpystring_INCLUDE_DIR=%pystring_INCLUDE_DIR% ^ + -Dyaml-cpp_DIR=%yaml_DIR% ^ + -Dyaml-cpp_LIBRARY=%yaml_LIBRARY% ^ + -Dyaml-cpp_INCLUDE_DIR=%yaml_INCLUDE_DIR% ^ %PROJECT_ROOT% if errorlevel 1 goto failed_to_generate diff --git a/scripts/internal/build_openColorIO_linux.bash b/scripts/internal/build_openColorIO_linux.bash new file mode 100644 index 000000000..865d1244f --- /dev/null +++ b/scripts/internal/build_openColorIO_linux.bash @@ -0,0 +1,131 @@ +#!/usr/bin/env bash +# +# Copyright (C) 2019, 2022, 2024 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# --------------------------------------------------------------------- +# +# Builds the OpenColorIO project. +# +# By default the minimal build is performed that will give us the +# library. This is not a "full" build with all features, bindings and +# programs. +# +# A static library is also preferred, to avoid shipping shared/dynamic +# libraries with mmSolver (which is a pain when dealing with a +# third-party studio's environment - it's easiest to embed everything +# and be done with it). +# +# This script is assumed to be called with a number of variables +# already set: +# +# - MAYA_VERSION +# - MAYA_LOCATION +# - PYTHON_EXE +# - CMAKE_EXE +# - CXX_STANDARD +# - OPENCOLORIO_TARBALL_NAME +# - OPENCOLORIO_TARBALL_EXTRACTED_DIR_NAME + +# The -e flag causes the script to exit as soon as one command returns +# a non-zero exit code. +set -ev + +# Store the current working directory, to return to. +CWD=`pwd` + +# What directory to build the project in? +BUILD_DIR_BASE="${PROJECT_ROOT}/../" + +# Install directory. +OPENCOLORIO_INSTALL_PATH="${BUILD_DIR_BASE}/build_opencolorio/install/maya${MAYA_VERSION}_linux/" + +# What type of build? "Release" or "Debug"? +BUILD_TYPE=Release + +# Allows you to see the build command lines, to help debugging build +# problems. Set to ON to enable, and OFF to disable. +MMSOLVER_BUILD_VERBOSE=OFF + +# Path to this script. +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +# The root of this project. +PROJECT_ROOT=`readlink -f ${DIR}/../..` +echo "Project Root: ${PROJECT_ROOT}" + +# Make sure source code archive is downloaded and exists. +SOURCE_TARBALL="${PROJECT_ROOT}/external/archives/${OPENCOLORIO_TARBALL_NAME}" +if [ ! -f "${SOURCE_TARBALL}" ]; then + echo "${SOURCE_TARBALL} does not exist." + echo "Please download the tar.gz file from https://github.com/AcademySoftwareFoundation/OpenColorIO/releases" + exit 1 +fi + +EXTRACT_OUT_DIR="${PROJECT_ROOT}/external/working/maya${MAYA_VERSION}_linux" +if [ ! -d "${EXTRACT_OUT_DIR}" ]; then + echo "${EXTRACT_OUT_DIR} does not exist, creating it..." + mkdir -p "${EXTRACT_OUT_DIR}" +fi +SOURCE_ROOT="${EXTRACT_OUT_DIR}/${OPENCOLORIO_TARBALL_EXTRACTED_DIR_NAME}/" +if [ ! -d "${SOURCE_ROOT}" ]; then + echo "${SOURCE_ROOT} does not exist, extracting tarball..." + # NOTE: Uses CMake to mirror the Windows build script. 'tar' is + # unlikely to be available on Windows. + ${CMAKE_EXE} -E chdir ${EXTRACT_OUT_DIR} tar xf ${SOURCE_TARBALL} +fi + +# We don't want to find system packages. +CMAKE_IGNORE_PATH="/lib;/lib64;/usr;/usr/lib;/usr/lib64;/usr/local;/usr/local/lib;/usr/local/lib64;" + +# Build OpenColorIO project +cd ${BUILD_DIR_BASE} +BUILD_DIR_NAME="cmake_linux_maya${MAYA_VERSION}_${BUILD_TYPE}" +BUILD_DIR="${BUILD_DIR_BASE}/build_opencolorio/${BUILD_DIR_NAME}" +mkdir -p ${BUILD_DIR} +cd ${BUILD_DIR} + +# Renaming the library name and C++ namespace, is so that software +# looking for the "regular" OpenColorIO will not conflict with the +# mmSolver library. +MMSOLVER_OCIO_LIBNAME_SUFFIX="_mmSolver" +MMSOLVER_OCIO_NAMESPACE="OpenColorIO_mmSolver" + +${CMAKE_EXE} \ + -DBUILD_SHARED_LIBS=OFF \ + -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \ + -DCMAKE_INSTALL_PREFIX=${OPENCOLORIO_INSTALL_PATH} \ + -DCMAKE_IGNORE_PATH=${CMAKE_IGNORE_PATH} \ + -DCMAKE_POSITION_INDEPENDENT_CODE=1 \ + -DCMAKE_CXX_STANDARD=${CXX_STANDARD} \ + -DCMAKE_VERBOSE_MAKEFILE=${MMSOLVER_BUILD_VERBOSE} \ + -DOCIO_INSTALL_EXT_PACKAGES=ALL \ + -DOCIO_USE_OIIO_FOR_APPS=OFF \ + -DOCIO_BUILD_APPS=OFF \ + -DOCIO_BUILD_TESTS=OFF \ + -DOCIO_BUILD_GPU_TESTS=OFF \ + -DOCIO_BUILD_DOCS=OFF \ + -DOCIO_BUILD_FROZEN_DOCS=OFF \ + -DOCIO_BUILD_PYTHON=OFF \ + -DOCIO_BUILD_OPENFX=OFF \ + -DOCIO_LIBNAME_SUFFIX=${MMSOLVER_OCIO_LIBNAME_SUFFIX} \ + -DOCIO_NAMESPACE=${MMSOLVER_OCIO_NAMESPACE} \ + ${SOURCE_ROOT} + +${CMAKE_EXE} --build . --parallel +${CMAKE_EXE} --install . + +# Return back project root directory. +cd ${CWD} diff --git a/scripts/internal/build_openColorIO_windows64.bat b/scripts/internal/build_openColorIO_windows64.bat new file mode 100644 index 000000000..70614c6b8 --- /dev/null +++ b/scripts/internal/build_openColorIO_windows64.bat @@ -0,0 +1,174 @@ +@ECHO OFF +:: +:: Copyright (C) 2022, 2023, 2024 David Cattermole. +:: +:: This file is part of mmSolver. +:: +:: mmSolver is free software: you can redistribute it and/or modify it +:: under the terms of the GNU Lesser General Public License as +:: published by the Free Software Foundation, either version 3 of the +:: License, or (at your option) any later version. +:: +:: mmSolver is distributed in the hope that it will be useful, +:: but WITHOUT ANY WARRANTY; without even the implied warranty of +:: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +:: GNU Lesser General Public License for more details. +:: +:: You should have received a copy of the GNU Lesser General Public License +:: along with mmSolver. If not, see . +:: --------------------------------------------------------------------- +:: +:: Builds the OpenColorIO project. +:: +:: By default the minimal build is performed that will give us the +:: library. This is not a "full" build with all features, bindings and +:: programs. +:: +:: A static library is also preferred, to avoid shipping shared/dynamic +:: libraries with mmSolver (which is a pain when dealing with a +:: third-party studio's environment - it's easiest to embed everything +:: and be done with it). +:: +:: This script is assumed to be called with a number of variables +:: already set: +:: +:: - MAYA_VERSION +:: - MAYA_LOCATION +:: - PYTHON_EXE +:: - CMAKE_EXE +:: - CXX_STANDARD +:: - OPENCOLORIO_TARBALL_NAME +:: - OPENCOLORIO_TARBALL_EXTRACTED_DIR_NAME +:: +:: NOTE: Do not call this script directly! This file should be called by +:: the build_mmSolver_windows64_mayaXXXX.bat files. + +:: The root of this project. +SET PROJECT_ROOT=%CD% +ECHO Project Root: %PROJECT_ROOT% + +:: What directory to build the project in? +SET BUILD_DIR_BASE=%PROJECT_ROOT%\.. + +:: Install directory. +SET OPENCOLORIO_INSTALL_PATH=%BUILD_DIR_BASE%\build_opencolorio\install\maya%MAYA_VERSION%_windows64\ + +:: What type of build? "Release" or "Debug"? +SET BUILD_TYPE=Release + +:: Allows you to see the build command lines, to help debugging build +:: problems. Set to ON to enable, and OFF to disable. +SET MMSOLVER_BUILD_VERBOSE=OFF + +:: Make sure source code archive is downloaded and exists. +SET SOURCE_TARBALL=%PROJECT_ROOT%\external\archives\%OPENCOLORIO_TARBALL_NAME% +IF NOT EXIST %SOURCE_TARBALL% ( + ECHO %SOURCE_TARBALL% does not exist. + ECHO Please download the tar.gz file from https://github.com/AcademySoftwareFoundation/OpenColorIO/releases + EXIT /b 1 +) + +SET EXTRACT_OUT_DIR=%PROJECT_ROOT%\external\working\maya%MAYA_VERSION%_windows64 +IF NOT EXIST %EXTRACT_OUT_DIR% ( + ECHO %EXTRACT_OUT_DIR% does not exist, creating it... + MKDIR %EXTRACT_OUT_DIR% +) +SET SOURCE_ROOT=%EXTRACT_OUT_DIR%\%OPENCOLORIO_TARBALL_EXTRACTED_DIR_NAME%\ +IF NOT EXIST %SOURCE_ROOT% ( + ECHO %SOURCE_ROOT% does not exist, extracting tarball... + :: The 'tar' command is unlikely to be available on Windows, so we + :: use CMake because we know it has 'tar'. + %CMAKE_EXE% -E chdir %EXTRACT_OUT_DIR% tar xf %SOURCE_TARBALL% +) + + +ECHO Building opencolorio... (%SOURCE_ROOT%) + +:: MinGW is a common install for developers on Windows and +:: if installed and used it will cause build conflicts and +:: errors, so we disable it. +SET IGNORE_INCLUDE_DIRECTORIES="" +IF EXIST "C:\MinGW" ( + SET IGNORE_INCLUDE_DIRECTORIES="C:\MinGW\bin;C:\MinGW\include" +) + +:: Optionally use "NMake Makefiles" as the build system generator. +SET CMAKE_GENERATOR=Ninja + +:: Force the compilier to be MSVC's cl.exe, so that if other +:: compiliers are installed, CMake doesn't get confused and try to use +:: it (such as clang). +SET CMAKE_C_COMPILER=cl +SET CMAKE_CXX_COMPILER=cl + +:: Build OpenColorIO project +SET BUILD_DIR_NAME=cmake_win64_maya%MAYA_VERSION%_%BUILD_TYPE% +SET BUILD_DIR=%BUILD_DIR_BASE%\build_opencolorio\%BUILD_DIR_NAME% +ECHO BUILD_DIR_BASE: %BUILD_DIR_BASE% +ECHO BUILD_DIR_NAME: %BUILD_DIR_NAME% +ECHO BUILD_DIR: %BUILD_DIR% +CHDIR "%BUILD_DIR_BASE%" +MKDIR "build_opencolorio" +CHDIR "%BUILD_DIR_BASE%\build_opencolorio\" +MKDIR "%BUILD_DIR_NAME%" +CHDIR "%BUILD_DIR%" + +:: Renaming the library name and C++ namespace, is so that software +:: looking for the "regular" OpenColorIO will not conflict with the +:: mmSolver library. +SET MMSOLVER_OCIO_LIBNAME_SUFFIX="_mmSolver" +SET MMSOLVER_OCIO_NAMESPACE="OpenColorIO_mmSolver" + +%CMAKE_EXE% -G %CMAKE_GENERATOR% ^ + -DBUILD_SHARED_LIBS=ON ^ + -DCMAKE_BUILD_TYPE=%BUILD_TYPE% ^ + -DCMAKE_INSTALL_PREFIX=%OPENCOLORIO_INSTALL_PATH% ^ + -DCMAKE_IGNORE_PATH=%IGNORE_INCLUDE_DIRECTORIES% ^ + -DCMAKE_C_COMPILER=%CMAKE_C_COMPILER% ^ + -DCMAKE_CXX_COMPILER=%CMAKE_CXX_COMPILER% ^ + -DCMAKE_CXX_STANDARD=%CXX_STANDARD% ^ + -DCMAKE_VERBOSE_MAKEFILE=%MMSOLVER_BUILD_VERBOSE% ^ + -DOCIO_INSTALL_EXT_PACKAGES=ALL ^ + -DOCIO_BUILD_APPS=OFF ^ + -DOCIO_USE_OIIO_FOR_APPS=OFF ^ + -DOCIO_BUILD_TESTS=OFF ^ + -DOCIO_BUILD_GPU_TESTS=OFF ^ + -DOCIO_BUILD_DOCS=OFF ^ + -DOCIO_BUILD_FROZEN_DOCS=OFF ^ + -DOCIO_BUILD_PYTHON=OFF ^ + -DOCIO_BUILD_OPENFX=OFF ^ + -DOCIO_USE_SSE=ON ^ + -DOCIO_LIBNAME_SUFFIX=%MMSOLVER_OCIO_LIBNAME_SUFFIX% ^ + -DOCIO_NAMESPACE=%MMSOLVER_OCIO_NAMESPACE% ^ + %SOURCE_ROOT% +IF errorlevel 1 GOTO failed_to_generate_cpp + +%CMAKE_EXE% --build . --parallel +IF errorlevel 1 GOTO failed_to_build_cpp + +%CMAKE_EXE% --install . +IF errorlevel 1 GOTO failed_to_install_cpp + +:: Return back project root directory. +CHDIR "%PROJECT_ROOT%" +EXIT /b 0 + +:failed_to_generate_cpp_header +ECHO Failed to Generate C++ header files from Rust. +EXIT /b 1 + +:failed_to_build_rust +ECHO Failed to build Rust code. +EXIT /b 1 + +:failed_to_generate_cpp +ECHO Failed to generate C++ build files. +EXIT /b 1 + +:failed_to_build_cpp +ECHO Failed to build C++ code. +EXIT /b 1 + +:failed_to_install_cpp +ECHO Failed to install C++ artifacts. +EXIT /b 1 diff --git a/scripts/internal/python_venv_activate.bash b/scripts/internal/python_venv_activate.bash index 4a7e5396f..5adfb427c 100755 --- a/scripts/internal/python_venv_activate.bash +++ b/scripts/internal/python_venv_activate.bash @@ -58,6 +58,7 @@ fi REQUIRE_PACKAGE_INSTALL=0 if [ ! -f "${PYTHON_VIRTUAL_ENV_ACTIVATE_SCRIPT}" ]; then echo "Setting up Python Virtual Environment ${PYTHON_VIRTUAL_ENV_DIR_NAME}" + ${PYTHON_EXE} --version ${PYTHON_EXE} -m venv ${PYTHON_VIRTUAL_ENV_DIR} REQUIRE_PACKAGE_INSTALL=1 fi @@ -69,14 +70,11 @@ source "${PYTHON_VIRTUAL_ENV_ACTIVATE_SCRIPT}" # Install requirements if [ ${REQUIRE_PACKAGE_INSTALL} -eq 1 ]; then + ${PYTHON_EXE} --version ${PYTHON_EXE} -m pip install --upgrade pip - ${PYTHON_EXE} -m pip install -r "${PROJECT_ROOT}/share/requirements-dev.txt" - ${PYTHON_EXE} -m pip install -r "${PROJECT_ROOT}/share/requirements-doc.txt" - REQUIRE_DEV_MAYA_VERSION_FILE="${PROJECT_ROOT}/share/requirements-dev-maya${MAYA_VERSION}.txt" - if [ -f "$REQUIRE_DEV_MAYA_VERSION_FILE" ]; then - ${PYTHON_EXE} -m pip install -r $REQUIRE_DEV_MAYA_VERSION_FILE - fi + REQUIRE_MAYA_VERSION_FILE="${PROJECT_ROOT}/share/python_requirements/requirements-maya${MAYA_VERSION}.txt" + ${PYTHON_EXE} -m pip install -r $REQUIRE_MAYA_VERSION_FILE fi cd ${PROJECT_ROOT} diff --git a/scripts/internal/python_venv_activate.bat b/scripts/internal/python_venv_activate.bat index 6e7deacc8..eea39559e 100644 --- a/scripts/internal/python_venv_activate.bat +++ b/scripts/internal/python_venv_activate.bat @@ -56,6 +56,7 @@ IF "%FRESH_PYTHON_VIRTUAL_ENV%"=="1" ( SET REQUIRE_PACKAGE_INSTALL=0 IF NOT EXIST %PYTHON_VIRTUAL_ENV_ACTIVATE_SCRIPT% ( ECHO Setting up Python Virtual Environment "%PYTHON_VIRTUAL_ENV_DIR_NAME%" + %PYTHON_EXE% --version %PYTHON_EXE% -m venv %PYTHON_VIRTUAL_ENV_DIR% SET REQUIRE_PACKAGE_INSTALL=1 ) @@ -65,15 +66,12 @@ ECHO Activating Python Virtual Environment "%PYTHON_VIRTUAL_ENV_DIR_NAME%" CALL %PYTHON_VIRTUAL_ENV_ACTIVATE_SCRIPT% :: Install requirements -SET MAYA_VERSION_REQUIRE_FILE=%PROJECT_ROOT%\share\requirements-dev-maya%MAYA_VERSION%.txt +SET MAYA_VERSION_REQUIRE_FILE=%PROJECT_ROOT%\share\python_requirements\requirements-maya%MAYA_VERSION%.txt IF "%REQUIRE_PACKAGE_INSTALL%"=="1" ( + %PYTHON_EXE% --version + :: TODO: Why is the PIP upgrade disabled on Windows? On Linux it's very important. :: %PYTHON_EXE% -m pip install --upgrade pip - %PYTHON_EXE% -m pip install -r "%PROJECT_ROOT%\share\requirements-dev.txt" - %PYTHON_EXE% -m pip install -r "%PROJECT_ROOT%\share\requirements-doc.txt" - - IF EXIST %MAYA_VERSION_REQUIRE_FILE% ( - %PYTHON_EXE% -m pip install -r %MAYA_VERSION_REQUIRE_FILE% - ) + %PYTHON_EXE% -m pip install -r %MAYA_VERSION_REQUIRE_FILE% ) :: Return back project root directory. diff --git a/scripts/python_formatter_run_black_edit.bash b/scripts/python_formatter_run_black_edit.bash old mode 100644 new mode 100755 diff --git a/scripts/python_linter_run_cpplint.bash b/scripts/python_linter_run_cpplint.bash old mode 100644 new mode 100755 diff --git a/scripts/python_linter_run_flake8.bash b/scripts/python_linter_run_flake8.bash old mode 100644 new mode 100755 diff --git a/scripts/python_linter_run_pylint.bash b/scripts/python_linter_run_pylint.bash old mode 100644 new mode 100755 diff --git a/scripts/python_linter_run_ruff.bash b/scripts/python_linter_run_ruff.bash old mode 100644 new mode 100755 diff --git a/scripts/python_linter_run_ruff_fix.bash b/scripts/python_linter_run_ruff_fix.bash old mode 100644 new mode 100755 diff --git a/scripts/python_venv_activate_maya2016.bash b/scripts/python_venv_activate_maya2016.bash old mode 100644 new mode 100755 diff --git a/scripts/python_venv_activate_maya2017.bash b/scripts/python_venv_activate_maya2017.bash old mode 100644 new mode 100755 diff --git a/scripts/python_venv_activate_maya2018.bash b/scripts/python_venv_activate_maya2018.bash old mode 100644 new mode 100755 diff --git a/scripts/python_venv_activate_maya2019.bash b/scripts/python_venv_activate_maya2019.bash old mode 100644 new mode 100755 diff --git a/scripts/python_venv_activate_maya2020.bash b/scripts/python_venv_activate_maya2020.bash old mode 100644 new mode 100755 index 0b3e2bcf4..9d051a6f9 --- a/scripts/python_venv_activate_maya2020.bash +++ b/scripts/python_venv_activate_maya2020.bash @@ -33,7 +33,7 @@ PROJECT_ROOT=`pwd` MAYA_VERSION=2020 # Python executable - edit this to point to an explicit python executable file. -PYTHON_EXE=python +PYTHON_EXE=python3 PYTHON_VIRTUAL_ENV_DIR_NAME="python_venv_linux_maya${MAYA_VERSION}" source "${PROJECT_ROOT}/scripts/internal/python_venv_activate.bash" diff --git a/scripts/python_venv_activate_maya2022.bash b/scripts/python_venv_activate_maya2022.bash old mode 100644 new mode 100755 index ae802cca8..f0ae856d1 --- a/scripts/python_venv_activate_maya2022.bash +++ b/scripts/python_venv_activate_maya2022.bash @@ -33,7 +33,7 @@ PROJECT_ROOT=`pwd` MAYA_VERSION=2022 # Python executable - edit this to point to an explicit python executable file. -PYTHON_EXE=python +PYTHON_EXE=python3 PYTHON_VIRTUAL_ENV_DIR_NAME="python_venv_linux_maya${MAYA_VERSION}" source "${PROJECT_ROOT}/scripts/internal/python_venv_activate.bash" diff --git a/scripts/python_venv_activate_maya2023.bash b/scripts/python_venv_activate_maya2023.bash old mode 100644 new mode 100755 index dc5a54251..391d6a2f7 --- a/scripts/python_venv_activate_maya2023.bash +++ b/scripts/python_venv_activate_maya2023.bash @@ -33,7 +33,7 @@ PROJECT_ROOT=`pwd` MAYA_VERSION=2023 # Python executable - edit this to point to an explicit python executable file. -PYTHON_EXE=python +PYTHON_EXE=python3 PYTHON_VIRTUAL_ENV_DIR_NAME="python_venv_linux_maya${MAYA_VERSION}" source "${PROJECT_ROOT}/scripts/internal/python_venv_activate.bash" diff --git a/scripts/python_venv_activate_maya2024.bash b/scripts/python_venv_activate_maya2024.bash old mode 100644 new mode 100755 diff --git a/scripts/python_venv_deactivate_maya2024.bat b/scripts/python_venv_deactivate_maya2024.bat new file mode 100644 index 000000000..415ad6dee --- /dev/null +++ b/scripts/python_venv_deactivate_maya2024.bat @@ -0,0 +1,28 @@ +@ECHO OFF +:: +:: Copyright (C) 2021 David Cattermole. +:: +:: This file is part of mmSolver. +:: +:: mmSolver is free software: you can redistribute it and/or modify it +:: under the terms of the GNU Lesser General Public License as +:: published by the Free Software Foundation, either version 3 of the +:: License, or (at your option) any later version. +:: +:: mmSolver is distributed in the hope that it will be useful, +:: but WITHOUT ANY WARRANTY; without even the implied warranty of +:: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +:: GNU Lesser General Public License for more details. +:: +:: You should have received a copy of the GNU Lesser General Public License +:: along with mmSolver. If not, see . +:: --------------------------------------------------------------------- +:: +:: Deactivates the Python development environment for mmSolver maya 2023. + +SET PROJECT_ROOT=%CD% + +SET MAYA_VERSION=2024 + +SET PYTHON_VIRTUAL_ENV_DIR_NAME=python_venv_windows64_maya%MAYA_VERSION% +CALL %PROJECT_ROOT%\scripts\internal\python_venv_deactivate.bat diff --git a/share/cmake/modules/Findldpk.cmake b/share/cmake/modules/Findldpk.cmake index a57b8c25c..b5272d5e9 100644 --- a/share/cmake/modules/Findldpk.cmake +++ b/share/cmake/modules/Findldpk.cmake @@ -142,7 +142,7 @@ if(NOT ldpk_FOUND AND MMSOLVER_DOWNLOAD_DEPENDENCIES AND ldpk_ALLOW_DOWNLOAD) set(ldpk_FOUND TRUE) set(ldpk_VERSION ${ldpk_FIND_VERSION}) - set(Ldpk_DIR "${_EXTERNAL_INSTALL_DIR}/ldpk/share/ldpk/cmake") + set(ldpk_DIR "${_EXTERNAL_INSTALL_DIR}/ldpk/share/ldpk/cmake") set(ldpk_INCLUDE_DIR "${_EXTERNAL_INSTALL_DIR}/ldpk/${CMAKE_INSTALL_INCLUDEDIR}") set(ldpk_URL diff --git a/share/cmake/modules/MMColorIOUtils.cmake b/share/cmake/modules/MMColorIOUtils.cmake new file mode 100644 index 000000000..2677ee2f1 --- /dev/null +++ b/share/cmake/modules/MMColorIOUtils.cmake @@ -0,0 +1,209 @@ +# Copyright (C) 2024 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# --------------------------------------------------------------------- +# +# CMake utilities for mmColorIO. +# + +macro(mmcolorio_find_packages) + message(STATUS "mmcolorio_find_packages") + + # OpenColorIO + find_package(OpenColorIO REQUIRED) + message(STATUS "OpenColorIO: Version: ${OpenColorIO_VERSION}") + + # OpenColorIO Dependencies + find_package(pystring REQUIRED) + message(STATUS "pystring: Found: ${pystring_FOUND}") + message(STATUS "pystring: Version: ${pystring_VERSION}") + message(STATUS "pystring: Libraries: ${pystring_LIBRARY}") + message(STATUS "pystring: Include Dir: ${pystring_INCLUDE_DIR}") + + # This is the (all-lower-case) 'expat' library namespace, which is the + # same as the 'EXPAT' library, except using ALL-UPPER-CASE name will + # use the inbuilt CMake FindEXPAT.cmake module which is not what + # OpenColorIO uses. + find_package(expat REQUIRED) + message(STATUS "expat: Found: ${expat_FOUND}") + message(STATUS "expat: Version: ${expat_VERSION}") + message(STATUS "expat: Libraries: ${expat_LIBRARY}") + message(STATUS "expat: Include Dir: ${expat_INCLUDE_DIR}") + + find_package(yaml-cpp REQUIRED) + message(STATUS "yaml-cpp: Found: ${yaml-cpp_FOUND}") + message(STATUS "yaml-cpp: Version: ${yaml-cpp_VERSION}") + message(STATUS "yaml-cpp: Libraries: ${yaml-cpp_LIBRARY}") + message(STATUS "yaml-cpp: Include Dir: ${yaml-cpp_INCLUDE_DIR}") + + if(OpenColorIO_VERSION VERSION_GREATER_EQUAL "2.1.0") + find_package(Imath REQUIRED) + message(STATUS "Imath: Found: ${Imath_FOUND}") + message(STATUS "Imath: Version: ${Imath_VERSION}") + message(STATUS "Imath: Libraries: ${Imath_LIBRARY}") + message(STATUS "Imath: Include Dir: ${Imath_INCLUDE_DIR}") + else() + find_package(Half REQUIRED) + message(STATUS "Half: Found: ${Half_FOUND}") + message(STATUS "Half: Version: ${Half_VERSION}") + message(STATUS "Half: Libraries: ${Half_LIBRARY}") + message(STATUS "Half: Include Dir: ${Half_INCLUDE_DIR}") + endif() + + if(OpenColorIO_VERSION VERSION_GREATER_EQUAL "2.2.0") + find_package(ZLIB REQUIRED) + message(STATUS "ZLIB: Found: ${ZLIB_FOUND}") + message(STATUS "ZLIB: Version: ${ZLIB_VERSION}") + message(STATUS "ZLIB: Libraries: ${ZLIB_LIBRARIES}") + message(STATUS "ZLIB: Include Dirs: ${ZLIB_INCLUDE_DIRS}") + + find_package(minizip-ng REQUIRED) + message(STATUS "minizip-ng: Found: ${minizip-ng_FOUND}") + message(STATUS "minizip-ng: Version: ${minizip-ng_VERSION}") + message(STATUS "minizip-ng: Libraries: ${minizip-ng_LIBRARY}") + message(STATUS "minizip-ng: Include Dir: ${minizip-ng_INCLUDE_DIR}") + endif() + +endmacro() + + +macro(mmcolorio_target_link_packages target) + message(STATUS "mmcolorio_target_link_packages: ${target}") + + find_package(yaml-cpp REQUIRED) + get_target_property(yaml_cpp_LOCATION_RELEASE + yaml-cpp + IMPORTED_LOCATION_RELEASE) + message(STATUS + "yaml_cpp_LOCATION_RELEASE: ${yaml_cpp_LOCATION_RELEASE}") + + find_package(expat REQUIRED) + find_package(pystring REQUIRED) + target_link_libraries(${target} + PRIVATE ${expat_LIBRARY} + PRIVATE ${pystring_LIBRARY} + PRIVATE ${yaml_cpp_LOCATION_RELEASE} + ) + + find_package(OpenColorIO REQUIRED) + find_package(ZLIB REQUIRED) + find_package(minizip-ng REQUIRED) + if(OpenColorIO_VERSION VERSION_GREATER_EQUAL "2.2.0") + target_link_libraries(${target} + PRIVATE ZLIB::ZLIB + PRIVATE ${minizip-ng_LIBRARY} + ) + endif() + + # The 'half' 16-bit float data type is used from Imath by default in + # OCIO 2.2+, but OpenEXR/IlmBase is used before that. + # + # The reason is that OpenEXR used to be the sole location for the + # 'half' data type (because it was supported as part of OpenEXR), + # but since many libraries wanted the 'half' data type without all + # of OpenEXR, it was refactored out of OpenEXR. + if(OpenColorIO_VERSION VERSION_GREATER_EQUAL "2.2.0") + find_package(Imath REQUIRED) + target_link_libraries(${target} + PRIVATE Imath::Imath + ) + else() + find_package(Half REQUIRED) + target_link_libraries(${target} + PRIVATE ${Half_LIBRARY} + ) + endif() + + if (WIN32) + find_package(OpenColorIO REQUIRED) + get_target_property(OpenColorIO_IMPLIB_RELEASE + OpenColorIO::OpenColorIO + IMPORTED_IMPLIB_RELEASE) + message(STATUS + "OpenColorIO_IMPLIB_RELEASE: ${OpenColorIO_IMPLIB_RELEASE}") + + get_target_property(OpenColorIO_LOCATION_RELEASE + OpenColorIO::OpenColorIO + IMPORTED_LOCATION_RELEASE) + message(STATUS + "OpenColorIO_LOCATION_RELEASE: ${OpenColorIO_LOCATION_RELEASE}") + + get_filename_component( + OpenColorIO_IMPLIB_RELEASE_ABS + ${OpenColorIO_IMPLIB_RELEASE} + REALPATH) + + target_link_libraries(${target} + PRIVATE ${OpenColorIO_IMPLIB_RELEASE_ABS} + ) + elseif (UNIX) + get_target_property(OpenColorIO_LOCATION_RELEASE + OpenColorIO::OpenColorIO + IMPORTED_LOCATION_RELEASE) + message(STATUS + "OpenColorIO_LOCATION_RELEASE: ${OpenColorIO_LOCATION_RELEASE}") + + target_link_libraries(${target} + PRIVATE ${OpenColorIO_LOCATION_RELEASE} + ) + endif () + +endmacro() + + +macro(mmcolorio_target_include_packages target) + message(STATUS "mmcolorio_target_include_packages: ${target}") + + message(STATUS "OpenColorIO: Version: ${OpenColorIO_VERSION}") + + if(OpenColorIO_VERSION VERSION_GREATER_EQUAL "2.1.0") + get_target_property(OpenColorIO_INCLUDE_DIR + OpenColorIO::OpenColorIO + INTERFACE_INCLUDE_DIRECTORIES) + else() + get_target_property(OpenColorIO_INCLUDE_DIR + OpenColorIO::OpenColorIOHeaders + INTERFACE_INCLUDE_DIRECTORIES) + endif() + + message(STATUS + "OpenColorIO_INCLUDE_DIR: ${OpenColorIO_INCLUDE_DIR}") + + target_include_directories(${target} + PRIVATE ${OpenColorIO_INCLUDE_DIR} + PRIVATE ${pystring_INCLUDE_DIR} + PRIVATE ${expat_INCLUDE_DIR} + PRIVATE ${yaml-cpp_INCLUDE_DIR} + ) + + if(OpenColorIO_VERSION VERSION_GREATER_EQUAL "2.1.0") + target_include_directories(${target} + PRIVATE ${Imath_INCLUDE_DIR} + ) + else() + target_include_directories(${target} + PRIVATE ${Half_INCLUDE_DIR} + ) + endif() + + if(OpenColorIO_VERSION VERSION_GREATER_EQUAL "2.2.0") + target_include_directories(${target} + PRIVATE ${ZLIB_INCLUDE_DIRS} + PRIVATE ${minizip-ng_INCLUDE_DIR} + ) + endif() + +endmacro() diff --git a/share/cmake/modules/MMCommonUtils.cmake b/share/cmake/modules/MMCommonUtils.cmake index ac24144a4..00deac1ba 100644 --- a/share/cmake/modules/MMCommonUtils.cmake +++ b/share/cmake/modules/MMCommonUtils.cmake @@ -1,4 +1,4 @@ -# Copyright (C) 2020, 2021, 2023 David Cattermole. +# Copyright (C) 2020, 2021, 2023, 2024 David Cattermole. # # This file is part of mmSolver. # @@ -16,9 +16,250 @@ # along with mmSolver. If not, see . # --------------------------------------------------------------------- # -# CMake utilities for mmSolver. +# CMake common utilities for mmSolver. # + +function(mm_common_apple_clang_set_global_compile_options) + # For MacOS with Clang (which is the supported compiler for Maya + # 2018+) + set(CMAKE_CXX_FLAGS "") # Zero out the C++ flags, we have complete control. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -arch x86_64") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -stdlib=libc++") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-multichar -Wno-comment -Wno-sign-compare") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated -Wno-reorder") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-depth-35 -fno-gnu-keywords") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -funsigned-char -fpascal-strings") # -pthread + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCC_GNU_") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOSMac_") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOSMacOSX_") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOSMac_MachO_") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_LANGUAGE_C_PLUS_PLUS") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.8") + + # Special MacOS linking stuff + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -headerpad_max_install_names") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -framework System") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -framework SystemConfiguration") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -framework CoreServices") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -framework Carbon") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -framework Cocoa") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -framework ApplicationServices") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -framework IOKit") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -bundle") + + set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") + set(CMAKE_CXX_FLAGS_RELEASE "-O3 -fPIC -fno-strict-aliasing -m64") +endfunction() + + +function(mm_common_windows_msvc_set_global_compile_options) + # For Visual Studio 11 2012 + set(CMAKE_CXX_FLAGS "") # Zero out the C++ flags, we have complete control. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GS") # Buffer Security Check + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:wchar_t") # wchar_t Is Native Type + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi") # Separate .pdb debug info file. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fp:precise") # Precise floating-point behavior + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:forScope") # Force Conformance in for Loop Scope + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR") # Enable Run-Time Type Information + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Gd") # __cdecl Calling Convention + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc") # Handle standard C++ Exceptions. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") # Warning Levels 1-4 enabled. + + add_compile_definitions(OSWin_) + add_compile_definitions(WIN32) + add_compile_definitions(_WINDOWS) + add_compile_definitions(_USRDLL) + add_compile_definitions(NT_PLUGIN) + add_compile_definitions(_HAS_ITERATOR_DEBUGGING=0) + add_compile_definitions(_SECURE_SCL=0) + add_compile_definitions(_SECURE_SCL_THROWS=0) + add_compile_definitions(_SECURE_SCL_DEPRECATE=0) + add_compile_definitions(_CRT_SECURE_NO_DEPRECATE) + add_compile_definitions(_CRT_SECURE_NO_WARNINGS) + add_compile_definitions(TBB_USE_DEBUG=0) + add_compile_definitions(__TBB_LIB_NAME=tbb.lib) + add_compile_definitions(_WINDLL) + add_compile_definitions(Bits64_) + add_compile_definitions(REQUIRE_IOSTREAM) + add_compile_definitions(NT_PLUGIN) + add_compile_definitions(USERDLL) + + # Use multithread-specific Run-Time Library. + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} /MD") + + add_compile_options("/arch:AVX2") + + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Gy") # Enable Function-Level Linking + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GF") # Eliminate Duplicate Strings + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2") # Optimize for Maximize Speed + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Oi") # Generate Intrinsic Functions + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL") # Whole program optimization + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /arch:AVX2") # Use AVX2 instructions + + # Link-time code generation. + # + # /LTGC and /GL work together and therefore are both enabled. + set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG") + set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG") + set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} /LTCG") + + # Use debug-specific Run-Time Library. + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} /MDd") + + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od") # Optimize for Debug. + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /RTC1") # Run-time error checks + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Ob0") # Disables inline expansions. + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /GR") # Enable Run-Time Type Information + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /GL") # Whole program optimization + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Oi") # Generate Intrinsic Functions + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Gy") # Enable Function-Level Linking + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Zi") # Debug Information Format + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /EHsc") # Handle standard C++ Exceptions. + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /analyze") # Code analysis + + # Ensure Google Logging does not use "ERROR" macro, because Windows + # doesn't support it. + add_compile_definitions(GLOG_NO_ABBREVIATED_SEVERITIES) + + # Ceres running on Windows can trigger a MSVC warning: + # + # "Compiler Warning (level 3) C4996: Your code uses a function, + # class member, variable, or typedef that's marked deprecated." + # + # To override this, we define "_CRT_NONSTDC_NO_DEPRECATE" + # + # https://docs.microsoft.com/en-us/previous-versions/ms235384(v=vs.100) + add_compile_definitions(_CRT_NONSTDC_NO_DEPRECATE) +endfunction() + + +# For Linux with GCC +function(mm_common_linux_gcc_set_cxx11_abi) + + if(NOT DEFINED MMSOLVER_VFX_PLATFORM) + message(FATAL_ERROR "Please define the VFX_PLATFORM variable (eg. with '-DVFX_PLATFORM=2020').") + endif() + + # Use the older C++11 ABI for std::string and std::list, to be + # compatible with RHEL/CentOS 7, Maya and the VFX Platform. + # + # https://vfxplatform.com/#footnote-gcc6 + # + # In VFX Platform CY2023, and the move to RHEL 8 or RHEL 9, + # the new default is to use "_GLIBCXX_USE_CXX11_ABI=1". + # + # https://vfxplatform.com/#footnote-gcc9 + if (MMSOLVER_VFX_PLATFORM VERSION_GREATER_EQUAL 2023) + # Must be enabled for RHEL 8/9, Alma Linux and Rocky Linux 8/9. + add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=1) + else () + add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0) + endif() + +endfunction() + + +# For Linux with GCC +function(mm_common_linux_gcc_set_global_compile_options) + + # Definitions + add_compile_definitions(Bits64_) + add_compile_definitions(UNIX) + add_compile_definitions(_BOOL) + add_compile_definitions(LINUX) + add_compile_definitions(linux) + add_compile_definitions(__linux__) + add_compile_definitions(OSLinux_) + add_compile_definitions(FUNCPROTO) + add_compile_definitions(_GNU_SOURCE) + add_compile_definitions(LINUX_64) + add_compile_definitions(REQUIRE_IOSTREAM) + + # Enable warnings. + add_compile_options(-Wall) + add_compile_options(-Wno-multichar) + add_compile_options(-Wno-comment) + add_compile_options(-Wno-sign-compare) + add_compile_options(-Wno-unused-parameter) + add_compile_options(-Wno-unused-parameter) + add_compile_options(-Wno-unused-variable) + add_compile_options(-Wno-unused-private-field) + add_compile_options(-Wno-deprecated) + add_compile_options(-Wno-reorder) + + # GCC Features + add_compile_options(-pthread) + add_compile_options(-fopenmp) + add_compile_options(-fvisibility=hidden) + add_compile_options(-funsigned-char) + add_compile_options(-fno-gnu-keywords) + add_compile_options(-fno-strict-aliasing) + + mm_common_linux_gcc_set_cxx11_abi() + + # How how many levels of 'depth' can templates use until the depth + # is too high? + # + # If this value is too high compile times will be too slow. + # + # '-ftemplate-depth-27' is required to compile under GCC 4.8.5. + # '-ftemplate-depth-35' is required to compile under GCC 5.5.x. + # + # In GCC 6.3.x, with C++11 the default depth is set to 900, but + # the C++11 standard says 1024 is the default. + add_compile_options(-ftemplate-depth-900) + + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0") # No optmizations. + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g") # Include debug symbols + + # Enable AddressSanitizer. + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address") + + # Nicer stack traces in error messages + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer") + + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") # Optimize for maximum performance. + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -m64") # Set 64-bit machine. + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -march=skylake") # Use AVX2 instructions +endfunction() + +function(mm_common_set_global_compile_options) + + if(CMAKE_BUILD_TYPE EQUAL "Release") + add_compile_definitions(NDEBUG) + elseif (CMAKE_BUILD_TYPE EQUAL "Debug") + add_compile_definitions(_DEBUG) + endif () + + # Compile Flags. + # + # TODO: Make this function take a target and set the compile + # arguments per-target. + # + # Release flags come from the Autodesk Maya build scripts (and + # Visual Studio project files). + if (MSVC) + mm_common_windows_msvc_set_global_compile_options() + elseif (APPLE) + mm_common_apple_clang_set_global_compile_options() + else () + mm_common_linux_gcc_set_global_compile_options() + endif () +endfunction() + + +function(mm_common_set_global_treat_warnings_as_errors) + if(MSVC) + add_compile_options(/WX) + else() + add_compile_options(-Werror) + endif() +endfunction() + + macro(mm_common_set_relative_library_rpath target relative_path) # HACK: We must change the RPATH variable for the library so that a # binary can find the shared object, even if it's not in the diff --git a/share/cmake/modules/MMSolverUtils.cmake b/share/cmake/modules/MMSolverUtils.cmake index a51e2885f..db269e0eb 100644 --- a/share/cmake/modules/MMSolverUtils.cmake +++ b/share/cmake/modules/MMSolverUtils.cmake @@ -1,4 +1,4 @@ -# Copyright (C) 2020 David Cattermole. +# Copyright (C) 2020, 2024 David Cattermole. # # This file is part of mmSolver. # @@ -20,217 +20,6 @@ # -function(set_global_maya_plugin_compile_options) - - if(CMAKE_BUILD_TYPE EQUAL "Release") - add_compile_definitions(NDEBUG) - elseif (CMAKE_BUILD_TYPE EQUAL "Debug") - add_compile_definitions(_DEBUG) - endif () - - # Compile Flags. - # - # TODO: Make this function take a target and set the compile - # arguments per-target. - # - # Release flags come from the Autodesk Maya build scripts (and - # Visual Studio project files). - if (MSVC) - # For Visual Studio 11 2012 - set(CMAKE_CXX_FLAGS "") # Zero out the C++ flags, we have complete control. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GS") # Buffer Security Check - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:wchar_t") # wchar_t Is Native Type - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi") # Separate .pdb debug info file. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fp:precise") # Precise floating-point behavior - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:forScope") # Force Conformance in for Loop Scope - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GR") # Enable Run-Time Type Information - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Gd") # __cdecl Calling Convention - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc") # Handle standard C++ Exceptions. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") # Warning Levels 1-4 enabled. - - add_compile_definitions(OSWin_) - add_compile_definitions(WIN32) - add_compile_definitions(_WINDOWS) - add_compile_definitions(_USRDLL) - add_compile_definitions(NT_PLUGIN) - add_compile_definitions(_HAS_ITERATOR_DEBUGGING=0) - add_compile_definitions(_SECURE_SCL=0) - add_compile_definitions(_SECURE_SCL_THROWS=0) - add_compile_definitions(_SECURE_SCL_DEPRECATE=0) - add_compile_definitions(_CRT_SECURE_NO_DEPRECATE) - add_compile_definitions(_CRT_SECURE_NO_WARNINGS) - add_compile_definitions(TBB_USE_DEBUG=0) - add_compile_definitions(__TBB_LIB_NAME=tbb.lib) - add_compile_definitions(_WINDLL) - add_compile_definitions(Bits64_) - add_compile_definitions(REQUIRE_IOSTREAM) - add_compile_definitions(NT_PLUGIN) - add_compile_definitions(USERDLL) - - # Use multithread-specific Run-Time Library. - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} /MD") - - add_compile_options("/arch:AVX2") - - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Gy") # Enable Function-Level Linking - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GF") # Eliminate Duplicate Strings - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2") # Optimize for Maximize Speed - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Oi") # Generate Intrinsic Functions - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL") # Whole program optimization - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /arch:AVX2") # Use AVX2 instructions - - # Link-time code generation. - # - # /LTGC and /GL work together and therefore are both enabled. - set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG") - set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG") - set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} /LTCG") - - # Use debug-specific Run-Time Library. - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} /MDd") - - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od") # Optimize for Debug. - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /RTC1") # Run-time error checks - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Ob0") # Disables inline expansions. - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /GR") # Enable Run-Time Type Information - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /GL") # Whole program optimization - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Oi") # Generate Intrinsic Functions - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Gy") # Enable Function-Level Linking - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Zi") # Debug Information Format - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /EHsc") # Handle standard C++ Exceptions. - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /analyze") # Code analysis - - # Ensure Google Logging does not use "ERROR" macro, because Windows - # doesn't support it. - add_compile_definitions(GLOG_NO_ABBREVIATED_SEVERITIES) - - # Ceres running on Windows can trigger a MSVC warning: - # - # "Compiler Warning (level 3) C4996: Your code uses a function, - # class member, variable, or typedef that's marked deprecated." - # - # To override this, we define "_CRT_NONSTDC_NO_DEPRECATE" - # - # https://docs.microsoft.com/en-us/previous-versions/ms235384(v=vs.100) - add_compile_definitions(_CRT_NONSTDC_NO_DEPRECATE) - - elseif (APPLE) - - # For MacOS with Clang (which is the supported compiler for Maya - # 2018+) - set(CMAKE_CXX_FLAGS "") # Zero out the C++ flags, we have complete control. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -arch x86_64") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -stdlib=libc++") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-multichar -Wno-comment -Wno-sign-compare") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-deprecated -Wno-reorder") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftemplate-depth-35 -fno-gnu-keywords") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -funsigned-char -fpascal-strings") # -pthread - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCC_GNU_") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOSMac_") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOSMacOSX_") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DOSMac_MachO_") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_LANGUAGE_C_PLUS_PLUS") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.8") - - # Special MacOS linking stuff - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -headerpad_max_install_names") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -framework System") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -framework SystemConfiguration") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -framework CoreServices") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -framework Carbon") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -framework Cocoa") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -framework ApplicationServices") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -framework IOKit") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -bundle") - - set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") - set(CMAKE_CXX_FLAGS_RELEASE "-O3 -fPIC -fno-strict-aliasing -m64") - - else () - # For Linux with GCC - - # Definitions - add_compile_definitions(Bits64_) - add_compile_definitions(UNIX) - add_compile_definitions(_BOOL) - add_compile_definitions(LINUX) - add_compile_definitions(linux) - add_compile_definitions(__linux__) - add_compile_definitions(OSLinux_) - add_compile_definitions(FUNCPROTO) - add_compile_definitions(_GNU_SOURCE) - add_compile_definitions(LINUX_64) - add_compile_definitions(REQUIRE_IOSTREAM) - - # Use the older C++11 ABI for std::string and std::list, to be - # compatible with RHEL/CentOS 7, Maya and the VFX Platform. - # - # https://vfxplatform.com/#footnote-gcc6 - # - # TODO: In VFX Platform CY2023, and the move to RHEL 8 or RHEL 9, - # the new default is to use "_GLIBCXX_USE_CXX11_ABI=1". - # - # https://vfxplatform.com/#footnote-gcc9 - # - # TODO: Expose this variable as a CMake option, so we can enable - # this for RHEL 8/9 and Rocky Linux 8. - add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0) - - # Enable warnings. - add_definitions(-Wall) - add_definitions(-Wno-multichar) - add_definitions(-Wno-comment) - add_definitions(-Wno-sign-compare) - add_definitions(-Wno-unused-parameter) - add_definitions(-Wno-unused-parameter) - add_definitions(-Wno-unused-variable) - add_definitions(-Wno-unused-private-field) - add_definitions(-Wno-deprecated) - add_definitions(-Wno-reorder) - - # GCC Features - add_definitions(-pthread) - add_definitions(-fopenmp) - add_definitions(-fvisibility=hidden) - add_definitions(-funsigned-char) - add_definitions(-fno-gnu-keywords) - add_definitions(-fno-strict-aliasing) - - # '-ftemplate-depth-27' is required to compile under GCC 4.8.5. - # '-ftemplate-depth-35' is required to compile under GCC 5.5.x. - # - # In GCC 6.3.x, with C++11 the default depth is set to 900, but - # the C++11 standard says 1024 is the default. - add_definitions(-ftemplate-depth-900) - - set(CMAKE_CXX_FLAGS_DEBUG "-O0") # No optmizations. - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g") # Include debug symbols - - # Enable AddressSanitizer. - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address") - - # Nicer stack traces in error messages - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer") - - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") # Optimize for maximum performance. - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -m64") # Set 64-bit machine. - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -march=skylake") # Use AVX2 instructions - - endif () -endfunction() - - -function(set_global_treat_warnings_as_errors) - if(MSVC) - add_compile_options(/WX) - else() - add_compile_options(-Werror) - endif() -endfunction() - - function(add_library_maya_plugin target source_files) if (APPLE) add_library(${target} MODULE "${source_files}") @@ -332,7 +121,7 @@ function(install_target_plugin_to_module target module_dir) endfunction() -# Install the Plug-In. +# Install executables. function(install_target_executable_to_module target module_dir) set_target_properties(${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${module_dir}") @@ -344,9 +133,9 @@ endfunction() # Install shared (dynamic) library. function(install_shared_library lib_file lib_file_dll install_dir) - # message(STATUS "INSTALL FILE: ${lib_file}") - # message(STATUS "INSTALL DLL : ${lib_file_dll}") - # message(STATUS "INSTALL DIR : ${install_dir}") + # message(STATUS "install_shared_library install file: ${lib_file}") + # message(STATUS "install_shared_library install dll : ${lib_file_dll}") + # message(STATUS "install_shared_library install dir : ${install_dir}") if (WIN32) if (EXISTS ${lib_file_dll}) install(FILES ${lib_file_dll} @@ -357,12 +146,17 @@ function(install_shared_library lib_file lib_file_dll install_dir) elseif (UNIX) string(FIND ${lib_file} ".so" find_so) if(${find_so} GREATER_EQUAL 0) - # Install only the real library, with the symlink name. - get_filename_component(absolute_lib_file ${lib_file} REALPATH) + # message(STATUS "install_shared_library lib_file : ${lib_file}") + + # Install only the real library, with the original name. + get_filename_component(abs_lib_file ${lib_file} REALPATH) get_filename_component(lib_file_name ${lib_file} NAME) - install(FILES ${absolute_lib_file} + # message(STATUS "install_shared_library abs_lib_file : ${abs_lib_file}") + # message(STATUS "install_shared_library lib_file_name: ${lib_file_name}") + install(FILES ${abs_lib_file} DESTINATION ${install_dir} - RENAME ${lib_file_name}) + RENAME ${lib_file_name} + ) # # Copies all files similar to ${lib_file} to the install # # directory. @@ -375,6 +169,37 @@ function(install_shared_library lib_file lib_file_dll install_dir) endfunction() +function(install_shared_library_with_name lib_file lib_file_dll lib_name install_dir) + # message(STATUS "install_shared_library install file: ${lib_file}") + # message(STATUS "install_shared_library install dll : ${lib_file_dll}") + # message(STATUS "install_shared_library install name: ${lib_name}") + # message(STATUS "install_shared_library install dir : ${install_dir}") + if (WIN32) + if (EXISTS ${lib_file_dll}) + install(FILES ${lib_file_dll} + DESTINATION ${install_dir}) + else () + message(FATAL_ERROR "Cannot find .dll file to install: ${lib_file_dll}") + endif () + elseif (UNIX) + string(FIND ${lib_file} ".so" find_so) + if(${find_so} GREATER_EQUAL 0) + # message(STATUS "install_shared_library lib_file : ${lib_file}") + + # Install only the real library, with the original name. + get_filename_component(abs_lib_file ${lib_file} REALPATH) + get_filename_component(lib_file_name ${lib_file} NAME) + # message(STATUS "install_shared_library abs_lib_file : ${abs_lib_file}") + + install(FILES ${abs_lib_file} + DESTINATION ${install_dir} + RENAME ${lib_name} + ) + endif () + endif () +endfunction() + + # Install many shared (dynamic) libraries. function(install_shared_libraries lib_files lib_files_dll install_dir) # message(STATUS "INSTALL FILES: ${lib_files}") @@ -421,6 +246,7 @@ function(compile_qt_ui_to_python_file PATH_SUFFIXES MacOS/ bin/ + libexec/ # Maya 2025 on Linux changed the directory. DOC "Maya provided Qt 'uic' executable path" ) @@ -484,6 +310,7 @@ function(compile_qt_resources_qrc_to_rcc_file MacOS bin3/ # Use Python 3.x location, in Maya 2022. bin/ + libexec/ # Maya 2025 on Linux changed the directory. DOC "Maya's Qt resource compiler (rcc) executable path" ) diff --git a/share/config/functions.json b/share/config/functions.json index 71c42796c..f962ec368 100644 --- a/share/config/functions.json +++ b/share/config/functions.json @@ -5,7 +5,7 @@ "name": "Solver", "name_shelf": "", "tooltip": "Open the MM Solver window.", - "icon_shelf": "mmSolverWindow_32x32.png", + "icon_shelf": "mmSolverWindow.png", "command": [ "import mmSolver.tools.solver.tool;", "mmSolver.tools.solver.tool.open_window();" @@ -15,7 +15,7 @@ "name": "Open Solver...", "name_shelf": "", "tooltip": "Open the MM Solver window.", - "icon_shelf": "mmSolverWindow_32x32.png", + "icon_shelf": "mmSolverWindow.png", "command": [ "import mmSolver.tools.solver.tool;", "mmSolver.tools.solver.tool.open_window();" @@ -24,7 +24,7 @@ "solver_run": { "name": "Run Solver", "name_shelf": "", - "icon_shelf": "mmSolverRun_32x32.png", + "icon_shelf": "mmSolverRun.png", "tooltip": "Run solver on currently active Collection.", "command": [ "import mmSolver.tools.solver.tool as tool;", @@ -34,7 +34,7 @@ "solver_run_current_frame": { "name": "Run Solver (Current Frame)", "name_shelf": "", - "icon_shelf": "mmSolverRunFrame_32x32.png", + "icon_shelf": "mmSolverRunFrame.png", "tooltip": "Run solver on currently active Collection on the current frame.", "command": [ "import mmSolver.tools.solver.tool as tool;", @@ -44,7 +44,7 @@ "navigate_root_frame_next": { "name": "Next User Frame", "name_shelf": "", - "icon_shelf": "keyFrameNext_32x32.png", + "icon_shelf": "keyFrameNext.png", "tooltip": "Jump to the next user frame.", "command": [ "import mmSolver.tools.navigaterootframes.tool as tool;", @@ -54,7 +54,7 @@ "navigate_root_frame_prev": { "name": "Previous User Frame", "name_shelf": "", - "icon_shelf": "keyFramePrevious_32x32.png", + "icon_shelf": "keyFramePrevious.png", "tooltip": "Jump to the previous user frame.", "command": [ "import mmSolver.tools.navigaterootframes.tool as tool;", @@ -64,7 +64,7 @@ "edit_root_frame_add": { "name": "Add User Frame", "name_shelf": "", - "icon_shelf": "keyFrameAdd_32x32.png", + "icon_shelf": "keyFrameAdd.png", "tooltip": "Add the current frame to the user frame list.", "command": [ "import mmSolver.tools.editrootframes.tool as tool;", @@ -74,7 +74,7 @@ "edit_root_frame_remove": { "name": "Remove User Frame", "name_shelf": "", - "icon_shelf": "keyFrameRemove_32x32.png", + "icon_shelf": "keyFrameRemove.png", "tooltip": "Remove the current frame from the user frame list.", "command": [ "import mmSolver.tools.editrootframes.tool as tool;", @@ -84,7 +84,7 @@ "file_io_tools": { "name": "File Input/Output Tools", "name_shelf": "", - "icon_shelf": "fileInputOutput_32x32.png", + "icon_shelf": "fileInputOutput.png", "tooltip": "File input and output Tools.", "command": ["pass"] }, @@ -98,7 +98,7 @@ "name": "Create Marker", "name_shelf": "", "tooltip": "Create a new Marker.", - "icon_shelf": "createMarker_32x32.png", + "icon_shelf": "createMarker.png", "command": [ "import mmSolver.tools.createmarker.tool;", "mmSolver.tools.createmarker.tool.main();" @@ -108,7 +108,7 @@ "name": "Marker Tools", "name_shelf": "", "tooltip": "Marker Tools", - "icon_shelf": "markerTools_32x32.png", + "icon_shelf": "markerTools.png", "command": ["pass"] }, "toggle_marker_lock": { @@ -123,7 +123,7 @@ "name": "Bundle Tools", "name_shelf": "", "tooltip": "Bundle Tools", - "icon_shelf": "bundleTools_32x32.png", + "icon_shelf": "bundleTools.png", "command": ["pass"] }, "toggle_bundle_lock": { @@ -249,21 +249,21 @@ "attr_tools": { "name": "Attribute Tools", "name_shelf": "", - "icon_shelf": "attributeTools_32x32.png", + "icon_shelf": "attributeTools.png", "tooltip": "Tools to adjust Attributes.", "command": ["pass"] }, "mb_tools": { "name": "Marker-Bundle Tools", "name_shelf": "", - "icon_shelf": "markerBundleLinkTools_32x32.png", + "icon_shelf": "markerBundleLinkTools.png", "tooltip": "Marker bundle link and unlink Tools.", "command": ["pass"] }, "mb_link_tools": { "name": "Marker-Bundle Link Tools", "name_shelf": "", - "icon_shelf": "markerBundleLinkTools_32x32.png", + "icon_shelf": "markerBundleLinkTools.png", "tooltip": "Marker bundle link and unlink Tools.", "command": ["pass"] }, @@ -287,7 +287,7 @@ "name": "Convert to Marker", "name_shelf": "", "tooltip": "Convert 3D Transform to Marker.", - "icon_shelf": "convertToMarker_32x32.png", + "icon_shelf": "convertToMarker.png", "command": [ "import mmSolver.tools.convertmarker.tool;", "mmSolver.tools.convertmarker.tool.main();" @@ -302,7 +302,7 @@ "name": "Convert to Marker...", "name_shelf": "", "tooltip": "Convert 3D Transform to Marker.", - "icon_shelf": "convertToMarker_32x32.png", + "icon_shelf": "convertToMarker.png", "command": [ "import mmSolver.tools.convertmarker.tool;", "mmSolver.tools.convertmarker.tool.open_window();" @@ -312,7 +312,7 @@ "name": "Load Marker...", "name_shelf": "", "tooltip": "Load Marker.", - "icon_shelf": "loadMarker_32x32.png", + "icon_shelf": "loadMarker.png", "command": [ "import mmSolver.tools.loadmarker.tool;", "mmSolver.tools.loadmarker.tool.open_window();" @@ -352,7 +352,7 @@ "name": "Create Bundle", "name_shelf": "", "tooltip": "Create Bundle.", - "icon_shelf": "createBundle_32x32.png", + "icon_shelf": "createBundle.png", "command": [ "import mmSolver.tools.createbundle.tool;", "mmSolver.tools.createbundle.tool.main();" @@ -373,7 +373,7 @@ "swap_marker_bundles": { "name": "Swap Markers / Bundles", "name_shelf": "", - "icon_shelf": "markerBundleSwapSelection_32x32.png", + "icon_shelf": "markerBundleSwapSelection.png", "tooltip": "Toggle Markers Bundles.", "command": [ "import mmSolver.tools.selection.tools as selection_tool;", @@ -383,7 +383,7 @@ "select_marker_bundles": { "name": "Select Markers and Bundles", "name_shelf": "", - "icon_shelf": "markerBundleCombineSelection_32x32.png", + "icon_shelf": "markerBundleCombineSelection.png", "tooltip": "Select Markers and Bundles.", "command": [ "import mmSolver.tools.selection.tools as selection_tool;", @@ -419,7 +419,7 @@ "display_tools": { "name": "Display Tools", "name_shelf": "", - "icon_shelf": "displayTools_32x32.png", + "icon_shelf": "displayTools.png", "tooltip": "Display Tools", "command": ["pass"] }, @@ -427,7 +427,7 @@ "name": "Create Sky Dome", "name_shelf": "", "tooltip": "Create a new Sky Dome.", - "icon_shelf": "createSkyDome_32x32.png", + "icon_shelf": "createSkyDome.png", "command": [ "import mmSolver.tools.createskydome.tool;", "import mmSolver.tools.createskydome.constant as const;", @@ -438,7 +438,7 @@ "name": "Create Axis Dome", "name_shelf": "", "tooltip": "Create a new Axis Dome.", - "icon_shelf": "createSkyDome_32x32.png", + "icon_shelf": "createSkyDome.png", "command": [ "import mmSolver.tools.createskydome.tool;", "import mmSolver.tools.createskydome.constant as const;", @@ -449,20 +449,54 @@ "name": "Create Horizon Line", "name_shelf": "", "tooltip": "Create a new Horizon Line.", - "icon_shelf": "createSkyDome_32x32.png", + "icon_shelf": "createSkyDome.png", "command": [ "import mmSolver.tools.createskydome.tool;", "import mmSolver.tools.createskydome.constant as const;", "mmSolver.tools.createskydome.tool.main(preset_name=const.PRESET_HORIZON_LINE_NAME);" ] }, + "toggle_viewport_geom": { + "name": "Toggle Viewport Geometry", + "name_shelf": "TglGeo", + "tooltip": "Toggle the visiblity of geometry in the active viewport.", + "command": [ + "import mmSolver.tools.toggleviewportgeom.tool;", + "mmSolver.tools.toggleviewportgeom.tool.main();" + ] + }, + "toggle_viewport_ctrls": { + "name": "Toggle Viewport Controls", + "name_shelf": "TglCtl", + "tooltip": "Toggle the visiblity of character controls in the active viewport.", + "command": [ + "import mmSolver.tools.toggleviewportctrls.tool;", + "mmSolver.tools.toggleviewportctrls.tool.main();" + ] + }, + "toggle_viewport_imgplns": { + "name": "Toggle Viewport Image Planes", + "name_shelf": "TglImPl", + "tooltip": "Toggle the visiblity of image planes in the active viewport.", + "command": [ + "import mmSolver.tools.toggleviewportimgplns.tool;", + "mmSolver.tools.toggleviewportimgplns.tool.main();" + ] + }, "general_tools": { "name": "General Tools", "name_shelf": "", - "icon_shelf": "generalTools_32x32.png", + "icon_shelf": "generalTools.png", "tooltip": "General Tools", "command": ["pass"] }, + "mesh_tools": { + "name": "Mesh Tools", + "name_shelf": "", + "icon_shelf": "meshTools.png", + "tooltip": "Mesh Tools", + "command": ["pass"] + }, "center_twodee_ui": { "name": "Offset 2D Center UI", "name_shelf": "Center", @@ -548,7 +582,7 @@ "zdepth_tools": { "name": "Z-Depth Tools", "name_shelf": "", - "icon_shelf": "zDepthTools_32x32.png", + "icon_shelf": "zDepthTools.png", "tooltip": "Screen-Space Z-Depth Tools", "command": ["pass"] }, @@ -617,6 +651,33 @@ "mmSolver.tools.undoredoscene.tool.main_redo();" ] }, + "mesh_from_points_ui": { + "name": "Mesh From Points...", + "name_shelf": "MeshFrPts", + "tooltip": "Open Mesh from Points UI, to create triangulated meshes.", + "command": [ + "import mmSolver.tools.meshfrompoints.tool;", + "mmSolver.tools.meshfrompoints.tool.main();" + ] + }, + "mesh_from_points_full_mesh": { + "name": "Create Full Mesh From Points", + "name_shelf": "FlMshPts", + "tooltip": "Create a triangulated full mesh from the selected nodes.", + "command": [ + "import mmSolver.tools.meshfrompoints.tool;", + "mmSolver.tools.meshfrompoints.tool.create_full_mesh();" + ] + }, + "mesh_from_points_border_mesh": { + "name": "Create Border Mesh From Points", + "name_shelf": "FlMshPts", + "tooltip": "Create a triangulated border mesh from the selected nodes.", + "command": [ + "import mmSolver.tools.meshfrompoints.tool;", + "mmSolver.tools.meshfrompoints.tool.create_border_mesh();" + ] + }, "reparent_under_node": { "name": "Reparent under Node (v1)", "name_shelf": "RePar", @@ -826,11 +887,47 @@ "mmSolver.tools.toggleobjectmotiontrail.tool.main();" ] }, + "enable_holdout_all_meshes": { + "name": "Enable Hold-Out For All Meshes", + "name_shelf": "EnHOAll", + "tooltip": "Enable Hold-Out for all meshes in scene.", + "command": [ + "import mmSolver.tools.setmeshholdouts.tool;", + "mmSolver.tools.setmeshholdouts.tool.enable_all_meshes();" + ] + }, + "disable_holdout_all_meshes": { + "name": "Disable Hold-Out For All Meshes", + "name_shelf": "DsHOAll", + "tooltip": "Disable Hold-Out for all meshes in scene.", + "command": [ + "import mmSolver.tools.setmeshholdouts.tool;", + "mmSolver.tools.setmeshholdouts.tool.disable_all_meshes();" + ] + }, + "enable_holdout_selected_meshes": { + "name": "Enable Hold-Out For Selected Meshes", + "name_shelf": "EnHOSel", + "tooltip": "Enable Hold-Out for selected meshes in scene.", + "command": [ + "import mmSolver.tools.setmeshholdouts.tool;", + "mmSolver.tools.setmeshholdouts.tool.enable_selected_meshes();" + ] + }, + "disable_holdout_selected_meshes": { + "name": "Disable Hold-Out For Selected Meshes", + "name_shelf": "DsHOSel", + "tooltip": "Disable Hold-Out for selected meshes in scene.", + "command": [ + "import mmSolver.tools.setmeshholdouts.tool;", + "mmSolver.tools.setmeshholdouts.tool.disable_selected_meshes();" + ] + }, "create_camera": { "name": "Create Camera", "name_shelf": "", "tooltip": "Create a new Camera.", - "icon_shelf": "createCamera_32x32.png", + "icon_shelf": "createCamera.png", "command": [ "import mmSolver.tools.createcamera.tool;", "mmSolver.tools.createcamera.tool.main();" @@ -840,20 +937,39 @@ "name": "Create Lens", "name_shelf": "", "tooltip": "Create a new Lens.", - "icon_shelf": "createLens_32x32.png", + "icon_shelf": "createLens.png", "command": [ "import mmSolver.tools.createlens.tool;", "mmSolver.tools.createlens.tool.main();" ] }, "create_image_plane": { + "name": "Create Image Plane... (version 1)", + "name_shelf": "", + "tooltip": "Create a new Image Plane.", + "icon_shelf": "createImagePlane.png", + "command": [ + "import mmSolver.tools.createimageplane.tool;", + "mmSolver.tools.createimageplane.tool.main_version_one();" + ] + }, + "create_image_plane2": { "name": "Create Image Plane...", "name_shelf": "", "tooltip": "Create a new Image Plane.", - "icon_shelf": "createImagePlane_32x32.png", + "icon_shelf": "createImagePlane.png", "command": [ "import mmSolver.tools.createimageplane.tool;", - "mmSolver.tools.createimageplane.tool.main();" + "mmSolver.tools.createimageplane.tool.main_version_two();" + ] + }, + "image_cache_preferences_window": { + "name": "Image Cache Preferences...", + "name_shelf": "ICPref", + "tooltip": "Open the Image Cache Preferences window.", + "command": [ + "import mmSolver.tools.imagecacheprefs.tool;", + "mmSolver.tools.imagecacheprefs.tool.open_window();" ] }, "camera_toggle_distortion": { @@ -902,7 +1018,7 @@ "name": "Camera Context", "name_shelf": "", "tooltip": "Display camera nodes.", - "icon_shelf": "cameraContext_32x32.png", + "icon_shelf": "cameraContext.png", "command": [ "pass" ] @@ -919,7 +1035,7 @@ "name": "Deform Marker", "name_shelf": "MkrOff", "tooltip": "Deform Marker", - "icon_shelf": "createMarker_32x32.png", + "icon_shelf": "createMarker.png", "command": ["pass"] }, "deform_marker_create_offset": { @@ -950,7 +1066,7 @@ "name": "Hotkey", "name_shelf": "", "tooltip": "Switch Hotkey Sets.", - "icon_shelf": "hotkeySwitcher_32x32.png", + "icon_shelf": "hotkeySwitcher.png", "command": [ "pass" ] @@ -996,14 +1112,14 @@ "name": "Camera Tools", "name_shelf": "", "tooltip": "Camera Tools", - "icon_shelf": "cameraTools_32x32.png", + "icon_shelf": "cameraTools.png", "command": ["pass"] }, "create_line": { "name": "Create Line", "name_shelf": "", "tooltip": "Create a new Line.", - "icon_shelf": "createLine_32x32.png", + "icon_shelf": "createLine.png", "command": [ "import mmSolver.tools.createline.tool;", "mmSolver.tools.createline.tool.main();" @@ -1013,7 +1129,7 @@ "name": "Line Tools", "name_shelf": "", "tooltip": "Line Tools", - "icon_shelf": "lineTools_32x32.png", + "icon_shelf": "lineTools.png", "command": ["pass"] }, "toggle_line_lock": { diff --git a/share/config/menu.json b/share/config/menu.json index c655ab6c6..6ee326983 100644 --- a/share/config/menu.json +++ b/share/config/menu.json @@ -8,7 +8,7 @@ "solver_run_current_frame", "---Create", "create_camera", - "create_image_plane", + "create_image_plane2", "create_lens", "create_marker", "convert_to_marker", @@ -37,7 +37,7 @@ "attr_tools/attribute_bake", "camera_tools/---Camera & Lens", "camera_tools/create_camera", - "camera_tools/create_image_plane", + "camera_tools/create_image_plane2", "camera_tools/create_lens", "camera_tools/---Distortion", "camera_tools/camera_toggle_distortion", @@ -46,6 +46,7 @@ "camera_tools/---Input Output", "camera_tools/copy_camera_to_clipboard", "camera_tools/load_lens", + "camera_tools/save_lens_file", "marker_tools/---Edit Marker", "marker_tools/toggle_marker_lock", "marker_tools/place_marker_manipulator", @@ -79,6 +80,10 @@ "display_tools/---Viewport", "display_tools/center_twodee", "display_tools/center_twodee_remove", + "display_tools/---", + "display_tools/toggle_viewport_geom", + "display_tools/toggle_viewport_ctrls", + "display_tools/toggle_viewport_imgplns", "display_tools/---Create", "display_tools/create_horizon_line", "display_tools/create_axis_dome", @@ -88,6 +93,11 @@ "display_tools/reset_object_colour", "display_tools/toggle_object_motion_trail", "display_tools/create_screen_space_motion_trail", + "display_tools/---Mesh Hold-Out", + "display_tools/enable_holdout_all_meshes", + "display_tools/disable_holdout_all_meshes", + "display_tools/enable_holdout_selected_meshes", + "display_tools/disable_holdout_selected_meshes", "zdepth_tools/---Z-Depth Screen-Space Tools", "zdepth_tools/screen_z_manipulator", "zdepth_tools/screen_space_rig_bake", @@ -95,6 +105,10 @@ "zdepth_tools/camera_object_scale_remove", "zdepth_tools/---", "zdepth_tools/screen_z_transform_bake", + "mesh_tools/---Create", + "mesh_tools/mesh_from_points_full_mesh", + "mesh_tools/mesh_from_points_border_mesh", + "mesh_tools/mesh_from_points_ui", "general_tools/---Parenting", "general_tools/reparent_under_node2", "general_tools/unparent_to_world2", @@ -116,6 +130,7 @@ "general_tools/sort_nodes_in_outliner", "general_tools/remove_all_solver_nodes", "general_tools/---Settings & Preferences", + "general_tools/image_cache_preferences_window", "general_tools/user_preferences_window", "file_io_tools/---Marker", "file_io_tools/load_marker_ui", @@ -156,6 +171,10 @@ "name": "Z-Depth Tools", "tearoff": true }, + "mesh_tools": { + "name": "Mesh Tools", + "tearoff": true + }, "general_tools": { "name": "General Tools", "tearoff": true diff --git a/share/config/shelf.json b/share/config/shelf.json index aa1b35c5d..5554df292 100644 --- a/share/config/shelf.json +++ b/share/config/shelf.json @@ -31,7 +31,7 @@ "attr_tools/attr_popup/attribute_bake", "camera_tools/camera_popup/---Camera & Lens", "camera_tools/camera_popup/create_camera", - "camera_tools/camera_popup/create_image_plane", + "camera_tools/camera_popup/create_image_plane2", "camera_tools/camera_popup/create_lens", "camera_tools/camera_popup/---Distortion", "camera_tools/camera_popup/camera_toggle_distortion", @@ -40,6 +40,7 @@ "camera_tools/camera_popup/---Input Output", "camera_tools/camera_popup/copy_camera_to_clipboard", "camera_tools/camera_popup/load_lens", + "camera_tools/camera_popup/save_lens_file", "marker_tools/marker_popup/---Edit Marker", "marker_tools/marker_popup/toggle_marker_lock", "marker_tools/marker_popup/place_marker_manipulator", @@ -76,6 +77,10 @@ "display_tools/display_popup/---Viewport", "display_tools/display_popup/center_twodee", "display_tools/display_popup/center_twodee_remove", + "display_tools/display_popup/---", + "display_tools/display_popup/toggle_viewport_geom", + "display_tools/display_popup/toggle_viewport_ctrls", + "display_tools/display_popup/toggle_viewport_imgplns", "display_tools/display_popup/---Create", "display_tools/display_popup/create_horizon_line", "display_tools/display_popup/create_axis_dome", @@ -85,6 +90,11 @@ "display_tools/display_popup/reset_object_colour", "display_tools/display_popup/toggle_object_motion_trail", "display_tools/display_popup/create_screen_space_motion_trail", + "display_tools/display_popup/---Mesh Hold-Out", + "display_tools/display_popup/enable_holdout_all_meshes", + "display_tools/display_popup/disable_holdout_all_meshes", + "display_tools/display_popup/enable_holdout_selected_meshes", + "display_tools/display_popup/disable_holdout_selected_meshes", "zdepth_tools/zdepth_popup/---Z-Depth Screen-Space Tools", "zdepth_tools/zdepth_popup/screen_z_manipulator", "zdepth_tools/zdepth_popup/screen_space_rig_bake", @@ -92,6 +102,10 @@ "zdepth_tools/zdepth_popup/camera_object_scale_remove", "zdepth_tools/zdepth_popup/---", "zdepth_tools/zdepth_popup/screen_z_transform_bake", + "mesh_tools/mesh_tools_popup/---Create", + "mesh_tools/mesh_tools_popup/mesh_from_points_full_mesh", + "mesh_tools/mesh_tools_popup/mesh_from_points_border_mesh", + "mesh_tools/mesh_tools_popup/mesh_from_points_ui", "general_tools/gen_tools_popup/---Parenting", "general_tools/gen_tools_popup/reparent_under_node2", "general_tools/gen_tools_popup/unparent_to_world2", @@ -113,6 +127,7 @@ "general_tools/gen_tools_popup/sort_nodes_in_outliner", "general_tools/gen_tools_popup/remove_all_solver_nodes", "general_tools/gen_tools_popup/---Settings & Preferences", + "general_tools/gen_tools_popup/image_cache_preferences_window", "general_tools/gen_tools_popup/user_preferences_window", "file_io_tools/file_io_popup/---Marker", "file_io_tools/file_io_popup/load_marker_ui", @@ -170,6 +185,10 @@ "popup": true, "popup_button": ["left", "right"] }, + "mesh_tools_popup": { + "popup": true, + "popup_button": ["left", "right"] + }, "gen_tools_popup": { "popup": true, "popup_button": ["left", "right"] diff --git a/share/config/shelf_minimal.json b/share/config/shelf_minimal.json index c1218d333..c9485f36c 100644 --- a/share/config/shelf_minimal.json +++ b/share/config/shelf_minimal.json @@ -8,7 +8,7 @@ "solver_tools/solver_popup/solver_run_current_frame", "create_tools/create_popup/---Camera & Lens", "create_tools/create_popup/create_camera", - "create_tools/create_popup/create_image_plane", + "create_tools/create_popup/create_image_plane2", "create_tools/create_popup/create_lens", "create_tools/create_popup/---Create Marker", "create_tools/create_popup/create_marker", @@ -43,7 +43,7 @@ "attr_tools/attr_popup/attribute_bake", "camera_tools/camera_popup/---Camera & Lens", "camera_tools/camera_popup/create_camera", - "camera_tools/camera_popup/create_image_plane", + "camera_tools/camera_popup/create_image_plane2", "camera_tools/camera_popup/create_lens", "camera_tools/camera_popup/---Transform", "camera_tools/camera_popup/set_camera_origin_frame", @@ -52,6 +52,7 @@ "camera_tools/camera_popup/---Input Output", "camera_tools/camera_popup/copy_camera_to_clipboard", "camera_tools/camera_popup/load_lens", + "camera_tools/camera_popup/save_lens_file", "marker_tools/marker_popup/---Edit Marker", "marker_tools/marker_popup/toggle_marker_lock", "marker_tools/marker_popup/place_marker_manipulator", @@ -87,6 +88,10 @@ "display_tools/display_popup/---Viewport", "display_tools/display_popup/center_twodee", "display_tools/display_popup/center_twodee_remove", + "display_tools/display_popup/---", + "display_tools/display_popup/toggle_viewport_geom", + "display_tools/display_popup/toggle_viewport_ctrls", + "display_tools/display_popup/toggle_viewport_imgplns", "display_tools/display_popup/---Create", "display_tools/display_popup/create_horizon_line", "display_tools/display_popup/create_axis_dome", @@ -96,6 +101,11 @@ "display_tools/display_popup/reset_object_colour", "display_tools/display_popup/toggle_object_motion_trail", "display_tools/display_popup/create_screen_space_motion_trail", + "display_tools/display_popup/---Mesh Hold-Out", + "display_tools/display_popup/enable_holdout_all_meshes", + "display_tools/display_popup/disable_holdout_all_meshes", + "display_tools/display_popup/enable_holdout_selected_meshes", + "display_tools/display_popup/disable_holdout_selected_meshes", "zdepth_tools/zdepth_popup/---Z-Depth Screen-Space Tools", "zdepth_tools/zdepth_popup/screen_z_manipulator", "zdepth_tools/zdepth_popup/screen_space_rig_bake", @@ -103,6 +113,10 @@ "zdepth_tools/zdepth_popup/camera_object_scale_remove", "zdepth_tools/zdepth_popup/---", "zdepth_tools/zdepth_popup/screen_z_transform_bake", + "mesh_tools/mesh_tools_popup/---Create", + "mesh_tools/mesh_tools_popup/mesh_from_points_full_mesh", + "mesh_tools/mesh_tools_popup/mesh_from_points_border_mesh", + "mesh_tools/mesh_tools_popup/mesh_from_points_ui", "general_tools/gen_tools_popup/---Parenting", "general_tools/gen_tools_popup/reparent_under_node2", "general_tools/gen_tools_popup/unparent_to_world2", @@ -124,6 +138,7 @@ "general_tools/gen_tools_popup/sort_nodes_in_outliner", "general_tools/gen_tools_popup/remove_all_solver_nodes", "general_tools/gen_tools_popup/---Settings & Preferences", + "general_tools/gen_tools_popup/image_cache_preferences_window", "general_tools/gen_tools_popup/user_preferences_window", "file_io_tools/file_io_popup/---Marker", "file_io_tools/file_io_popup/load_marker_ui", diff --git a/share/docker/Dockerfile_maya2019 b/share/docker/Dockerfile_maya2019 index 6e4bc91f0..8330f035c 100644 --- a/share/docker/Dockerfile_maya2019 +++ b/share/docker/Dockerfile_maya2019 @@ -1,5 +1,6 @@ # To create and run the docker container in PowerShell or BASH: # +# $ cd /path/to/project/root/mayaMatchMoveSolver/ # $ docker build --file share/docker/Dockerfile_maya2019 -t mmsolver-linux-maya2019-build . # $ docker run --rm --interactive --volume "${PWD}:/mmSolver" --tty mmsolver-linux-maya2019-build # @@ -26,12 +27,30 @@ FROM centos:7 +# Pull CentOS 7.x Packages from the Vault, because CentOS 7.x is now +# End-Of-Life (EOL) as of June 30tb, 2024. +# +# https://vault.centos.org/centos/7/ +# https://serverfault.com/questions/1161816/mirrorlist-centos-org-no-longer-resolve +RUN sed -i 's/mirror.centos.org/vault.centos.org/g' /etc/yum.repos.d/CentOS-*.repo \ + && sed -i 's/^#.*baseurl=http/baseurl=http/g' /etc/yum.repos.d/CentOS-*.repo \ + && sed -i 's/^mirrorlist=http/#mirrorlist=http/g' /etc/yum.repos.d/CentOS-*.repo + +# Install Software Collections (SCL) repo for the extended devtools. +# Install Extra Packages Enterprise Linux (EPEL) repo for extra tools. +# Install Yum and RPM tools to speed up installations. RUN yum install --assumeyes \ centos-release-scl \ epel-release \ deltarpm \ yum-utils +# Now new repos are installed, make sure they're all from the Vault. +RUN sed -i 's/mirror.centos.org/vault.centos.org/g' /etc/yum.repos.d/CentOS-*.repo \ + && sed -i 's/^#.*baseurl=http/baseurl=http/g' /etc/yum.repos.d/CentOS-*.repo \ + && sed -i 's/^mirrorlist=http/#mirrorlist=http/g' /etc/yum.repos.d/CentOS-*.repo \ + && yum clean all && yum makecache + # OpenSource "mesa" OpenGL Driver. RUN yum install --assumeyes \ mesa-libGLw \ @@ -131,7 +150,7 @@ RUN curl https://sh.rustup.rs -sSf | sh -s -- -y \ # Install Maya from archive. ADD ./external/archives/Autodesk_Maya_2019_Linux_64bit.tgz /temp -RUN rpm -Uvh /temp/Maya*.rpm && rm -r /temp +RUN rpm -Uvh /temp/Maya2019*.rpm && rm -r /temp ENV MAYA_LOCATION=/usr/autodesk/maya/ ENV PATH=$MAYA_LOCATION/bin:$PATH diff --git a/share/docker/Dockerfile_maya2020 b/share/docker/Dockerfile_maya2020 index 881cd9273..6dce6e489 100644 --- a/share/docker/Dockerfile_maya2020 +++ b/share/docker/Dockerfile_maya2020 @@ -1,5 +1,6 @@ # To create and run the docker container in PowerShell or BASH: # +# $ cd /path/to/project/root/mayaMatchMoveSolver/ # $ docker build --file share/docker/Dockerfile_maya2020 -t mmsolver-linux-maya2020-build . # $ docker run --rm --interactive --volume "${PWD}:/mmSolver" --tty mmsolver-linux-maya2020-build # @@ -26,12 +27,30 @@ FROM centos:7 +# Pull CentOS 7.x Packages from the Vault, because CentOS 7.x is now +# End-Of-Life (EOL) as of June 30tb, 2024. +# +# https://vault.centos.org/centos/7/ +# https://serverfault.com/questions/1161816/mirrorlist-centos-org-no-longer-resolve +RUN sed -i 's/mirror.centos.org/vault.centos.org/g' /etc/yum.repos.d/CentOS-*.repo \ + && sed -i 's/^#.*baseurl=http/baseurl=http/g' /etc/yum.repos.d/CentOS-*.repo \ + && sed -i 's/^mirrorlist=http/#mirrorlist=http/g' /etc/yum.repos.d/CentOS-*.repo + +# Install Software Collections (SCL) repo for the extended devtools. +# Install Extra Packages Enterprise Linux (EPEL) repo for extra tools. +# Install Yum and RPM tools to speed up installations. RUN yum install --assumeyes \ centos-release-scl \ epel-release \ deltarpm \ yum-utils +# Now new repos are installed, make sure they're all from the Vault. +RUN sed -i 's/mirror.centos.org/vault.centos.org/g' /etc/yum.repos.d/CentOS-*.repo \ + && sed -i 's/^#.*baseurl=http/baseurl=http/g' /etc/yum.repos.d/CentOS-*.repo \ + && sed -i 's/^mirrorlist=http/#mirrorlist=http/g' /etc/yum.repos.d/CentOS-*.repo \ + && yum clean all && yum makecache + # OpenSource "mesa" OpenGL Driver. RUN yum install --assumeyes \ mesa-libGLw \ @@ -128,7 +147,7 @@ RUN curl https://sh.rustup.rs -sSf | sh -s -- -y \ # Install Maya from archive. ADD ./external/archives/Autodesk_Maya_2020_ML_Linux_64bit.tgz /temp -RUN rpm -Uvh /temp/Packages/Maya*.rpm && rm -r /temp +RUN rpm -Uvh /temp/Packages/Maya2020*.rpm && rm -r /temp ENV MAYA_LOCATION=/usr/autodesk/maya/ ENV PATH=$MAYA_LOCATION/bin:$PATH diff --git a/share/docker/Dockerfile_maya2022 b/share/docker/Dockerfile_maya2022 index 4cfc6856f..128d2b5ee 100644 --- a/share/docker/Dockerfile_maya2022 +++ b/share/docker/Dockerfile_maya2022 @@ -1,5 +1,6 @@ # To create and run the docker container in PowerShell or BASH: # +# $ cd /path/to/project/root/mayaMatchMoveSolver/ # $ docker build --file share/docker/Dockerfile_maya2022 -t mmsolver-linux-maya2022-build . # $ docker run --rm --interactive --volume "${PWD}:/mmSolver" --tty mmsolver-linux-maya2022-build # @@ -26,12 +27,30 @@ FROM centos:7 +# Pull CentOS 7.x Packages from the Vault, because CentOS 7.x is now +# End-Of-Life (EOL) as of June 30tb, 2024. +# +# https://vault.centos.org/centos/7/ +# https://serverfault.com/questions/1161816/mirrorlist-centos-org-no-longer-resolve +RUN sed -i 's/mirror.centos.org/vault.centos.org/g' /etc/yum.repos.d/CentOS-*.repo \ + && sed -i 's/^#.*baseurl=http/baseurl=http/g' /etc/yum.repos.d/CentOS-*.repo \ + && sed -i 's/^mirrorlist=http/#mirrorlist=http/g' /etc/yum.repos.d/CentOS-*.repo + +# Install Software Collections (SCL) repo for the extended devtools. +# Install Extra Packages Enterprise Linux (EPEL) repo for extra tools. +# Install Yum and RPM tools to speed up installations. RUN yum install --assumeyes \ centos-release-scl \ epel-release \ deltarpm \ yum-utils +# Now new repos are installed, make sure they're all from the Vault. +RUN sed -i 's/mirror.centos.org/vault.centos.org/g' /etc/yum.repos.d/CentOS-*.repo \ + && sed -i 's/^#.*baseurl=http/baseurl=http/g' /etc/yum.repos.d/CentOS-*.repo \ + && sed -i 's/^mirrorlist=http/#mirrorlist=http/g' /etc/yum.repos.d/CentOS-*.repo \ + && yum clean all && yum makecache + # OpenSource "mesa" OpenGL Driver. RUN yum install --assumeyes \ mesa-libGLw \ @@ -135,7 +154,7 @@ RUN curl https://sh.rustup.rs -sSf | sh -s -- -y \ # Install Maya from archive. ADD ./external/archives/Autodesk_Maya_2022_ML_Linux_64bit.tgz /temp -RUN rpm -Uvh /temp/Packages/Maya*.rpm && rm -r /temp +RUN rpm -Uvh /temp/Packages/Maya2022*.rpm && rm -r /temp ENV MAYA_LOCATION=/usr/autodesk/maya/ ENV PATH=$MAYA_LOCATION/bin:$PATH diff --git a/share/docker/Dockerfile_maya2023 b/share/docker/Dockerfile_maya2023 index c321beaa8..a3e86c1c8 100644 --- a/share/docker/Dockerfile_maya2023 +++ b/share/docker/Dockerfile_maya2023 @@ -1,5 +1,6 @@ # To create and run the docker container in PowerShell or BASH: # +# $ cd /path/to/project/root/mayaMatchMoveSolver/ # $ docker build --file share/docker/Dockerfile_maya2023 -t mmsolver-linux-maya2023-build . # $ docker run --rm --interactive --volume "${PWD}:/mmSolver" --tty mmsolver-linux-maya2023-build # @@ -26,12 +27,30 @@ FROM centos:7 +# Pull CentOS 7.x Packages from the Vault, because CentOS 7.x is now +# End-Of-Life (EOL) as of June 30tb, 2024. +# +# https://vault.centos.org/centos/7/ +# https://serverfault.com/questions/1161816/mirrorlist-centos-org-no-longer-resolve +RUN sed -i 's/mirror.centos.org/vault.centos.org/g' /etc/yum.repos.d/CentOS-*.repo \ + && sed -i 's/^#.*baseurl=http/baseurl=http/g' /etc/yum.repos.d/CentOS-*.repo \ + && sed -i 's/^mirrorlist=http/#mirrorlist=http/g' /etc/yum.repos.d/CentOS-*.repo + +# Install Software Collections (SCL) repo for the extended devtools. +# Install Extra Packages Enterprise Linux (EPEL) repo for extra tools. +# Install Yum and RPM tools to speed up installations. RUN yum install --assumeyes \ centos-release-scl \ epel-release \ deltarpm \ yum-utils +# Now new repos are installed, make sure they're all from the Vault. +RUN sed -i 's/mirror.centos.org/vault.centos.org/g' /etc/yum.repos.d/CentOS-*.repo \ + && sed -i 's/^#.*baseurl=http/baseurl=http/g' /etc/yum.repos.d/CentOS-*.repo \ + && sed -i 's/^mirrorlist=http/#mirrorlist=http/g' /etc/yum.repos.d/CentOS-*.repo \ + && yum clean all && yum makecache + # OpenSource "mesa" OpenGL Driver. RUN yum install --assumeyes \ mesa-libGLw \ @@ -135,7 +154,7 @@ RUN curl https://sh.rustup.rs -sSf | sh -s -- -y \ # Install Maya from archive. ADD ./external/archives/Autodesk_Maya_2023_ML_Linux_64bit.tgz /temp -RUN rpm -Uvh /temp/Packages/Maya*.rpm && rm -r /temp +RUN rpm -Uvh /temp/Packages/Maya2023*.rpm && rm -r /temp ENV MAYA_LOCATION=/usr/autodesk/maya/ ENV PATH=$MAYA_LOCATION/bin:$PATH diff --git a/share/docker/Dockerfile_maya2024 b/share/docker/Dockerfile_maya2024 index 3da0cd0ff..b18f3b9e8 100644 --- a/share/docker/Dockerfile_maya2024 +++ b/share/docker/Dockerfile_maya2024 @@ -1,5 +1,6 @@ # To create and run the docker container in PowerShell or BASH: # +# $ cd /path/to/project/root/mayaMatchMoveSolver/ # $ docker build --file share/docker/Dockerfile_maya2024 -t mmsolver-linux-maya2024-build . # $ docker run --rm --interactive --volume "${PWD}:/mmSolver" --tty mmsolver-linux-maya2024-build # @@ -32,6 +33,7 @@ FROM rockylinux:8 # And this forum post: # https://forums.autodesk.com/t5/maya-forum/install-maya-2023-update-3-on-rocky-linux-8-7-instructions/td-p/11735138 +# Install Extra Packages Enterprise Linux (EPEL) repo for extra tools. RUN dnf install --assumeyes https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm \ && dnf install --assumeyes epel-release \ && dnf update --assumeyes \ diff --git a/share/docker/Dockerfile_maya2025 b/share/docker/Dockerfile_maya2025 new file mode 100644 index 000000000..cbc72745b --- /dev/null +++ b/share/docker/Dockerfile_maya2025 @@ -0,0 +1,163 @@ +# To create and run the docker container in PowerShell or BASH: +# +# $ cd /path/to/project/root/mayaMatchMoveSolver/ +# $ docker build --file share/docker/Dockerfile_maya2025 -t mmsolver-linux-maya2025-build . +# $ docker run --rm --interactive --volume "${PWD}:/mmSolver" --tty mmsolver-linux-maya2025-build +# +# +# Once the docker image is built and run, you can execute the +# following... +# +# +# Install Python and run tools: +# +# $ source ./scripts/python_venv_activate_maya2025.bash +# $ ./scripts/python_linter_run_pylint.bash +# $ ./scripts/python_linter_run_flake8.bash +# $ ./scripts/python_linter_run_cpplint.bash +# $ ./scripts/python_formatter_run_black_check.bash +# $ deactivate +# +# +# Build CMake project: +# +# $ ./scripts/build_mmSolver_linux_maya2025.bash +# $ mayapy tests/runTests.py +# + +FROM rockylinux:8 + +# Maya documentation for installing on RHEL8 / Rocky8: +# https://help.autodesk.com/view/MAYAUL/2025/ENU/?guid=GUID-D2B5433C-E0D2-421B-9BD8-24FED217FD7F +# +# And this forum post: +# https://forums.autodesk.com/t5/maya-forum/install-maya-2023-update-3-on-rocky-linux-8-7-instructions/td-p/11735138 + +# Install Extra Packages Enterprise Linux (EPEL) repo for extra tools. +RUN dnf install --assumeyes https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm \ + && dnf install --assumeyes epel-release \ + && dnf update --assumeyes \ + && dnf makecache + +# General packages +RUN dnf install --assumeyes \ + glibc \ + libSM \ + libICE \ + zlib \ + openssl-libs \ + nss \ + libnsl \ + dbus \ + redhat-lsb-core \ + pcre-utf16 \ + pciutils \ + libXdamage + +# Multimedia Packages +RUN dnf install --assumeyes \ + mesa-libGL \ + mesa-libGL-devel \ + mesa-libGLU \ + mesa-libGLw \ + gamin \ + audiofile-devel \ + e2fsprogs-libs \ + libcap \ + libdrm \ + libmng \ + speech-dispatcher \ + cups \ + libpng15 + +# X Window – Xcb – X11 Packages +RUN dnf install --assumeyes \ + libX11 \ + libXScrnSaver \ + libXau \ + libXcomposite \ + libXcursor \ + libXext \ + libXfixes \ + libXi \ + libXinerama \ + libXmu \ + libXp \ + libXpm \ + libXrandr \ + libXrender \ + libXt \ + libXtst \ + libxcb \ + libxkbcommon \ + libxkbcommon-x11 \ + libxshmfence \ + xcb-util \ + xcb-util-image \ + xcb-util-keysyms \ + xcb-util-renderutil \ + xcb-util-wm \ + xorg-x11-server-Xorg \ + xorg-x11-server-Xvfb + +# Install fonts needed by Maya. +# This is probably only needed by the GUI (which we will not open), +# but it's good to have everything needed, just in case. +RUN dnf install --assumeyes \ + fontconfig \ + freetype \ + xorg-x11-fonts-ISO8859-1-100dpi \ + xorg-x11-fonts-ISO8859-1-75dpi \ + liberation-mono-fonts \ + liberation-fonts-common \ + liberation-sans-fonts \ + liberation-serif-fonts + +# OpenSource "mesa" OpenGL Driver. +RUN dnf install --assumeyes \ + mesa-libGLw \ + mesa-libGLU \ + mesa-libGL-devel \ + mesa-libEGL-devel \ + mesa-libGLES-devel \ + mesa-libGLU-devel \ + mesa-libGLw-devel \ + libglvnd \ + libglvnd-opengl \ + libglvnd-egl \ + libglvnd-glx \ + libglvnd-gles \ + libglvnd-core-devel \ + libglvnd-devel + +# Install latest stable Rust with 'rustup'. +# +# TODO: Define a minimum Rust version to install. +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y \ + && source ${HOME}/.cargo/env + +# Development tools for Maya 2025. +RUN dnf install --assumeyes \ + git \ + python39 \ + cmake \ + clang-tools-extra \ + gcc-toolset-11 + +# Install Maya from archive. +ADD ./external/archives/Autodesk_Maya_2025_1_Update_Linux.tgz /temp +# 'rpm --force' is needed to override the dependency conflict of using +# 'tcl' (which is required by 'gcc-toolset-11', because it's a +# "software collection" using environment modules written in tcl). +RUN rpm -Uvh --force /temp/Packages/Maya2025*.rpm && rm -r /temp +ENV MAYA_LOCATION=/usr/autodesk/maya/ +ENV PATH=$MAYA_LOCATION/bin:$PATH + +# Workaround for Maya "Segmentation fault (core dumped)" issue. +# See https://forums.autodesk.com/t5/maya-general/render-crash-on-linux/m-p/5608552/highlight/true +ENV MAYA_DISABLE_CIP=1 + +WORKDIR /mmSolver + +# Maya 2025 development environment. +ENTRYPOINT [ "scl", "enable", "gcc-toolset-11", "bash" ] diff --git a/share/icons/README.md b/share/icons/README.md new file mode 100644 index 000000000..e17a76c32 --- /dev/null +++ b/share/icons/README.md @@ -0,0 +1,61 @@ +# Icons + +The icons used in mmSolver are contained in this directory. + +The "edit/" sub-directory contain the editable files, used to create the icons. + +## Guidelines + +The icons in mmSolver should: +- be readable and obvious to users. +- be consistent and share a common language with all other icons in + mmSolver (and in Autodesk Maya). +- be defined in vector formats whenever possible (to allow rendering + bitmaps at higher resolutions for Hi-DPI screens). +- use open standard formats (like SVG format) where possible to avoid + proprietary formats and avoid vendor lock-in. + +Note: [Inkscape](https://inkscape.org/) is a really good Free Software +for creating vector artwork icons - it is recommended to use it for +creating the icons for mmSolver. + +## Shelf Icon Naming Conventions + +Icons for Maya shelf buttons require the following naming convention: +``` +name.png # The default 32x32 icon. +name_150.png # The 150% 48x48 icon. +name_200.png # The 200% 64x64 icon. +``` + +Please see [High resolution shelf +icons](https://help.autodesk.com/view/MAYAUL/2025/ENU/?guid=GUID-F2900709-59D3-4E67-A217-4FECC84053BE) +for more details. + +## Node Icon Naming Conventions + +Icons for Maya nodes in the Autodesk Maya Outliner require the +following naming convention: +``` +out_nodeType.png +``` + +Additionally the icons must have a resolution of 20x20. + +## Building Icons + +To speed up the process of outputting the bitmap icons with the +correct naming convention and resolution, there is a script (currently +only on Windows) to automate the process. + +This uses [Inkscape](https://inkscape.org/) to render the SVG icons +into PNG files. + +On Windows: +```cmd +:: Go to root of the icons directory. +> CD \share/icons/ + +:: Runs Inkscape for all files and exports all PNG/SVG files. +> build_icons.bat +``` diff --git a/share/icons/attributeTools.png b/share/icons/attributeTools.png new file mode 100644 index 000000000..dfad6f0de Binary files /dev/null and b/share/icons/attributeTools.png differ diff --git a/share/icons/attributeTools_150.png b/share/icons/attributeTools_150.png new file mode 100644 index 000000000..8fb2b920a Binary files /dev/null and b/share/icons/attributeTools_150.png differ diff --git a/share/icons/attributeTools_200.png b/share/icons/attributeTools_200.png new file mode 100644 index 000000000..d75e0e986 Binary files /dev/null and b/share/icons/attributeTools_200.png differ diff --git a/share/icons/attributeTools_32x32.png b/share/icons/attributeTools_32x32.png deleted file mode 100644 index ce218c23d..000000000 Binary files a/share/icons/attributeTools_32x32.png and /dev/null differ diff --git a/share/icons/build_icons.bash b/share/icons/build_icons.bash new file mode 100644 index 000000000..09a92637a --- /dev/null +++ b/share/icons/build_icons.bash @@ -0,0 +1,33 @@ +#!/bin/bash +# +# Copyright (C) 2024 David Cattermole. +# +# This file is part of mmSolver. +# +# mmSolver is free software: you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# mmSolver is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with mmSolver. If not, see . +# --------------------------------------------------------------------- +# +# Runs 'inkscape' to export PNG files from .svg files. +# +# This file exports multiple resolutions using a naming convention. + +# Any subsequent commands which fail will cause the shell script to +# exit immediately. +set -e + +THIS_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +# TODO: Write this script to match the Windows .bat equal. + +cd ${THIS_DIR} diff --git a/share/icons/build_icons.bat b/share/icons/build_icons.bat new file mode 100644 index 000000000..220416828 --- /dev/null +++ b/share/icons/build_icons.bat @@ -0,0 +1,111 @@ +@ECHO OFF +SETLOCAL +:: +:: Copyright (C) 2024 David Cattermole. +:: +:: This file is part of mmSolver. +:: +:: mmSolver is free software: you can redistribute it and/or modify it +:: under the terms of the GNU Lesser General Public License as +:: published by the Free Software Foundation, either version 3 of the +:: License, or (at your option) any later version. +:: +:: mmSolver is distributed in the hope that it will be useful, +:: but WITHOUT ANY WARRANTY; without even the implied warranty of +:: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +:: GNU Lesser General Public License for more details. +:: +:: You should have received a copy of the GNU Lesser General Public License +:: along with mmSolver. If not, see . +:: --------------------------------------------------------------------- +:: +:: Runs 'inkscape' to export PNG files from .svg files. +:: +:: This file exports multiple resolutions using a naming convention. + +SET THIS_DIR=%~dp0 + +SET INKSCAPE_EXE="%PROGRAMFILES%\Inkscape\inkscape.com" + +SET OUTPUT_DIR=%THIS_DIR% + +:: Export node PNG icons at 20 x 20 resolution, for use in the Outliner. +:: +:: 20 x 20 is the resolution expected by Maya's Outliner. +SET OUT_RESOLUTION=20 +for /r %%i in (edit\node\*.svg) do ( + echo ------------------------------------------------------------- + echo Input file: %%i + echo Output file: %OUTPUT_DIR%\out_%%~ni.png + + %INKSCAPE_EXE% ^ + --without-gui ^ + --export-area-page ^ + --export-width=%OUT_RESOLUTION% ^ + --export-height=%OUT_RESOLUTION% ^ + --file="%%i" ^ + --export-png="%OUTPUT_DIR%\out_%%~ni.png" +) + +:: Export node plain SVG icons. +SET OUT_RESOLUTION=20 +for /r %%i in (edit\node\*.svg) do ( + echo ------------------------------------------------------------- + echo Input file: %%i + echo Output file: %OUTPUT_DIR%\%%~ni.svg + + %INKSCAPE_EXE% ^ + --without-gui ^ + --export-area-page ^ + --file="%%i" ^ + --export-plain-svg="%OUTPUT_DIR%\%%~ni.svg" +) + +:: Export shelf PNG icons at 32 x 32 resolution. +SET OUT_RESOLUTION=32 +for /r %%i in (edit\shelf\*.svg) do ( + echo ------------------------------------------------------------- + echo Input file: %%i + echo Output file: %OUTPUT_DIR%\%%~ni_%OUT_RESOLUTION%x%OUT_RESOLUTION%.png + + %INKSCAPE_EXE% ^ + --without-gui ^ + --export-area-page ^ + --export-width=%OUT_RESOLUTION% ^ + --export-height=%OUT_RESOLUTION% ^ + --file="%%i" ^ + --export-png="%OUTPUT_DIR%\%%~ni.png" +) + +:: Export shelf PNG icons at 48 x 48 resolution (150% scale). +SET OUT_RESOLUTION=48 +for /r %%i in (edit\shelf\*.svg) do ( + echo ------------------------------------------------------------- + echo Input file: %%i + echo Output file: %OUTPUT_DIR%\%%~ni_150.png + + %INKSCAPE_EXE% ^ + --without-gui ^ + --export-area-page ^ + --export-width=%OUT_RESOLUTION% ^ + --export-height=%OUT_RESOLUTION% ^ + --file="%%i" ^ + --export-png="%OUTPUT_DIR%\%%~ni_150.png" +) + + +:: Export shelf PNG icons at 64 x 64 resolution (200% scale). +SET OUT_RESOLUTION=64 +for /r %%i in (edit\shelf\*.svg) do ( + echo ------------------------------------------------------------- + echo Input file: %%i + echo Output file: %OUTPUT_DIR%\%%~ni_200.png + + %INKSCAPE_EXE% ^ + --without-gui ^ + --export-area-page ^ + --export-width=%OUT_RESOLUTION% ^ + --export-height=%OUT_RESOLUTION% ^ + --file="%%i" ^ + --export-png="%OUTPUT_DIR%\%%~ni_200.png" +) diff --git a/share/icons/bundleTools_32x32.png b/share/icons/bundleTools.png similarity index 100% rename from share/icons/bundleTools_32x32.png rename to share/icons/bundleTools.png diff --git a/share/icons/bundleTools_150.png b/share/icons/bundleTools_150.png new file mode 100644 index 000000000..f6f789638 Binary files /dev/null and b/share/icons/bundleTools_150.png differ diff --git a/share/icons/bundleTools_200.png b/share/icons/bundleTools_200.png new file mode 100644 index 000000000..46717ed4c Binary files /dev/null and b/share/icons/bundleTools_200.png differ diff --git a/share/icons/cameraContext.png b/share/icons/cameraContext.png new file mode 100644 index 000000000..4707c119d Binary files /dev/null and b/share/icons/cameraContext.png differ diff --git a/share/icons/cameraContext_150.png b/share/icons/cameraContext_150.png new file mode 100644 index 000000000..216951cb3 Binary files /dev/null and b/share/icons/cameraContext_150.png differ diff --git a/share/icons/cameraContext_200.png b/share/icons/cameraContext_200.png new file mode 100644 index 000000000..4228f021b Binary files /dev/null and b/share/icons/cameraContext_200.png differ diff --git a/share/icons/cameraContext_32x32.png b/share/icons/cameraContext_32x32.png deleted file mode 100644 index 7db856e23..000000000 Binary files a/share/icons/cameraContext_32x32.png and /dev/null differ diff --git a/share/icons/cameraTools_32x32.png b/share/icons/cameraTools.png similarity index 100% rename from share/icons/cameraTools_32x32.png rename to share/icons/cameraTools.png diff --git a/share/icons/cameraTools_150.png b/share/icons/cameraTools_150.png new file mode 100644 index 000000000..33872b12f Binary files /dev/null and b/share/icons/cameraTools_150.png differ diff --git a/share/icons/cameraTools_200.png b/share/icons/cameraTools_200.png new file mode 100644 index 000000000..9a7ace08c Binary files /dev/null and b/share/icons/cameraTools_200.png differ diff --git a/share/icons/convertToMarker.png b/share/icons/convertToMarker.png new file mode 100644 index 000000000..dbc739f28 Binary files /dev/null and b/share/icons/convertToMarker.png differ diff --git a/share/icons/convertToMarker_150.png b/share/icons/convertToMarker_150.png new file mode 100644 index 000000000..7c762518b Binary files /dev/null and b/share/icons/convertToMarker_150.png differ diff --git a/share/icons/convertToMarker_200.png b/share/icons/convertToMarker_200.png new file mode 100644 index 000000000..5b7be3426 Binary files /dev/null and b/share/icons/convertToMarker_200.png differ diff --git a/share/icons/convertToMarker_32x32.png b/share/icons/convertToMarker_32x32.png deleted file mode 100644 index 1cad89268..000000000 Binary files a/share/icons/convertToMarker_32x32.png and /dev/null differ diff --git a/share/icons/createBundle.png b/share/icons/createBundle.png new file mode 100644 index 000000000..41e47977e Binary files /dev/null and b/share/icons/createBundle.png differ diff --git a/share/icons/createBundle_150.png b/share/icons/createBundle_150.png new file mode 100644 index 000000000..0d8c7f17e Binary files /dev/null and b/share/icons/createBundle_150.png differ diff --git a/share/icons/createBundle_200.png b/share/icons/createBundle_200.png new file mode 100644 index 000000000..0d1d4dd1d Binary files /dev/null and b/share/icons/createBundle_200.png differ diff --git a/share/icons/createBundle_32x32.png b/share/icons/createBundle_32x32.png deleted file mode 100644 index 4ebc20663..000000000 Binary files a/share/icons/createBundle_32x32.png and /dev/null differ diff --git a/share/icons/createCamera.png b/share/icons/createCamera.png new file mode 100644 index 000000000..3468ead7b Binary files /dev/null and b/share/icons/createCamera.png differ diff --git a/share/icons/createCamera_150.png b/share/icons/createCamera_150.png new file mode 100644 index 000000000..c2720c562 Binary files /dev/null and b/share/icons/createCamera_150.png differ diff --git a/share/icons/createCamera_200.png b/share/icons/createCamera_200.png new file mode 100644 index 000000000..b66aaa6f6 Binary files /dev/null and b/share/icons/createCamera_200.png differ diff --git a/share/icons/createCamera_32x32.png b/share/icons/createCamera_32x32.png deleted file mode 100644 index 7ff30c86a..000000000 Binary files a/share/icons/createCamera_32x32.png and /dev/null differ diff --git a/share/icons/createImagePlane.png b/share/icons/createImagePlane.png new file mode 100644 index 000000000..5a2b9eb0a Binary files /dev/null and b/share/icons/createImagePlane.png differ diff --git a/share/icons/createImagePlane_150.png b/share/icons/createImagePlane_150.png new file mode 100644 index 000000000..6b7730e01 Binary files /dev/null and b/share/icons/createImagePlane_150.png differ diff --git a/share/icons/createImagePlane_200.png b/share/icons/createImagePlane_200.png new file mode 100644 index 000000000..48ae9222d Binary files /dev/null and b/share/icons/createImagePlane_200.png differ diff --git a/share/icons/createImagePlane_32x32.png b/share/icons/createImagePlane_32x32.png deleted file mode 100644 index 072d4214d..000000000 Binary files a/share/icons/createImagePlane_32x32.png and /dev/null differ diff --git a/share/icons/createLens.png b/share/icons/createLens.png new file mode 100644 index 000000000..6ca9460ca Binary files /dev/null and b/share/icons/createLens.png differ diff --git a/share/icons/createLens_150.png b/share/icons/createLens_150.png new file mode 100644 index 000000000..e0b5caad7 Binary files /dev/null and b/share/icons/createLens_150.png differ diff --git a/share/icons/createLens_200.png b/share/icons/createLens_200.png new file mode 100644 index 000000000..9c05e0762 Binary files /dev/null and b/share/icons/createLens_200.png differ diff --git a/share/icons/createLens_32x32.png b/share/icons/createLens_32x32.png deleted file mode 100644 index 698f3da89..000000000 Binary files a/share/icons/createLens_32x32.png and /dev/null differ diff --git a/share/icons/createMarker.png b/share/icons/createMarker.png new file mode 100644 index 000000000..32864eeb7 Binary files /dev/null and b/share/icons/createMarker.png differ diff --git a/share/icons/createMarker_150.png b/share/icons/createMarker_150.png new file mode 100644 index 000000000..2126400fe Binary files /dev/null and b/share/icons/createMarker_150.png differ diff --git a/share/icons/createMarker_200.png b/share/icons/createMarker_200.png new file mode 100644 index 000000000..65a4eff17 Binary files /dev/null and b/share/icons/createMarker_200.png differ diff --git a/share/icons/createMarker_32x32.png b/share/icons/createMarker_32x32.png deleted file mode 100644 index 97acc5c1b..000000000 Binary files a/share/icons/createMarker_32x32.png and /dev/null differ diff --git a/share/icons/displayTools_32x32.png b/share/icons/displayTools.png similarity index 100% rename from share/icons/displayTools_32x32.png rename to share/icons/displayTools.png diff --git a/share/icons/displayTools_150.png b/share/icons/displayTools_150.png new file mode 100644 index 000000000..792d6cc4d Binary files /dev/null and b/share/icons/displayTools_150.png differ diff --git a/share/icons/displayTools_200.png b/share/icons/displayTools_200.png new file mode 100644 index 000000000..4dfd6bce5 Binary files /dev/null and b/share/icons/displayTools_200.png differ diff --git a/share/icons/edit/mmBundleShape.svg b/share/icons/edit/node/mmBundleShape.svg similarity index 100% rename from share/icons/edit/mmBundleShape.svg rename to share/icons/edit/node/mmBundleShape.svg diff --git a/share/icons/edit/mmImagePlaneShape.svg b/share/icons/edit/node/mmImagePlaneShape.svg similarity index 100% rename from share/icons/edit/mmImagePlaneShape.svg rename to share/icons/edit/node/mmImagePlaneShape.svg diff --git a/share/icons/edit/node/mmImagePlaneShape2.svg b/share/icons/edit/node/mmImagePlaneShape2.svg new file mode 100644 index 000000000..99c326e88 --- /dev/null +++ b/share/icons/edit/node/mmImagePlaneShape2.svg @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/edit/mmLineShape.svg b/share/icons/edit/node/mmLineShape.svg similarity index 100% rename from share/icons/edit/mmLineShape.svg rename to share/icons/edit/node/mmLineShape.svg diff --git a/share/icons/edit/mmMarkerGroupTransform.svg b/share/icons/edit/node/mmMarkerGroupTransform.svg similarity index 100% rename from share/icons/edit/mmMarkerGroupTransform.svg rename to share/icons/edit/node/mmMarkerGroupTransform.svg diff --git a/share/icons/edit/mmMarkerShape.svg b/share/icons/edit/node/mmMarkerShape.svg similarity index 100% rename from share/icons/edit/mmMarkerShape.svg rename to share/icons/edit/node/mmMarkerShape.svg diff --git a/share/icons/edit/mmMarkerTransform.svg b/share/icons/edit/node/mmMarkerTransform.svg similarity index 100% rename from share/icons/edit/mmMarkerTransform.svg rename to share/icons/edit/node/mmMarkerTransform.svg diff --git a/share/icons/edit/mmSkyDomeShape.svg b/share/icons/edit/node/mmSkyDomeShape.svg similarity index 100% rename from share/icons/edit/mmSkyDomeShape.svg rename to share/icons/edit/node/mmSkyDomeShape.svg diff --git a/share/icons/edit/attributeTools.svg b/share/icons/edit/shelf/attributeTools.svg similarity index 100% rename from share/icons/edit/attributeTools.svg rename to share/icons/edit/shelf/attributeTools.svg diff --git a/share/icons/edit/bundleTools.svg b/share/icons/edit/shelf/bundleTools.svg similarity index 100% rename from share/icons/edit/bundleTools.svg rename to share/icons/edit/shelf/bundleTools.svg diff --git a/share/icons/edit/cameraContext.svg b/share/icons/edit/shelf/cameraContext.svg similarity index 100% rename from share/icons/edit/cameraContext.svg rename to share/icons/edit/shelf/cameraContext.svg diff --git a/share/icons/edit/cameraTools.svg b/share/icons/edit/shelf/cameraTools.svg similarity index 100% rename from share/icons/edit/cameraTools.svg rename to share/icons/edit/shelf/cameraTools.svg diff --git a/share/icons/edit/convertToMarker.svg b/share/icons/edit/shelf/convertToMarker.svg similarity index 100% rename from share/icons/edit/convertToMarker.svg rename to share/icons/edit/shelf/convertToMarker.svg diff --git a/share/icons/edit/createBundle.svg b/share/icons/edit/shelf/createBundle.svg similarity index 100% rename from share/icons/edit/createBundle.svg rename to share/icons/edit/shelf/createBundle.svg diff --git a/share/icons/edit/createCamera.svg b/share/icons/edit/shelf/createCamera.svg similarity index 100% rename from share/icons/edit/createCamera.svg rename to share/icons/edit/shelf/createCamera.svg diff --git a/share/icons/edit/createImagePlane.svg b/share/icons/edit/shelf/createImagePlane.svg similarity index 100% rename from share/icons/edit/createImagePlane.svg rename to share/icons/edit/shelf/createImagePlane.svg diff --git a/share/icons/edit/createLens.svg b/share/icons/edit/shelf/createLens.svg similarity index 100% rename from share/icons/edit/createLens.svg rename to share/icons/edit/shelf/createLens.svg diff --git a/share/icons/edit/createMarker.svg b/share/icons/edit/shelf/createMarker.svg similarity index 100% rename from share/icons/edit/createMarker.svg rename to share/icons/edit/shelf/createMarker.svg diff --git a/share/icons/edit/displayTools.svg b/share/icons/edit/shelf/displayTools.svg similarity index 100% rename from share/icons/edit/displayTools.svg rename to share/icons/edit/shelf/displayTools.svg diff --git a/share/icons/edit/fileInputOutput.svg b/share/icons/edit/shelf/fileInputOutput.svg similarity index 100% rename from share/icons/edit/fileInputOutput.svg rename to share/icons/edit/shelf/fileInputOutput.svg diff --git a/share/icons/edit/generalTools.svg b/share/icons/edit/shelf/generalTools.svg similarity index 100% rename from share/icons/edit/generalTools.svg rename to share/icons/edit/shelf/generalTools.svg diff --git a/share/icons/edit/hotkeySwitcher.svg b/share/icons/edit/shelf/hotkeySwitcher.svg similarity index 100% rename from share/icons/edit/hotkeySwitcher.svg rename to share/icons/edit/shelf/hotkeySwitcher.svg diff --git a/share/icons/edit/keyFrameAdd.svg b/share/icons/edit/shelf/keyFrameAdd.svg similarity index 100% rename from share/icons/edit/keyFrameAdd.svg rename to share/icons/edit/shelf/keyFrameAdd.svg diff --git a/share/icons/edit/keyFrameNext.svg b/share/icons/edit/shelf/keyFrameNext.svg similarity index 100% rename from share/icons/edit/keyFrameNext.svg rename to share/icons/edit/shelf/keyFrameNext.svg diff --git a/share/icons/edit/keyFramePrevious.svg b/share/icons/edit/shelf/keyFramePrevious.svg similarity index 100% rename from share/icons/edit/keyFramePrevious.svg rename to share/icons/edit/shelf/keyFramePrevious.svg diff --git a/share/icons/edit/keyFrameRemove.svg b/share/icons/edit/shelf/keyFrameRemove.svg similarity index 100% rename from share/icons/edit/keyFrameRemove.svg rename to share/icons/edit/shelf/keyFrameRemove.svg diff --git a/share/icons/edit/lensTools.svg b/share/icons/edit/shelf/lensTools.svg similarity index 100% rename from share/icons/edit/lensTools.svg rename to share/icons/edit/shelf/lensTools.svg diff --git a/share/icons/edit/lineTools.svg b/share/icons/edit/shelf/lineTools.svg similarity index 100% rename from share/icons/edit/lineTools.svg rename to share/icons/edit/shelf/lineTools.svg diff --git a/share/icons/edit/loadMarker.svg b/share/icons/edit/shelf/loadMarker.svg similarity index 100% rename from share/icons/edit/loadMarker.svg rename to share/icons/edit/shelf/loadMarker.svg diff --git a/share/icons/edit/markerBundleCombineSelection.svg b/share/icons/edit/shelf/markerBundleCombineSelection.svg similarity index 100% rename from share/icons/edit/markerBundleCombineSelection.svg rename to share/icons/edit/shelf/markerBundleCombineSelection.svg diff --git a/share/icons/edit/markerBundleLinkTools.svg b/share/icons/edit/shelf/markerBundleLinkTools.svg similarity index 100% rename from share/icons/edit/markerBundleLinkTools.svg rename to share/icons/edit/shelf/markerBundleLinkTools.svg diff --git a/share/icons/edit/markerBundleSwapSelection.svg b/share/icons/edit/shelf/markerBundleSwapSelection.svg similarity index 100% rename from share/icons/edit/markerBundleSwapSelection.svg rename to share/icons/edit/shelf/markerBundleSwapSelection.svg diff --git a/share/icons/edit/markerTools.svg b/share/icons/edit/shelf/markerTools.svg similarity index 100% rename from share/icons/edit/markerTools.svg rename to share/icons/edit/shelf/markerTools.svg diff --git a/share/icons/edit/shelf/meshTools.svg b/share/icons/edit/shelf/meshTools.svg new file mode 100644 index 000000000..ba879e92c --- /dev/null +++ b/share/icons/edit/shelf/meshTools.svg @@ -0,0 +1,648 @@ + + + +image/svg+xmlMeshMesh diff --git a/share/icons/edit/mmSolverRun.svg b/share/icons/edit/shelf/mmSolverRun.svg similarity index 100% rename from share/icons/edit/mmSolverRun.svg rename to share/icons/edit/shelf/mmSolverRun.svg diff --git a/share/icons/edit/mmSolverRunFrame.svg b/share/icons/edit/shelf/mmSolverRunFrame.svg similarity index 100% rename from share/icons/edit/mmSolverRunFrame.svg rename to share/icons/edit/shelf/mmSolverRunFrame.svg diff --git a/share/icons/edit/mmSolverWindow.svg b/share/icons/edit/shelf/mmSolverWindow.svg similarity index 100% rename from share/icons/edit/mmSolverWindow.svg rename to share/icons/edit/shelf/mmSolverWindow.svg diff --git a/share/icons/edit/selectionTools.svg b/share/icons/edit/shelf/selectionTools.svg similarity index 100% rename from share/icons/edit/selectionTools.svg rename to share/icons/edit/shelf/selectionTools.svg diff --git a/share/icons/edit/zDepthTools.svg b/share/icons/edit/shelf/zDepthTools.svg similarity index 100% rename from share/icons/edit/zDepthTools.svg rename to share/icons/edit/shelf/zDepthTools.svg diff --git a/share/icons/fileInputOutput.png b/share/icons/fileInputOutput.png new file mode 100644 index 000000000..15183fc6a Binary files /dev/null and b/share/icons/fileInputOutput.png differ diff --git a/share/icons/fileInputOutput_150.png b/share/icons/fileInputOutput_150.png new file mode 100644 index 000000000..ae51f8131 Binary files /dev/null and b/share/icons/fileInputOutput_150.png differ diff --git a/share/icons/fileInputOutput_200.png b/share/icons/fileInputOutput_200.png new file mode 100644 index 000000000..0bef0c049 Binary files /dev/null and b/share/icons/fileInputOutput_200.png differ diff --git a/share/icons/fileInputOutput_32x32.png b/share/icons/fileInputOutput_32x32.png deleted file mode 100644 index 45a5b0050..000000000 Binary files a/share/icons/fileInputOutput_32x32.png and /dev/null differ diff --git a/share/icons/generalTools.png b/share/icons/generalTools.png new file mode 100644 index 000000000..29555e820 Binary files /dev/null and b/share/icons/generalTools.png differ diff --git a/share/icons/generalTools_150.png b/share/icons/generalTools_150.png new file mode 100644 index 000000000..415af6642 Binary files /dev/null and b/share/icons/generalTools_150.png differ diff --git a/share/icons/generalTools_200.png b/share/icons/generalTools_200.png new file mode 100644 index 000000000..a14b38d35 Binary files /dev/null and b/share/icons/generalTools_200.png differ diff --git a/share/icons/generalTools_32x32.png b/share/icons/generalTools_32x32.png deleted file mode 100644 index 5e13b3110..000000000 Binary files a/share/icons/generalTools_32x32.png and /dev/null differ diff --git a/share/icons/hotkeySwitcher.png b/share/icons/hotkeySwitcher.png new file mode 100644 index 000000000..1adac3bb5 Binary files /dev/null and b/share/icons/hotkeySwitcher.png differ diff --git a/share/icons/hotkeySwitcher_150.png b/share/icons/hotkeySwitcher_150.png new file mode 100644 index 000000000..0e4e34b4c Binary files /dev/null and b/share/icons/hotkeySwitcher_150.png differ diff --git a/share/icons/hotkeySwitcher_200.png b/share/icons/hotkeySwitcher_200.png new file mode 100644 index 000000000..95c9bbe6f Binary files /dev/null and b/share/icons/hotkeySwitcher_200.png differ diff --git a/share/icons/hotkeySwitcher_32x32.png b/share/icons/hotkeySwitcher_32x32.png deleted file mode 100644 index 383f7a153..000000000 Binary files a/share/icons/hotkeySwitcher_32x32.png and /dev/null differ diff --git a/share/icons/keyFrameAdd_32x32.png b/share/icons/keyFrameAdd.png similarity index 100% rename from share/icons/keyFrameAdd_32x32.png rename to share/icons/keyFrameAdd.png diff --git a/share/icons/keyFrameAdd_150.png b/share/icons/keyFrameAdd_150.png new file mode 100644 index 000000000..2c398b47c Binary files /dev/null and b/share/icons/keyFrameAdd_150.png differ diff --git a/share/icons/keyFrameAdd_200.png b/share/icons/keyFrameAdd_200.png new file mode 100644 index 000000000..a25159dc7 Binary files /dev/null and b/share/icons/keyFrameAdd_200.png differ diff --git a/share/icons/keyFrameNext_32x32.png b/share/icons/keyFrameNext.png similarity index 100% rename from share/icons/keyFrameNext_32x32.png rename to share/icons/keyFrameNext.png diff --git a/share/icons/keyFrameNext_150.png b/share/icons/keyFrameNext_150.png new file mode 100644 index 000000000..ffc72ce98 Binary files /dev/null and b/share/icons/keyFrameNext_150.png differ diff --git a/share/icons/keyFrameNext_200.png b/share/icons/keyFrameNext_200.png new file mode 100644 index 000000000..df2dcd42a Binary files /dev/null and b/share/icons/keyFrameNext_200.png differ diff --git a/share/icons/keyFramePrevious.png b/share/icons/keyFramePrevious.png new file mode 100644 index 000000000..adf8248d3 Binary files /dev/null and b/share/icons/keyFramePrevious.png differ diff --git a/share/icons/keyFramePrevious_150.png b/share/icons/keyFramePrevious_150.png new file mode 100644 index 000000000..0c1d22fa8 Binary files /dev/null and b/share/icons/keyFramePrevious_150.png differ diff --git a/share/icons/keyFramePrevious_200.png b/share/icons/keyFramePrevious_200.png new file mode 100644 index 000000000..d0bed8303 Binary files /dev/null and b/share/icons/keyFramePrevious_200.png differ diff --git a/share/icons/keyFramePrevious_32x32.png b/share/icons/keyFramePrevious_32x32.png deleted file mode 100644 index 7ef8608da..000000000 Binary files a/share/icons/keyFramePrevious_32x32.png and /dev/null differ diff --git a/share/icons/keyFrameRemove_32x32.png b/share/icons/keyFrameRemove.png similarity index 100% rename from share/icons/keyFrameRemove_32x32.png rename to share/icons/keyFrameRemove.png diff --git a/share/icons/keyFrameRemove_150.png b/share/icons/keyFrameRemove_150.png new file mode 100644 index 000000000..e2db7c3d5 Binary files /dev/null and b/share/icons/keyFrameRemove_150.png differ diff --git a/share/icons/keyFrameRemove_200.png b/share/icons/keyFrameRemove_200.png new file mode 100644 index 000000000..bb5738957 Binary files /dev/null and b/share/icons/keyFrameRemove_200.png differ diff --git a/share/icons/lensTools.png b/share/icons/lensTools.png new file mode 100644 index 000000000..01fe3eeb8 Binary files /dev/null and b/share/icons/lensTools.png differ diff --git a/share/icons/lensTools_150.png b/share/icons/lensTools_150.png new file mode 100644 index 000000000..64d6e4ded Binary files /dev/null and b/share/icons/lensTools_150.png differ diff --git a/share/icons/lensTools_200.png b/share/icons/lensTools_200.png new file mode 100644 index 000000000..7e7150a26 Binary files /dev/null and b/share/icons/lensTools_200.png differ diff --git a/share/icons/lensTools_32x32.png b/share/icons/lensTools_32x32.png deleted file mode 100644 index 20bb50191..000000000 Binary files a/share/icons/lensTools_32x32.png and /dev/null differ diff --git a/share/icons/lineTools_32x32.png b/share/icons/lineTools.png similarity index 100% rename from share/icons/lineTools_32x32.png rename to share/icons/lineTools.png diff --git a/share/icons/lineTools_150.png b/share/icons/lineTools_150.png new file mode 100644 index 000000000..7812e6cae Binary files /dev/null and b/share/icons/lineTools_150.png differ diff --git a/share/icons/lineTools_200.png b/share/icons/lineTools_200.png new file mode 100644 index 000000000..11a6f21e7 Binary files /dev/null and b/share/icons/lineTools_200.png differ diff --git a/share/icons/loadMarker.png b/share/icons/loadMarker.png new file mode 100644 index 000000000..529ecbe99 Binary files /dev/null and b/share/icons/loadMarker.png differ diff --git a/share/icons/loadMarker_150.png b/share/icons/loadMarker_150.png new file mode 100644 index 000000000..64017a772 Binary files /dev/null and b/share/icons/loadMarker_150.png differ diff --git a/share/icons/loadMarker_200.png b/share/icons/loadMarker_200.png new file mode 100644 index 000000000..da4b2d49f Binary files /dev/null and b/share/icons/loadMarker_200.png differ diff --git a/share/icons/loadMarker_32x32.png b/share/icons/loadMarker_32x32.png deleted file mode 100644 index 2af97ddc0..000000000 Binary files a/share/icons/loadMarker_32x32.png and /dev/null differ diff --git a/share/icons/markerBundleCombineSelection.png b/share/icons/markerBundleCombineSelection.png new file mode 100644 index 000000000..ff2d39c3c Binary files /dev/null and b/share/icons/markerBundleCombineSelection.png differ diff --git a/share/icons/markerBundleCombineSelection_150.png b/share/icons/markerBundleCombineSelection_150.png new file mode 100644 index 000000000..9bcc69b71 Binary files /dev/null and b/share/icons/markerBundleCombineSelection_150.png differ diff --git a/share/icons/markerBundleCombineSelection_200.png b/share/icons/markerBundleCombineSelection_200.png new file mode 100644 index 000000000..a0922aaca Binary files /dev/null and b/share/icons/markerBundleCombineSelection_200.png differ diff --git a/share/icons/markerBundleCombineSelection_32x32.png b/share/icons/markerBundleCombineSelection_32x32.png deleted file mode 100644 index f7aa7dfe5..000000000 Binary files a/share/icons/markerBundleCombineSelection_32x32.png and /dev/null differ diff --git a/share/icons/markerBundleLinkTools.png b/share/icons/markerBundleLinkTools.png new file mode 100644 index 000000000..c490da755 Binary files /dev/null and b/share/icons/markerBundleLinkTools.png differ diff --git a/share/icons/markerBundleLinkTools_150.png b/share/icons/markerBundleLinkTools_150.png new file mode 100644 index 000000000..2869b5524 Binary files /dev/null and b/share/icons/markerBundleLinkTools_150.png differ diff --git a/share/icons/markerBundleLinkTools_200.png b/share/icons/markerBundleLinkTools_200.png new file mode 100644 index 000000000..efe540cef Binary files /dev/null and b/share/icons/markerBundleLinkTools_200.png differ diff --git a/share/icons/markerBundleLinkTools_32x32.png b/share/icons/markerBundleLinkTools_32x32.png deleted file mode 100644 index d23077971..000000000 Binary files a/share/icons/markerBundleLinkTools_32x32.png and /dev/null differ diff --git a/share/icons/markerBundleSwapSelection.png b/share/icons/markerBundleSwapSelection.png new file mode 100644 index 000000000..2e9e55e96 Binary files /dev/null and b/share/icons/markerBundleSwapSelection.png differ diff --git a/share/icons/markerBundleSwapSelection_150.png b/share/icons/markerBundleSwapSelection_150.png new file mode 100644 index 000000000..607b2935c Binary files /dev/null and b/share/icons/markerBundleSwapSelection_150.png differ diff --git a/share/icons/markerBundleSwapSelection_200.png b/share/icons/markerBundleSwapSelection_200.png new file mode 100644 index 000000000..9e255aab4 Binary files /dev/null and b/share/icons/markerBundleSwapSelection_200.png differ diff --git a/share/icons/markerBundleSwapSelection_32x32.png b/share/icons/markerBundleSwapSelection_32x32.png deleted file mode 100644 index 9bfd89efc..000000000 Binary files a/share/icons/markerBundleSwapSelection_32x32.png and /dev/null differ diff --git a/share/icons/markerTools_32x32.png b/share/icons/markerTools.png similarity index 100% rename from share/icons/markerTools_32x32.png rename to share/icons/markerTools.png diff --git a/share/icons/markerTools_150.png b/share/icons/markerTools_150.png new file mode 100644 index 000000000..93f54358d Binary files /dev/null and b/share/icons/markerTools_150.png differ diff --git a/share/icons/markerTools_200.png b/share/icons/markerTools_200.png new file mode 100644 index 000000000..b4eef3797 Binary files /dev/null and b/share/icons/markerTools_200.png differ diff --git a/share/icons/meshTools.png b/share/icons/meshTools.png new file mode 100644 index 000000000..378a38122 Binary files /dev/null and b/share/icons/meshTools.png differ diff --git a/share/icons/meshTools_150.png b/share/icons/meshTools_150.png new file mode 100644 index 000000000..e1704fbbc Binary files /dev/null and b/share/icons/meshTools_150.png differ diff --git a/share/icons/meshTools_200.png b/share/icons/meshTools_200.png new file mode 100644 index 000000000..1112b8138 Binary files /dev/null and b/share/icons/meshTools_200.png differ diff --git a/share/icons/mmBundleShape.svg b/share/icons/mmBundleShape.svg index 22cc6c7f5..1f144644f 100644 --- a/share/icons/mmBundleShape.svg +++ b/share/icons/mmBundleShape.svg @@ -1,53 +1,52 @@ - - + height="64px" + width="64px"> - - - - + refX="0.0" + refY="0.0" + orient="auto"> + transform="scale(0.8) translate(12.5,0)" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none" + d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " + id="path5670" /> + mode="lighten" /> + + + + image/svg+xml + + + + + + style="filter:url(#filter6313)" + id="layer1"> + transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,30.399608,-11.249227)"> + d="m 35,29 c 0,2.761424 -2.238576,5 -5,5 -2.761424,0 -5,-2.238576 -5,-5 0,-2.761424 2.238576,-5 5,-5 2.761424,0 5,2.238576 5,5 z" + style="fill:#000000;stroke:#000000;stroke-width:1.09090912;stroke-linejoin:miter;stroke-miterlimit:4;marker-start:none" + transform="matrix(0.91666667,0,0,0.91666668,4.4999999,5.4166663)" /> + d="m 35,29 c 0,2.761424 -2.238576,5 -5,5 -2.761424,0 -5,-2.238576 -5,-5 0,-2.761424 2.238576,-5 5,-5 2.761424,0 5,2.238576 5,5 z" + style="fill:#36f047;fill-opacity:1;stroke:#36f047;stroke-width:1.09090912;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;marker-start:none" + transform="matrix(0.64818122,0.64818122,-0.64818123,0.64818123,31.309353,-5.5075849)" /> + d="m 42.766029,-26.450687 5.97307,0 0,18.0005184 -5.97307,0 0,-18.0005184 z" + style="fill:#36f047;fill-opacity:1;stroke:#36f047;stroke-width:0.99999964;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" + transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)" /> + d="m 42.744606,9.0500851 5.97307,0 0,18.0005189 -5.97307,0 0,-18.0005189 z" + style="fill:#36f047;fill-opacity:1;stroke:#36f047;stroke-width:0.99999964;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" + transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)" /> + d="m 54.744576,-2.4501736 18.000001,0 0,6 -18.000001,0 0,-6 z" + style="fill:#36f047;fill-opacity:1;stroke:#36f047;stroke-width:0.99999964;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" + transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)" /> + d="m 18.744608,-2.4501736 18.000002,0 0,6 -18.000002,0 0,-6 z" + style="fill:#36f047;fill-opacity:1;stroke:#36f047;stroke-width:0.99999964;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1" + transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)" /> diff --git a/share/icons/mmImagePlaneShape.svg b/share/icons/mmImagePlaneShape.svg new file mode 100644 index 000000000..e00192f85 --- /dev/null +++ b/share/icons/mmImagePlaneShape.svg @@ -0,0 +1,109 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/mmImagePlaneShape2.svg b/share/icons/mmImagePlaneShape2.svg new file mode 100644 index 000000000..e00192f85 --- /dev/null +++ b/share/icons/mmImagePlaneShape2.svg @@ -0,0 +1,109 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/mmLineShape.svg b/share/icons/mmLineShape.svg new file mode 100644 index 000000000..d67ba3219 --- /dev/null +++ b/share/icons/mmLineShape.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/mmMarkerGroupTransform.svg b/share/icons/mmMarkerGroupTransform.svg index 1bff2d87d..03cba7595 100644 --- a/share/icons/mmMarkerGroupTransform.svg +++ b/share/icons/mmMarkerGroupTransform.svg @@ -1,177 +1,191 @@ - - + height="64px" + width="64px"> + refX="0.0" + refY="0.0" + orient="auto"> + transform="scale(0.8) translate(12.5,0)" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none" + d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " + id="path5670" /> + id="filter6313-0" + color-interpolation-filters="sRGB"> + mode="lighten" /> + + + + image/svg+xml + + + + + + style="display:inline" + id="layer1"> + id="g3746" + transform="matrix(0.25692303,0,0,0.25692303,-0.6043351,8.4859998)"> + id="g3874" + transform="matrix(0.64606019,0,0,0.64558621,10.629909,10.845696)"> + style="fill:#e83b3b;fill-opacity:1;stroke:#e83b3b;stroke-width:1.09090912000000007;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none" /> + style="fill:#e83b3b;fill-opacity:1;stroke:#e83b3b;stroke-width:0.99999963999999997;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> + style="fill:#e83b3b;fill-opacity:1;stroke:#e83b3b;stroke-width:0.99999963999999997;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> + style="fill:#e83b3b;fill-opacity:1;stroke:#e83b3b;stroke-width:0.99999963999999997;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> + style="fill:#e83b3b;fill-opacity:1;stroke:#e83b3b;stroke-width:0.99999963999999997;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> diff --git a/share/icons/mmMarkerShape.svg b/share/icons/mmMarkerShape.svg index 3b121f6a3..c946188ae 100644 --- a/share/icons/mmMarkerShape.svg +++ b/share/icons/mmMarkerShape.svg @@ -1,29 +1,42 @@ - - + height="64px" + width="64px"> + refX="0.0" + refY="0.0" + orient="auto"> + transform="scale(0.8) translate(12.5,0)" + style="fill-rule:evenodd;stroke:#000000;stroke-width:1.0pt;marker-start:none" + d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z " + id="path5670" /> + + + + image/svg+xml + + + + + + style="fill:#e83b3b;fill-opacity:1;stroke:#e83b3b;stroke-width:1.09090912000000007;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker-start:none" /> + style="fill:#e83b3b;fill-opacity:1;stroke:#e83b3b;stroke-width:0.99999963999999997;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> + style="fill:#e83b3b;fill-opacity:1;stroke:#e83b3b;stroke-width:0.99999963999999997;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> + style="fill:#e83b3b;fill-opacity:1;stroke:#e83b3b;stroke-width:0.99999963999999997;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> + style="fill:#e83b3b;fill-opacity:1;stroke:#e83b3b;stroke-width:0.99999963999999997;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" /> diff --git a/share/icons/mmMarkerTransform.svg b/share/icons/mmMarkerTransform.svg new file mode 100644 index 000000000..2e6701430 --- /dev/null +++ b/share/icons/mmMarkerTransform.svg @@ -0,0 +1,127 @@ + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/share/icons/mmSkyDomeShape.svg b/share/icons/mmSkyDomeShape.svg index 263b275be..8cc3a5dda 100644 --- a/share/icons/mmSkyDomeShape.svg +++ b/share/icons/mmSkyDomeShape.svg @@ -88,7 +88,7 @@ image/svg+xml - + diff --git a/share/icons/mmSolverRun_32x32.png b/share/icons/mmSolverRun.png similarity index 100% rename from share/icons/mmSolverRun_32x32.png rename to share/icons/mmSolverRun.png diff --git a/share/icons/mmSolverRunFrame_32x32.png b/share/icons/mmSolverRunFrame.png similarity index 100% rename from share/icons/mmSolverRunFrame_32x32.png rename to share/icons/mmSolverRunFrame.png diff --git a/share/icons/mmSolverRunFrame_150.png b/share/icons/mmSolverRunFrame_150.png new file mode 100644 index 000000000..bafa948cb Binary files /dev/null and b/share/icons/mmSolverRunFrame_150.png differ diff --git a/share/icons/mmSolverRunFrame_200.png b/share/icons/mmSolverRunFrame_200.png new file mode 100644 index 000000000..07e58d86b Binary files /dev/null and b/share/icons/mmSolverRunFrame_200.png differ diff --git a/share/icons/mmSolverRun_150.png b/share/icons/mmSolverRun_150.png new file mode 100644 index 000000000..0e8d388d4 Binary files /dev/null and b/share/icons/mmSolverRun_150.png differ diff --git a/share/icons/mmSolverRun_200.png b/share/icons/mmSolverRun_200.png new file mode 100644 index 000000000..44291fbc4 Binary files /dev/null and b/share/icons/mmSolverRun_200.png differ diff --git a/share/icons/mmSolverWindow_32x32.png b/share/icons/mmSolverWindow.png similarity index 100% rename from share/icons/mmSolverWindow_32x32.png rename to share/icons/mmSolverWindow.png diff --git a/share/icons/mmSolverWindow_150.png b/share/icons/mmSolverWindow_150.png new file mode 100644 index 000000000..7603d3a6d Binary files /dev/null and b/share/icons/mmSolverWindow_150.png differ diff --git a/share/icons/mmSolverWindow_200.png b/share/icons/mmSolverWindow_200.png new file mode 100644 index 000000000..a7034aec7 Binary files /dev/null and b/share/icons/mmSolverWindow_200.png differ diff --git a/share/icons/out_mmBundleShape.png b/share/icons/out_mmBundleShape.png index 17a6d7023..fb230f545 100644 Binary files a/share/icons/out_mmBundleShape.png and b/share/icons/out_mmBundleShape.png differ diff --git a/share/icons/out_mmImagePlaneShape.png b/share/icons/out_mmImagePlaneShape.png index 2c0d8526a..ba9e09d4e 100644 Binary files a/share/icons/out_mmImagePlaneShape.png and b/share/icons/out_mmImagePlaneShape.png differ diff --git a/share/icons/out_mmImagePlaneShape2.png b/share/icons/out_mmImagePlaneShape2.png new file mode 100644 index 000000000..ba9e09d4e Binary files /dev/null and b/share/icons/out_mmImagePlaneShape2.png differ diff --git a/share/icons/out_mmLineShape.png b/share/icons/out_mmLineShape.png index c1f5a3848..755908404 100644 Binary files a/share/icons/out_mmLineShape.png and b/share/icons/out_mmLineShape.png differ diff --git a/share/icons/out_mmMarkerGroupTransform.png b/share/icons/out_mmMarkerGroupTransform.png index 6e746397d..5ee041ae7 100644 Binary files a/share/icons/out_mmMarkerGroupTransform.png and b/share/icons/out_mmMarkerGroupTransform.png differ diff --git a/share/icons/out_mmMarkerShape.png b/share/icons/out_mmMarkerShape.png index b854c0ac1..78fe139e7 100644 Binary files a/share/icons/out_mmMarkerShape.png and b/share/icons/out_mmMarkerShape.png differ diff --git a/share/icons/out_mmMarkerTransform.png b/share/icons/out_mmMarkerTransform.png new file mode 100644 index 000000000..00ec76321 Binary files /dev/null and b/share/icons/out_mmMarkerTransform.png differ diff --git a/share/icons/selectionTools.png b/share/icons/selectionTools.png new file mode 100644 index 000000000..6d7e85a57 Binary files /dev/null and b/share/icons/selectionTools.png differ diff --git a/share/icons/selectionTools_150.png b/share/icons/selectionTools_150.png new file mode 100644 index 000000000..5795e1595 Binary files /dev/null and b/share/icons/selectionTools_150.png differ diff --git a/share/icons/selectionTools_200.png b/share/icons/selectionTools_200.png new file mode 100644 index 000000000..78b5b6a98 Binary files /dev/null and b/share/icons/selectionTools_200.png differ diff --git a/share/icons/selectionTools_32x32.png b/share/icons/selectionTools_32x32.png deleted file mode 100644 index a17549a5d..000000000 Binary files a/share/icons/selectionTools_32x32.png and /dev/null differ diff --git a/share/icons/zDepthTools.png b/share/icons/zDepthTools.png new file mode 100644 index 000000000..d5170ee2d Binary files /dev/null and b/share/icons/zDepthTools.png differ diff --git a/share/icons/zDepthTools_150.png b/share/icons/zDepthTools_150.png new file mode 100644 index 000000000..c817e055f Binary files /dev/null and b/share/icons/zDepthTools_150.png differ diff --git a/share/icons/zDepthTools_200.png b/share/icons/zDepthTools_200.png new file mode 100644 index 000000000..3f8968f20 Binary files /dev/null and b/share/icons/zDepthTools_200.png differ diff --git a/share/icons/zDepthTools_32x32.png b/share/icons/zDepthTools_32x32.png deleted file mode 100644 index e7718d3e7..000000000 Binary files a/share/icons/zDepthTools_32x32.png and /dev/null differ diff --git a/share/maya_python_plugins/MMMarkerBundleShape.py b/share/maya_python_plugins/MMMarkerBundleShape.py index 654d73728..1328f6709 100644 --- a/share/maya_python_plugins/MMMarkerBundleShape.py +++ b/share/maya_python_plugins/MMMarkerBundleShape.py @@ -497,7 +497,7 @@ def initializePlugin(obj): MMMarkerBundleShape.initialize, om.MPxNode.kLocatorNode, MMMarkerBundleShape.drawDbClassification) - except: + except RuntimeError: sys.stderr.write("Failed to register node\n") raise try: @@ -505,7 +505,7 @@ def initializePlugin(obj): MMMarkerBundleShape.drawDbClassification, MMMarkerBundleShape.drawRegistrantId, MarkerBundleShapeDrawOverride.creator) - except: + except RuntimeError: sys.stderr.write("Failed to register override\n") raise @@ -514,15 +514,13 @@ def uninitializePlugin(obj): plugin = om.MFnPlugin(obj) try: plugin.deregisterNode(MMMarkerBundleShape.id) - except: + except RuntimeError: sys.stderr.write("Failed to deregister node\n") pass try: omr.MDrawRegistry.deregisterDrawOverrideCreator( MMMarkerBundleShape.drawDbClassification, MMMarkerBundleShape.drawRegistrantId) - except: + except RuntimeError: sys.stderr.write("Failed to deregister override\n") pass - - diff --git a/share/python_requirements/README.md b/share/python_requirements/README.md new file mode 100644 index 000000000..4f9363bc1 --- /dev/null +++ b/share/python_requirements/README.md @@ -0,0 +1,9 @@ +# Python Package Requirement Files + +This directory is the home to "requirements.txt" files, which are used +by Python's PIP package manager to install python packages. + +There are different files for different usages. The "github" +requirement files are used by various GitHub actions, and are reduced +to the minimum required dependencies, to improve speed of the GitHub +actions. diff --git a/share/python_requirements/requirements-github-build-and-deploy-docs.txt b/share/python_requirements/requirements-github-build-and-deploy-docs.txt new file mode 100644 index 000000000..7730af7ce --- /dev/null +++ b/share/python_requirements/requirements-github-build-and-deploy-docs.txt @@ -0,0 +1,10 @@ +# .github/workflows/build_and_deploy_docs.yml +# +# Expects Python 3.7.x + +# Required packages for building documentation on GitHub. +# +# NOTE: No versions are specified to allow the package version resolver +# to resolve correctly. +Sphinx +furo diff --git a/share/python_requirements/requirements-github-lint-code.txt b/share/python_requirements/requirements-github-lint-code.txt new file mode 100644 index 000000000..09b143295 --- /dev/null +++ b/share/python_requirements/requirements-github-lint-code.txt @@ -0,0 +1,11 @@ +# .github/workflows/lint_code.yml +# +# Expects Python 3.7.x, because that's the minimum supported version +# by Ruff. + +# Required packages for linting code on GitHub. +# +# NOTE: No versions are specified to allow the package version resolver +# to resolve correctly. +ruff +cpplint diff --git a/share/python_requirements/requirements-maya2018.txt b/share/python_requirements/requirements-maya2018.txt new file mode 100644 index 000000000..cbec4fc22 --- /dev/null +++ b/share/python_requirements/requirements-maya2018.txt @@ -0,0 +1,17 @@ +# Python Package Requirements. +# +# Expects Python 3.6.x + +# Tools needed for code linting and formatting. +# +# NOTE: No versions are explicitly to allow the PIP version resolver to find a match. +black +pylint +flake8 +cpplint + +# Tools needed for documentation building. +# +# NOTE: No versions are explicitly to allow the PIP version resolver to find a match. +Sphinx +furo diff --git a/share/python_requirements/requirements-maya2019.txt b/share/python_requirements/requirements-maya2019.txt new file mode 100644 index 000000000..2b0a98de2 --- /dev/null +++ b/share/python_requirements/requirements-maya2019.txt @@ -0,0 +1,13 @@ +# Python Package Requirements. +# +# Expects Python 3.6.x + +# Tools needed for code linting and formatting. +black == 22.8.0 +pylint == 2.13.9 +flake8 == 5.0.4 +cpplint == 1.6.1 + +# Tools needed for documentation building. +Sphinx == 4.3.2 +furo == 2022.4.7 diff --git a/share/python_requirements/requirements-maya2020.txt b/share/python_requirements/requirements-maya2020.txt new file mode 100644 index 000000000..2b0a98de2 --- /dev/null +++ b/share/python_requirements/requirements-maya2020.txt @@ -0,0 +1,13 @@ +# Python Package Requirements. +# +# Expects Python 3.6.x + +# Tools needed for code linting and formatting. +black == 22.8.0 +pylint == 2.13.9 +flake8 == 5.0.4 +cpplint == 1.6.1 + +# Tools needed for documentation building. +Sphinx == 4.3.2 +furo == 2022.4.7 diff --git a/share/python_requirements/requirements-maya2022.txt b/share/python_requirements/requirements-maya2022.txt new file mode 100644 index 000000000..2b0a98de2 --- /dev/null +++ b/share/python_requirements/requirements-maya2022.txt @@ -0,0 +1,13 @@ +# Python Package Requirements. +# +# Expects Python 3.6.x + +# Tools needed for code linting and formatting. +black == 22.8.0 +pylint == 2.13.9 +flake8 == 5.0.4 +cpplint == 1.6.1 + +# Tools needed for documentation building. +Sphinx == 4.3.2 +furo == 2022.4.7 diff --git a/share/python_requirements/requirements-maya2023.txt b/share/python_requirements/requirements-maya2023.txt new file mode 100644 index 000000000..2b0a98de2 --- /dev/null +++ b/share/python_requirements/requirements-maya2023.txt @@ -0,0 +1,13 @@ +# Python Package Requirements. +# +# Expects Python 3.6.x + +# Tools needed for code linting and formatting. +black == 22.8.0 +pylint == 2.13.9 +flake8 == 5.0.4 +cpplint == 1.6.1 + +# Tools needed for documentation building. +Sphinx == 4.3.2 +furo == 2022.4.7 diff --git a/share/python_requirements/requirements-maya2024.txt b/share/python_requirements/requirements-maya2024.txt new file mode 100644 index 000000000..00f68cbaf --- /dev/null +++ b/share/python_requirements/requirements-maya2024.txt @@ -0,0 +1,14 @@ +# Python Package Requirements. +# +# Expects Python 3.9.x + +# Tools needed for code linting and formatting. +black == 24.8.0 +pylint == 3.2.7 +flake8 == 7.1.1 +cpplint == 1.6.1 +ruff == 0.6.5 + +# Tools needed for documentation building. +Sphinx == 7.4.7 +furo == 2024.8.6 diff --git a/share/requirements-dev-maya2024.txt b/share/requirements-dev-maya2024.txt deleted file mode 100644 index 2bc0c6f01..000000000 --- a/share/requirements-dev-maya2024.txt +++ /dev/null @@ -1 +0,0 @@ -ruff==0.0.261 diff --git a/share/requirements-dev.txt b/share/requirements-dev.txt deleted file mode 100644 index 86ac84c72..000000000 --- a/share/requirements-dev.txt +++ /dev/null @@ -1,4 +0,0 @@ -black==24.3.0 -pylint==2.13.9 -flake8==4.0.1 -cpplint==1.6.0 diff --git a/share/requirements-doc.txt b/share/requirements-doc.txt deleted file mode 100644 index 89637b009..000000000 --- a/share/requirements-doc.txt +++ /dev/null @@ -1,2 +0,0 @@ -Sphinx>=4.5.0 -furo>=2022.2.23 diff --git a/share/shader/CMakeLists.txt b/share/shader/CMakeLists.txt index ad6be6614..d5559ab3c 100644 --- a/share/shader/CMakeLists.txt +++ b/share/shader/CMakeLists.txt @@ -4,6 +4,7 @@ install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/mmEdge.ogsfx ${CMAKE_CURRENT_SOURCE_DIR}/mmLayerMerge.ogsfx ${CMAKE_CURRENT_SOURCE_DIR}/mmSilhouette.ogsfx + ${CMAKE_CURRENT_SOURCE_DIR}/mmImagePlane.ogsfx ${CMAKE_CURRENT_SOURCE_DIR}/ocgImagePlaneSolid.ogsfx ${CMAKE_CURRENT_SOURCE_DIR}/ocgImagePlaneTextured.ogsfx DESTINATION "${MODULE_FULL_NAME}/shader") diff --git a/share/shader/mmImagePlane.ogsfx b/share/shader/mmImagePlane.ogsfx new file mode 100644 index 000000000..faa3b8b27 --- /dev/null +++ b/share/shader/mmImagePlane.ogsfx @@ -0,0 +1,196 @@ +#version 400 +// Copyright (C) 2021, 2023, 2024 David Cattermole. +// +// This file is part of mmSolver. +// +// mmSolver is free software: you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// mmSolver is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with mmSolver. If not, see . +// -------------------------------------------------------------------- +// +// Draw a texture on an image plane. + +// Global variables provided by Maya. +uniform mat4 gWVPXf : WorldViewProjection; + + +// Color and alpha modifications. +uniform vec4 gColorGain : DIFFUSE = {1, 1, 1, 1}; +uniform float gColorExposure = 0.0f; +uniform float gColorGamma = 1.0f; +uniform float gColorSoftClip = 1.0f; +uniform float gAlphaGain = 1.0f; +uniform float4x4 gColorSaturationMatrix < string UIWidget = "None"; >; + +// Should we use the alpha channel from the image? +uniform bool gIgnoreAlpha = false; + +// Flip or flop the image. +uniform bool gFlip = false; +uniform bool gFlop = false; + +// What type of channels to display to the user. +// +// NOTE: Matches the mmsolver::ImageDisplayChannel enum class. +#define DISPLAY_CHANNEL_RGBA (0) +#define DISPLAY_CHANNEL_RGB (1) +#define DISPLAY_CHANNEL_RED (2) +#define DISPLAY_CHANNEL_GREEN (3) +#define DISPLAY_CHANNEL_BLUE (4) +#define DISPLAY_CHANNEL_ALPHA (5) +#define DISPLAY_CHANNEL_LUMINANCE (6) +uniform int gDisplayChannel = 0; + +// When the image is not found the fallback color should be shown. +uniform bool gImageNotFound = false; +uniform vec4 gFallbackColor : DIFFUSE = {0.3f, 0.0f, 0.0f, 1}; + +// The main image texture displaying RGBA values to the user. +uniform texture2D gImageTexture +< + string ResourceName = ""; + string ResourceType = "2D"; +>; + +uniform sampler2D gImageTextureSampler = sampler_state +{ + Texture = ; + TEXTURE_MIN_FILTER = NEAREST; + TEXTURE_MAG_FILTER = NEAREST; + TEXTURE_WRAP_S = CLAMP_TO_EDGE; + TEXTURE_WRAP_T = CLAMP_TO_EDGE; + TEXTURE_WRAP_R = CLAMP_TO_EDGE; +}; + + +// Vertex Shader inputs. +attribute VS_INPUT { + vec3 Pos : POSITION; + vec2 UV : TEXCOORD0; +}; + +// Vertex Shader data outputs, to be used by the Pixel Shader. +attribute SHADER_DATA { + vec2 UV; +}; + +// Vertex Shader +GLSLShader VS_mmImagePlane { + void main() { + gl_Position = gWVPXf * vec4(Pos, 1); + + vec2 uv = UV; + if (gFlop) { + uv.x -= 0.5f; + uv.x *= -1.0f; + uv.x += 0.5f; + } + + if (gFlip) { + uv.y -= 0.5f; + uv.y *= -1.0f; + uv.y += 0.5f; + } + + VS_OUTPUT.UV = uv; + } +} + +// Pixel Shader Outputs +attribute PIXEL_DATA { + vec4 color_out : COLOR0; +} + +GLSLShader PS_mmImagePlane_Main { + + // Simple Reinhard tone-mapping operator. + // + // blend_value is a blend between no adjustment and the full + // tone-mapping (0.0 to 1.0). + vec3 apply_soft_clip(vec3 in_color, float blend_value) { + vec3 adjusted = in_color / (1.0 + in_color); + return mix(in_color, adjusted, blend_value); + } + + vec4 convert_to_luminance_only(vec4 in_color) { + // Luminance weights + // + // From Mozilla: + // https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_Colors_and_Luminance + float r_weight = 0.2126f; // Luminance Red + float g_weight = 0.7152f; // Luminance Green + float b_weight = 0.0722f; // Luminance Blue + + mat4 matrix; + matrix[0] = vec4(r_weight, g_weight, b_weight, 0.0); + matrix[1] = vec4(r_weight, g_weight, b_weight, 0.0); + matrix[2] = vec4(r_weight, g_weight, b_weight, 0.0); + matrix[3] = vec4(0.0, 0.0, 0.0, 1.0); + + return in_color * matrix; + } + + // The OCIO function will be replaced and injected at runtime into + // this line. This function is used as a stand-in. + vec4 OCIODisplay(vec4 passthrough) { return passthrough; } + + void main() { + vec4 texture_color = OCIODisplay(texture2D(gImageTextureSampler, PS_INPUT.UV)); + if (gIgnoreAlpha) { + texture_color.a = 1.0f; + } + + vec3 gamma = 1.0f / vec3(gColorGamma, gColorGamma, gColorGamma); + + texture_color.rgb *= gColorGain.rgb; + texture_color.rgb *= pow(2.0, gColorExposure); + texture_color.rgb = max(vec3(0.0f, 0.0f, 0.0f), texture_color.rgb); + texture_color.rgb = pow(texture_color.rgb, gamma); + texture_color *= gColorSaturationMatrix; + texture_color.rgb = apply_soft_clip(texture_color.rgb, gColorSoftClip); + + texture_color.a *= gAlphaGain; + + if (texture_color.a > 1.0f) { + texture_color.a = 1.0f; + } else if (texture_color.a < 0.0f) { + texture_color.a = 0.0f; + } + + color_out = texture_color; + if (gDisplayChannel == DISPLAY_CHANNEL_RGB) { + color_out = vec4(color_out.rgb, 1.0f); + } else if (gDisplayChannel == DISPLAY_CHANNEL_RED) { + color_out = vec4(color_out.rrr, 1.0f); + } else if (gDisplayChannel == DISPLAY_CHANNEL_GREEN) { + color_out = vec4(color_out.ggg, 1.0f); + } else if (gDisplayChannel == DISPLAY_CHANNEL_BLUE) { + color_out = vec4(color_out.bbb, 1.0f); + } else if (gDisplayChannel == DISPLAY_CHANNEL_ALPHA) { + color_out = vec4(color_out.aaa, 1.0f); + } else if (gDisplayChannel == DISPLAY_CHANNEL_LUMINANCE) { + color_out = convert_to_luminance_only(color_out); + } + } +} + +technique Main +< +// Tell Maya to support transparency for this technique. +string Transparency = "Transparent"; // or "Opaque". +> +{ + pass p0 { + VertexShader(in VS_INPUT, out SHADER_DATA VS_OUTPUT) = VS_mmImagePlane; + PixelShader(in SHADER_DATA PS_INPUT, out PIXEL_DATA) = PS_mmImagePlane_Main; + } +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 922147c16..5603c7bca 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -29,41 +29,51 @@ set(SOURCE_FILES mmSolver/adjust/adjust_cminpack_base.cpp mmSolver/adjust/adjust_cminpack_lmder.cpp mmSolver/adjust/adjust_cminpack_lmdif.cpp + mmSolver/adjust/adjust_measureErrors.cpp mmSolver/adjust/adjust_relationships.cpp mmSolver/adjust/adjust_results_helpers.cpp mmSolver/adjust/adjust_results_setMarkerData.cpp mmSolver/adjust/adjust_results_setSolveData.cpp mmSolver/adjust/adjust_setParameters.cpp - mmSolver/adjust/adjust_measureErrors.cpp mmSolver/adjust/adjust_solveFunc.cpp mmSolver/calibrate/calibrate_common.cpp mmSolver/calibrate/vanishing_point.cpp - mmSolver/cmd/arg_flags_attr_details.cpp - mmSolver/cmd/arg_flags_attr_details.h - mmSolver/cmd/arg_flags_solve_frames.cpp - mmSolver/cmd/arg_flags_solve_frames.h - mmSolver/cmd/arg_flags_solve_info.cpp - mmSolver/cmd/arg_flags_solve_info.h - mmSolver/cmd/arg_flags_solve_log.cpp - mmSolver/cmd/arg_flags_solve_log.h - mmSolver/cmd/arg_flags_solve_object.cpp - mmSolver/cmd/arg_flags_solve_object.h - mmSolver/cmd/arg_flags_solve_scene_graph.cpp - mmSolver/cmd/arg_flags_solve_scene_graph.h mmSolver/cmd/MMCameraPoseFromPointsCmd.cpp mmSolver/cmd/MMCameraRelativePoseCmd.cpp mmSolver/cmd/MMCameraSolveCmd.cpp + mmSolver/cmd/MMColorIOCmd.cpp mmSolver/cmd/MMConvertImageCmd.cpp + mmSolver/cmd/MMImageCacheCmd.cpp mmSolver/cmd/MMMarkerHomographyCmd.cpp + mmSolver/cmd/MMMemoryGPUCmd.cpp + mmSolver/cmd/MMMemorySystemCmd.cpp mmSolver/cmd/MMReadImageCmd.cpp mmSolver/cmd/MMReprojectionCmd.cpp + mmSolver/cmd/MMSolver2Cmd.cpp mmSolver/cmd/MMSolverAffectsCmd.cpp mmSolver/cmd/MMSolverCmd.cpp - mmSolver/cmd/MMSolver2Cmd.cpp mmSolver/cmd/MMSolverSceneGraphCmd.cpp mmSolver/cmd/MMSolverTypeCmd.cpp mmSolver/cmd/MMTestCameraMatrixCmd.cpp + mmSolver/cmd/arg_flags_attr_details.cpp + mmSolver/cmd/arg_flags_attr_details.h + mmSolver/cmd/arg_flags_solve_frames.cpp + mmSolver/cmd/arg_flags_solve_frames.h + mmSolver/cmd/arg_flags_solve_info.cpp + mmSolver/cmd/arg_flags_solve_info.h + mmSolver/cmd/arg_flags_solve_log.cpp + mmSolver/cmd/arg_flags_solve_log.h + mmSolver/cmd/arg_flags_solve_object.cpp + mmSolver/cmd/arg_flags_solve_object.h + mmSolver/cmd/arg_flags_solve_scene_graph.cpp + mmSolver/cmd/arg_flags_solve_scene_graph.h mmSolver/core/reprojection.cpp + mmSolver/image/ImageCache.cpp + mmSolver/image/ImagePixelData.cpp + mmSolver/image/PixelDataType.cpp + mmSolver/image/TextureData.cpp + mmSolver/image/image_convert.cpp + mmSolver/image/image_io.cpp mmSolver/mayahelper/maya_attr.cpp mmSolver/mayahelper/maya_bundle.cpp mmSolver/mayahelper/maya_camera.cpp @@ -74,6 +84,7 @@ set(SOURCE_FILES mmSolver/mayahelper/maya_utils.cpp mmSolver/node/MMCameraCalibrateNode.cpp mmSolver/node/MMImagePlaneTransformNode.cpp + mmSolver/node/MMImageSequenceFrameLogicNode.cpp mmSolver/node/MMLensData.cpp mmSolver/node/MMLensDeformerNode.cpp mmSolver/node/MMLensEvaluateNode.cpp @@ -88,34 +99,41 @@ set(SOURCE_FILES mmSolver/node/MMMarkerTransformNode.cpp mmSolver/node/MMReprojectionNode.cpp mmSolver/node/node_line_utils.cpp - mmSolver/sfm/camera_relative_pose.cpp + mmSolver/pluginMain.cpp mmSolver/sfm/camera_from_known_points.cpp + mmSolver/sfm/camera_relative_pose.cpp mmSolver/sfm/homography.cpp mmSolver/sfm/sfm_utils.cpp - mmSolver/shape/ShapeDrawUtils.cpp - mmSolver/shape/MarkerShapeNode.cpp - mmSolver/shape/MarkerDrawOverride.cpp - mmSolver/shape/BundleShapeNode.cpp mmSolver/shape/BundleDrawOverride.cpp - mmSolver/shape/ImagePlaneShapeNode.cpp + mmSolver/shape/BundleShapeNode.cpp + mmSolver/shape/ImagePlaneGeometry2Override.cpp mmSolver/shape/ImagePlaneGeometryOverride.cpp - mmSolver/shape/LineShapeNode.cpp + mmSolver/shape/ImagePlaneShape2Node.cpp + mmSolver/shape/ImagePlaneShapeNode.cpp + mmSolver/shape/ImagePlaneUtils.cpp mmSolver/shape/LineDrawOverride.cpp - mmSolver/shape/SkyDomeShapeNode.cpp + mmSolver/shape/LineShapeNode.cpp + mmSolver/shape/MarkerDrawOverride.cpp + mmSolver/shape/MarkerShapeNode.cpp + mmSolver/shape/ShapeDrawUtils.cpp mmSolver/shape/SkyDomeDrawOverride.cpp + mmSolver/shape/SkyDomeShapeNode.cpp mmSolver/utilities/debug_utils.cpp + mmSolver/utilities/memory_gpu_utils.cpp + mmSolver/utilities/memory_system_utils.cpp + mmSolver/utilities/memory_utils.cpp mmSolver/utilities/number_utils.cpp + mmSolver/utilities/path_utils.cpp mmSolver/utilities/string_utils.cpp - mmSolver/pluginMain.cpp ) if (MMSOLVER_BUILD_RENDERER) set(SOURCE_FILES ${SOURCE_FILES} - mmSolver/render/MMRendererBasicCmd.cpp + mmSolver/render/MMRendererStandardCmd.cpp mmSolver/render/MMRendererSilhouetteCmd.cpp - mmSolver/render/RenderOverrideBasic.cpp - mmSolver/render/RenderGlobalsBasicNode.cpp + mmSolver/render/RenderOverrideStandard.cpp + mmSolver/render/RenderGlobalsStandardNode.cpp mmSolver/render/RenderGlobalsSilhouetteNode.cpp mmSolver/render/RenderOverrideSilhouette.cpp mmSolver/render/ops/ClearOperation.cpp @@ -154,8 +172,9 @@ endif () # Set global flags and options that need to apply to the Maya # plug-in. See 'MMSolverUtils' CMake module for more information. -set_global_treat_warnings_as_errors() -set_global_maya_plugin_compile_options() +include(MMCommonUtils) +mm_common_set_global_treat_warnings_as_errors() +mm_common_set_global_compile_options() # MM Solver standalone libraries. find_package(mmsolverlibs_cpp REQUIRED) @@ -200,18 +219,62 @@ if(MAYA_FOUND) ) endif() +# MM Color IO dependencies +include(MMColorIOUtils) +mmcolorio_find_packages() +mmcolorio_target_link_packages(mmSolver) # Link all libraries. target_link_libraries(mmSolver - PRIVATE - cminpack::cminpack - glog::glog - Eigen3::Eigen - ceres - openMVG + PRIVATE cminpack::cminpack + PRIVATE glog::glog + PRIVATE Eigen3::Eigen + PRIVATE ceres + PRIVATE openMVG ) target_compile_definitions(mmSolver PRIVATE MMSOLVER_USE_CMINPACK) target_compile_definitions(mmSolver PRIVATE MMSOLVER_USE_OPENMVG) install_target_plugin_to_module(mmSolver "${MODULE_FULL_NAME}") + + +# TODO: Extract this logic into a reusable function/macro in a CMake +# module. +if (WIN32) + get_target_property(OpenColorIO_IMPLIB_RELEASE + OpenColorIO::OpenColorIO + IMPORTED_IMPLIB_RELEASE) + message(STATUS + "OpenColorIO_IMPLIB_RELEASE: ${OpenColorIO_IMPLIB_RELEASE}") + + get_target_property(OpenColorIO_LOCATION_RELEASE + OpenColorIO::OpenColorIO + IMPORTED_LOCATION_RELEASE) + message(STATUS + "OpenColorIO_LOCATION_RELEASE: ${OpenColorIO_LOCATION_RELEASE}") + + install_shared_library( + ${OpenColorIO_IMPLIB_RELEASE} + ${OpenColorIO_LOCATION_RELEASE} + "${MODULE_FULL_NAME}/lib") +elseif (UNIX) + + get_target_property(OpenColorIO_LOCATION_RELEASE + OpenColorIO::OpenColorIO + IMPORTED_LOCATION_RELEASE) + message(STATUS + "OpenColorIO_LOCATION_RELEASE: ${OpenColorIO_LOCATION_RELEASE}") + + get_target_property(OpenColorIO_SONAME_RELEASE + OpenColorIO::OpenColorIO + IMPORTED_SONAME_RELEASE) + message(STATUS + "OpenColorIO_SONAME_RELEASE: ${OpenColorIO_SONAME_RELEASE}") + + install_shared_library_with_name( + ${OpenColorIO_LOCATION_RELEASE} + ${OpenColorIO_LOCATION_RELEASE} + ${OpenColorIO_SONAME_RELEASE} + "${MODULE_FULL_NAME}/lib") +endif () diff --git a/src/mmSolver/cmd/MMColorIOCmd.cpp b/src/mmSolver/cmd/MMColorIOCmd.cpp new file mode 100644 index 000000000..a13a3250e --- /dev/null +++ b/src/mmSolver/cmd/MMColorIOCmd.cpp @@ -0,0 +1,402 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + * Command for running mmColorIO. + * + * The mmColorIO command is responsible for querying and setting + * details about the input/output of colours in mmSolver, for + * converting colours, images and textures from one colour space to + * another (using OpenColorIO under the hood). + * + * MEL: + * // List all known colour spaces. + * mmColorIO -listColorSpacesAll + * + * // List all known active colour spaces. + * mmColorIO -listColorSpacesActive + * + * // List all known inactive colour spaces. + * mmColorIO -listColorSpacesInactive + * + * // Get "scene linear" role colour space. + * mmColorIO -roleSceneLinear + * + * // Check if the given colour space exists. + * mmColorIO -colorSpaceExists "ACEScg" + * + * // Guess colour space from given file. + * mmColorIO -guessColorSpaceFromFile "/path/to/file.jpg" + * + * // Get the current config details. + * mmColorIO -configName + * mmColorIO -configDescription + * mmColorIO -configSearchPath + * mmColorIO -configWorkingDirectory + * + */ + +#include "MMColorIOCmd.h" + +// STD +#include +#include +#include +#include + +// Maya +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// MM Solver +#include + +#include "mmSolver/utilities/debug_utils.h" +#include "mmSolver/utilities/path_utils.h" + +namespace mmsolver { + +MMColorIOCmd::~MMColorIOCmd() {} + +void *MMColorIOCmd::creator() { return new MMColorIOCmd(); } + +MString MMColorIOCmd::cmdName() { return MString("mmColorIO"); } + +/* + * Tell Maya we have a syntax function. + */ +bool MMColorIOCmd::hasSyntax() const { return true; } + +bool MMColorIOCmd::isUndoable() const { return false; } + +/* + * Add flags to the command syntax + */ +MSyntax MMColorIOCmd::newSyntax() { + MSyntax syntax; + syntax.enableQuery(false); + syntax.enableEdit(false); + + syntax.addFlag(LIST_COLOR_SPACES_ALL_FLAG, LIST_COLOR_SPACES_ALL_FLAG_LONG, + MSyntax::kBoolean); + syntax.addFlag(LIST_COLOR_SPACES_ACTIVE_FLAG, + LIST_COLOR_SPACES_ACTIVE_FLAG_LONG, MSyntax::kBoolean); + syntax.addFlag(LIST_COLOR_SPACES_INACTIVE_FLAG, + LIST_COLOR_SPACES_INACTIVE_FLAG_LONG, MSyntax::kBoolean); + + syntax.addFlag(ROLE_DEFAULT_FLAG, ROLE_DEFAULT_FLAG_LONG, + MSyntax::kBoolean); + syntax.addFlag(ROLE_REFERENCE_FLAG, ROLE_REFERENCE_FLAG_LONG, + MSyntax::kBoolean); + syntax.addFlag(ROLE_DATA_FLAG, ROLE_DATA_FLAG_LONG, MSyntax::kBoolean); + syntax.addFlag(ROLE_COLOR_PICKING_FLAG, ROLE_COLOR_PICKING_FLAG_LONG, + MSyntax::kBoolean); + syntax.addFlag(ROLE_SCENE_LINEAR_FLAG, ROLE_SCENE_LINEAR_FLAG_LONG, + MSyntax::kBoolean); + syntax.addFlag(ROLE_COMPOSITING_LOG_FLAG, ROLE_COMPOSITING_LOG_FLAG_LONG, + MSyntax::kBoolean); + syntax.addFlag(ROLE_COLOR_TIMING_FLAG, ROLE_COLOR_TIMING_FLAG_LONG, + MSyntax::kBoolean); + syntax.addFlag(ROLE_TEXTURE_PAINT_FLAG, ROLE_TEXTURE_PAINT_FLAG_LONG, + MSyntax::kBoolean); + syntax.addFlag(ROLE_MATTE_PAINT_FLAG, ROLE_MATTE_PAINT_FLAG_LONG, + MSyntax::kBoolean); + syntax.addFlag(ROLE_RENDERING_FLAG, ROLE_RENDERING_FLAG_LONG, + MSyntax::kBoolean); + + syntax.addFlag(CONFIG_NAME_FLAG, CONFIG_NAME_FLAG_LONG, MSyntax::kBoolean); + syntax.addFlag(CONFIG_DESCRIPTION_FLAG, CONFIG_DESCRIPTION_FLAG_LONG, + MSyntax::kBoolean); + syntax.addFlag(CONFIG_SEARCH_PATH_FLAG, CONFIG_SEARCH_PATH_FLAG_LONG, + MSyntax::kBoolean); + syntax.addFlag(CONFIG_WORKING_DIRECTORY_FLAG, + CONFIG_WORKING_DIRECTORY_FLAG_LONG, MSyntax::kBoolean); + + syntax.addFlag(COLOR_SPACE_EXISTS_FLAG, COLOR_SPACE_EXISTS_FLAG_LONG, + MSyntax::kString); + + syntax.addFlag(GUESS_COLOR_SPACE_FROM_FILE_FLAG, + GUESS_COLOR_SPACE_FROM_FILE_FLAG_LONG, MSyntax::kString); + + return syntax; +} + +/* + * Parse command line arguments + */ +MStatus MMColorIOCmd::parseArgs(const MArgList &args) { + MStatus status = MStatus::kSuccess; + + MArgDatabase argData(syntax(), args, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_list_color_spaces_all = + argData.isFlagSet(LIST_COLOR_SPACES_ALL_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_list_color_spaces_active = + argData.isFlagSet(LIST_COLOR_SPACES_ACTIVE_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_list_color_spaces_inactive = + argData.isFlagSet(LIST_COLOR_SPACES_INACTIVE_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_role_color_picking = argData.isFlagSet(ROLE_COLOR_PICKING_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_role_color_timing = argData.isFlagSet(ROLE_COLOR_TIMING_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_role_compositing_log = + argData.isFlagSet(ROLE_COMPOSITING_LOG_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_role_data = argData.isFlagSet(ROLE_DATA_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_role_default = argData.isFlagSet(ROLE_DEFAULT_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_role_matte_paint = argData.isFlagSet(ROLE_MATTE_PAINT_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_role_reference = argData.isFlagSet(ROLE_REFERENCE_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_role_rendering = argData.isFlagSet(ROLE_RENDERING_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_role_scene_linear = argData.isFlagSet(ROLE_SCENE_LINEAR_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_role_texture_paint = argData.isFlagSet(ROLE_TEXTURE_PAINT_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_config_name = argData.isFlagSet(CONFIG_NAME_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_config_description = argData.isFlagSet(CONFIG_DESCRIPTION_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_config_search_path = argData.isFlagSet(CONFIG_SEARCH_PATH_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_config_working_directory = + argData.isFlagSet(CONFIG_WORKING_DIRECTORY_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_guess_color_space_from_file = + argData.isFlagSet(GUESS_COLOR_SPACE_FROM_FILE_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + if (m_guess_color_space_from_file) { + status = argData.getFlagArgument(GUESS_COLOR_SPACE_FROM_FILE_FLAG, 0, + m_file_path); + CHECK_MSTATUS_AND_RETURN_IT(status); + } + + m_color_space_exists = argData.isFlagSet(COLOR_SPACE_EXISTS_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + if (m_color_space_exists) { + status = argData.getFlagArgument(COLOR_SPACE_EXISTS_FLAG, 0, + m_color_space_name); + CHECK_MSTATUS_AND_RETURN_IT(status); + } + + return status; +} + +MStatus MMColorIOCmd::doIt(const MArgList &args) { + MStatus status = MStatus::kSuccess; + const bool verbose = false; + + // Read all the flag arguments. + status = parseArgs(args); + CHECK_MSTATUS_AND_RETURN_IT(status); + + if (m_list_color_spaces_all || m_list_color_spaces_active || + m_list_color_spaces_inactive) { + auto visibility = mmcolorio::ColorSpaceVisibility::kUnknown; + if (m_list_color_spaces_all) { + visibility = mmcolorio::ColorSpaceVisibility::kAll; + } else if (m_list_color_spaces_active) { + visibility = mmcolorio::ColorSpaceVisibility::kActive; + } else if (m_list_color_spaces_inactive) { + visibility = mmcolorio::ColorSpaceVisibility::kInactive; + } else { + // Should not get here. + assert(false); + } + + std::vector color_space_names = + mmcolorio::get_color_space_names(visibility); + + MStringArray outResult; + for (auto i = 0; i < color_space_names.size(); i++) { + std::string color_space_name = color_space_names[i]; + MMSOLVER_MAYA_VRB("mmColorIO: get color space names: i=" + << i << "color_space_name=\"" << color_space_name + << "\"."); + + outResult.append(MString(color_space_name.c_str())); + } + + // Maya idiosyncrasy: Return None/nothing if a command does + // not list anything. + if (outResult.length() > 0) { + MMColorIOCmd::setResult(outResult); + } + } else if (m_role_color_picking || m_role_color_timing || + m_role_compositing_log || m_role_data || m_role_default || + m_role_matte_paint || m_role_reference || m_role_rendering || + m_role_scene_linear || m_role_texture_paint) { + const char *color_space_name = ""; + if (m_role_color_picking) { + color_space_name = mmcolorio::get_role_color_space_name( + mmcolorio::ColorSpaceRole::kColorPicking); + } else if (m_role_color_timing) { + color_space_name = mmcolorio::get_role_color_space_name( + mmcolorio::ColorSpaceRole::kColorTiming); + } else if (m_role_compositing_log) { + color_space_name = mmcolorio::get_role_color_space_name( + mmcolorio::ColorSpaceRole::kCompositingLog); + } else if (m_role_data) { + color_space_name = mmcolorio::get_role_color_space_name( + mmcolorio::ColorSpaceRole::kData); + } else if (m_role_default) { + color_space_name = mmcolorio::get_role_color_space_name( + mmcolorio::ColorSpaceRole::kDefault); + } else if (m_role_matte_paint) { + color_space_name = mmcolorio::get_role_color_space_name( + mmcolorio::ColorSpaceRole::kMattePaint); + } else if (m_role_reference) { + color_space_name = mmcolorio::get_role_color_space_name( + mmcolorio::ColorSpaceRole::kReference); + } else if (m_role_rendering) { + color_space_name = mmcolorio::get_role_color_space_name( + mmcolorio::ColorSpaceRole::kRendering); + } else if (m_role_scene_linear) { + color_space_name = mmcolorio::get_role_color_space_name( + mmcolorio::ColorSpaceRole::kSceneLinear); + } else if (m_role_texture_paint) { + color_space_name = mmcolorio::get_role_color_space_name( + mmcolorio::ColorSpaceRole::kTexturePaint); + } else { + MMSOLVER_MAYA_ERR("mmColorIO: The role type to query is invalid!"); + return MStatus::kFailure; + } + + MMSOLVER_MAYA_VRB("mmColorIO: role name: " + << "\"" << color_space_name << "\"."); + + MString outResult = MString(color_space_name); + + // Maya idiosyncrasy: Return None/nothing if a command does + // not list anything. + if (outResult.length() > 0) { + MMColorIOCmd::setResult(outResult); + } + } else if (m_guess_color_space_from_file) { + MMSOLVER_MAYA_VRB("mmColorIO: guess color space from file: " + << "\"" << m_file_path.asChar() << "\"."); + + if (m_file_path.length() > 0) { + MString resolved_file_path = m_file_path; + status = mmpath::resolve_input_file_path(resolved_file_path); + CHECK_MSTATUS_AND_RETURN_IT(status); + + const char *color_space_name = + mmcolorio::guess_color_space_name_from_file_path( + resolved_file_path.asChar()); + MString outResult = MString(color_space_name); + + // Maya idiosyncrasy: Return None/nothing if a command does + // not list anything. + if (outResult.length() > 0) { + MMColorIOCmd::setResult(outResult); + } + } + } else if (m_color_space_exists) { + MMSOLVER_MAYA_VRB("mmColorIO: color space: " + << "\"" << m_color_space_name.asChar() << "\"."); + + if (m_color_space_name.length() > 0) { + const bool exists = + mmcolorio::color_space_name_exists(m_color_space_name.asChar()); + MMSOLVER_MAYA_VRB("mmColorIO: color space exists: " << exists); + + MMColorIOCmd::setResult(exists); + } + } else if (m_config_name) { + const char *config_name = mmcolorio::get_config_name(); + MMSOLVER_MAYA_VRB("mmColorIO: config name: " + << "\"" << config_name << "\"."); + + // Maya idiosyncrasy: Return None/nothing if a command does + // not list anything. + if (std::strlen(config_name) > 0) { + MMColorIOCmd::setResult(MString(config_name)); + } + } else if (m_config_description) { + const char *config_description = mmcolorio::get_config_description(); + MMSOLVER_MAYA_VRB("mmColorIO: config description: " + << "\"" << config_description << "\"."); + + // Maya idiosyncrasy: Return None/nothing if a command does + // not list anything. + if (std::strlen(config_description) > 0) { + MMColorIOCmd::setResult(MString(config_description)); + } + } else if (m_config_search_path) { + const char *config_search_path = mmcolorio::get_config_search_path(); + MMSOLVER_MAYA_VRB("mmColorIO: config search path: " + << "\"" << config_search_path << "\"."); + + // Maya idiosyncrasy: Return None/nothing if a command does + // not list anything. + if (std::strlen(config_search_path) > 0) { + MMColorIOCmd::setResult(MString(config_search_path)); + } + } else if (m_config_working_directory) { + const char *config_working_directory = + mmcolorio::get_config_working_directory(); + MMSOLVER_MAYA_VRB("mmColorIO: config working directory: " + << "\"" << config_working_directory << "\"."); + + // Maya idiosyncrasy: Return None/nothing if a command does + // not list anything. + if (std::strlen(config_working_directory) > 0) { + MMColorIOCmd::setResult(MString(config_working_directory)); + } + } + + return status; +} + +} // namespace mmsolver diff --git a/src/mmSolver/cmd/MMColorIOCmd.h b/src/mmSolver/cmd/MMColorIOCmd.h new file mode 100644 index 000000000..1b7395eaf --- /dev/null +++ b/src/mmSolver/cmd/MMColorIOCmd.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + * Header for mmColorIO Maya command. + */ + +#ifndef MAYA_MM_COLOR_IO_CMD_H +#define MAYA_MM_COLOR_IO_CMD_H + +// Maya +#include +#include +#include +#include +#include +#include +#include +#include + +// Command arguments and command name: +#define COLOR_SPACE_EXISTS_FLAG "-ce" +#define COLOR_SPACE_EXISTS_FLAG_LONG "-colorSpaceExists" + +#define CONFIG_DESCRIPTION_FLAG "-de" +#define CONFIG_DESCRIPTION_FLAG_LONG "-configDescription" + +#define CONFIG_NAME_FLAG "-nm" +#define CONFIG_NAME_FLAG_LONG "-configName" + +#define CONFIG_SEARCH_PATH_FLAG "-sp" +#define CONFIG_SEARCH_PATH_FLAG_LONG "-configSearchPath" + +#define CONFIG_WORKING_DIRECTORY_FLAG "-wd" +#define CONFIG_WORKING_DIRECTORY_FLAG_LONG "-configWorkingDirectory" + +#define GUESS_COLOR_SPACE_FROM_FILE_FLAG "-gc" +#define GUESS_COLOR_SPACE_FROM_FILE_FLAG_LONG "-guessColorSpaceFromFile" + +#define LIST_COLOR_SPACES_ACTIVE_FLAG "-la" +#define LIST_COLOR_SPACES_ACTIVE_FLAG_LONG "-listColorSpacesActive" + +#define LIST_COLOR_SPACES_ALL_FLAG "-lc" +#define LIST_COLOR_SPACES_ALL_FLAG_LONG "-listColorSpacesAll" + +#define LIST_COLOR_SPACES_INACTIVE_FLAG "-li" +#define LIST_COLOR_SPACES_INACTIVE_FLAG_LONG "-listColorSpacesInactive" + +// TODO: Add the InterchangeScene and InterchangeDisplay roles? +#define ROLE_COLOR_PICKING_FLAG "-rp" +#define ROLE_COLOR_PICKING_FLAG_LONG "-roleColorPicking" +#define ROLE_COLOR_TIMING_FLAG "-rt" +#define ROLE_COLOR_TIMING_FLAG_LONG "-roleColorTiming" +#define ROLE_COMPOSITING_LOG_FLAG "-rg" +#define ROLE_COMPOSITING_LOG_FLAG_LONG "-roleCompositingLog" +#define ROLE_DATA_FLAG "-ra" +#define ROLE_DATA_FLAG_LONG "-roleData" +#define ROLE_DEFAULT_FLAG "-rd" +#define ROLE_DEFAULT_FLAG_LONG "-roleDefault" +#define ROLE_MATTE_PAINT_FLAG "-ri" +#define ROLE_MATTE_PAINT_FLAG_LONG "-roleMattePaint" +#define ROLE_REFERENCE_FLAG "-rr" +#define ROLE_REFERENCE_FLAG_LONG "-roleReference" +#define ROLE_RENDERING_FLAG "-rn" +#define ROLE_RENDERING_FLAG_LONG "-roleRendering" +#define ROLE_SCENE_LINEAR_FLAG "-rl" +#define ROLE_SCENE_LINEAR_FLAG_LONG "-roleSceneLinear" +#define ROLE_TEXTURE_PAINT_FLAG "-rx" +#define ROLE_TEXTURE_PAINT_FLAG_LONG "-roleTexturePaint" + +namespace mmsolver { + +class MMColorIOCmd : public MPxCommand { +public: + MMColorIOCmd() + : m_color_space_exists(false) + , m_config_description(false) + , m_config_name(false) + , m_config_search_path(false) + , m_config_working_directory(false) + , m_guess_color_space_from_file(false) + , m_list_color_spaces_active(false) + , m_list_color_spaces_all(false) + , m_list_color_spaces_inactive(false) + , m_role_color_picking(false) + , m_role_color_timing(false) + , m_role_compositing_log(false) + , m_role_data(false) + , m_role_default(false) + , m_role_matte_paint(false) + , m_role_reference(false) + , m_role_rendering(false) + , m_role_scene_linear(false) + , m_role_texture_paint(false){}; + + virtual ~MMColorIOCmd(); + + virtual bool hasSyntax() const; + static MSyntax newSyntax(); + + virtual MStatus doIt(const MArgList &args); + + virtual bool isUndoable() const; + + static void *creator(); + + static MString cmdName(); + +private: + MStatus parseArgs(const MArgList &args); + + bool m_color_space_exists; + bool m_config_description; + bool m_config_name; + bool m_config_search_path; + bool m_config_working_directory; + bool m_guess_color_space_from_file; + bool m_list_color_spaces_active; + bool m_list_color_spaces_all; + bool m_list_color_spaces_inactive; + bool m_role_color_picking; + bool m_role_color_timing; + bool m_role_compositing_log; + bool m_role_data; + bool m_role_default; + bool m_role_matte_paint; + bool m_role_reference; + bool m_role_rendering; + bool m_role_scene_linear; + bool m_role_texture_paint; + + MString m_file_path; + MString m_color_space_name; +}; + +} // namespace mmsolver + +#endif // MAYA_MM_COLOR_IO_CMD_H diff --git a/src/mmSolver/cmd/MMConvertImageCmd.cpp b/src/mmSolver/cmd/MMConvertImageCmd.cpp index e1be18b50..81e477b1e 100644 --- a/src/mmSolver/cmd/MMConvertImageCmd.cpp +++ b/src/mmSolver/cmd/MMConvertImageCmd.cpp @@ -54,14 +54,7 @@ // Maya #include #include -#include #include -#include -#include -#include -#include -#include -#include #include #include #include @@ -69,14 +62,12 @@ // MM Solver #include +#include "mmSolver/image/image_convert.h" #include "mmSolver/utilities/debug_utils.h" #include "mmSolver/utilities/string_utils.h" namespace mmsolver { -using mmmath::clamp; -using mmmath::fast_pow; - // For an image sequence the 'file_path' should contain at least one // character '#', which will be replaced with the 'frame_number', with // a padding width of 'frame_padding'. @@ -151,250 +142,6 @@ MStatus find_existing_file_path(MFileObject &file_object, return status; } -MStatus guess_output_format_pixel_type(const MString &in_output_format, - MImage::MPixelType &out_pixel_type) { - MStatus status = MStatus::kSuccess; - - MString output_format(in_output_format); - output_format.toLowerCase(); - - if (output_format == MString("exr")) { - out_pixel_type = MImage::kFloat; - } else if (output_format == MString("hdr")) { - out_pixel_type = MImage::kFloat; - } else { - out_pixel_type = MImage::kByte; - } - - return status; -} - -MStatus guess_file_path_pixel_type(const MString &in_file_path, - MImage::MPixelType &out_pixel_type) { - MStatus status = MStatus::kSuccess; - - MString file_path(in_file_path); - file_path.toLowerCase(); - - MStringArray splits; - file_path.split('.', splits); - MString file_extension = splits[splits.length() - 1]; - - guess_output_format_pixel_type(file_extension, out_pixel_type); - - return status; -} - -MStatus resize_image(MImage &image, const double resize_scale) { - MStatus status = MStatus::kSuccess; - - MImage::MPixelType pixel_type = image.pixelType(); - if (pixel_type != MImage::kByte) { - MMSOLVER_MAYA_WRN( - "mmConvertImage: " - << "Maya does not support resizing floating-point pixels."); - } - - uint32_t src_width = 2; - uint32_t src_height = 2; - status = image.getSize(src_width, src_height); - CHECK_MSTATUS_AND_RETURN_IT(status); - - auto dst_width_float = static_cast(src_width) * resize_scale; - auto dst_height_float = static_cast(src_height) * resize_scale; - auto dst_width = static_cast(dst_width_float); - auto dst_height = static_cast(dst_height_float); - if ((src_width != dst_width) && (src_height != dst_height)) { - const auto preserve_aspect_ratio = true; - - // TODO: Replace this with a hand-written resize function. The - // MImage.resize() method appears to have a bug whereby the - // resized image is offset by +1 pixel in X and Y. - // - // If we can use Rust, then we have these easily available, - // and they appear to do exactly what we need, very quickly: - // https://crates.io/crates/fast_image_resize - // https://crates.io/crates/resize - // - // NOTE: MImage.resize() only works on 8-bit images, not - // floating point. - status = image.resize(dst_width, dst_height, preserve_aspect_ratio); - CHECK_MSTATUS_AND_RETURN_IT(status); - } - - return status; -} - -MStatus convert_image(const MString &src_file_path, - const MString &dst_file_path, - // Common output formats include: als, bmp, cin, - // gif, jpg, rla, sgi, tga, tif, iff. "iff" is - // default. - const MString &dst_output_format, - const double resize_scale) { - MStatus status = MStatus::kSuccess; - - if (src_file_path == dst_file_path) { - status = MS::kFailure; - MMSOLVER_MAYA_ERR("mmConvertImage: " - << "Cannot have source and destination as same path: " - << src_file_path.asChar()); - CHECK_MSTATUS_AND_RETURN_IT(status); - } - - auto image = MImage(); - // kUnknown attempts to load the native pixel type. - auto src_pixel_type = MImage::kUnknown; - status = image.readFromFile( - src_file_path, - src_pixel_type // The desired pixel format is unknown. - ); - if (status != MS::kSuccess) { - MMSOLVER_MAYA_ERR("mmConvertImage: " - << "Image file path could not be read: " - << src_file_path.asChar()); - CHECK_MSTATUS_AND_RETURN_IT(status); - } - src_pixel_type = image.pixelType(); - const bool src_is_rgba = image.isRGBA(); - - // Maya always stores 4 channels (according to the - // documentation). - const auto channels = 4; - - // Guess the Pixel Type for the output image. - MImage::MPixelType dst_pixel_type; - MImage::MPixelType format_pixel_type; - status = - guess_output_format_pixel_type(dst_output_format, format_pixel_type); - CHECK_MSTATUS_AND_RETURN_IT(status); - status = guess_file_path_pixel_type(dst_file_path, dst_pixel_type); - CHECK_MSTATUS_AND_RETURN_IT(status); - if (format_pixel_type != dst_pixel_type) { - MMSOLVER_MAYA_WRN( - "mmConvertImage: " - << "The destination file extension and output format seem " - "to contradict each other. file path: " - << dst_file_path.asChar() << " output format: \"" - << dst_output_format.asChar() << "\""); - } - - if (src_pixel_type == dst_pixel_type) { - // Try to resize. We can only resize kByte - Maya is limited - // with the pixel types it can resize. - if (src_pixel_type == MImage::kByte) { - status = resize_image(image, resize_scale); - if (status != MS::kSuccess) { - MMSOLVER_MAYA_ERR("mmConvertImage: " - << "Failed to resize image file: " - << src_file_path.asChar()); - return status; - } - } - - // No conversion is needed. We write out the 8-bit image - // directly. - status = image.writeToFile(dst_file_path, dst_output_format); - if (status != MS::kSuccess) { - MMSOLVER_MAYA_ERR("mmConvertImage: " - << "Failed to write image file: " - << dst_file_path.asChar() << " output format: \"" - << dst_output_format.asChar() << "\""); - CHECK_MSTATUS_AND_RETURN_IT(status); - } - } else { - // Convert 32-bit to 8-bit integer. We assume the image - // has not been resized yet. - if (image.pixelType() == MImage::kByte) { - status = MS::kFailure; - MMSOLVER_MAYA_ERR("mmConvertImage: " - << "Failed to write image file: " - << dst_file_path.asChar() << " output format: \"" - << dst_output_format.asChar() << "\""); - return status; - } - - // Do gamma correction before converting. - uint32_t image_width = 2; - uint32_t image_height = 2; - status = image.getSize(image_width, image_height); - CHECK_MSTATUS_AND_RETURN_IT(status); - - // Get exponent based on if we are converting to/from floating - // point or not. Make (linear color space) pixels brighter. - const float gamma = 2.2F; - float exponent = 1.0F / gamma; - - // TODO: Use the Autodesk 'synColor' library to change the - // color space for an 8-bit file format. This library is - // available in the Maya devkit. - // - // https://forums.autodesk.com/t5/maya-programming/how-to-export-colors-with-correct-color-management/td-p/10515406 - - // Make sure we do our color management with floating point - // numbers, to avoid loss of detail. This does not do anything - // if the pixel format is already MImage::kFloat. - float *float_pixels = image.floatPixels(); - if (float_pixels == nullptr) { - // The data pointer should be valid because we have - // already read and operated on the pixels, so it seems - // very wrong that the data wouldn't be valid. - status = MS::kFailure; - MMSOLVER_MAYA_ERR("mmConvertImage: " - << "Failed to get floating point pixel data: " - << src_file_path.asChar()); - return status; - } - - MImage out_image; - out_image.create(image_width, image_height, channels, MImage::kByte); - unsigned char *pixels = out_image.pixels(); - - // Apply gamma correction. - for (auto y = 0; y < image_height; ++y) { - for (auto x = 0; x < image_width; ++x) { - auto index = (y * image_width * channels) + (x * channels); - auto r = - clamp(fast_pow(float_pixels[index + 0], exponent) * 255.0F, - 0.0, 255.0); - auto g = - clamp(fast_pow(float_pixels[index + 1], exponent) * 255.0F, - 0.0, 255.0); - auto b = - clamp(fast_pow(float_pixels[index + 2], exponent) * 255.0F, - 0.0, 255.0); - auto a = clamp(float_pixels[index + 3] * 255.0F, 0.0, 255.0); - pixels[index + 0] = static_cast(r); - pixels[index + 1] = static_cast(g); - pixels[index + 2] = static_cast(b); - // Alpha does not need to be gamma corrected. - pixels[index + 3] = static_cast(a); - } - } - out_image.setRGBA(true); - - // Try to resize. We can only resize kByte - Maya is - // limited with the pixel types it can resize. - status = resize_image(out_image, resize_scale); - if (status != MS::kSuccess) { - MMSOLVER_MAYA_ERR("mmConvertImage: " - << "Failed to resize image file: " - << src_file_path.asChar()); - return status; - } - - status = out_image.writeToFile(dst_file_path, dst_output_format); - if (status != MS::kSuccess) { - MMSOLVER_MAYA_ERR("mmConvertImage: " - << "Failed to write image file: " - << dst_file_path.asChar() << " output format: \"" - << dst_output_format.asChar() << "\""); - CHECK_MSTATUS_AND_RETURN_IT(status); - } - } - return status; -} - MMConvertImageCmd::~MMConvertImageCmd() {} void *MMConvertImageCmd::creator() { return new MMConvertImageCmd(); } @@ -603,8 +350,8 @@ MStatus MMConvertImageCmd::doIt(const MArgList &args) { continue; } - status = convert_image(src_file_path, dst_file_path, - m_dst_output_format, m_resize_scale); + status = image::convert_image(src_file_path, dst_file_path, + m_dst_output_format, m_resize_scale); if (status != MS::kSuccess) { MMSOLVER_MAYA_WRN("mmConvertImage: " << "Failed to convert image: " diff --git a/src/mmSolver/cmd/MMImageCacheCmd.cpp b/src/mmSolver/cmd/MMImageCacheCmd.cpp new file mode 100644 index 000000000..dc8224b7c --- /dev/null +++ b/src/mmSolver/cmd/MMImageCacheCmd.cpp @@ -0,0 +1,761 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#include "MMImageCacheCmd.h" + +// STD +#include +#include +#include + +// Maya +#include +#include +#include +#include +#include + +// Maya Viewport 2.0 +#include + +// MM Solver +#include + +#include "mmSolver/image/ImageCache.h" +#include "mmSolver/mayahelper/maya_string_utils.h" +#include "mmSolver/utilities/debug_utils.h" +#include "mmSolver/utilities/path_utils.h" +#include "mmSolver/utilities/string_utils.h" + +// Command arguments and command name: +#define GPU_CAPACITY_FLAG "-gpc" +#define GPU_CAPACITY_FLAG_LONG "-gpuCapacity" +#define CPU_CAPACITY_FLAG "-cpc" +#define CPU_CAPACITY_FLAG_LONG "-cpuCapacity" + +#define GPU_USED_FLAG "-gpu" +#define GPU_USED_FLAG_LONG "-gpuUsed" +#define CPU_USED_FLAG "-cpu" +#define CPU_USED_FLAG_LONG "-cpuUsed" + +#define GPU_ITEM_COUNT_FLAG "-gic" +#define GPU_ITEM_COUNT_FLAG_LONG "-gpuItemCount" +#define CPU_ITEM_COUNT_FLAG "-cic" +#define CPU_ITEM_COUNT_FLAG_LONG "-cpuItemCount" + +#define GPU_ERASE_ITEMS_FLAG "-gei" +#define GPU_ERASE_ITEMS_FLAG_LONG "-gpuEraseItems" +#define CPU_ERASE_ITEMS_FLAG "-cei" +#define CPU_ERASE_ITEMS_FLAG_LONG "-cpuEraseItems" + +#define GPU_ERASE_GROUP_ITEMS_FLAG "-geg" +#define GPU_ERASE_GROUP_ITEMS_FLAG_LONG "-gpuEraseGroupItems" +#define CPU_ERASE_GROUP_ITEMS_FLAG "-ceg" +#define CPU_ERASE_GROUP_ITEMS_FLAG_LONG "-cpuEraseGroupItems" + +#define GPU_GROUP_COUNT_FLAG "-ggc" +#define GPU_GROUP_COUNT_FLAG_LONG "-gpuGroupCount" +#define CPU_GROUP_COUNT_FLAG "-cgc" +#define CPU_GROUP_COUNT_FLAG_LONG "-cpuGroupCount" + +#define GPU_GROUP_NAMES_FLAG "-ggn" +#define GPU_GROUP_NAMES_FLAG_LONG "-gpuGroupNames" +#define CPU_GROUP_NAMES_FLAG "-cgn" +#define CPU_GROUP_NAMES_FLAG_LONG "-cpuGroupNames" + +#define GPU_GROUP_ITEM_COUNT_FLAG "-ggt" +#define GPU_GROUP_ITEM_COUNT_FLAG_LONG "-gpuGroupItemCount" +#define CPU_GROUP_ITEM_COUNT_FLAG "-cgt" +#define CPU_GROUP_ITEM_COUNT_FLAG_LONG "-cpuGroupItemCount" + +#define GPU_GROUP_ITEM_NAMES_FLAG "-gin" +#define GPU_GROUP_ITEM_NAMES_FLAG_LONG "-gpuGroupItemNames" +#define CPU_GROUP_ITEM_NAMES_FLAG "-cin" +#define CPU_GROUP_ITEM_NAMES_FLAG_LONG "-cpuGroupItemNames" + +#define BRIEF_TEXT_FLAG "-btx" +#define BRIEF_TEXT_FLAG_LONG "-briefText" + +namespace mmsolver { + +MMImageCacheCmd::~MMImageCacheCmd() {} + +void *MMImageCacheCmd::creator() { return new MMImageCacheCmd(); } + +MString MMImageCacheCmd::cmdName() { return MString("mmImageCache"); } + +/* + * Tell Maya we have a syntax function. + */ +bool MMImageCacheCmd::hasSyntax() const { return true; } + +bool MMImageCacheCmd::isUndoable() const { return true; } + +/* + * Add flags to the command syntax + */ +MSyntax MMImageCacheCmd::newSyntax() { + MStatus status = MStatus::kSuccess; + + MSyntax syntax; + syntax.enableQuery(true); + syntax.enableEdit(true); + + syntax.setObjectType(MSyntax::kStringObjects); + + CHECK_MSTATUS(syntax.addFlag(GPU_CAPACITY_FLAG, GPU_CAPACITY_FLAG_LONG, + MSyntax::kString)); + CHECK_MSTATUS(syntax.addFlag(CPU_CAPACITY_FLAG, CPU_CAPACITY_FLAG_LONG, + MSyntax::kString)); + + CHECK_MSTATUS(syntax.addFlag(GPU_USED_FLAG, GPU_USED_FLAG_LONG)); + CHECK_MSTATUS(syntax.addFlag(CPU_USED_FLAG, CPU_USED_FLAG_LONG)); + + CHECK_MSTATUS( + syntax.addFlag(GPU_ITEM_COUNT_FLAG, GPU_ITEM_COUNT_FLAG_LONG)); + CHECK_MSTATUS( + syntax.addFlag(CPU_ITEM_COUNT_FLAG, CPU_ITEM_COUNT_FLAG_LONG)); + + CHECK_MSTATUS( + syntax.addFlag(GPU_ERASE_ITEMS_FLAG, GPU_ERASE_ITEMS_FLAG_LONG)); + CHECK_MSTATUS( + syntax.addFlag(CPU_ERASE_ITEMS_FLAG, CPU_ERASE_ITEMS_FLAG_LONG)); + + CHECK_MSTATUS(syntax.addFlag(GPU_ERASE_GROUP_ITEMS_FLAG, + GPU_ERASE_GROUP_ITEMS_FLAG_LONG)); + CHECK_MSTATUS(syntax.addFlag(CPU_ERASE_GROUP_ITEMS_FLAG, + CPU_ERASE_GROUP_ITEMS_FLAG_LONG)); + + CHECK_MSTATUS( + syntax.addFlag(GPU_GROUP_COUNT_FLAG, GPU_GROUP_COUNT_FLAG_LONG)); + CHECK_MSTATUS( + syntax.addFlag(CPU_GROUP_COUNT_FLAG, CPU_GROUP_COUNT_FLAG_LONG)); + + CHECK_MSTATUS( + syntax.addFlag(GPU_GROUP_NAMES_FLAG, GPU_GROUP_NAMES_FLAG_LONG)); + CHECK_MSTATUS( + syntax.addFlag(CPU_GROUP_NAMES_FLAG, CPU_GROUP_NAMES_FLAG_LONG)); + + CHECK_MSTATUS(syntax.addFlag(GPU_GROUP_ITEM_COUNT_FLAG, + GPU_GROUP_ITEM_COUNT_FLAG_LONG)); + CHECK_MSTATUS(syntax.addFlag(CPU_GROUP_ITEM_COUNT_FLAG, + CPU_GROUP_ITEM_COUNT_FLAG_LONG)); + + CHECK_MSTATUS(syntax.addFlag(GPU_GROUP_ITEM_NAMES_FLAG, + GPU_GROUP_ITEM_NAMES_FLAG_LONG)); + CHECK_MSTATUS(syntax.addFlag(CPU_GROUP_ITEM_NAMES_FLAG, + CPU_GROUP_ITEM_NAMES_FLAG_LONG)); + + CHECK_MSTATUS(syntax.addFlag(BRIEF_TEXT_FLAG, BRIEF_TEXT_FLAG_LONG)); + + return syntax; +} + +/* + * Parse command line arguments + */ +MStatus MMImageCacheCmd::parseArgs(const MArgList &args) { + MStatus status = MStatus::kSuccess; + const bool verbose = false; + + MArgDatabase argData(syntax(), args, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_is_query = argData.isQuery(); + m_is_edit = argData.isEdit(); + + MMSOLVER_MAYA_VRB( + "MMImageCacheCmd::parseArgs: " + "m_is_query=" + << m_is_query); + MMSOLVER_MAYA_VRB( + "MMImageCacheCmd::parseArgs: " + "m_is_edit=" + << m_is_edit); + + // Get string objects given to the command. + MStringArray string_objects; + argData.getObjects(string_objects); + + // TODO: Do we actually use the group name? Or can it be removed? + m_group_name = ""; + if (string_objects.length() > 0) { + const std::string item_name = string_objects[0].asChar(); + m_group_name = item_name; + } + MMSOLVER_MAYA_VRB( + "MMImageCacheCmd::parseArgs: " + "m_group_name=\"" + << m_group_name << "\""); + + for (auto i = 0; i < string_objects.length(); i++) { + const std::string item_name = string_objects[i].asChar(); + m_item_names.push_back(item_name); + MMSOLVER_MAYA_VRB( + "MMImageCacheCmd::parseArgs: " + "m_item_names[" + << i << "]=\"" << item_name << "\""); + } + + const bool has_gpu_capacity = argData.isFlagSet(GPU_CAPACITY_FLAG, &status); + const bool has_cpu_capacity = argData.isFlagSet(CPU_CAPACITY_FLAG, &status); + const bool has_gpu_used = argData.isFlagSet(GPU_USED_FLAG, &status); + const bool has_cpu_used = argData.isFlagSet(CPU_USED_FLAG, &status); + + const bool has_gpu_item_count = + argData.isFlagSet(GPU_ITEM_COUNT_FLAG, &status); + const bool has_cpu_item_count = + argData.isFlagSet(CPU_ITEM_COUNT_FLAG, &status); + + const bool has_gpu_erase_items = + argData.isFlagSet(GPU_ERASE_ITEMS_FLAG, &status); + const bool has_cpu_erase_items = + argData.isFlagSet(CPU_ERASE_ITEMS_FLAG, &status); + + const bool has_gpu_erase_group_items = + argData.isFlagSet(GPU_ERASE_GROUP_ITEMS_FLAG, &status); + const bool has_cpu_erase_group_items = + argData.isFlagSet(CPU_ERASE_GROUP_ITEMS_FLAG, &status); + + const bool has_gpu_group_count = + argData.isFlagSet(GPU_GROUP_COUNT_FLAG, &status); + const bool has_cpu_group_count = + argData.isFlagSet(CPU_GROUP_COUNT_FLAG, &status); + const bool has_gpu_group_names = + argData.isFlagSet(GPU_GROUP_NAMES_FLAG, &status); + const bool has_cpu_group_names = + argData.isFlagSet(CPU_GROUP_NAMES_FLAG, &status); + + const bool has_gpu_group_item_count = + argData.isFlagSet(GPU_GROUP_ITEM_COUNT_FLAG, &status); + const bool has_cpu_group_item_count = + argData.isFlagSet(CPU_GROUP_ITEM_COUNT_FLAG, &status); + const bool has_gpu_group_item_names = + argData.isFlagSet(GPU_GROUP_ITEM_NAMES_FLAG, &status); + const bool has_cpu_group_item_names = + argData.isFlagSet(CPU_GROUP_ITEM_NAMES_FLAG, &status); + + const bool has_print_brief = argData.isFlagSet(BRIEF_TEXT_FLAG, &status); + + if (m_is_query) { + if (has_gpu_capacity) { + m_command_flag = ImageCacheFlagMode::kGpuCapacity; + m_output_type = ImageCacheOutputType::kSize; + } else if (has_cpu_capacity) { + m_command_flag = ImageCacheFlagMode::kCpuCapacity; + m_output_type = ImageCacheOutputType::kSize; + } else if (has_gpu_used) { + m_command_flag = ImageCacheFlagMode::kGpuUsed; + m_output_type = ImageCacheOutputType::kSize; + } else if (has_cpu_used) { + m_command_flag = ImageCacheFlagMode::kCpuUsed; + m_output_type = ImageCacheOutputType::kSize; + } else if (has_gpu_item_count) { + m_command_flag = ImageCacheFlagMode::kGpuItemCount; + m_output_type = ImageCacheOutputType::kSize; + } else if (has_cpu_item_count) { + m_command_flag = ImageCacheFlagMode::kCpuItemCount; + m_output_type = ImageCacheOutputType::kSize; + } else if (has_gpu_group_count) { + m_command_flag = ImageCacheFlagMode::kGpuGroupCount; + m_output_type = ImageCacheOutputType::kSize; + } else if (has_cpu_group_count) { + m_command_flag = ImageCacheFlagMode::kCpuGroupCount; + m_output_type = ImageCacheOutputType::kSize; + } else if (has_gpu_group_names) { + m_command_flag = ImageCacheFlagMode::kGpuGroupNames; + m_output_type = ImageCacheOutputType::kStringArray; + } else if (has_cpu_group_names) { + m_command_flag = ImageCacheFlagMode::kCpuGroupNames; + m_output_type = ImageCacheOutputType::kStringArray; + } else if (has_gpu_group_item_count || has_cpu_group_item_count || + has_gpu_group_item_names || has_cpu_group_item_names) { + if (string_objects.length() != 1) { + status = MStatus::kFailure; + status.perror( + "mmImageCache: " + "One group name must be given to command!"); + return status; + } + + if (has_gpu_group_item_count) { + m_command_flag = ImageCacheFlagMode::kGpuGroupItemCount; + m_output_type = ImageCacheOutputType::kSize; + } else if (has_cpu_group_item_count) { + m_command_flag = ImageCacheFlagMode::kCpuGroupItemCount; + m_output_type = ImageCacheOutputType::kSize; + } else if (has_gpu_group_item_names) { + m_command_flag = ImageCacheFlagMode::kGpuGroupItemNames; + m_output_type = ImageCacheOutputType::kStringArray; + } else if (has_cpu_group_item_names) { + m_command_flag = ImageCacheFlagMode::kCpuGroupItemNames; + m_output_type = ImageCacheOutputType::kStringArray; + } + } else if (has_print_brief) { + m_command_flag = ImageCacheFlagMode::kGenerateBriefText; + m_output_type = ImageCacheOutputType::kString; + } else { + MMSOLVER_MAYA_ERR( + "MMImageCacheCmd::parseArgs: " + "Invalid command query flag! " + "m_command_flag=" + << static_cast(m_command_flag)); + return MStatus::kFailure; + } + } else if (m_is_edit) { + image::ImageCache &image_cache = image::ImageCache::getInstance(); + + if (has_gpu_capacity) { + m_command_flag = ImageCacheFlagMode::kGpuCapacity; + MString mstring; + status = argData.getFlagArgument(GPU_CAPACITY_FLAG, 0, mstring); + CHECK_MSTATUS_AND_RETURN_IT(status); + m_gpu_capacity_bytes = + mmmayastring::mstringToNumber(mstring); + + // Store the current value, so we can undo later. + m_previous_gpu_capacity_bytes = + image_cache.get_gpu_capacity_bytes(); + } else if (has_cpu_capacity) { + m_command_flag = ImageCacheFlagMode::kCpuCapacity; + MString mstring; + status = argData.getFlagArgument(CPU_CAPACITY_FLAG, 0, mstring); + CHECK_MSTATUS_AND_RETURN_IT(status); + m_cpu_capacity_bytes = + mmmayastring::mstringToNumber(mstring); + + // Store the current value, so we can undo later. + m_previous_cpu_capacity_bytes = + image_cache.get_cpu_capacity_bytes(); + } else if (has_gpu_erase_items) { + m_command_flag = ImageCacheFlagMode::kGpuEraseItems; + m_output_type = ImageCacheOutputType::kSize; + } else if (has_cpu_erase_items) { + m_command_flag = ImageCacheFlagMode::kCpuEraseItems; + m_output_type = ImageCacheOutputType::kSize; + } else if (has_gpu_erase_group_items) { + m_command_flag = ImageCacheFlagMode::kGpuEraseGroupItems; + m_output_type = ImageCacheOutputType::kSize; + } else if (has_cpu_erase_group_items) { + m_command_flag = ImageCacheFlagMode::kCpuEraseGroupItems; + m_output_type = ImageCacheOutputType::kSize; + } else { + MMSOLVER_MAYA_ERR( + "MMImageCacheCmd::parseArgs: " + "Invalid command flag! " + "m_command_flag=" + << static_cast(m_command_flag)); + return MStatus::kFailure; + } + } else { + MMSOLVER_MAYA_ERR( + "MMImageCacheCmd::parseArgs: " + "Command is not in query or edit mode! " + "Please use query or edit mode."); + return MStatus::kFailure; + } + + MMSOLVER_MAYA_VRB( + "MMImageCacheCmd::parseArgs: " + "Command flag=" + << static_cast(m_command_flag)); + + return status; +} + +inline MStatus get_texture_manager( + MHWRender::MTextureManager *&texture_manager) { + MHWRender::MRenderer *renderer = MHWRender::MRenderer::theRenderer(); + if (!renderer) { + MMSOLVER_MAYA_ERR( + "MMImageCacheCmd::get_texture_manager: " + "Could not get MRenderer!"); + return MStatus::kFailure; + } + + texture_manager = renderer->getTextureManager(); + if (!texture_manager) { + MMSOLVER_MAYA_ERR( + "MMImageCacheCmd::get_texture_manager: " + "Could not get MTextureManager!"); + return MStatus::kFailure; + } + + return MStatus::kSuccess; +} + +inline MStatus set_values(image::ImageCache &image_cache, + const ImageCacheFlagMode command_flag, + const size_t gpu_capacity_bytes, + const size_t cpu_capacity_bytes, + const std::vector &item_names, + const std::string &group_name, + size_t &out_item_count) { + const bool verbose = false; + MStatus status = MStatus::kSuccess; + + const auto command_flag_int = static_cast(command_flag); + if (verbose) { + std::stringstream item_names_ss; + for (auto it = item_names.begin(); it != item_names.end(); it++) { + auto item_name = *it; + item_names_ss << item_name << ";"; + } + MMSOLVER_MAYA_VRB("MMImageCacheCmd::set_values: " + << "command_flag=" << command_flag_int + << " gpu_capacity_bytes=" << gpu_capacity_bytes + << " cpu_capacity_bytes=" << cpu_capacity_bytes + << " item_names=" << item_names_ss.str() + << " item_names.size()=" << item_names.size() + << " group_name=" << group_name.c_str() + << " group_name.size()=" << group_name.size()); + } + + out_item_count = 0; + if (command_flag == ImageCacheFlagMode::kGpuCapacity) { + MMSOLVER_MAYA_VRB("MMImageCacheCmd::set_values: " + << "flag=\"" << GPU_CAPACITY_FLAG_LONG << "\""); + + MHWRender::MTextureManager *texture_manager = nullptr; + status = get_texture_manager(texture_manager); + CHECK_MSTATUS_AND_RETURN_IT(status); + + image_cache.set_gpu_capacity_bytes(texture_manager, gpu_capacity_bytes); + } else if (command_flag == ImageCacheFlagMode::kCpuCapacity) { + MMSOLVER_MAYA_VRB("MMImageCacheCmd::set_values: " + << "flag=\"" << CPU_CAPACITY_FLAG_LONG << "\""); + + image_cache.set_cpu_capacity_bytes(cpu_capacity_bytes); + } else if (command_flag == ImageCacheFlagMode::kGpuEraseItems) { + MMSOLVER_MAYA_VRB("MMImageCacheCmd::set_values: " + << "flag=\"" << GPU_ERASE_ITEMS_FLAG_LONG << "\""); + + MHWRender::MTextureManager *texture_manager = nullptr; + status = get_texture_manager(texture_manager); + CHECK_MSTATUS_AND_RETURN_IT(status); + + for (auto it = item_names.begin(); it != item_names.end(); it++) { + auto item_name = *it; + MMSOLVER_MAYA_VRB("MMImageCacheCmd::set_values: " + << "item_name=\"" << item_name.c_str() << "\""); + const bool ok = + image_cache.gpu_erase_item(texture_manager, item_name); + out_item_count += static_cast(ok); + } + } else if (command_flag == ImageCacheFlagMode::kCpuEraseItems) { + MMSOLVER_MAYA_VRB("MMImageCacheCmd::set_values: " + << "flag=\"" << CPU_ERASE_ITEMS_FLAG_LONG << "\""); + + for (auto it = item_names.begin(); it != item_names.end(); it++) { + auto item_name = *it; + MMSOLVER_MAYA_VRB("MMImageCacheCmd::set_values: " + << "item_name=\"" << item_name.c_str() << "\""); + const bool ok = image_cache.cpu_erase_item(item_name); + out_item_count += static_cast(ok); + } + } else if (command_flag == ImageCacheFlagMode::kGpuEraseGroupItems) { + MMSOLVER_MAYA_VRB("MMImageCacheCmd::set_values: " + << "flag=\"" << CPU_ERASE_GROUP_ITEMS_FLAG_LONG + << "\""); + + if (group_name.size() == 0) { + MMSOLVER_MAYA_ERR("MMImageCacheCmd::set_values: " + << "\"" << GPU_ERASE_GROUP_ITEMS_FLAG_LONG + << "\" " + << "flag needs a group name, but none given! " + "group_name.size()=" + << static_cast(group_name.size())); + return MStatus::kFailure; + } + + MHWRender::MTextureManager *texture_manager = nullptr; + status = get_texture_manager(texture_manager); + CHECK_MSTATUS_AND_RETURN_IT(status); + + for (auto it = item_names.begin(); it != item_names.end(); it++) { + auto item_name = *it; + MMSOLVER_MAYA_VRB("MMImageCacheCmd::set_values: " + << "item_name=\"" << item_name.c_str() << "\""); + out_item_count += + image_cache.gpu_erase_group_items(texture_manager, item_name); + } + } else if (command_flag == ImageCacheFlagMode::kCpuEraseGroupItems) { + MMSOLVER_MAYA_VRB("MMImageCacheCmd::set_values: " + << "flag=\"" << CPU_ERASE_GROUP_ITEMS_FLAG_LONG + << "\""); + + if (group_name.size() == 0) { + MMSOLVER_MAYA_ERR("MMImageCacheCmd::set_values: " + << "\"" << CPU_ERASE_GROUP_ITEMS_FLAG_LONG + << "\" " + << "flag needs a group name, but none given! " + "group_name.size()=" + << static_cast(group_name.size())); + return MStatus::kFailure; + } + + MMSOLVER_MAYA_VRB("MMImageCacheCmd::set_values: " + << "group_name=\"" << group_name.c_str() << "\""); + + for (auto it = item_names.begin(); it != item_names.end(); it++) { + auto item_name = *it; + MMSOLVER_MAYA_VRB("MMImageCacheCmd::set_values: " + << "item_name=\"" << item_name.c_str() << "\""); + out_item_count += image_cache.cpu_erase_group_items(item_name); + } + } else { + MMSOLVER_MAYA_ERR( + "MMImageCacheCmd::set_values: " + "Invalid command flag! " + "command_flag=" + << static_cast(command_flag)); + return MStatus::kFailure; + } + + MMSOLVER_MAYA_VRB( + "MMImageCacheCmd::set_values: out_item_count=" << out_item_count); + + return status; +} + +MStatus get_value_size(image::ImageCache &image_cache, + const ImageCacheFlagMode command_flag, + const std::string &group_name, size_t &out_value) { + out_value = 0; + + if (command_flag == ImageCacheFlagMode::kGpuCapacity) { + out_value = image_cache.get_gpu_capacity_bytes(); + } else if (command_flag == ImageCacheFlagMode::kCpuCapacity) { + out_value = image_cache.get_cpu_capacity_bytes(); + } else if (command_flag == ImageCacheFlagMode::kGpuUsed) { + out_value = image_cache.get_gpu_used_bytes(); + } else if (command_flag == ImageCacheFlagMode::kCpuUsed) { + out_value = image_cache.get_cpu_used_bytes(); + } else if (command_flag == ImageCacheFlagMode::kGpuItemCount) { + out_value = image_cache.get_gpu_item_count(); + } else if (command_flag == ImageCacheFlagMode::kCpuItemCount) { + out_value = image_cache.get_cpu_item_count(); + } else if (command_flag == ImageCacheFlagMode::kGpuGroupCount) { + out_value = image_cache.get_gpu_group_count(); + } else if (command_flag == ImageCacheFlagMode::kCpuGroupCount) { + out_value = image_cache.get_cpu_group_count(); + } else if (command_flag == ImageCacheFlagMode::kGpuGroupItemCount) { + out_value = image_cache.gpu_group_item_count(group_name); + } else if (command_flag == ImageCacheFlagMode::kCpuGroupItemCount) { + out_value = image_cache.cpu_group_item_count(group_name); + } else { + MMSOLVER_MAYA_ERR( + "MMImageCacheCmd::get_value_size: " + "Invalid command flag! " + "command_flag=" + << static_cast(command_flag)); + return MStatus::kFailure; + } + + return MStatus::kSuccess; +} + +MStatus get_value_string(image::ImageCache &image_cache, + const ImageCacheFlagMode command_flag, + MString &out_result) { + if (command_flag == ImageCacheFlagMode::kGenerateBriefText) { + out_result = image_cache.generate_cache_brief_text(); + } else { + MMSOLVER_MAYA_ERR( + "MMImageCacheCmd::get_value_string: " + "Invalid command flag! " + "command_flag=" + << static_cast(command_flag)); + return MStatus::kFailure; + } + + return MStatus::kSuccess; +} + +MStatus get_value_string_array(image::ImageCache &image_cache, + const ImageCacheFlagMode command_flag, + const std::string &group_name, + MStringArray &out_results) { + bool ok = true; + std::vector outputs; + + if (command_flag == ImageCacheFlagMode::kGpuGroupNames) { + image_cache.gpu_group_names(outputs); + } else if (command_flag == ImageCacheFlagMode::kCpuGroupNames) { + image_cache.cpu_group_names(outputs); + } else if (command_flag == ImageCacheFlagMode::kGpuGroupItemNames) { + if (group_name.size() == 0) { + MMSOLVER_MAYA_ERR("MMImageCacheCmd::get_value_string_array: " + << "\"" << GPU_GROUP_ITEM_NAMES_FLAG_LONG << "\" " + << "flag needs a group name, but none given! " + "group_name.size()=" + << static_cast(group_name.size())); + return MStatus::kFailure; + } + + ok = image_cache.gpu_group_item_names(group_name, outputs); + } else if (command_flag == ImageCacheFlagMode::kCpuGroupItemNames) { + if (group_name.size() == 0) { + MMSOLVER_MAYA_ERR("MMImageCacheCmd::get_value_string_array: " + << "\"" << CPU_GROUP_ITEM_NAMES_FLAG_LONG << "\" " + << "flag needs a group name, but none given! " + "group_name.size()=" + << static_cast(group_name.size())); + return MStatus::kFailure; + } + + ok = image_cache.cpu_group_item_names(group_name, outputs); + } else { + MMSOLVER_MAYA_ERR( + "MMImageCacheCmd::get_value_string_array: " + "Invalid command flag! " + "command_flag=" + << static_cast(command_flag)); + return MStatus::kFailure; + } + + if (!ok) { + MMSOLVER_MAYA_ERR( + "MMImageCacheCmd::get_value_string_array: " + "failed to apply action! ok=" + << static_cast(ok)); + return MStatus::kFailure; + } + + out_results.clear(); + for (auto i = 0; i < outputs.size(); i++) { + const auto value = outputs[i]; + out_results.append(MString(value.c_str())); + } + + return MStatus::kSuccess; +} + +MStatus MMImageCacheCmd::doIt(const MArgList &args) { + MStatus status = MStatus::kSuccess; + const bool verbose = false; + + // Read all the flag arguments. + status = parseArgs(args); + CHECK_MSTATUS_AND_RETURN_IT(status); + + if (m_is_query) { + image::ImageCache &image_cache = image::ImageCache::getInstance(); + + if (m_output_type == ImageCacheOutputType::kString) { + MString result; + status = get_value_string(image_cache, m_command_flag, result); + CHECK_MSTATUS_AND_RETURN_IT(status); + MMImageCacheCmd::setResult(result); + } else if (m_output_type == ImageCacheOutputType::kSize) { + size_t result = 0; + status = get_value_size(image_cache, m_command_flag, m_group_name, + result); + CHECK_MSTATUS_AND_RETURN_IT(status); + + MString result_string(mmmayastring::numberToMString(result)); + MMImageCacheCmd::setResult(result_string); + } else if (m_output_type == ImageCacheOutputType::kStringArray) { + MStringArray results; + status = get_value_string_array(image_cache, m_command_flag, + m_group_name, results); + CHECK_MSTATUS_AND_RETURN_IT(status); + MMImageCacheCmd::setResult(results); + } else { + MMSOLVER_MAYA_ERR( + "MMImageCacheCmd::doIt: " + "Invalid command query flag! " + "value=" + << static_cast(m_command_flag)); + return MStatus::kFailure; + } + + } else if (m_is_edit) { + image::ImageCache &image_cache = image::ImageCache::getInstance(); + + size_t item_count = 0; + status = set_values(image_cache, m_command_flag, m_gpu_capacity_bytes, + m_cpu_capacity_bytes, m_item_names, m_group_name, + item_count); + CHECK_MSTATUS_AND_RETURN_IT(status); + + if (m_output_type == ImageCacheOutputType::kSize) { + MString result_string(mmmayastring::numberToMString(item_count)); + MMImageCacheCmd::setResult(result_string); + } + } else { + MMSOLVER_MAYA_ERR( + "MMImageCacheCmd::doIt: " + "Command is not in query or edit mode! " + "Please use query or edit mode."); + return MStatus::kFailure; + } + + return status; +} + +MStatus MMImageCacheCmd::redoIt() { + MStatus status = MStatus::kSuccess; + if (m_is_edit) { + image::ImageCache &image_cache = image::ImageCache::getInstance(); + + size_t item_count = 0; + status = set_values(image_cache, m_command_flag, m_gpu_capacity_bytes, + m_cpu_capacity_bytes, m_item_names, m_group_name, + item_count); + CHECK_MSTATUS_AND_RETURN_IT(status); + + if (m_output_type == ImageCacheOutputType::kSize) { + MString result_string(mmmayastring::numberToMString(item_count)); + MMImageCacheCmd::setResult(result_string); + } + } + return status; +} + +MStatus MMImageCacheCmd::undoIt() { + MStatus status = MStatus::kSuccess; + if (m_is_edit) { + if ((m_command_flag == ImageCacheFlagMode::kGpuEraseItems) || + (m_command_flag == ImageCacheFlagMode::kCpuEraseItems) || + (m_command_flag == ImageCacheFlagMode::kGpuEraseGroupItems) || + (m_command_flag == ImageCacheFlagMode::kCpuEraseGroupItems)) { + status = MStatus::kFailure; + MMSOLVER_MAYA_ERR( + "MMImageCacheCmd::undoIt: " + "Invalid command edit flag! " + "value=" + << static_cast(m_command_flag)); + } else { + image::ImageCache &image_cache = image::ImageCache::getInstance(); + + size_t item_count = 0; + status = set_values(image_cache, m_command_flag, + m_previous_gpu_capacity_bytes, + m_previous_cpu_capacity_bytes, m_item_names, + m_group_name, item_count); + CHECK_MSTATUS_AND_RETURN_IT(status); + } + } + return status; +} + +} // namespace mmsolver diff --git a/src/mmSolver/cmd/MMImageCacheCmd.h b/src/mmSolver/cmd/MMImageCacheCmd.h new file mode 100644 index 000000000..5c1274044 --- /dev/null +++ b/src/mmSolver/cmd/MMImageCacheCmd.h @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + * 'mmImageCache' Command for querying and manipulating the MM Solver + * ImageCache. + * + * The mmImageCache command is responsible for querying and setting + * details of the underlying image cache. + * + * + * Use cases: + * + * - Get total amount of used CPU memory, displayed in a UI. + * + * - Get total amount of CPU memory on the system. + * + * - Get amount of CPU memory used by the current process. + * + * - Get total GPU memory. + * + * - Get used GPU memory. + * + * - Display on an image plane node... + * - How much memory the currently loaded image sequence is expected + * to take up. + * - How much memory each frame takes up. + * - What is the currently used CPU and GPU memory by this image + * sequence/image plane. + * + * - UI to display overall memory usage, with... + * - Each loaded image sequence. + * - How much memory is used by each image sequence (CPU and GPU). + * - The total CPU and GPU memory on the system. + * - The used CPU and GPU memory on the system. + * - A widget to set the Image Cache memory available as a percentage + * or fixed memory amount. + * + * + * MEL: + * // Return the number of used GPU memory bytes by the image cache. + * mmImageCache -query -gpuUsed; + * + * // Return the number of free GPU memory bytes by the image cache. + * mmImageCache -query -gpuFree; + * + * // Return the total number of GPU memory bytes that is allowed + * // to be to be used by the image cache. + * mmImageCache -query -gpuCapacity; + * + * // Set the number of GPU memory bytes that is allowed to be used. + * mmImageCache -edit -gpuCapacity 1000; + * + * // Get the amount of data that the image contains. + * // + * // This will not actually read the file into memory, just enough + * // to find the data type and dimensions. + * string $image_sequence = "/path/to/image.####.png"; + * string $image_file = "/path/to/image.1001.png"; + * string $data_header[] = `mmReadImage -dataHeader $image_file`; + * string $data_size = $data_header[4]; + * + * // Set the number of CPU memory bytes that is allowed to be used. + * int $start_frame = 1001; + * int $end_frame = 1101; + * int $frame_count = $end_frame - $start_frame; + * int $extra_buffer = 3; // allow a few more images just in case. + * // In reality we should check if the system has enough free + * // CPU memory, before attempting to load the full image sequence + * // or not. + * int $new_capacity = ($data_size * ($frame_count + $extra_buffer)) + * mmImageCache -edit -cpuCapacity $new_capacity; + * + * // Read the image sequence into RAM. + * // + * // This call will block until all the images are read and loaded. + * // Progress will be printed to std::cout. + * mmImageCache -cpuLoadImageSequence + * -startFrame $start_frame + * -endFrame $end_frame + * $image_sequence; + * + * // Loads the images (already in the CPU cache - see above), and + * // writes them out to the Disk cache. + * mmImageCache -writeDiskCache + * -startFrame $start_frame + * -endFrame $end_frame + * $image_sequence; + * + */ + +#ifndef MAYA_MM_IMAGE_CACHE_CMD_H +#define MAYA_MM_IMAGE_CACHE_CMD_H + +// STL +#include +#include + +// Maya +#include +#include +#include +#include +#include +#include +#include + +namespace mmsolver { + +enum class ImageCacheFlagMode : uint8_t { + kGpuCapacity = 0, + kCpuCapacity, + kGpuUsed, + kCpuUsed, + kGpuItemCount, + kCpuItemCount, + kGpuEraseGroupItems, + kCpuEraseGroupItems, + kGpuEraseItems, + kCpuEraseItems, + kGpuGroupCount, + kCpuGroupCount, + kGpuGroupNames, + kCpuGroupNames, + kGpuGroupItemCount, + kCpuGroupItemCount, + kGpuGroupItemNames, + kCpuGroupItemNames, + kGenerateBriefText, + kUnknown = 255 +}; + +enum class ImageCacheOutputType : uint8_t { + kSize = 0, + kString, + kStringArray, + kUnknown = 255 +}; + +class MMImageCacheCmd : public MPxCommand { +public: + MMImageCacheCmd() + : m_is_query(false) + , m_is_edit(false) + , m_command_flag(ImageCacheFlagMode::kUnknown) + , m_output_type(ImageCacheOutputType::kUnknown) + , m_previous_gpu_capacity_bytes(0) + , m_previous_cpu_capacity_bytes(0) + , m_gpu_capacity_bytes(0) + , m_cpu_capacity_bytes(0) + , m_item_names() + , m_group_name(){}; + + virtual ~MMImageCacheCmd(); + + virtual bool hasSyntax() const; + static MSyntax newSyntax(); + + virtual MStatus doIt(const MArgList &args); + virtual bool isUndoable() const; + virtual MStatus undoIt(); + virtual MStatus redoIt(); + + static void *creator(); + + static MString cmdName(); + +private: + MStatus parseArgs(const MArgList &args); + + bool m_is_edit; + bool m_is_query; + + ImageCacheFlagMode m_command_flag; + ImageCacheOutputType m_output_type; + + // The previous values, before the command was run. + size_t m_previous_gpu_capacity_bytes; + size_t m_previous_cpu_capacity_bytes; + + size_t m_gpu_capacity_bytes; + size_t m_cpu_capacity_bytes; + + std::vector m_item_names; + std::string m_group_name; +}; + +} // namespace mmsolver + +#endif // MAYA_MM_IMAGE_CACHE_CMD_H diff --git a/src/mmSolver/cmd/MMMemoryGPUCmd.cpp b/src/mmSolver/cmd/MMMemoryGPUCmd.cpp new file mode 100644 index 000000000..efd56fd49 --- /dev/null +++ b/src/mmSolver/cmd/MMMemoryGPUCmd.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#include "MMMemoryGPUCmd.h" + +// STD +#include +#include +#include + +// Maya +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// MM Solver +#include + +#include "mmSolver/mayahelper/maya_string_utils.h" +#include "mmSolver/utilities/debug_utils.h" +#include "mmSolver/utilities/memory_gpu_utils.h" +#include "mmSolver/utilities/path_utils.h" +#include "mmSolver/utilities/string_utils.h" + +// Command arguments and command name: +#define MEMORY_TOTAL_FLAG "-tot" +#define MEMORY_TOTAL_FLAG_LONG "-total" + +#define MEMORY_FREE_FLAG "-fre" +#define MEMORY_FREE_FLAG_LONG "-free" + +#define MEMORY_USED_FLAG "-usd" +#define MEMORY_USED_FLAG_LONG "-used" + +#define MEMORY_AS_KILOBYTES_FLAG "-kb" +#define MEMORY_AS_KILOBYTES_FLAG_LONG "-asKiloBytes" + +#define MEMORY_AS_MEGABYTES_FLAG "-mb" +#define MEMORY_AS_MEGABYTES_FLAG_LONG "-asMegaBytes" + +#define MEMORY_AS_GIGABYTES_FLAG "-gb" +#define MEMORY_AS_GIGABYTES_FLAG_LONG "-asGigaBytes" + +namespace mmsolver { + +MMMemoryGPUCmd::~MMMemoryGPUCmd() {} + +void *MMMemoryGPUCmd::creator() { return new MMMemoryGPUCmd(); } + +MString MMMemoryGPUCmd::cmdName() { return MString("mmMemoryGPU"); } + +/* + * Tell Maya we have a syntax function. + */ +bool MMMemoryGPUCmd::hasSyntax() const { return true; } + +bool MMMemoryGPUCmd::isUndoable() const { return false; } + +/* + * Add flags to the command syntax + */ +MSyntax MMMemoryGPUCmd::newSyntax() { + MSyntax syntax; + syntax.enableQuery(true); + syntax.enableEdit(false); + + syntax.addFlag(MEMORY_TOTAL_FLAG, MEMORY_TOTAL_FLAG_LONG); + syntax.addFlag(MEMORY_FREE_FLAG, MEMORY_FREE_FLAG_LONG); + syntax.addFlag(MEMORY_USED_FLAG, MEMORY_USED_FLAG_LONG); + + syntax.addFlag(MEMORY_AS_KILOBYTES_FLAG, MEMORY_AS_KILOBYTES_FLAG_LONG); + syntax.addFlag(MEMORY_AS_MEGABYTES_FLAG, MEMORY_AS_MEGABYTES_FLAG_LONG); + syntax.addFlag(MEMORY_AS_GIGABYTES_FLAG, MEMORY_AS_GIGABYTES_FLAG_LONG); + + return syntax; +} + +/* + * Parse command line arguments + */ +MStatus MMMemoryGPUCmd::parseArgs(const MArgList &args) { + MStatus status = MStatus::kSuccess; + + MArgDatabase argData(syntax(), args, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + if (!argData.isQuery()) { + status = MStatus::kFailure; + MMSOLVER_MAYA_ERR( + "mmsolver::MMMemoryGPUCmd::parseArgs: " + "Command must be in query mode!"); + return status; + } + + m_memory_total = argData.isFlagSet(MEMORY_TOTAL_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_memory_free = argData.isFlagSet(MEMORY_FREE_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_memory_used = argData.isFlagSet(MEMORY_USED_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + const bool as_kilobytes = + argData.isFlagSet(MEMORY_AS_KILOBYTES_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + const bool as_megabytes = + argData.isFlagSet(MEMORY_AS_MEGABYTES_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + const bool as_gigabytes = + argData.isFlagSet(MEMORY_AS_GIGABYTES_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_memory_unit = mmmemory::MemoryUnit::kBytes; + if (as_kilobytes) { + m_memory_unit = mmmemory::MemoryUnit::kKiloBytes; + } else if (as_megabytes) { + m_memory_unit = mmmemory::MemoryUnit::kMegaBytes; + } else if (as_gigabytes) { + m_memory_unit = mmmemory::MemoryUnit::kGigaBytes; + } + + return status; +} + +MStatus MMMemoryGPUCmd::doIt(const MArgList &args) { + MStatus status = MStatus::kSuccess; + const bool verbose = false; + + // Read all the flag arguments. + status = parseArgs(args); + CHECK_MSTATUS_AND_RETURN_IT(status); + + size_t bytes_value = 0; + if (m_memory_total) { + status = mmmemorygpu::memory_total_size_in_bytes(bytes_value); + CHECK_MSTATUS_AND_RETURN_IT(status); + } else if (m_memory_free) { + status = mmmemorygpu::memory_free_size_in_bytes(bytes_value); + CHECK_MSTATUS_AND_RETURN_IT(status); + } else if (m_memory_used) { + status = mmmemorygpu::memory_used_size_in_bytes(bytes_value); + CHECK_MSTATUS_AND_RETURN_IT(status); + } else { + MMSOLVER_MAYA_ERR( + "mmsolver::MMMemoryGPUCmd::doIt: " + "Give a flag to the command!"); + return MStatus::kFailure; + } + + if (m_memory_unit == mmmemory::MemoryUnit::kBytes) { + // It is only possible to return a maximum of "unsigned int" + // (unsigned 32-bit number) from a Maya command, but our + // maximum may exceed that size, so we must return the full + // number converted to a string, then have the Python (or + // MEL?) code convert that to an integer. + MString number_mstring = mmmayastring::numberToMString(bytes_value); + MMMemoryGPUCmd::setResult(number_mstring); + } else { + double outResult = + mmmemory::bytes_as_double(bytes_value, m_memory_unit); + MMMemoryGPUCmd::setResult(outResult); + } + + return status; +} + +} // namespace mmsolver diff --git a/src/mmSolver/cmd/MMMemoryGPUCmd.h b/src/mmSolver/cmd/MMMemoryGPUCmd.h new file mode 100644 index 000000000..a4a34f208 --- /dev/null +++ b/src/mmSolver/cmd/MMMemoryGPUCmd.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + * 'mmMemoryGPU' Command is responsible for querying details about + * the operating systems's memory. + * + * MEL: + * // Return the number of used memory by the image cache. + * mmMemoryGPU -query -total -asMegaBytes; + * mmMemoryGPU -query -free -asMegaBytes; + * mmMemoryGPU -query -used -asMegaBytes; + * + */ + +#ifndef MAYA_MM_MEMORY_GPU_CMD_H +#define MAYA_MM_MEMORY_GPU_CMD_H + +// Maya +#include +#include +#include +#include +#include +#include +#include +#include + +// MM Solver +#include "mmSolver/utilities/memory_utils.h" + +namespace mmsolver { + +class MMMemoryGPUCmd : public MPxCommand { +public: + MMMemoryGPUCmd() + : m_memory_total(false) + , m_memory_free(false) + , m_memory_used(false) + , m_memory_unit(mmmemory::MemoryUnit::kBytes){}; + + virtual ~MMMemoryGPUCmd(); + + virtual bool hasSyntax() const; + static MSyntax newSyntax(); + + virtual MStatus doIt(const MArgList &args); + + virtual bool isUndoable() const; + + static void *creator(); + + static MString cmdName(); + +private: + MStatus parseArgs(const MArgList &args); + + bool m_memory_total; + bool m_memory_free; + bool m_memory_used; + mmmemory::MemoryUnit m_memory_unit; +}; + +} // namespace mmsolver + +#endif // MAYA_MM_MEMORY_GPU_CMD_H diff --git a/src/mmSolver/cmd/MMMemorySystemCmd.cpp b/src/mmSolver/cmd/MMMemorySystemCmd.cpp new file mode 100644 index 000000000..05ddcf616 --- /dev/null +++ b/src/mmSolver/cmd/MMMemorySystemCmd.cpp @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#include "MMMemorySystemCmd.h" + +// STD +#include +#include +#include + +// Maya +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// MM Solver +#include + +#include "mmSolver/mayahelper/maya_string_utils.h" +#include "mmSolver/utilities/debug_utils.h" +#include "mmSolver/utilities/memory_system_utils.h" +#include "mmSolver/utilities/memory_utils.h" +#include "mmSolver/utilities/path_utils.h" +#include "mmSolver/utilities/string_utils.h" + +// Command arguments and command name: +#define SYSTEM_PHYSICAL_MEMORY_TOTAL_FLAG "-tot" +#define SYSTEM_PHYSICAL_MEMORY_TOTAL_FLAG_LONG "-systemPhysicalMemoryTotal" + +#define SYSTEM_PHYSICAL_MEMORY_FREE_FLAG "-fre" +#define SYSTEM_PHYSICAL_MEMORY_FREE_FLAG_LONG "-systemPhysicalMemoryFree" + +#define SYSTEM_PHYSICAL_MEMORY_USED_FLAG "-usd" +#define SYSTEM_PHYSICAL_MEMORY_USED_FLAG_LONG "-systemPhysicalMemoryUsed" + +#define PROCESS_MEMORY_USED_FLAG "-pud" +#define PROCESS_MEMORY_USED_FLAG_LONG "-processMemoryUsed" + +#define MEMORY_AS_KILOBYTES_FLAG "-kb" +#define MEMORY_AS_KILOBYTES_FLAG_LONG "-asKiloBytes" + +#define MEMORY_AS_MEGABYTES_FLAG "-mb" +#define MEMORY_AS_MEGABYTES_FLAG_LONG "-asMegaBytes" + +#define MEMORY_AS_GIGABYTES_FLAG "-gb" +#define MEMORY_AS_GIGABYTES_FLAG_LONG "-asGigaBytes" + +namespace mmsolver { + +MMMemorySystemCmd::~MMMemorySystemCmd() {} + +void *MMMemorySystemCmd::creator() { return new MMMemorySystemCmd(); } + +MString MMMemorySystemCmd::cmdName() { return MString("mmMemorySystem"); } + +/* + * Tell Maya we have a syntax function. + */ +bool MMMemorySystemCmd::hasSyntax() const { return true; } + +bool MMMemorySystemCmd::isUndoable() const { return false; } + +/* + * Add flags to the command syntax + */ +MSyntax MMMemorySystemCmd::newSyntax() { + MSyntax syntax; + syntax.enableQuery(true); + syntax.enableEdit(false); + + syntax.addFlag(SYSTEM_PHYSICAL_MEMORY_TOTAL_FLAG, + SYSTEM_PHYSICAL_MEMORY_TOTAL_FLAG_LONG); + syntax.addFlag(SYSTEM_PHYSICAL_MEMORY_FREE_FLAG, + SYSTEM_PHYSICAL_MEMORY_FREE_FLAG_LONG); + syntax.addFlag(SYSTEM_PHYSICAL_MEMORY_USED_FLAG, + SYSTEM_PHYSICAL_MEMORY_USED_FLAG_LONG); + + syntax.addFlag(PROCESS_MEMORY_USED_FLAG, PROCESS_MEMORY_USED_FLAG_LONG); + + syntax.addFlag(MEMORY_AS_KILOBYTES_FLAG, MEMORY_AS_KILOBYTES_FLAG_LONG); + syntax.addFlag(MEMORY_AS_MEGABYTES_FLAG, MEMORY_AS_MEGABYTES_FLAG_LONG); + syntax.addFlag(MEMORY_AS_GIGABYTES_FLAG, MEMORY_AS_GIGABYTES_FLAG_LONG); + + return syntax; +} + +/* + * Parse command line arguments + */ +MStatus MMMemorySystemCmd::parseArgs(const MArgList &args) { + MStatus status = MStatus::kSuccess; + + MArgDatabase argData(syntax(), args, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + if (!argData.isQuery()) { + status = MStatus::kFailure; + MMSOLVER_MAYA_ERR( + "mmsolver::MMMemorySystemCmd::parseArgs: " + "Command must be in query mode!"); + return status; + } + + m_system_physical_memory_total = + argData.isFlagSet(SYSTEM_PHYSICAL_MEMORY_TOTAL_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_system_physical_memory_free = + argData.isFlagSet(SYSTEM_PHYSICAL_MEMORY_FREE_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_system_physical_memory_used = + argData.isFlagSet(SYSTEM_PHYSICAL_MEMORY_USED_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_process_memory_used = + argData.isFlagSet(PROCESS_MEMORY_USED_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + const bool as_kilobytes = + argData.isFlagSet(MEMORY_AS_KILOBYTES_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + const bool as_megabytes = + argData.isFlagSet(MEMORY_AS_MEGABYTES_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + const bool as_gigabytes = + argData.isFlagSet(MEMORY_AS_GIGABYTES_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_memory_unit = mmmemory::MemoryUnit::kBytes; + if (as_kilobytes) { + m_memory_unit = mmmemory::MemoryUnit::kKiloBytes; + } else if (as_megabytes) { + m_memory_unit = mmmemory::MemoryUnit::kMegaBytes; + } else if (as_gigabytes) { + m_memory_unit = mmmemory::MemoryUnit::kGigaBytes; + } + + return status; +} + +MStatus MMMemorySystemCmd::doIt(const MArgList &args) { + MStatus status = MStatus::kSuccess; + const bool verbose = false; + + // Read all the flag arguments. + status = parseArgs(args); + CHECK_MSTATUS_AND_RETURN_IT(status); + + size_t bytes_value = 0; + if (m_system_physical_memory_total) { + bytes_value = mmmemorysystem::system_physical_memory_total(); + } else if (m_system_physical_memory_free) { + bytes_value = mmmemorysystem::system_physical_memory_free(); + } else if (m_system_physical_memory_used) { + bytes_value = mmmemorysystem::system_physical_memory_used(); + } else if (m_process_memory_used) { + size_t peak_resident_set_size = 0; + size_t current_resident_set_size = 0; + mmmemorysystem::process_memory_usage(peak_resident_set_size, + current_resident_set_size); + bytes_value = current_resident_set_size; + } else { + MMSOLVER_MAYA_ERR( + "mmsolver::MMMemorySystemCmd::doIt: " + "Give a flag to the command!"); + return MStatus::kFailure; + } + + if (m_memory_unit == mmmemory::MemoryUnit::kBytes) { + // It is only possible to return a maximum of "unsigned int" + // (unsigned 32-bit number) from a Maya command, but our + // maximum may exceed that size, so we must return the full + // number converted to a string, then have the Python (or + // MEL?) code convert that to an integer. + MString number_mstring = mmmayastring::numberToMString(bytes_value); + MMMemorySystemCmd::setResult(number_mstring); + } else { + double outResult = + mmmemory::bytes_as_double(bytes_value, m_memory_unit); + MMMemorySystemCmd::setResult(outResult); + } + return status; +} + +} // namespace mmsolver diff --git a/src/mmSolver/cmd/MMMemorySystemCmd.h b/src/mmSolver/cmd/MMMemorySystemCmd.h new file mode 100644 index 000000000..74d55f063 --- /dev/null +++ b/src/mmSolver/cmd/MMMemorySystemCmd.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + * 'mmMemorySystem' Command is responsible for querying details about + * the operating systems's memory. + * + * MEL: + * // Return the number of used memory by the image cache. + * mmMemorySystem -query -systemPhysicalMemoryTotal -asMegaBytes; + * mmMemorySystem -query -systemPhysicalMemoryFree -asMegaBytes; + * mmMemorySystem -query -systemPhysicalMemoryUsed -asMegaBytes; + * + * // Return the amount of memory used by the current process. + * mmMemorySystem -query -processMemoryUsed -asMegaBytes; + * + */ + +#ifndef MAYA_MM_MEMORY_SYSTEM_CMD_H +#define MAYA_MM_MEMORY_SYSTEM_CMD_H + +// Maya +#include +#include +#include +#include +#include +#include +#include +#include + +// MM Solver +#include "mmSolver/utilities/memory_utils.h" + +namespace mmsolver { + +class MMMemorySystemCmd : public MPxCommand { +public: + MMMemorySystemCmd() + : m_system_physical_memory_total(false) + , m_system_physical_memory_free(false) + , m_system_physical_memory_used(false) + , m_process_memory_used(false) + , m_memory_unit(mmmemory::MemoryUnit::kBytes){}; + + virtual ~MMMemorySystemCmd(); + + virtual bool hasSyntax() const; + static MSyntax newSyntax(); + + virtual MStatus doIt(const MArgList &args); + + virtual bool isUndoable() const; + + static void *creator(); + + static MString cmdName(); + +private: + MStatus parseArgs(const MArgList &args); + + bool m_system_physical_memory_total; + bool m_system_physical_memory_free; + bool m_system_physical_memory_used; + bool m_process_memory_used; + mmmemory::MemoryUnit m_memory_unit; +}; + +} // namespace mmsolver + +#endif // MAYA_MM_MEMORY_SYSTEM_CMD_H diff --git a/src/mmSolver/cmd/MMReadImageCmd.cpp b/src/mmSolver/cmd/MMReadImageCmd.cpp index 5c4c32625..ee1e2f40f 100644 --- a/src/mmSolver/cmd/MMReadImageCmd.cpp +++ b/src/mmSolver/cmd/MMReadImageCmd.cpp @@ -18,6 +18,27 @@ * ==================================================================== * * Command for running mmReadImage. + * + * MEL: + * // Example image file path. + * string $file_path = "/path/to/image.png"; + * + * // Get the width and height of the input image. + * mmReadImage -query -widthHeight $file_path; + * // For example, returns [1920, 1080] + * + * // Get the image's header details and pixel data size. + * mmReadImage -query -dataHeader $file_path; + * // For example, returns ["1920", "1080", "4", "1", "8294400"] + * // Index 0 is image width. + * // Index 1 is image height. + * // Index 2 is image channel count. + * // Index 3 is image bytes-per-channel count. + * // Index 4 is image size in bytes. + * + * // Full file path to the image name. + * string $resolved = `mmReadImage -query -resolveFilePath $file_path`; + * // Returns the resolved file path, if it exists, None otherwise. */ #include "MMReadImageCmd.h" @@ -27,18 +48,36 @@ #include #include #include -#include #include -#include -#include #include -#include #include #include #include // MM Solver +#include +#include + +#include "mmSolver/image/ImagePixelData.h" +#include "mmSolver/image/PixelDataType.h" +#include "mmSolver/image/image_io.h" +#include "mmSolver/mayahelper/maya_string_utils.h" #include "mmSolver/utilities/debug_utils.h" +#include "mmSolver/utilities/path_utils.h" +#include "mmSolver/utilities/string_utils.h" + +// Command arguments and command name: +#define FILE_PATH_FLAG "-fp" +#define FILE_PATH_FLAG_LONG "-filePath" + +#define WIDTH_HEIGHT_FLAG "-wh" +#define WIDTH_HEIGHT_FLAG_LONG "-widthHeight" + +#define DATA_HEADER_FLAG "-dhr" +#define DATA_HEADER_FLAG_LONG "-dataHeader" + +#define RESOLVE_FILE_PATH_FLAG "-rfp" +#define RESOLVE_FILE_PATH_FLAG_LONG "-resolveFilePath" namespace mmsolver { @@ -67,8 +106,10 @@ MSyntax MMReadImageCmd::newSyntax() { auto maxNumObjects = 1; syntax.setObjectType(MSyntax::kStringObjects, minNumObjects, maxNumObjects); - syntax.addFlag(WIDTH_HEIGHT_FLAG, WIDTH_HEIGHT_FLAG_LONG, - MSyntax::kBoolean); + syntax.addFlag(WIDTH_HEIGHT_FLAG, WIDTH_HEIGHT_FLAG_LONG); + syntax.addFlag(DATA_HEADER_FLAG, DATA_HEADER_FLAG_LONG); + syntax.addFlag(RESOLVE_FILE_PATH_FLAG, RESOLVE_FILE_PATH_FLAG_LONG); + return syntax; } @@ -81,6 +122,21 @@ MStatus MMReadImageCmd::parseArgs(const MArgList &args) { MArgDatabase argData(syntax(), args, &status); CHECK_MSTATUS_AND_RETURN_IT(status); + // Query Flag + const bool query = argData.isQuery(&status); + CHECK_MSTATUS(status); + if (status != MStatus::kSuccess) { + status.perror("mmReadImage: Could not get the query flag"); + return status; + } + + if (!query) { + status = MStatus::kFailure; + status.perror("mmReadImage command must query using the 'query' flag"); + return status; + } + + // Get the file path. MStringArray objects; argData.getObjects(objects); if (objects.length() == 0) { @@ -99,78 +155,138 @@ MStatus MMReadImageCmd::parseArgs(const MArgList &args) { } m_file_path = objects[0]; - // Query Flag - const bool query = argData.isQuery(&status); - CHECK_MSTATUS(status); - if (status != MStatus::kSuccess) { - status.perror("mmReadImage: Could not get the query flag"); - return status; - } - if (!query) { - status = MStatus::kFailure; - status.perror("mmReadImage command must query using the 'query' flag"); - return status; - } - m_query_width_height = argData.isFlagSet(WIDTH_HEIGHT_FLAG, &status); CHECK_MSTATUS_AND_RETURN_IT(status); + + m_query_data_header = argData.isFlagSet(DATA_HEADER_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + + m_query_resolve_file_path = + argData.isFlagSet(RESOLVE_FILE_PATH_FLAG, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + return status; +} + +MStatus read_image_header(const MString &file_path, uint32_t &out_image_width, + uint32_t &out_image_height, uint8_t &out_num_channels, + uint8_t &out_bytes_per_channel, + MHWRender::MRasterFormat &out_texture_format, + image::PixelDataType &out_pixel_data_type) { + MStatus status = MStatus::kSuccess; + MImage temp_mimage; + mmimage::ImagePixelBuffer temp_pixel_buffer; + mmimage::ImageMetaData temp_meta_data; + + out_image_width = 0; + out_image_height = 0; + out_num_channels = 0; + out_bytes_per_channel = 0; + + // We won't use the image data anyway. + void *pixel_data = nullptr; + + // TODO: Can we read just the file header to get the image size? + // This would remove the need to read the entire image for this + // command's usage. + status = image::read_image_file(temp_mimage, temp_pixel_buffer, + temp_meta_data, file_path, out_image_width, + out_image_height, out_num_channels, + out_bytes_per_channel, out_texture_format, + out_pixel_data_type, pixel_data); + if (status != MS::kSuccess) { + MMSOLVER_MAYA_WRN("mmReadImage: " + << "Image file path could not be read: " + << file_path.asChar()); + } + return status; } MStatus MMReadImageCmd::doIt(const MArgList &args) { + const bool verbose = false; MStatus status = MStatus::kSuccess; // Read all the flag arguments. status = parseArgs(args); CHECK_MSTATUS_AND_RETURN_IT(status); - auto file_object = MFileObject(); - file_object.setRawFullName(m_file_path); - file_object.setResolveMethod(MFileObject::kInputFile); - - bool path_exists = file_object.exists(); - if (!path_exists) { - MString resolved_file_path = file_object.resolvedFullName(); - status = MS::kFailure; - MMSOLVER_MAYA_WRN("mmReadImage: Could not find file path " - << "\"" << m_file_path.asChar() - << "\", resolved path " - << "\"" << resolved_file_path.asChar() << "\"."); - return status; - } - MString resolved_file_path = file_object.resolvedFullName(); - if (resolved_file_path.length() > 0) { - // MMSOLVER_MAYA_INFO( - // "mmReadImage: resolved file path " - // << "\"" << resolved_file_path.asChar() << "\"."); - m_file_path = file_object.resolvedFullName(); - } - - if (m_query_width_height) { - auto image = MImage(); - // kUnknown attempts to load the native pixel type. - auto pixel_type = MImage::kUnknown; - status = image.readFromFile( - m_file_path, - pixel_type // The desired pixel format is unknown. - ); - if (status != MS::kSuccess) { - status = MS::kSuccess; - MMSOLVER_MAYA_WRN("mmReadImage: " - << "Image file path could not be read: " - << m_file_path.asChar()); - return status; + if (m_query_resolve_file_path) { + status = mmpath::resolve_input_file_path(m_file_path); + if (status == MStatus::kSuccess) { + MMReadImageCmd::setResult(m_file_path); + } else { + // Pretend everything is fine. + status = MStatus::kSuccess; } + } else if (m_query_width_height) { + status = mmpath::resolve_input_file_path(m_file_path); + CHECK_MSTATUS_AND_RETURN_IT(status); - uint32_t image_width = 2; - uint32_t image_height = 2; - image.getSize(image_width, image_height); + uint32_t image_width = 0; + uint32_t image_height = 0; + uint8_t num_channels = 0; + uint8_t bytes_per_channel = 0; + MHWRender::MRasterFormat texture_format; + image::PixelDataType pixel_data_type; + + status = read_image_header(m_file_path, image_width, image_height, + num_channels, bytes_per_channel, + texture_format, pixel_data_type); + CHECK_MSTATUS_AND_RETURN_IT(status); MIntArray outResult; outResult.append(image_width); outResult.append(image_height); MMReadImageCmd::setResult(outResult); + } else if (m_query_data_header) { + status = mmpath::resolve_input_file_path(m_file_path); + CHECK_MSTATUS_AND_RETURN_IT(status); + + // NOTE: We do not want to have to call mmReadImage multiple + // times. We want to get as much data as possible in a single + // call, because subsequent calls will need to re-read the + // image. + + uint32_t image_width = 0; + uint32_t image_height = 0; + uint8_t num_channels = 0; + uint8_t bytes_per_channel = 0; + MHWRender::MRasterFormat texture_format; + image::PixelDataType pixel_data_type; + + status = read_image_header(m_file_path, image_width, image_height, + num_channels, bytes_per_channel, + texture_format, pixel_data_type); + CHECK_MSTATUS_AND_RETURN_IT(status); + + void *pixel_data = nullptr; + image::ImagePixelData image_pixel_data(pixel_data, image_width, + image_height, num_channels, + pixel_data_type); + const size_t byte_count = image_pixel_data.byte_count(); + + // Some of the numbers may be more than 'int' can hold, so we + // must return as a MString. + MString width_mstring( + mmmayastring::numberToMString(image_width)); + MString height_mstring( + mmmayastring::numberToMString(image_height)); + MString num_channels_mstring( + mmmayastring::numberToMString(num_channels)); + MString bytes_per_channel_mstring( + mmmayastring::numberToMString(bytes_per_channel)); + MString byte_count_mstring( + mmmayastring::numberToMString(byte_count)); + + MStringArray outResult; + outResult.append(width_mstring); + outResult.append(height_mstring); + outResult.append(num_channels_mstring); + outResult.append(bytes_per_channel_mstring); + outResult.append(byte_count_mstring); + MMReadImageCmd::setResult(outResult); } + return status; } diff --git a/src/mmSolver/cmd/MMReadImageCmd.h b/src/mmSolver/cmd/MMReadImageCmd.h index f148d8437..aed05ac8e 100644 --- a/src/mmSolver/cmd/MMReadImageCmd.h +++ b/src/mmSolver/cmd/MMReadImageCmd.h @@ -33,13 +33,6 @@ #include #include -// Command arguments and command name: -#define FILE_PATH_FLAG "-fp" -#define FILE_PATH_FLAG_LONG "-filePath" - -#define WIDTH_HEIGHT_FLAG "-wh" -#define WIDTH_HEIGHT_FLAG_LONG "-widthHeight" - namespace mmsolver { class MMReadImageCmd : public MPxCommand { @@ -64,6 +57,8 @@ class MMReadImageCmd : public MPxCommand { MString m_file_path; bool m_query_width_height; + bool m_query_data_header; + bool m_query_resolve_file_path; }; } // namespace mmsolver diff --git a/src/mmSolver/cmd/MMSolverTypeCmd.cpp b/src/mmSolver/cmd/MMSolverTypeCmd.cpp index da5e673e9..27fde6ea4 100644 --- a/src/mmSolver/cmd/MMSolverTypeCmd.cpp +++ b/src/mmSolver/cmd/MMSolverTypeCmd.cpp @@ -45,9 +45,9 @@ // MM Solver #include "mmSolver/adjust/adjust_base.h" +#include "mmSolver/mayahelper/maya_string_utils.h" #include "mmSolver/mayahelper/maya_utils.h" #include "mmSolver/utilities/debug_utils.h" -#include "mmSolver/utilities/string_utils.h" namespace mmsolver { @@ -207,9 +207,9 @@ MStatus MMSolverTypeCmd::doIt(const MArgList &args) { MString item = ""; if (m_index) { - std::string index_string = - mmstring::numberToString(index); - item += MString(index_string.c_str()); + MString index_mstring = + mmmayastring::numberToMString(index); + item += index_mstring; item += "="; } item += name.c_str(); @@ -237,8 +237,9 @@ MStatus MMSolverTypeCmd::doIt(const MArgList &args) { std::string name = solverType.second; if (m_index) { - std::string index_string = mmstring::numberToString(index); - outResult += MString(index_string.c_str()); + MString index_mstring = + mmmayastring::numberToMString(index); + outResult += index_mstring; outResult += "="; } outResult += name.c_str(); diff --git a/src/mmSolver/image/ImageCache.cpp b/src/mmSolver/image/ImageCache.cpp new file mode 100644 index 000000000..d5a4415c8 --- /dev/null +++ b/src/mmSolver/image/ImageCache.cpp @@ -0,0 +1,1089 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#include "ImageCache.h" + +// Get M_PI constant +#define _USE_MATH_DEFINES +#include + +// STL +#include +#include +#include +#include +#include + +// Maya +#include +#include + +// Maya Viewport 2.0 +#include + +// MM Solver +#include + +#include "ImagePixelData.h" +#include "PixelDataType.h" +#include "TextureData.h" +#include "image_io.h" +#include "mmSolver/mayahelper/maya_utils.h" +#include "mmSolver/render/shader/shader_utils.h" +#include "mmSolver/shape/constant_texture_data.h" +#include "mmSolver/utilities/hash_utils.h" +#include "mmSolver/utilities/number_utils.h" +#include "mmSolver/utilities/path_utils.h" +#include "mmSolver/utilities/string_utils.h" + +namespace mmsolver { +namespace image { + +MTexture *read_texture_image_file(MHWRender::MTextureManager *texture_manager, + ImageCache &image_cache, MImage &temp_image, + mmimage::ImagePixelBuffer &temp_pixel_buffer, + mmimage::ImageMetaData &temp_meta_data, + const MString &file_pattern, + const MString &file_path, + const bool do_texture_update) { + assert(texture_manager != nullptr); + + const bool verbose = false; + MMSOLVER_MAYA_VRB("mmsolver::ImageCache: read_texture_image_file:" + << " file_path=" << file_path.asChar()); + + MString resolved_file_path = file_path; + MStatus status = mmpath::resolve_input_file_path(resolved_file_path); + if (status != MS::kSuccess) { + MMSOLVER_MAYA_VRB("mmsolver::ImageCache: read_texture_image_file:" + << " file does not exist \"" + << resolved_file_path.asChar() << "\"."); + return nullptr; + } + + const std::string item_key = std::string(resolved_file_path.asChar()); + TextureData texture_data = image_cache.gpu_find_item(item_key); + + MMSOLVER_MAYA_VRB( + "mmsolver::ImageCache: read_texture_image_file: findTexture: " + << texture_data.is_valid()); + MMSOLVER_MAYA_VRB( + "mmsolver::ImageCache: read_texture_image_file: do_texture_update=" + << do_texture_update); + if (texture_data.is_valid() && (do_texture_update == false)) { + MMSOLVER_MAYA_VRB("mmsolver::ImageCache: read_texture_image_file DONE1:" + << " texture=" << texture_data.texture()); + return texture_data.texture(); + } + + // TODO: We should test if the file exists, then cache + // result. This avoids us having to check the disk each time we + // hit a frame that is outside the frame range of the image + // sequence. This would mean that we would then need to be able to + // flush that cached result - for example if the image sequence + // changes. Alternatively, we could could pre-cache the file path + // existence as soon as the user changes the file path. Another + // approach would be to expose a Maya command that would allow + // Python user code to add the list of valid images into the + // cache. + + ImagePixelData image_pixel_data = image_cache.cpu_find_item(item_key); + + uint32_t width = 0; + uint32_t height = 0; + uint8_t num_channels = 4; + uint8_t bytes_per_channel = 0; + MHWRender::MRasterFormat texture_format; + PixelDataType pixel_data_type = PixelDataType::kUnknown; + void *maya_owned_pixel_data = nullptr; + + const bool image_pixel_data_valid = image_pixel_data.is_valid(); + if (image_pixel_data_valid) { + maya_owned_pixel_data = image_pixel_data.pixel_data(); + width = image_pixel_data.width(); + height = image_pixel_data.height(); + num_channels = image_pixel_data.num_channels(); + pixel_data_type = image_pixel_data.pixel_data_type(); + bytes_per_channel = + convert_pixel_data_type_to_bytes_per_channel(pixel_data_type); + + if (pixel_data_type == PixelDataType::kU8) { + // Assumes the 8-bit data is "RGBA". + texture_format = MHWRender::kR8G8B8A8_UNORM; + } else if (pixel_data_type == PixelDataType::kF32) { + texture_format = MHWRender::kR32G32B32A32_FLOAT; + } + + } else { + status = read_image_file( + temp_image, temp_pixel_buffer, temp_meta_data, resolved_file_path, + width, height, num_channels, bytes_per_channel, texture_format, + pixel_data_type, maya_owned_pixel_data); + if (status != MS::kSuccess) { + return nullptr; + } + + // TODO: any image manipulations required can be done here. + // TODO: Apply colour correction via OIIO. + // // image.verticalFlip(); + } + + if (!maya_owned_pixel_data) { + MMSOLVER_MAYA_ERR("mmsolver::ImageCache: read_texture_image_file: " + << "Invalid pixel data!"); + return nullptr; + } + + ImagePixelData gpu_image_pixel_data = + ImagePixelData(static_cast(maya_owned_pixel_data), width, + height, num_channels, pixel_data_type); + + // All group names are normalised to use UNIX-style path + // separators, so that the internal values are all consistent. + MString normalised_file_pattern(file_pattern); + normalised_file_pattern.substitute("\\", "/"); + const std::string group_name = + std::string(normalised_file_pattern.asChar()); + + texture_data = image_cache.gpu_insert_item(texture_manager, group_name, + item_key, gpu_image_pixel_data); + MMSOLVER_MAYA_VRB("mmsolver::ImageCache: read_texture_image_file: " + << "gpu_inserted=" << texture_data.texture()); + + // Duplicate the Maya-owned pixel data for our image cache. + const size_t pixel_data_byte_count = + width * height * num_channels * bytes_per_channel; + image_pixel_data = ImagePixelData(); + const bool allocated_ok = image_pixel_data.allocate_pixels( + width, height, num_channels, pixel_data_type); + if (allocated_ok == false) { + MMSOLVER_MAYA_ERR("mmsolver::ImageCache: read_texture_image_file: " + << "Could not allocate pixel data!"); + return nullptr; + } + assert(image_pixel_data.is_valid() == true); + assert(image_pixel_data.byte_count() == pixel_data_byte_count); + std::memcpy(image_pixel_data.pixel_data(), maya_owned_pixel_data, + pixel_data_byte_count); + + const bool cpu_inserted = + image_cache.cpu_insert_item(group_name, item_key, image_pixel_data); + MMSOLVER_MAYA_VRB("mmsolver::ImageCache: read_texture_image_file: " + << "cpu_inserted=" << cpu_inserted); + + MMSOLVER_MAYA_VRB("mmsolver::ImageCache: read_texture_image_file DONE2:" + << " texture=" << texture_data.texture()); + + return texture_data.texture(); +} + +void ImageCache::set_gpu_capacity_bytes( + MHWRender::MTextureManager *texture_manager, const size_t value) { + const bool verbose = false; + m_gpu_capacity_bytes = value; + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::set_gpu_capacity_bytes: " + << "m_gpu_capacity_bytes=" << m_gpu_capacity_bytes); + + // Because we must always ensure our used memory is less than + // the given capacity. + // + // If we are at capacity remove the least recently used items + // until our capacity is under 'new_used_bytes' or we reach the minimum + // number of items + while (!m_gpu_item_map.empty() && + (m_gpu_item_map.size() > m_gpu_item_count_minumum) && + (m_gpu_used_bytes > m_gpu_capacity_bytes)) { + const CacheEvictionResult result = + ImageCache::gpu_evict_one_item(texture_manager); + if (result != CacheEvictionResult::kSuccess) { + break; + } + } +} + +void ImageCache::set_cpu_capacity_bytes(const size_t value) { + const bool verbose = false; + m_cpu_capacity_bytes = value; + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::set_cpu_capacity_bytes: " + << "m_cpu_capacity_bytes=" << m_cpu_capacity_bytes); + + // Because we must always ensure our used memory is less than + // the given capacity. + // + // If we are at capacity remove the least recently used items + // until our capacity is under 'new_used_bytes' or we reach the minimum + // number of items + while (!m_cpu_item_map.empty() && + (m_cpu_item_map.size() > m_cpu_item_count_minumum) && + (m_cpu_used_bytes > m_cpu_capacity_bytes)) { + const CacheEvictionResult result = ImageCache::cpu_evict_one_item(); + if (result != CacheEvictionResult::kSuccess) { + break; + } + } +} + +inline std::string generate_cache_brief(const char *prefix_str, + const size_t item_count, + const size_t item_min_count, + const size_t capacity_bytes, + const size_t used_bytes) { + const size_t capacity_megabytes = capacity_bytes / BYTES_TO_MEGABYTES; + const size_t used_megabytes = used_bytes / BYTES_TO_MEGABYTES; + + std::string capacity_megabytes_str = + mmstring::numberToStringWithCommas(capacity_megabytes); + + std::string used_megabytes_str = + mmstring::numberToStringWithCommas(used_megabytes); + + double used_percent = 0.0; + if (capacity_bytes > 0) { + used_percent = (static_cast(used_bytes) / + static_cast(capacity_bytes)) * + 100.0; + } + + std::stringstream ss; + ss << prefix_str << "item_count=" << item_count << " items " + << "| minimum=" << item_min_count << " items " + << "| used=" << used_megabytes_str << "MB " + << "| capacity=" << capacity_megabytes_str << "MB " + << "| percent=" << used_percent << '%'; + return ss.str(); +} + +MString ImageCache::generate_cache_brief_text() const { + std::string gpu_cache_text = generate_cache_brief( + "GPU cache | ", m_gpu_item_map.size(), m_gpu_item_count_minumum, + m_gpu_capacity_bytes, m_gpu_used_bytes); + std::string cpu_cache_text = generate_cache_brief( + "CPU cache | ", m_cpu_item_map.size(), m_cpu_item_count_minumum, + m_cpu_capacity_bytes, m_cpu_used_bytes); + + std::stringstream ss; + ss << gpu_cache_text << std::endl << cpu_cache_text << std::endl; + + std::string string = ss.str(); + MString mstring = MString(string.c_str()); + return mstring; +} + +void ImageCache::print_cache_brief() const { + std::string gpu_cache_text = generate_cache_brief( + "GPU cache | ", m_gpu_item_map.size(), m_gpu_item_count_minumum, + m_gpu_capacity_bytes, m_gpu_used_bytes); + std::string cpu_cache_text = generate_cache_brief( + "CPU cache | ", m_cpu_item_map.size(), m_gpu_item_count_minumum, + m_cpu_capacity_bytes, m_cpu_used_bytes); + + MMSOLVER_MAYA_INFO( + "mmsolver::ImageCache::print_cache_brief: " << gpu_cache_text); + MMSOLVER_MAYA_INFO( + "mmsolver::ImageCache::print_cache_brief: " << cpu_cache_text); + return; +} + +void ImageCache::gpu_group_names(GPUVectorString &out_group_names) const { + const bool verbose = false; + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::gpu_group_names: "); + + out_group_names.clear(); + out_group_names.reserve(m_gpu_group_map.size()); + for (auto it = m_gpu_group_map.begin(); it != m_gpu_group_map.end(); it++) { + const GPUCacheString value = it->first; + out_group_names.push_back(value); + } + // The set is unordered, so lets make the output of this function + // consistent for end users. + std::sort(out_group_names.begin(), out_group_names.end()); + + return; +} + +void ImageCache::cpu_group_names(CPUVectorString &out_group_names) const { + const bool verbose = false; + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::cpu_group_names: "); + + out_group_names.clear(); + out_group_names.reserve(m_cpu_group_map.size()); + for (auto it = m_cpu_group_map.begin(); it != m_cpu_group_map.end(); it++) { + const CPUCacheString value = it->first; + out_group_names.push_back(value); + } + // The set is unordered, so lets make the output of this function + // consistent for end users. + std::sort(out_group_names.begin(), out_group_names.end()); + + return; +} + +size_t ImageCache::gpu_group_item_count( + const GPUCacheString &group_name) const { + const auto group_search = m_gpu_group_map.find(group_name); + const bool group_found = group_search != m_gpu_group_map.end(); + if (!group_found) { + return 0; + } + + const GPUGroupSet &values_set = group_search->second; + return values_set.size(); +} + +size_t ImageCache::cpu_group_item_count( + const CPUCacheString &group_name) const { + const auto group_search = m_cpu_group_map.find(group_name); + const bool group_found = group_search != m_cpu_group_map.end(); + if (!group_found) { + return 0; + } + + const CPUGroupSet &values_set = group_search->second; + return values_set.size(); +} + +bool ImageCache::gpu_group_item_names( + const GPUCacheString &group_name, + GPUVectorString &out_group_item_names) const { + out_group_item_names.clear(); + const auto group_search = m_gpu_group_map.find(group_name); + const bool group_found = group_search != m_gpu_group_map.end(); + if (!group_found) { + return false; + } + + const GPUGroupSet &values_set = group_search->second; + out_group_item_names.reserve(values_set.size()); + for (auto it = values_set.begin(); it != values_set.end(); it++) { + GPUCacheString value = *it; + out_group_item_names.push_back(value); + } + // The set is unordered, so lets make the output of this function + // consistent for end users. + std::sort(out_group_item_names.begin(), out_group_item_names.end()); + + return true; +} + +bool ImageCache::cpu_group_item_names( + const CPUCacheString &group_name, + CPUVectorString &out_group_item_names) const { + out_group_item_names.clear(); + const auto group_search = m_cpu_group_map.find(group_name); + const bool group_found = group_search != m_cpu_group_map.end(); + if (!group_found) { + return false; + } + + const CPUGroupSet &values_set = group_search->second; + out_group_item_names.reserve(values_set.size()); + for (auto it = values_set.begin(); it != values_set.end(); it++) { + CPUCacheString value = *it; + out_group_item_names.push_back(value); + } + // The set is unordered, so lets make the output of this function + // consistent for end users. + std::sort(out_group_item_names.begin(), out_group_item_names.end()); + + return true; +} + +bool ImageCache::gpu_insert_group(const GPUCacheString &group_name, + const GPUCacheString &file_path) { + const GPUGroupMapIt group_search = m_gpu_group_map.find(group_name); + const bool group_found = group_search != m_gpu_group_map.end(); + if (!group_found) { + GPUGroupSet values_set; + values_set.insert(file_path); + + const auto group_map_pair = + m_gpu_group_map.insert(std::make_pair(group_name, values_set)); + const bool group_map_ok = group_map_pair.second; + assert(group_map_ok == true); + } else { + GPUGroupSet &values_set = group_search->second; + values_set.insert(file_path); + } + return true; +} + +bool ImageCache::cpu_insert_group(const CPUCacheString &group_name, + const CPUCacheString &file_path) { + const CPUGroupMapIt group_search = m_cpu_group_map.find(group_name); + const bool group_found = group_search != m_cpu_group_map.end(); + if (!group_found) { + CPUGroupSet values_set; + values_set.insert(file_path); + + const auto group_map_pair = + m_cpu_group_map.insert(std::make_pair(group_name, values_set)); + const bool group_map_ok = group_map_pair.second; + assert(group_map_ok == true); + } else { + CPUGroupSet &values_set = group_search->second; + values_set.insert(file_path); + } + return true; +} + +static void update_texture(MTexture *texture, + const ImageCache::CPUCacheValue &image_pixel_data) { + const bool verbose = false; + + // No need for MIP-maps. + const bool generate_mip_maps = false; + + // TODO: If the 'texture_desc' is different than the + // current texture, it cannot be updated, and must be + // released and a new texture created instead. + + // The default value of this argument is 0. This means to + // use the texture's "width * number of bytes per pixel". + uint32_t rowPitch = 0; + + MHWRender::MTextureUpdateRegion *region = nullptr; + void *pixel_data = image_pixel_data.pixel_data(); + MStatus status = + texture->update(pixel_data, generate_mip_maps, rowPitch, region); + CHECK_MSTATUS(status); +} + +ImageCache::GPUCacheValue ImageCache::gpu_insert_item( + MHWRender::MTextureManager *texture_manager, + const GPUCacheString &group_name, const GPUCacheString &file_path, + const ImageCache::CPUCacheValue &image_pixel_data) { + assert(texture_manager != nullptr); + assert(image_pixel_data.is_valid()); + const bool verbose = false; + + const GPUGroupKey item_key = mmsolver::hash::make_hash(file_path); + const GPUGroupKey group_key = mmsolver::hash::make_hash(group_name); + + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::gpu_insert_item: " + << "item_key=" << item_key + << " group_name=" << group_name.c_str() + << " file_path=" << file_path.c_str()); + + GPUCacheValue texture_data = GPUCacheValue(); + const ImageCache::GPUMapIt item_search = m_gpu_item_map.find(item_key); + const bool item_found = item_search != m_gpu_item_map.end(); + if (!item_found) { + // If we are at capacity, make room for new entry. + const size_t image_data_size = image_pixel_data.byte_count(); + + const CacheEvictionResult evict_result = + ImageCache::gpu_evict_enough_for_new_item(texture_manager, + image_data_size); + if (evict_result == CacheEvictionResult::kFailed) { + MMSOLVER_MAYA_WRN( + "mmsolver::ImageCache::gpu_insert_item: evicting memory " + "failed!"); + ImageCache::print_cache_brief(); + } + + const bool allocate_ok = texture_data.allocate_texture( + texture_manager, image_pixel_data.pixel_data(), + image_pixel_data.width(), image_pixel_data.height(), + image_pixel_data.num_channels(), + image_pixel_data.pixel_data_type()); + if (!allocate_ok) { + MMSOLVER_MAYA_ERR( + "mmsolver::ImageCache: gpu_insert_item: " + "Could not allocate texture!"); + } + + if (!texture_data.is_valid()) { + return GPUCacheValue(); + } + + m_gpu_used_bytes += texture_data.byte_count(); + assert(m_gpu_used_bytes <= m_gpu_capacity_bytes); + + // Make 'item_key' the most-recently-used item key, because when we + // insert an item into the cache, it's used most recently. + ImageCache::GPUKeyListIt item_key_iterator = + m_gpu_key_list.insert(m_gpu_key_list.end(), item_key); + + // Create the key-value entry, linked to the usage record. + const auto item_map_pair = m_gpu_item_map.insert(std::make_pair( + item_key, std::make_pair(item_key_iterator, texture_data))); + + const auto inserted_key_iterator = item_map_pair.first; + const bool item_ok = item_map_pair.second; + assert(ok == true); + + const bool group_ok = + ImageCache::gpu_insert_group(group_name, file_path); + assert(group_ok == true); + + } else { + ImageCache::GPUKeyListIt item_iterator = item_search->second.first; + texture_data = item_search->second.second; + if (!texture_data.is_valid()) { + MMSOLVER_MAYA_ERR( + "mmsolver::ImageCache: gpu_insert_item: " + "Found texture is invalid!"); + return ImageCache::GPUCacheValue(); + } + + move_iterator_to_back_of_key_list(m_gpu_key_list, item_iterator); + + update_texture(texture_data.texture(), image_pixel_data); + } + + return texture_data; +} + +bool ImageCache::cpu_insert_item(const CPUCacheString &group_name, + const CPUCacheString &file_path, + const CPUCacheValue &image_pixel_data) { + const bool verbose = false; + + const CPUGroupKey item_key = mmsolver::hash::make_hash(file_path); + const CPUGroupKey group_key = mmsolver::hash::make_hash(group_name); + + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::cpu_insert_item: " + << "item_key=" << item_key + << " group_name=" << group_name.c_str() + << " file_path=" << file_path.c_str()); + + const CPUMapIt item_search = m_cpu_item_map.find(item_key); + const bool item_found = item_search != m_cpu_item_map.end(); + if (item_found) { + ImageCache::cpu_erase_item(item_key); + } + + // If we are at capacity, make room for new entry. + const size_t image_data_size = image_pixel_data.byte_count(); + const CacheEvictionResult evict_result = + ImageCache::cpu_evict_enough_for_new_item(image_data_size); + if (evict_result == CacheEvictionResult::kFailed) { + MMSOLVER_MAYA_WRN( + "mmsolver::ImageCache::cpu_insert_item: evicting memory failed!"); + ImageCache::print_cache_brief(); + } + + m_cpu_used_bytes += image_data_size; + assert(m_cpu_used_bytes <= m_cpu_capacity_bytes); + + // Because we are inserting into the cache, the 'key' is the + // most-recently-used item. + CPUKeyListIt item_key_iterator = + m_cpu_key_list.insert(m_cpu_key_list.end(), item_key); + + const auto item_map_pair = m_cpu_item_map.insert(std::make_pair( + item_key, std::make_pair(item_key_iterator, image_pixel_data))); + + const auto inserted_key_iterator = item_map_pair.first; + const bool item_map_ok = item_map_pair.second; + assert(item_map_ok == true); + + const bool group_ok = ImageCache::cpu_insert_group(group_name, file_path); + assert(group_ok == true); + + return true; +} + +ImageCache::GPUCacheValue ImageCache::gpu_find_item( + const GPUCacheString &file_path) { + const bool verbose = false; + + const GPUGroupKey item_key = mmsolver::hash::make_hash(file_path); + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::gpu_find_item: " + << "item_key=" << item_key << " file_path=\"" + << file_path.c_str() << "\""); + return ImageCache::gpu_find_item(item_key); +} + +ImageCache::GPUCacheValue ImageCache::gpu_find_item( + const GPUCacheKey item_key) { + const bool verbose = false; + + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::gpu_find_item: " + << "item_key=" << item_key); + + const GPUMapIt item_search = m_gpu_item_map.find(item_key); + const bool item_found = item_search != m_gpu_item_map.end(); + if (item_found) { + GPUKeyListIt item_iterator = item_search->second.first; + GPUCacheValue item_value = item_search->second.second; + move_iterator_to_back_of_key_list(m_gpu_key_list, item_iterator); + return item_value; + } + return GPUCacheValue(); +} + +ImageCache::CPUCacheValue ImageCache::cpu_find_item( + const CPUCacheString &file_path) { + const bool verbose = false; + + const CPUGroupKey item_key = mmsolver::hash::make_hash(file_path); + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::cpu_find_item: " + << "item_key=" << item_key << " file_path=\"" + << file_path.c_str() << "\""); + return ImageCache::cpu_find_item(item_key); +} + +ImageCache::CPUCacheValue ImageCache::cpu_find_item( + const CPUCacheKey item_key) { + const bool verbose = false; + + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::cpu_find_item: " + << "item_key=" << item_key); + + const CPUMapIt item_search = m_cpu_item_map.find(item_key); + const bool item_found = item_search != m_cpu_item_map.end(); + if (item_found) { + CPUKeyListIt item_iterator = item_search->second.first; + CPUCacheValue item_value = item_search->second.second; + move_iterator_to_back_of_key_list(m_cpu_key_list, item_iterator); + return item_value; + } + return CPUCacheValue(); +} + +CacheEvictionResult ImageCache::gpu_evict_one_item( + MHWRender::MTextureManager *texture_manager) { + const bool verbose = false; + + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::gpu_evict_one_item: "); + MMSOLVER_MAYA_VRB( + "mmsolver::ImageCache::gpu_evict_one_item: " + "before m_gpu_used_bytes=" + << m_gpu_used_bytes); + + assert(texture_manager != nullptr); + if (m_gpu_key_list.empty() || + (m_gpu_item_map.size() <= m_gpu_item_count_minumum)) { + return CacheEvictionResult::kNotNeeded; + } + + if (m_gpu_key_list.empty()) { + return CacheEvictionResult::kNotNeeded; + } + const GPUCacheKey item_key = m_gpu_key_list.front(); + if (item_key == 0) { + return CacheEvictionResult::kFailed; + } + const GPUMapIt item_key_iterator = m_gpu_item_map.find(item_key); + assert(item_key_iterator != m_gpu_item_map.end()); + + GPUCacheValue texture_data = item_key_iterator->second.second; + m_gpu_used_bytes -= texture_data.byte_count(); + texture_data.deallocate_texture(texture_manager); + + m_gpu_item_map.erase(item_key_iterator); + m_gpu_key_list.pop_front(); + + gpu_remove_item_from_group(item_key); + + MMSOLVER_MAYA_VRB( + "mmsolver::ImageCache::gpu_evict_one_item: " + "after m_gpu_used_bytes=" + << m_gpu_used_bytes); + return CacheEvictionResult::kSuccess; +} + +CacheEvictionResult ImageCache::cpu_evict_one_item() { + const bool verbose = false; + + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::cpu_evict_one_item: "); + MMSOLVER_MAYA_VRB( + "mmsolver::ImageCache::cpu_evict_one_item: " + "before m_cpu_used_bytes=" + << m_cpu_used_bytes); + + if (m_cpu_key_list.empty() || + (m_cpu_item_map.size() <= m_cpu_item_count_minumum)) { + return CacheEvictionResult::kNotNeeded; + } + + if (m_cpu_key_list.empty()) { + return CacheEvictionResult::kNotNeeded; + } + const CPUCacheKey item_key = m_cpu_key_list.front(); + if (item_key == 0) { + return CacheEvictionResult::kFailed; + } + const CPUMapIt item_key_iterator = m_cpu_item_map.find(item_key); + assert(item_key_iterator != m_cpu_item_map.end()); + + CPUCacheValue image_pixel_data = item_key_iterator->second.second; + m_cpu_used_bytes -= image_pixel_data.byte_count(); + image_pixel_data.deallocate_pixels(); + + m_cpu_item_map.erase(item_key_iterator); + m_cpu_key_list.pop_front(); + + cpu_remove_item_from_group(item_key); + + MMSOLVER_MAYA_VRB( + "mmsolver::ImageCache::cpu_evict_one_item: " + "after m_cpu_used_bytes=" + << m_cpu_used_bytes); + return CacheEvictionResult::kSuccess; +} + +CacheEvictionResult ImageCache::gpu_evict_enough_for_new_item( + MHWRender::MTextureManager *texture_manager, + const size_t new_memory_chunk_size) { + const bool verbose = false; + + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::gpu_evict_enough_for_new_item: "); + + if (m_gpu_key_list.empty() || + (m_gpu_item_map.size() <= m_gpu_item_count_minumum)) { + return CacheEvictionResult::kNotNeeded; + } + + CacheEvictionResult result = CacheEvictionResult::kSuccess; + // If we are at capacity remove the least recently used items + // until we have enough room to store 'new_memory_chunk_size'. + size_t new_used_bytes = m_gpu_used_bytes + new_memory_chunk_size; + MMSOLVER_MAYA_VRB( + "mmsolver::ImageCache::gpu_evict_enough_for_new_item: " + "new_used_bytes=" + << new_used_bytes); + while (!m_gpu_item_map.empty() && + (m_gpu_item_map.size() > m_gpu_item_count_minumum) && + (new_used_bytes > m_gpu_capacity_bytes)) { + const CacheEvictionResult evict_result = + ImageCache::gpu_evict_one_item(texture_manager); + if (evict_result != CacheEvictionResult::kSuccess) { + result = evict_result; + break; + } + new_used_bytes = m_gpu_used_bytes + new_memory_chunk_size; + MMSOLVER_MAYA_VRB( + "mmsolver::ImageCache::gpu_evict_enough_for_new_item: " + "new_used_bytes=" + << new_used_bytes); + } + + return result; +} + +CacheEvictionResult ImageCache::cpu_evict_enough_for_new_item( + const size_t new_memory_chunk_size) { + const bool verbose = false; + + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::cpu_evict_enough_for_new_item: "); + + if (m_cpu_key_list.empty() || + (m_cpu_item_map.size() <= m_cpu_item_count_minumum)) { + return CacheEvictionResult::kNotNeeded; + } + + CacheEvictionResult result = CacheEvictionResult::kSuccess; + // If we are at capacity remove the least recently used items + // until we have enough room to store 'new_memory_chunk_size'. + size_t new_used_bytes = m_cpu_used_bytes + new_memory_chunk_size; + MMSOLVER_MAYA_VRB( + "mmsolver::ImageCache::cpu_evict_enough_for_new_item: " + "new_used_bytes=" + << new_used_bytes); + while (!m_cpu_item_map.empty() && + (m_cpu_item_map.size() > m_cpu_item_count_minumum) && + (new_used_bytes > m_cpu_capacity_bytes)) { + const CacheEvictionResult evict_result = + ImageCache::cpu_evict_one_item(); + if (evict_result != CacheEvictionResult::kSuccess) { + result = evict_result; + break; + } + new_used_bytes = m_cpu_used_bytes + new_memory_chunk_size; + MMSOLVER_MAYA_VRB( + "mmsolver::ImageCache::cpu_evict_enough_for_new_item: " + "new_used_bytes=" + << new_used_bytes); + } + + return result; +} + +size_t ImageCache::gpu_remove_item_from_group(const GPUCacheKey item_key) { + const bool verbose = false; + + MMSOLVER_MAYA_VRB( + "mmsolver::ImageCache::gpu_remove_item_from_group: " + "item_key=" + << item_key); + + size_t count = 0; + for (auto it = m_gpu_group_map.begin(); it != m_gpu_group_map.end(); + /* no increment */) { + const GPUCacheString group_name = it->first; + GPUGroupSet &values_set = it->second; + + const GPUGroupKey group_key = mmsolver::hash::make_hash(group_name); + + // NOTE: This is a O(n) linear operation. + for (auto it2 = values_set.begin(); it2 != values_set.end(); + /* no increment */) { + const GPUGroupKey item_value_hash = mmsolver::hash::make_hash(*it2); + + if (item_key == item_value_hash) { + it2 = values_set.erase(it2); + count += 1; + MMSOLVER_MAYA_VRB( + "mmsolver::ImageCache::gpu_remove_item_from_group: " + << "group_key=" << group_key << " item_key=" << item_key + << " item_value_hash=" << item_value_hash << " - got it"); + } else { + MMSOLVER_MAYA_VRB( + "mmsolver::ImageCache::gpu_remove_item_from_group: " + << "item_key=" << item_key + << " item_value_hash=" << item_value_hash); + ++it2; + } + } + + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::gpu_remove_item_from_group: " + << "values_set.size()=" << values_set.size()); + + if (values_set.size() == 0) { + it = m_gpu_group_map.erase(it); + } else { + ++it; + } + } + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::gpu_remove_item_from_group: " + << "count=" << count); + + return count; +} + +size_t ImageCache::cpu_remove_item_from_group(const CPUCacheKey item_key) { + const bool verbose = false; + + MMSOLVER_MAYA_VRB( + "mmsolver::ImageCache::cpu_remove_item_from_group: " + "item_key=" + << item_key); + + size_t count = 0; + for (auto it = m_cpu_group_map.begin(); it != m_cpu_group_map.end(); + /* no increment */) { + const CPUCacheString group_name = it->first; + CPUGroupSet &values_set = it->second; + + const CPUGroupKey group_key = mmsolver::hash::make_hash(group_name); + + // NOTE: This is a O(n) linear operation. + for (auto it2 = values_set.begin(); it2 != values_set.end(); + /* no increment */) { + const CPUGroupKey item_value_hash = mmsolver::hash::make_hash(*it2); + + if (item_key == item_value_hash) { + it2 = values_set.erase(it2); + count += 1; + MMSOLVER_MAYA_VRB( + "mmsolver::ImageCache::cpu_remove_item_from_group: " + << "group_key=" << group_key << " item_key=" << item_key + << " item_value_hash=" << item_value_hash << " - got it"); + } else { + MMSOLVER_MAYA_VRB( + "mmsolver::ImageCache::cpu_remove_item_from_group: " + << "item_key=" << item_key + << " item_value_hash=" << item_value_hash); + ++it2; + } + } + + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::cpu_remove_item_from_group: " + << "values_set.size()=" << values_set.size()); + + if (values_set.size() == 0) { + it = m_cpu_group_map.erase(it); + } else { + ++it; + } + } + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::cpu_remove_item_from_group: " + << "count=" << count); + + return count; +} + +bool ImageCache::gpu_erase_item(MHWRender::MTextureManager *texture_manager, + const GPUCacheString &file_path) { + const bool verbose = false; + const GPUGroupKey item_key = mmsolver::hash::make_hash(file_path); + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::gpu_erase_item: " + << "item_key=" << item_key << " file_path=\"" + << file_path.c_str() << "\""); + return ImageCache::gpu_erase_item(texture_manager, item_key); +} + +bool ImageCache::gpu_erase_item(MHWRender::MTextureManager *texture_manager, + const GPUCacheKey item_key) { + const bool verbose = false; + + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::gpu_erase_item: " + << "item_key=" << item_key); + const GPUMapIt item_search = m_gpu_item_map.find(item_key); + const bool item_found = item_search != m_gpu_item_map.end(); + if (item_found) { + GPUCacheValue texture_data = item_search->second.second; + m_gpu_used_bytes -= texture_data.byte_count(); + texture_data.deallocate_texture(texture_manager); + + m_gpu_item_map.erase(item_search); + + // NOTE: This is a O(n) linear operation, and can be very + // slow since the list items is spread out in memory. + m_gpu_key_list.remove(item_key); + + // NOTE: This is a O(n) linear operation. + gpu_remove_item_from_group(item_key); + } + return item_found; +} + +bool ImageCache::cpu_erase_item(const CPUCacheString &file_path) { + const bool verbose = false; + const CPUGroupKey item_key = mmsolver::hash::make_hash(file_path); + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::cpu_erase_item: " + << "item_key=" << item_key << " file_path=\"" + << file_path.c_str() << "\""); + return ImageCache::cpu_erase_item(item_key); +} + +bool ImageCache::cpu_erase_item(const CPUCacheKey item_key) { + const bool verbose = false; + + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::cpu_erase_item: " + << "item_key=" << item_key); + const CPUMapIt item_search = m_cpu_item_map.find(item_key); + const bool item_found = item_search != m_cpu_item_map.end(); + if (item_found) { + CPUCacheValue image_pixel_data = item_search->second.second; + m_cpu_used_bytes -= image_pixel_data.byte_count(); + image_pixel_data.deallocate_pixels(); + + m_cpu_item_map.erase(item_search); + + // NOTE: This is a O(n) linear operation, and can be very + // slow since the list items is spread out in memory. + m_cpu_key_list.remove(item_key); + + // NOTE: This is a O(n) linear operation. + cpu_remove_item_from_group(item_key); + } + return item_found; +} + +size_t ImageCache::gpu_erase_group_items( + MHWRender::MTextureManager *texture_manager, + const GPUCacheString &group_name) { + const bool verbose = false; + + if (verbose) { + const GPUGroupKey group_key = mmsolver::hash::make_hash(group_name); + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::gpu_erase_group_items: " + << "group_key=" << group_key << " group_name=\"" + << group_name.c_str() << "\""); + } + + // Loop until all items in the group are removed. + size_t iteration = 0; + size_t count = 0; + while (true) { + const GPUGroupMapIt group_search = m_gpu_group_map.find(group_name); + const bool group_found = group_search != m_gpu_group_map.end(); + if (!group_found) { + if (iteration == 0) { + // When a user gives the wrong group name, we will + // print the warning, but if the group name is not + // valid after a removal (iteration > 0), then the + // user shouldn't be notified of that the group is not + // available. + MMSOLVER_MAYA_WRN( + "mmsolver::ImageCache: gpu_erase_group_items: " + "Group name \"" + << group_name << "\" not found!"); + } + break; + } + + GPUGroupSet &values_set = group_search->second; + GPUCacheString value = *values_set.begin(); + // NOTE: This call will invalidate the 'values_set' value, + // because 'ImageCache::m_gpu_group_map' is changed by + // 'ImageCache::gpu_remove_item_from_group()' via + // 'ImageCache::gpu_erase_item()'. + const bool ok = ImageCache::gpu_erase_item(texture_manager, value); + count += static_cast(ok); + + iteration++; + } + + return count; +} + +size_t ImageCache::cpu_erase_group_items(const CPUCacheString &group_name) { + const bool verbose = false; + + if (verbose) { + const CPUGroupKey group_key = mmsolver::hash::make_hash(group_name); + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::cpu_erase_group_items: " + << "group_key=" << group_key << " group_name=\"" + << group_name.c_str() << "\""); + } + + // Loop until all items in the group are removed. + size_t iteration = 0; + size_t count = 0; + while (true) { + const CPUGroupMapIt group_search = m_cpu_group_map.find(group_name); + const bool group_found = group_search != m_cpu_group_map.end(); + if (!group_found) { + if (iteration == 0) { + // When a user gives the wrong group name, we will + // print the warning, but if the group name is not + // valid after a removal (iteration > 0), then the + // user shouldn't be notified of that the group is not + // available. + MMSOLVER_MAYA_WRN( + "mmsolver::ImageCache: cpu_erase_group_items: " + "Group name \"" + << group_name << "\" not found!"); + } + break; + } + + CPUGroupSet &values_set = group_search->second; + CPUCacheString value = *values_set.begin(); + // NOTE: This call will invalidate the 'values_set' value, + // because 'ImageCache::m_cpu_group_map' is changed by + // 'ImageCache::cpu_remove_item_from_group()' via + // 'ImageCache::cpu_erase_item()'. + const bool ok = ImageCache::cpu_erase_item(value); + count += static_cast(ok); + + iteration++; + } + + return count; +} + +} // namespace image +} // namespace mmsolver diff --git a/src/mmSolver/image/ImageCache.h b/src/mmSolver/image/ImageCache.h new file mode 100644 index 000000000..225f26c21 --- /dev/null +++ b/src/mmSolver/image/ImageCache.h @@ -0,0 +1,410 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#ifndef MM_SOLVER_IMAGE_IMAGE_CACHE_H +#define MM_SOLVER_IMAGE_IMAGE_CACHE_H + +// STL +#include +#include +#include +#include +#include + +// Maya +#include +#include + +// Maya Viewport 2.0 +#include + +// MM Solver +#include +#include + +#include "ImagePixelData.h" +#include "PixelDataType.h" +#include "TextureData.h" +#include "mmSolver/utilities/debug_utils.h" +#include "mmSolver/utilities/number_utils.h" + +namespace mmsolver { +namespace image { + +// The ImageCache, used to load and cache images into GPU, CPU and +// Disk. +// +// Least-Recently-Used (LRU) Cache using standard C++11 data +// structures. +// +// Inspired by: +// https://web.archive.org/web/20161203001546/http://timday.bitbucket.org/lru.html +// +// Singleton design pattern for C++11: +// https://stackoverflow.com/a/1008289 +// +// TODO: Use 4 layers of reading textures. +// +// Layer 0: The texture is GPU memory, in the +// MHWRender::MTextureManager. If the texture is not in GPU memory, +// look in the (CPU) Image Cache. +// +// Layer 1: The texture pixel data is stored in RAM, in the (CPU) +// Image Cache. If the texture pixel data cannot be found in the Image +// Cache, look in the Disk Cache. +// +// Layer 2: The texture pixel data is stored on disk in a fast-to-read +// (8-bit) image format (https://qoiformat.org/ +// https://crates.io/crates/qoi) which as been converted to sRGB +// colour space. If the file cannot be found, read the original file +// path. +// +// Layer 3: The original file path should be read and decoded, and +// then placed into a queue to be saved into the disk cache. The queue +// of images are processed off the main thread, so that the +// interactive session does not slow down. +// +// TODO: Support converting to 8-bit LDR image pixels before +// storing. For example converting to sRGB colour space, and applying +// tone-mapping to avoid clipping colours, so that as much detail is +// retained - even if it's not colour accurate. +// + +enum class CacheEvictionResult : uint8_t { kSuccess = 0, kNotNeeded, kFailed }; + +struct ImageCache { + using HashValue = uint64_t; + + using GPUCacheKey = HashValue; + using CPUCacheKey = HashValue; + using GPUCacheValue = TextureData; + using CPUCacheValue = ImagePixelData; + using GPUCacheString = std::string; + using CPUCacheString = std::string; + + using GPUVectorString = std::vector; + using CPUVectorString = std::vector; + + using GPUKeyList = std::list; + using CPUKeyList = std::list; + using GPUKeyListIt = GPUKeyList::iterator; + using CPUKeyListIt = CPUKeyList::iterator; + + using GPUMap = + std::unordered_map>; + using CPUMap = + std::unordered_map>; + using GPUMapIt = GPUMap::iterator; + using CPUMapIt = CPUMap::iterator; + + using GPUGroupKey = HashValue; + using CPUGroupKey = HashValue; + using GPUGroupSet = std::unordered_set; + using CPUGroupSet = std::unordered_set; + using GPUGroupSetIt = GPUGroupSet::iterator; + using CPUGroupSetIt = CPUGroupSet::iterator; + + using GPUGroupMap = std::unordered_map; + using CPUGroupMap = std::unordered_map; + using GPUGroupMapIt = GPUGroupMap::iterator; + using CPUGroupMapIt = CPUGroupMap::iterator; + +public: + static ImageCache &getInstance() { + static ImageCache instance; // Guaranteed to be destroyed. + // Instantiated on first use. + return instance; + } + +private: + // Constructor. The {} brackets are needed here. + ImageCache() + : m_gpu_capacity_bytes(0) + , m_cpu_capacity_bytes(0) + , m_gpu_item_count_minumum(1) + , m_cpu_item_count_minumum(1) + , m_gpu_used_bytes(0) + , m_cpu_used_bytes(0) {} + + template + void move_iterator_to_back_of_key_list(KeyList &key_list, + KeyListIt key_list_iterator) { + // The back of the list is the "most recently used" key. + key_list.splice(key_list.end(), key_list, key_list_iterator); + } + + // Evict cached items until a new memory chunk can fit in. + CacheEvictionResult gpu_evict_enough_for_new_item( + MHWRender::MTextureManager *texture_manager, + const size_t new_memory_chunk_size); + CacheEvictionResult cpu_evict_enough_for_new_item( + const size_t new_memory_chunk_size); + + // Add group name into cache, associated with the file path. + // + // This is only used internally as a helper method. + bool gpu_insert_group(const GPUCacheString &group_name, + const GPUCacheString &file_path); + bool cpu_insert_group(const CPUCacheString &group_name, + const CPUCacheString &file_path); + + // Removes the given item key from the groups. + size_t gpu_remove_item_from_group(const GPUCacheKey item_key); + size_t cpu_remove_item_from_group(const CPUCacheKey item_key); + +public: + // Get the capacity of the cache. + size_t get_gpu_capacity_bytes() const { + const bool verbose = false; + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::get_gpu_capacity_bytes: " + << "m_gpu_capacity_bytes=" << m_gpu_capacity_bytes); + return m_gpu_capacity_bytes; + } + size_t get_cpu_capacity_bytes() const { + const bool verbose = false; + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::get_cpu_capacity_bytes: " + << "m_cpu_capacity_bytes=" << m_cpu_capacity_bytes); + return m_cpu_capacity_bytes; + } + + // Get amount of bytes used by the cache. + size_t get_gpu_used_bytes() const { + const bool verbose = false; + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::get_gpu_used_bytes: " + << "m_gpu_used_bytes=" << m_gpu_used_bytes); + return m_gpu_used_bytes; + } + size_t get_cpu_used_bytes() const { + const bool verbose = false; + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::get_cpu_used_bytes: " + << "m_cpu_used_bytes=" << m_cpu_used_bytes); + return m_cpu_used_bytes; + } + + // Get the number of items in the cache. + size_t get_gpu_item_count() const { + const bool verbose = false; + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::get_gpu_item_count: " + << "m_gpu_item_map.size()=" << m_gpu_item_map.size()); + return m_gpu_item_map.size(); + } + size_t get_cpu_item_count() const { + const bool verbose = false; + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::get_cpu_item_count: " + << "m_cpu_item_map.size()=" << m_cpu_item_map.size()); + return m_cpu_item_map.size(); + } + + // Set the capacity of the cache. + void set_gpu_capacity_bytes(MHWRender::MTextureManager *texture_manager, + const size_t value); + void set_cpu_capacity_bytes(const size_t value); + + // Debug functions to display internals of the cache. + MString generate_cache_brief_text() const; + void print_cache_brief() const; + + // Get the number of groups in the cache. + size_t get_gpu_group_count() const { + const bool verbose = false; + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::get_gpu_group_count: " + << "m_gpu_group_map.size()=" + << m_gpu_group_map.size()); + return m_gpu_group_map.size(); + } + size_t get_cpu_group_count() const { + const bool verbose = false; + MMSOLVER_MAYA_VRB("mmsolver::ImageCache::get_cpu_group_count: " + << "m_cpu_group_map.size()=" + << m_cpu_group_map.size()); + return m_cpu_group_map.size(); + } + + // Get sorted vector of group names. + void gpu_group_names(GPUVectorString &out_group_names) const; + void cpu_group_names(CPUVectorString &out_group_names) const; + + // Get the number of items in the given group name. + size_t gpu_group_item_count(const GPUCacheString &group_name) const; + size_t cpu_group_item_count(const CPUCacheString &group_name) const; + + // Item names in a group. + bool gpu_group_item_names(const GPUCacheString &group_name, + GPUVectorString &out_group_item_names) const; + bool cpu_group_item_names(const CPUCacheString &group_name, + CPUVectorString &out_group_item_names) const; + + // TODO: Set/Get Disk cache location. Used to find disk-cached + // files. This should be a directory on a very fast disk. + // + // This class should allow a set of threads used for writing CPU + // cache data to image files. These files should be very fast to + // read into the CPU memory. + // + // We would need methods such as: + // + // bool write_cpu_item_to_disk(const CPUCacheKey &key, const bool + // background=true); + // + // bool wait_for_disk_writes(); + // + // bool disk_find(const DiskCacheKey &key); + // + // bool disk_find_file_path(const DiskCacheKey &key); + + // Insert pixels into CPU cache. + // + // If the file path is already in the image cache, the previous + // value is erased. + // + // Returns true/false, if the the data was inserted or not. + bool cpu_insert_item(const CPUCacheString &group_name, + const CPUCacheString &file_path, + const CPUCacheValue &image_pixel_data); + + // Insert and upload some pixels to the GPU and cache the result. + // + // Returns the GPUCacheValue inserted into the GPU cache. + GPUCacheValue gpu_insert_item(MHWRender::MTextureManager *texture_manager, + const GPUCacheString &group_name, + const GPUCacheString &file_path, + const CPUCacheValue &image_pixel_data); + + // Find the item key in the GPU/CPU cache. + // + // Returns the GPUCacheValue at the item key, or nullptr. + // + // Returns the CPUCacheValue at the item key, or constructs a default + // value and returns it. + GPUCacheValue gpu_find_item(const GPUCacheString &file_path); + GPUCacheValue gpu_find_item(const GPUCacheKey item_key); + CPUCacheValue cpu_find_item(const CPUCacheString &file_path); + CPUCacheValue cpu_find_item(const CPUCacheKey item_key); + + // TODO: Add a 'gpu/cpu_prefetch()' method, used to add the images + // into a prefetching queue. + // + // For the GPU, the images are uploaded to the GPU, + // asynchronously. + // + // For the CPU, the images are read from disk cache, if they are + // not in memory. + // + // There must also be a way to wait for all prefetched images to + // be loaded before continuing - a "blocking" function call. + + // Evict the least recently used item from the GPU cache. + // + // Returns true/false, if an item was removed from the cache or + // not. + CacheEvictionResult gpu_evict_one_item( + MHWRender::MTextureManager *texture_manager); + CacheEvictionResult cpu_evict_one_item(); + + // Remove the file path from the image GPU/CPU cache. + // + // NOTE: Due to the way the LRU cache works, this can be quite + // slow to to remove a specific file path from the cache. + // + // Returns true/false, if the item was removed or not. + bool gpu_erase_item(MHWRender::MTextureManager *texture_manager, + const GPUCacheString &file_path); + bool gpu_erase_item(MHWRender::MTextureManager *texture_manager, + const GPUCacheKey item_key); + bool cpu_erase_item(const CPUCacheString &file_path); + bool cpu_erase_item(const CPUCacheKey item_key); + + // Erase all items in the given group_name. + size_t gpu_erase_group_items(MHWRender::MTextureManager *texture_manager, + const GPUCacheString &group_name); + size_t cpu_erase_group_items(const GPUCacheString &group_name); + + // C++ 11; deleting the methods we don't want to ensure they can never be + // used. + // + // Note: Scott Meyers mentions in his Effective Modern C++ book, + // that deleted functions should generally be public as it results + // in better error messages due to the compilers behavior to check + // accessibility before deleted status. + ImageCache(ImageCache const &) = delete; + void operator=(ImageCache const &) = delete; + +private: + // Amount of memory capacity. + size_t m_gpu_capacity_bytes; + size_t m_cpu_capacity_bytes; + + // How much memory is currently stored in the cached? + size_t m_gpu_used_bytes; + size_t m_cpu_used_bytes; + + // The minimum number of items that are allowed in the cache. We + // want to retain a fixed number of images, to avoid invalid + // conditions in the cache. + size_t m_gpu_item_count_minumum; + size_t m_cpu_item_count_minumum; + + // A Map of keys to values. + // + // An unordered hash map is used to map file path strings to CPU + // Image Data and GPU texture resources. + // + // These maps also contain pointers into the 'key list' data + // structures, allowing us to map from the values to the 'key + // list's. + GPUMap m_gpu_item_map; + CPUMap m_cpu_item_map; + + // List of keys. + // + // A double-ended linked list (std::list) is used because we want + // to be able to push/pop and access the (front/back) ends of the + // list as fast as possible. + // + // Additionally, the linked list ensure that the pointers of each + // item in the list stays consistent over time, so that pointers + // into the memory can be used directly without fear that the + // underlying memory will be cleared. + // + // The 'front' of the list is the "least recently used" key. The + // 'back' of the list is the "most recently used" key. + GPUKeyList m_gpu_key_list; + CPUKeyList m_cpu_key_list; + + // A Map of groups to a Set of key values. + // + // This map can be used to find all the loaded values used by an + // image sequence. + GPUGroupMap m_gpu_group_map; + CPUGroupMap m_cpu_group_map; +}; + +MTexture *read_texture_image_file(MHWRender::MTextureManager *texture_manager, + ImageCache &image_cache, MImage &temp_image, + mmimage::ImagePixelBuffer &temp_pixel_buffer, + mmimage::ImageMetaData &temp_meta_data, + const MString &file_pattern, + const MString &file_path, + const bool do_texture_update); +} // namespace image +} // namespace mmsolver + +#endif // MM_SOLVER_IMAGE_IMAGE_CACHE_H diff --git a/src/mmSolver/image/ImagePixelData.cpp b/src/mmSolver/image/ImagePixelData.cpp new file mode 100644 index 000000000..e1da8cbc0 --- /dev/null +++ b/src/mmSolver/image/ImagePixelData.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#include "ImagePixelData.h" + +// Get M_PI constant +#define _USE_MATH_DEFINES +#include + +// STL +#include +#include +#include +#include + +// Maya +#include +#include + +// Maya Viewport 2.0 +#include + +// MM Solver +#include + +#include "PixelDataType.h" +#include "mmSolver/mayahelper/maya_utils.h" +#include "mmSolver/render/shader/shader_utils.h" +#include "mmSolver/shape/constant_texture_data.h" +#include "mmSolver/utilities/number_utils.h" +#include "mmSolver/utilities/path_utils.h" + +namespace mmsolver { +namespace image { + +bool ImagePixelData::allocate_pixels(const uint32_t width, + const uint32_t height, + const uint8_t num_channels, + const PixelDataType pixel_data_type) { + m_width = width; + m_height = height; + m_num_channels = num_channels; + m_pixel_data_type = pixel_data_type; + + const size_t pixel_data_byte_count = ImagePixelData::byte_count(); + + if (pixel_data_byte_count == 0) { + MMSOLVER_MAYA_ERR("mmsolver::ImagePixelData:allocate_pixels: " + << "Invalid image size for allocating pixel data!" + << " width=" << m_width << " height=" << m_height + << " num_channels=" << m_num_channels + << " pixel_data_type=" + << static_cast(m_pixel_data_type)); + return false; + } + + bool ok = false; + void *data = std::malloc(pixel_data_byte_count); + if (data) { + m_pixel_data = data; + ok = true; + } else { + ok = false; + MMSOLVER_MAYA_ERR("mmsolver::ImagePixelData:allocate_pixels: " + << "Could not allocate pixel data!" + << " requested=" << pixel_data_byte_count << "B" + << " requested=" + << (static_cast(pixel_data_byte_count) * 1e-6) + << "MB"); + } + + return ok; +} + +void ImagePixelData::deallocate_pixels() { std::free(m_pixel_data); } + +} // namespace image +} // namespace mmsolver diff --git a/src/mmSolver/image/ImagePixelData.h b/src/mmSolver/image/ImagePixelData.h new file mode 100644 index 000000000..ca9fbe5bb --- /dev/null +++ b/src/mmSolver/image/ImagePixelData.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#ifndef MM_SOLVER_IMAGE_IMAGE_PIXEL_DATA_H +#define MM_SOLVER_IMAGE_IMAGE_PIXEL_DATA_H + +// STL +#include +#include +#include +#include + +// Maya +#include +#include + +// Maya Viewport 2.0 +#include + +// MM Solver +#include + +#include "PixelDataType.h" +#include "mmSolver/utilities/debug_utils.h" + +namespace mmsolver { +namespace image { + +struct ImagePixelData { + ImagePixelData() + : m_pixel_data(nullptr) + , m_width(0) + , m_height(0) + , m_num_channels(0) + , m_pixel_data_type(PixelDataType::kUnknown){}; + + ImagePixelData(void *pixel_data, const uint32_t width, + const uint32_t height, const uint8_t num_channels, + const PixelDataType pixel_data_type) + : m_pixel_data(pixel_data) + , m_width(width) + , m_height(height) + , m_num_channels(num_channels) + , m_pixel_data_type(pixel_data_type){}; + + ~ImagePixelData() = default; + + bool allocate_pixels(const uint32_t width, const uint32_t height, + const uint8_t num_channels, + const PixelDataType pixel_data_type); + void deallocate_pixels(); + + bool is_valid() const { + return (m_pixel_data != nullptr) && (m_width != 0) && (m_height != 0) && + (m_num_channels != 0) && + (m_pixel_data_type != PixelDataType::kUnknown); + }; + + void *pixel_data() const { return m_pixel_data; }; + uint32_t width() const { return m_width; } + uint32_t height() const { return m_height; } + uint8_t num_channels() const { return m_num_channels; } + PixelDataType pixel_data_type() const { return m_pixel_data_type; } + + size_t byte_count() const { + const uint8_t bytes_per_channel = + convert_pixel_data_type_to_bytes_per_channel(m_pixel_data_type); + return m_width * m_height * m_num_channels * bytes_per_channel; + } + +private: + void *m_pixel_data; + uint32_t m_width; + uint32_t m_height; + uint8_t m_num_channels; + PixelDataType m_pixel_data_type; +}; + +} // namespace image +} // namespace mmsolver + +#endif // MM_SOLVER_IMAGE_IMAGE_PIXEL_DATA_H diff --git a/src/mmSolver/image/PixelDataType.cpp b/src/mmSolver/image/PixelDataType.cpp new file mode 100644 index 000000000..7eaba9636 --- /dev/null +++ b/src/mmSolver/image/PixelDataType.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#include "PixelDataType.h" + +namespace mmsolver { +namespace image {} // namespace image +} // namespace mmsolver diff --git a/src/mmSolver/image/PixelDataType.h b/src/mmSolver/image/PixelDataType.h new file mode 100644 index 000000000..995d624df --- /dev/null +++ b/src/mmSolver/image/PixelDataType.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#ifndef MM_SOLVER_IMAGE_PIXEL_DATA_TYPE_H +#define MM_SOLVER_IMAGE_PIXEL_DATA_TYPE_H + +// STL +#include +#include +#include +#include + +// Maya +#include +#include + +// Maya Viewport 2.0 +#include + +// MM Solver +#include + +#include "mmSolver/utilities/debug_utils.h" + +namespace mmsolver { +namespace image { + +enum class PixelDataType : uint8_t { + kU8 = 0, + kF32, + + // Always the second to last, so it's equal to the number of + // options. + kCount, + + // When the pixel data type is not initialized or invalid. + kUnknown = 255, +}; + +static uint8_t convert_pixel_data_type_to_bytes_per_channel( + PixelDataType pixel_data_type) { + uint8_t bytes_per_channel = 0; + if (pixel_data_type == PixelDataType::kU8) { + // 8-bit unsigned integers use 1 byte. + bytes_per_channel = 1; + } else if (pixel_data_type == PixelDataType::kF32) { + // 32-bit floats use 4 bytes. + bytes_per_channel = 4; + } else { + bytes_per_channel = 0; + MMSOLVER_MAYA_ERR( + "mmsolver::image::convert_pixel_data_type_to_bytes_per_channel: " + << "Invalid pixel type is " << static_cast(pixel_data_type)); + } + return bytes_per_channel; +} + +static MHWRender::MRasterFormat convert_pixel_data_type_to_texture_format( + const PixelDataType pixel_data_type) { + if (pixel_data_type == PixelDataType::kU8) { + // Assumes the 8-bit data is "RGBA". + return MHWRender::kR8G8B8A8_UNORM; + } else if (pixel_data_type == PixelDataType::kF32) { + return MHWRender::kR32G32B32A32_FLOAT; + } + + return MHWRender::MRasterFormat(); +} + +} // namespace image +} // namespace mmsolver + +#endif // MM_SOLVER_IMAGE_PIXEL_DATA_TYPE_H diff --git a/src/mmSolver/image/TextureData.cpp b/src/mmSolver/image/TextureData.cpp new file mode 100644 index 000000000..fae747514 --- /dev/null +++ b/src/mmSolver/image/TextureData.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#include "TextureData.h" + +// Get M_PI constant +#define _USE_MATH_DEFINES +#include + +// STL +#include +#include +#include +#include + +// Maya +#include +#include + +// Maya Viewport 2.0 +#include + +// MM Solver +#include + +#include "ImagePixelData.h" +#include "PixelDataType.h" +#include "mmSolver/mayahelper/maya_utils.h" +#include "mmSolver/render/shader/shader_utils.h" +#include "mmSolver/shape/constant_texture_data.h" +#include "mmSolver/utilities/number_utils.h" +#include "mmSolver/utilities/path_utils.h" + +namespace mmsolver { +namespace image { + +bool TextureData::allocate_texture(MHWRender::MTextureManager *texture_manager, + void *pixel_data, const uint32_t width, + const uint32_t height, + const uint8_t num_channels, + const PixelDataType pixel_data_type) { + assert(texture_manager != nullptr); + + m_width = width; + m_height = height; + m_num_channels = num_channels; + m_pixel_data_type = pixel_data_type; + + const uint8_t bytes_per_channel = + convert_pixel_data_type_to_bytes_per_channel(pixel_data_type); + const size_t pixel_data_byte_count = TextureData::byte_count(); + + if (pixel_data_byte_count == 0) { + MMSOLVER_MAYA_ERR("mmsolver::TextureData:allocate_pixels: " + << "Invalid image size for allocating pixel data!" + << " width=" << m_width << " height=" << m_height + << " num_channels=" << m_num_channels + << " pixel_data_type=" + << static_cast(m_pixel_data_type)); + return false; + } + + bool ok = false; + + MHWRender::MTextureDescription texture_desc; + texture_desc.setToDefault2DTexture(); + texture_desc.fWidth = m_width; + texture_desc.fHeight = m_height; + texture_desc.fDepth = 1; + + texture_desc.fMipmaps = 1; + texture_desc.fArraySlices = 1; + texture_desc.fTextureType = MHWRender::kImage2D; + texture_desc.fFormat = + convert_pixel_data_type_to_texture_format(m_pixel_data_type); + + texture_desc.fBytesPerRow = m_num_channels * bytes_per_channel * m_width; + texture_desc.fBytesPerSlice = texture_desc.fBytesPerRow * m_height; + + // No need for MIP-maps. + const bool generate_mip_maps = false; + + // If the texture name provided is an empty string then the + // texture will not be cached as part of the internal texture + // caching system. Thus each such call to this method will + // create a new texture. + + // Pre-allocated empty string to be re-used inside the class, to + // avoid reallocations. + const MString g_empty_string; + + MTexture *texture = texture_manager->acquireTexture( + g_empty_string, texture_desc, pixel_data, generate_mip_maps); + if (texture) { + m_texture = texture; + ok = true; + } else { + ok = false; + MMSOLVER_MAYA_ERR("mmsolver::TextureData:allocate_texture: " + << "Could not acquire texture!" + << " requested=" << pixel_data_byte_count << "B" + << " requested=" + << (static_cast(pixel_data_byte_count) * 1e-6) + << "MB"); + } + + return ok; +} + +void TextureData::deallocate_texture( + MHWRender::MTextureManager *texture_manager) { + assert(texture_manager != nullptr); + texture_manager->releaseTexture(m_texture); +} + +} // namespace image +} // namespace mmsolver diff --git a/src/mmSolver/image/TextureData.h b/src/mmSolver/image/TextureData.h new file mode 100644 index 000000000..dc46f38d7 --- /dev/null +++ b/src/mmSolver/image/TextureData.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#ifndef MM_SOLVER_IMAGE_TEXTURE_DATA_H +#define MM_SOLVER_IMAGE_TEXTURE_DATA_H + +// STL +#include +#include +#include +#include + +// Maya +#include +#include + +// Maya Viewport 2.0 +#include + +// MM Solver +#include + +#include "PixelDataType.h" +#include "mmSolver/utilities/debug_utils.h" + +namespace mmsolver { +namespace image { + +struct TextureData { + TextureData() + : m_texture(nullptr) + , m_width(0) + , m_height(0) + , m_num_channels(0) + , m_pixel_data_type(PixelDataType::kUnknown){}; + + TextureData(MTexture *texture, const uint32_t width, const uint32_t height, + const uint8_t num_channels, const PixelDataType pixel_data_type) + : m_texture(texture) + , m_width(width) + , m_height(height) + , m_num_channels(num_channels) + , m_pixel_data_type(pixel_data_type){}; + + ~TextureData() = default; + + bool allocate_texture(MHWRender::MTextureManager *texture_manager, + void *pixel_data, const uint32_t width, + const uint32_t height, const uint8_t num_channels, + const PixelDataType pixel_data_type); + void deallocate_texture(MHWRender::MTextureManager *texture_manager); + + bool is_valid() const { + return (m_texture != nullptr) && (m_width != 0) && (m_height != 0) && + (m_num_channels != 0) && + (m_pixel_data_type != PixelDataType::kUnknown); + }; + + MTexture *texture() const { return m_texture; }; + uint32_t width() const { return m_width; } + uint32_t height() const { return m_height; } + uint8_t num_channels() const { return m_num_channels; } + PixelDataType pixel_data_type() const { return m_pixel_data_type; } + + size_t byte_count() const { + const uint8_t bytes_per_channel = + convert_pixel_data_type_to_bytes_per_channel(m_pixel_data_type); + return m_width * m_height * m_num_channels * bytes_per_channel; + } + +private: + MTexture *m_texture; + uint32_t m_width; + uint32_t m_height; + uint8_t m_num_channels; + PixelDataType m_pixel_data_type; +}; + +} // namespace image +} // namespace mmsolver + +#endif // MM_SOLVER_IMAGE_TEXTURE_DATA_H diff --git a/src/mmSolver/image/image_convert.cpp b/src/mmSolver/image/image_convert.cpp new file mode 100644 index 000000000..beaaf62e5 --- /dev/null +++ b/src/mmSolver/image/image_convert.cpp @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2022, 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#include "image_convert.h" + +// Get M_PI constant +#define _USE_MATH_DEFINES +#include + +// STL +#include +#include +#include +#include +#include + +// Maya +#include +#include +#include + +// MM Solver +#include +#include + +#include "PixelDataType.h" +#include "mmSolver/mayahelper/maya_utils.h" +#include "mmSolver/render/shader/shader_utils.h" +#include "mmSolver/shape/constant_texture_data.h" +#include "mmSolver/utilities/number_utils.h" +#include "mmSolver/utilities/path_utils.h" + +namespace mmsolver { +namespace image { + +using mmmath::clamp; +using mmmath::fast_pow; + +MStatus guess_output_format_pixel_type(const MString &in_output_format, + MImage::MPixelType &out_pixel_type) { + MStatus status = MStatus::kSuccess; + + MString output_format(in_output_format); + output_format.toLowerCase(); + + if (output_format == MString("exr")) { + out_pixel_type = MImage::kFloat; + } else if (output_format == MString("hdr")) { + out_pixel_type = MImage::kFloat; + } else { + out_pixel_type = MImage::kByte; + } + + return status; +} + +MStatus guess_file_path_pixel_type(const MString &in_file_path, + MImage::MPixelType &out_pixel_type) { + MStatus status = MStatus::kSuccess; + + MString file_path(in_file_path); + file_path.toLowerCase(); + + MStringArray splits; + file_path.split('.', splits); + MString file_extension = splits[splits.length() - 1]; + + guess_output_format_pixel_type(file_extension, out_pixel_type); + + return status; +} + +MStatus resize_image(MImage &image, const double resize_scale) { + MStatus status = MStatus::kSuccess; + + MImage::MPixelType pixel_type = image.pixelType(); + if (pixel_type != MImage::kByte) { + MMSOLVER_MAYA_WRN( + "mmConvertImage: " + << "Maya does not support resizing floating-point pixels."); + } + + uint32_t src_width = 2; + uint32_t src_height = 2; + status = image.getSize(src_width, src_height); + CHECK_MSTATUS_AND_RETURN_IT(status); + + auto dst_width_float = static_cast(src_width) * resize_scale; + auto dst_height_float = static_cast(src_height) * resize_scale; + auto dst_width = static_cast(dst_width_float); + auto dst_height = static_cast(dst_height_float); + if ((src_width != dst_width) && (src_height != dst_height)) { + const auto preserve_aspect_ratio = true; + + // TODO: Replace this with a hand-written resize function. The + // MImage.resize() method appears to have a bug whereby the + // resized image is offset by +1 pixel in X and Y. + // + // If we can use Rust, then we have these easily available, + // and they appear to do exactly what we need, very quickly: + // https://crates.io/crates/fast_image_resize + // https://crates.io/crates/resize + // + // NOTE: MImage.resize() only works on 8-bit images, not + // floating point. + status = image.resize(dst_width, dst_height, preserve_aspect_ratio); + CHECK_MSTATUS_AND_RETURN_IT(status); + } + + return status; +} + +// This tool should be able to convert an image sequence with one +// image format to another. +// +// Mostly this tool is intended to convert non-native image formats to +// Maya IFF for increased speed. +MStatus convert_image(const MString &src_file_path, + const MString &dst_file_path, + // Common output formats include: als, bmp, cin, + // gif, jpg, rla, sgi, tga, tif, iff. "iff" is + // default. + const MString &dst_output_format, + const double resize_scale) { + MStatus status = MStatus::kSuccess; + + if (src_file_path == dst_file_path) { + status = MS::kFailure; + MMSOLVER_MAYA_ERR("mmConvertImage: " + << "Cannot have source and destination as same path: " + << src_file_path.asChar()); + CHECK_MSTATUS_AND_RETURN_IT(status); + } + + auto image = MImage(); + // kUnknown attempts to load the native pixel type. + auto src_pixel_type = MImage::kUnknown; + status = image.readFromFile( + src_file_path, + src_pixel_type // The desired pixel format is unknown. + ); + if (status != MS::kSuccess) { + MMSOLVER_MAYA_ERR("mmConvertImage: " + << "Image file path could not be read: " + << src_file_path.asChar()); + CHECK_MSTATUS_AND_RETURN_IT(status); + } + src_pixel_type = image.pixelType(); + const bool src_is_rgba = image.isRGBA(); + + // Maya always stores 4 channels (according to the + // documentation). + const auto channels = 4; + + // Guess the Pixel Type for the output image. + MImage::MPixelType dst_pixel_type; + MImage::MPixelType format_pixel_type; + status = + guess_output_format_pixel_type(dst_output_format, format_pixel_type); + CHECK_MSTATUS_AND_RETURN_IT(status); + status = guess_file_path_pixel_type(dst_file_path, dst_pixel_type); + CHECK_MSTATUS_AND_RETURN_IT(status); + if (format_pixel_type != dst_pixel_type) { + MMSOLVER_MAYA_WRN( + "mmConvertImage: " + << "The destination file extension and output format seem " + "to contradict each other. file path: " + << dst_file_path.asChar() << " output format: \"" + << dst_output_format.asChar() << "\""); + } + + if (src_pixel_type == dst_pixel_type) { + // Try to resize. We can only resize kByte - Maya is limited + // with the pixel types it can resize. + if (src_pixel_type == MImage::kByte) { + status = resize_image(image, resize_scale); + if (status != MS::kSuccess) { + MMSOLVER_MAYA_ERR("mmConvertImage: " + << "Failed to resize image file: " + << src_file_path.asChar()); + return status; + } + } + + // No conversion is needed. We write out the 8-bit image + // directly. + status = image.writeToFile(dst_file_path, dst_output_format); + if (status != MS::kSuccess) { + MMSOLVER_MAYA_ERR("mmConvertImage: " + << "Failed to write image file: " + << dst_file_path.asChar() << " output format: \"" + << dst_output_format.asChar() << "\""); + CHECK_MSTATUS_AND_RETURN_IT(status); + } + } else { + // Convert 32-bit to 8-bit integer. We assume the image + // has not been resized yet. + if (image.pixelType() == MImage::kByte) { + status = MS::kFailure; + MMSOLVER_MAYA_ERR("mmConvertImage: " + << "Failed to write image file: " + << dst_file_path.asChar() << " output format: \"" + << dst_output_format.asChar() << "\""); + return status; + } + + // Do gamma correction before converting. + uint32_t image_width = 2; + uint32_t image_height = 2; + status = image.getSize(image_width, image_height); + CHECK_MSTATUS_AND_RETURN_IT(status); + + // Get exponent based on if we are converting to/from floating + // point or not. Make (linear color space) pixels brighter. + const float gamma = 2.2F; + float exponent = 1.0F / gamma; + + // TODO: Use the OpenColorIO library to change the + // color space for an 8-bit file format. + + // Make sure we do our color management with floating point + // numbers, to avoid loss of detail. This does not do anything + // if the pixel format is already MImage::kFloat. + float *float_pixels = image.floatPixels(); + if (float_pixels == nullptr) { + // The data pointer should be valid because we have + // already read and operated on the pixels, so it seems + // very wrong that the data wouldn't be valid. + status = MS::kFailure; + MMSOLVER_MAYA_ERR("mmConvertImage: " + << "Failed to get floating point pixel data: " + << src_file_path.asChar()); + return status; + } + + MImage out_image; + out_image.create(image_width, image_height, channels, MImage::kByte); + unsigned char *pixels = out_image.pixels(); + + // Apply gamma correction. + for (auto y = 0; y < image_height; ++y) { + for (auto x = 0; x < image_width; ++x) { + auto index = (y * image_width * channels) + (x * channels); + auto r = + clamp(fast_pow(float_pixels[index + 0], exponent) * 255.0F, + 0.0, 255.0); + auto g = + clamp(fast_pow(float_pixels[index + 1], exponent) * 255.0F, + 0.0, 255.0); + auto b = + clamp(fast_pow(float_pixels[index + 2], exponent) * 255.0F, + 0.0, 255.0); + auto a = clamp(float_pixels[index + 3] * 255.0F, 0.0, 255.0); + pixels[index + 0] = static_cast(r); + pixels[index + 1] = static_cast(g); + pixels[index + 2] = static_cast(b); + // Alpha does not need to be gamma corrected. + pixels[index + 3] = static_cast(a); + } + } + out_image.setRGBA(true); + + // Try to resize. We can only resize kByte - Maya is + // limited with the pixel types it can resize. + status = resize_image(out_image, resize_scale); + if (status != MS::kSuccess) { + MMSOLVER_MAYA_ERR("mmConvertImage: " + << "Failed to resize image file: " + << src_file_path.asChar()); + return status; + } + + status = out_image.writeToFile(dst_file_path, dst_output_format); + if (status != MS::kSuccess) { + MMSOLVER_MAYA_ERR("mmConvertImage: " + << "Failed to write image file: " + << dst_file_path.asChar() << " output format: \"" + << dst_output_format.asChar() << "\""); + CHECK_MSTATUS_AND_RETURN_IT(status); + } + } + return status; +} + +} // namespace image +} // namespace mmsolver diff --git a/src/mmSolver/image/image_convert.h b/src/mmSolver/image/image_convert.h new file mode 100644 index 000000000..7885e0014 --- /dev/null +++ b/src/mmSolver/image/image_convert.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022, 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#ifndef MM_SOLVER_IMAGE_IMAGE_CONVERT_H +#define MM_SOLVER_IMAGE_IMAGE_CONVERT_H + +// Maya +#include + +namespace mmsolver { +namespace image { + +MStatus convert_image(const MString &src_file_path, + const MString &dst_file_path, + const MString &dst_output_format, + const double resize_scale); + +} // namespace image +} // namespace mmsolver + +#endif // MM_SOLVER_IMAGE_IMAGE_CONVERT_H diff --git a/src/mmSolver/image/image_io.cpp b/src/mmSolver/image/image_io.cpp new file mode 100644 index 000000000..00492dd80 --- /dev/null +++ b/src/mmSolver/image/image_io.cpp @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#include "image_io.h" + +// Get M_PI constant +#define _USE_MATH_DEFINES +#include + +// STL +#include +#include +#include +#include +#include + +// Maya +#include +#include + +// Maya Viewport 2.0 +#include + +// MM Solver +#include +#include + +#include "PixelDataType.h" +#include "mmSolver/mayahelper/maya_utils.h" +#include "mmSolver/render/shader/shader_utils.h" +#include "mmSolver/shape/constant_texture_data.h" +#include "mmSolver/utilities/number_utils.h" +#include "mmSolver/utilities/path_utils.h" + +namespace mmsolver { +namespace image { + +void *get_maya_mimage_pixel_data(const MImage &maya_mimage, + const PixelDataType pixel_data_type, + const uint32_t width, const uint32_t height, + const uint8_t num_channels, + uint8_t &out_bytes_per_channel, + MHWRender::MRasterFormat &out_texture_format) { + const bool verbose = false; + + const uint32_t print_num_pixels = 8; + void *pixel_data = nullptr; + if (pixel_data_type == PixelDataType::kU8) { + MMSOLVER_MAYA_VRB("mmsolver::image_io::get_maya_mimage_pixel_data:" + << " pixel_data_type=PixelDataType::kU8"); + + // 8-bit unsigned integers use 1 byte. + out_bytes_per_channel = 1; + + const bool is_rgba = maya_mimage.isRGBA(); + MMSOLVER_MAYA_VRB("mmsolver::image_io::get_maya_mimage_pixel_data:" + << " is_rgba=" << is_rgba); + if (is_rgba) { + out_texture_format = MHWRender::kR8G8B8A8_UNORM; + } else { + out_texture_format = MHWRender::kB8G8R8A8; + } + + unsigned char *pixels = maya_mimage.pixels(); + + if (verbose) { + for (uint32_t row = 0; row <= print_num_pixels; row++) { + const uint32_t index = row * num_channels; + const uint32_t r = static_cast(pixels[index + 0]); + const uint32_t g = static_cast(pixels[index + 1]); + const uint32_t b = static_cast(pixels[index + 2]); + const uint32_t a = static_cast(pixels[index + 3]); + MMSOLVER_MAYA_VRB( + "mmsolver::image_io::get_maya_mimage_pixel_data:" + << " row=" << row << " pixel=" << r << ", " << g << ", " + << b << ", " << a); + } + } + + // TODO: Allow giving the explicit input and output color + // space names. + // + // TODO: Allow outputting 32-bit/16-bit float pixel data, to + // ensure the plate doesn't get quantize. + // + // mmcolorio::image_convert_srgb_to_linear_srgb_u8(pixels, width, + // height, + // num_channels); + + // mmcolorio::test_opencolorio(pixels, width, height, + // num_channels); + + pixel_data = static_cast(pixels); + } else if (pixel_data_type == PixelDataType::kF32) { + MMSOLVER_MAYA_VRB("mmsolver::image_io::get_maya_mimage_pixel_data:" + << " pixel_data_type=PixelDataType::kF32"); + + // 32-bit floats use 4 bytes. + out_bytes_per_channel = 4; + + out_texture_format = MHWRender::kR32G32B32A32_FLOAT; + + float *floatPixels = maya_mimage.floatPixels(); + + if (verbose) { + for (uint32_t row = 0; row <= print_num_pixels; row++) { + const uint32_t index = row * num_channels; + const float r = floatPixels[index + 0]; + const float g = floatPixels[index + 1]; + const float b = floatPixels[index + 2]; + const float a = floatPixels[index + 3]; + MMSOLVER_MAYA_VRB( + "mmsolver::image_io::get_maya_mimage_pixel_data:" + << " row=" << row << " pixel=" << r << ", " << g << ", " + << b << ", " << a); + } + } + + // mmcolorio::image_convert_srgb_to_linear_srgb_f32( + // floatPixels, width, height, num_channels); + + pixel_data = static_cast(floatPixels); + } else { + MMSOLVER_MAYA_ERR("mmsolver::image_io::get_maya_mimage_pixel_data: " + << "Invalid pixel type is " + << static_cast(pixel_data_type)); + return nullptr; + } + + return pixel_data; +} + +PixelDataType convert_mpixel_type_to_pixel_data_type( + const MImage::MPixelType pixel_type) { + PixelDataType pixel_data_type = PixelDataType::kUnknown; + if (pixel_type == MImage::MPixelType::kByte) { + pixel_data_type = PixelDataType::kU8; + } else if (pixel_type == MImage::MPixelType::kFloat) { + pixel_data_type = PixelDataType::kF32; + } else { + MMSOLVER_MAYA_WRN( + "mmsolver::image_io::convert_mpixel_type_to_pixel_data_type: " + "Invalid MImage::MPixelType value. " + "value=" + << static_cast(pixel_type)); + } + return pixel_data_type; +} + +// NOTE: The pointer 'out_pixel_data' returned is actually a pointer +// into the 'MImage maya_mimage' data. When that object goes out of +// scope, accessing that data will be come undefined! +MStatus read_with_maya_mimage(MImage &maya_mimage, const MString &file_path, + const MImage::MPixelType pixel_type, + uint32_t &out_width, uint32_t &out_height, + uint8_t &out_num_channels, + uint8_t &out_bytes_per_channel, + MHWRender::MRasterFormat &out_texture_format, + PixelDataType &out_pixel_data_type, + void *&out_pixel_data) { + const bool verbose = false; + + MStatus status = MStatus::kSuccess; + + status = maya_mimage.readFromFile(file_path, pixel_type); + CHECK_MSTATUS(status); + if (status != MS::kSuccess) { + MMSOLVER_MAYA_WRN("mmsolver::image_io::read_with_maya_mimage:" + << " failed to read image \"" << file_path.asChar() + << "\"."); + return status; + } + + maya_mimage.getSize(out_width, out_height); + MMSOLVER_MAYA_VRB("mmsolver::image_io::read_with_maya_mimage:" + << " width=" << out_width << " height=" << out_height); + + out_pixel_data_type = convert_mpixel_type_to_pixel_data_type(pixel_type); + + out_pixel_data = get_maya_mimage_pixel_data( + maya_mimage, out_pixel_data_type, out_width, out_height, + out_num_channels, out_bytes_per_channel, out_texture_format); + + return status; +} + +// NOTE: The pointer 'out_pixel_data' returned is actually a pointer +// into the 'mmimage::ImagePixelBuffer pixel_buffer' data. When that +// object goes out of scope, accessing that data will be come +// undefined! +MStatus read_exr_with_mmimage(mmimage::ImagePixelBuffer &pixel_buffer, + mmimage::ImageMetaData &meta_data, + const MString &file_path, uint32_t &out_width, + uint32_t &out_height, uint8_t &out_num_channels, + uint8_t &out_bytes_per_channel, + MHWRender::MRasterFormat &out_texture_format, + PixelDataType &out_pixel_data_type, + void *&out_pixel_data) { + const bool verbose = false; + + MStatus status = MStatus::kSuccess; + + const std::string input_file_path_string = file_path.asChar(); + const rust::Str input_file_path(input_file_path_string.c_str()); + + // TODO: Support 3-channel RGB EXR images. + const bool vertical_flip = true; + bool read_ok = mmimage::image_read_pixels_exr_f32x4( + input_file_path, vertical_flip, meta_data, pixel_buffer); + + if (!read_ok) { + MMSOLVER_MAYA_WRN("mmsolver::image_io::read_exr_with_mmimage:" + << " failed to read image \"" << file_path.asChar() + << "\"."); + return MStatus::kFailure; + } + + size_t num_channels = pixel_buffer.num_channels(); + if ((num_channels != 3) && (num_channels != 4)) { + MMSOLVER_MAYA_WRN("mmsolver::image_io::read_exr_with_mmimage:" + << " image has " << num_channels + << " which is supported; \"" << file_path.asChar() + << "\"."); + return MStatus::kFailure; + } + + if (num_channels == 3) { + out_texture_format = MHWRender::kR32G32B32_FLOAT; + } else if (num_channels == 4) { + out_texture_format = MHWRender::kR32G32B32A32_FLOAT; + } else { + MMSOLVER_MAYA_ERR("mmsolver::image_io::read_exr_with_mmimage:" + << " should never get here; \"" << file_path.asChar() + << "\"."); + return MStatus::kFailure; + } + + out_width = pixel_buffer.image_width(); + out_height = pixel_buffer.image_height(); + out_num_channels = num_channels; + + // Other data types are unsupported right now. + out_pixel_data_type = PixelDataType::kF32; + out_bytes_per_channel = 4; // f32 is 4-bytes per element. + + MMSOLVER_MAYA_VRB( + "mmsolver::image_io::read_exr_with_mmimage:" + << " width=" << out_width << " height=" << out_height + << " num_channels=" << static_cast(out_num_channels) + << " bytes_per_channel=" << static_cast(out_bytes_per_channel) + << " pixel_data_type=" << static_cast(out_pixel_data_type) + << " sizeof(mmimage::PixelF32x4)=" << sizeof(mmimage::PixelF32x4)); + + const rust::Slice slice = + pixel_buffer.as_slice_f32x4(); + + out_pixel_data = (void *)slice.data(); + MMSOLVER_MAYA_VRB("mmsolver::image_io::read_exr_with_mmimage:" + << " out_pixel_data=" << out_pixel_data); + + return status; +} + +// NOTE: The pointer 'out_pixel_data' returned is actually a pointer +// into the 'MImage maya_mimage' or 'mmimage::ImagePixelBuffer +// pixel_buffer' data (depending on the format). When that object goes +// out of scope, accessing that data will be come undefined! +MStatus read_image_file(MImage &maya_mimage, + mmimage::ImagePixelBuffer &pixel_buffer, + mmimage::ImageMetaData &meta_data, + const MString &file_path, uint32_t &out_width, + uint32_t &out_height, uint8_t &out_num_channels, + uint8_t &out_bytes_per_channel, + MHWRender::MRasterFormat &out_texture_format, + PixelDataType &out_pixel_data_type, + void *&out_pixel_data) { + const bool verbose = false; + MStatus status = MStatus::kSuccess; + + MMSOLVER_MAYA_VRB("mmsolver::image_io::read_image_file:" + << " file_path=" << file_path.asChar()); + + // We assume the file extension is standard and common. + // Non-standard file extensions will fail to use the expected + // image reader. + MString file_extension = ""; + int32_t dot_char_index = file_path.rindexW('.'); + if (dot_char_index >= 0) { + file_extension = + file_path.substringW(dot_char_index + 1, file_path.length()); + file_extension.toLowerCase(); + } + + MMSOLVER_MAYA_VRB("mmsolver::image_io::read_image_file:" + << " file_extension=" << file_extension.asChar()); + + if (file_extension == "exr") { + MMSOLVER_MAYA_VRB("mmsolver::image_io::read_image_file:" + << "read_exr_with_mmimage..."); + status = read_exr_with_mmimage( + pixel_buffer, meta_data, file_path, out_width, out_height, + out_num_channels, out_bytes_per_channel, out_texture_format, + out_pixel_data_type, out_pixel_data); + CHECK_MSTATUS_AND_RETURN_IT(status); + } else { + MMSOLVER_MAYA_VRB("mmsolver::image_io::read_image_file:" + << "read_with_maya_mimage..."); + + // Maya always reads images as RGBA. + out_num_channels = 4; + + // Maya is meant to be able to load pixels as float (kFloat), + // but in my attempts it just fails, so lets hard-code to 8-bit. + const MImage::MPixelType pixel_type = MImage::MPixelType::kByte; + + status = read_with_maya_mimage( + maya_mimage, file_path, pixel_type, out_width, out_height, + out_num_channels, out_bytes_per_channel, out_texture_format, + out_pixel_data_type, out_pixel_data); + CHECK_MSTATUS_AND_RETURN_IT(status); + } + + return status; +} + +} // namespace image +} // namespace mmsolver diff --git a/src/mmSolver/image/image_io.h b/src/mmSolver/image/image_io.h new file mode 100644 index 000000000..10250b075 --- /dev/null +++ b/src/mmSolver/image/image_io.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#ifndef MM_SOLVER_IMAGE_IMAGE_IO_H +#define MM_SOLVER_IMAGE_IMAGE_IO_H + +// STL +#include +#include +#include +#include + +// Maya +#include +#include + +// Maya Viewport 2.0 +#include + +// MM Solver +#include +#include + +#include "PixelDataType.h" +#include "mmSolver/utilities/debug_utils.h" + +namespace mmsolver { +namespace image { + +MStatus read_image_file(MImage &maya_mimage, + mmimage::ImagePixelBuffer &pixel_buffer, + mmimage::ImageMetaData &meta_data, + const MString &file_path, uint32_t &out_width, + uint32_t &out_height, uint8_t &out_num_channels, + uint8_t &out_bytes_per_channel, + MHWRender::MRasterFormat &out_texture_format, + PixelDataType &out_pixel_data_type, + void *&out_pixel_data); + +} // namespace image +} // namespace mmsolver + +#endif // MM_SOLVER_IMAGE_IMAGE_IO_H diff --git a/src/mmSolver/mayahelper/maya_string_utils.cpp b/src/mmSolver/mayahelper/maya_string_utils.cpp new file mode 100644 index 000000000..be9c8bab4 --- /dev/null +++ b/src/mmSolver/mayahelper/maya_string_utils.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#include "maya_string_utils.h" + +namespace mmmayastring {} // namespace mmmayastring diff --git a/src/mmSolver/mayahelper/maya_string_utils.h b/src/mmSolver/mayahelper/maya_string_utils.h new file mode 100644 index 000000000..f4a3ea303 --- /dev/null +++ b/src/mmSolver/mayahelper/maya_string_utils.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + * Generic string helper functions for Maya. + */ + +#ifndef MAYA_STRING_UTILS_H +#define MAYA_STRING_UTILS_H + +// STL +#include // fabs +#include +#include // stringstream +#include + +// MM Solver +#include "mmSolver/utilities/string_utils.h" + +// Maya +#include + +namespace mmmayastring { + +/*! Convert a number to a Maya MString + * + * Convert 42 to "42". + */ +template +MString numberToMString(NUM_TYPE num) { + std::string string = mmstring::numberToString(num); + MString mstring(string.c_str()); + return mstring; +} + +/*! Convert a Maya MString to a number. + * + * Convert "3.14" to 3.14. + */ +template +NUM_TYPE mstringToNumber(const MString &text) { + std::string text_string = text.asChar(); + return mmstring::stringToNumber(text_string); +} + +} // namespace mmmayastring + +#endif // MAYA_STRING_UTILS_H diff --git a/src/mmSolver/mayahelper/maya_utils.cpp b/src/mmSolver/mayahelper/maya_utils.cpp index e2266796a..e7b854ffa 100644 --- a/src/mmSolver/mayahelper/maya_utils.cpp +++ b/src/mmSolver/mayahelper/maya_utils.cpp @@ -44,9 +44,9 @@ MStatus MMNodeInitUtils::attributeAffectsMulti( MObject outputAttr = outputAttrs[j]; status = MPxNode::attributeAffects(inputAttr, outputAttr); if (status != MS::kSuccess) { - MStreamUtils::stdErrorStream() - << "ERROR: attributeAffects failed at " - << "input_index=" << i << " output_index=" << j << '\n'; + MMSOLVER_MAYA_ERR( + "MMNodeInitUtils::attributeAffects: Failed at " + << "input_index=" << i << " output_index=" << j); CHECK_MSTATUS(status); } } diff --git a/src/mmSolver/mayahelper/maya_utils.h b/src/mmSolver/mayahelper/maya_utils.h index 324fc2f65..e26743ede 100644 --- a/src/mmSolver/mayahelper/maya_utils.h +++ b/src/mmSolver/mayahelper/maya_utils.h @@ -105,9 +105,22 @@ MStatus constructAttrAffectsName(const MString &attrName, namespace mmsolver { +static inline MStatus getNodeAttrPlug(const MDagPath &objPath, + const MObject &attr, MPlug &out_plug) { + MStatus status = MS::kSuccess; + MObject node = objPath.node(&status); + if (status) { + out_plug = MPlug(node, attr); + if (out_plug.isNull()) { + return MS::kFailure; + } + } + return status; +} + static inline MStatus getNodeAttr(const MDagPath &objPath, const MObject &attr, MDistance &value) { - MStatus status; + MStatus status = MS::kSuccess; MObject node = objPath.node(&status); if (status) { MPlug plug(node, attr); @@ -121,7 +134,7 @@ static inline MStatus getNodeAttr(const MDagPath &objPath, const MObject &attr, static inline MStatus getNodeAttr(const MDagPath &objPath, const MObject &attr, bool &value) { - MStatus status; + MStatus status = MS::kSuccess; MObject node = objPath.node(&status); if (status) { MPlug plug(node, attr); @@ -135,7 +148,7 @@ static inline MStatus getNodeAttr(const MDagPath &objPath, const MObject &attr, static inline MStatus getNodeAttr(const MDagPath &objPath, const MObject &attr, int32_t &value) { - MStatus status; + MStatus status = MS::kSuccess; MObject node = objPath.node(&status); if (status) { MPlug plug(node, attr); @@ -149,7 +162,7 @@ static inline MStatus getNodeAttr(const MDagPath &objPath, const MObject &attr, static inline MStatus getNodeAttr(const MDagPath &objPath, const MObject &attr, uint32_t &value) { - MStatus status; + MStatus status = MS::kSuccess; MObject node = objPath.node(&status); if (status) { MPlug plug(node, attr); @@ -163,7 +176,7 @@ static inline MStatus getNodeAttr(const MDagPath &objPath, const MObject &attr, static inline MStatus getNodeAttr(const MDagPath &objPath, const MObject &attr, short &value) { - MStatus status; + MStatus status = MS::kSuccess; MObject node = objPath.node(&status); if (status) { MPlug plug(node, attr); @@ -177,7 +190,7 @@ static inline MStatus getNodeAttr(const MDagPath &objPath, const MObject &attr, static inline MStatus getNodeAttr(const MDagPath &objPath, const MObject &attr, float &value) { - MStatus status; + MStatus status = MS::kSuccess; MObject node = objPath.node(&status); if (status) { MPlug plug(node, attr); @@ -191,7 +204,7 @@ static inline MStatus getNodeAttr(const MDagPath &objPath, const MObject &attr, static inline MStatus getNodeAttr(const MDagPath &objPath, const MObject &attr, double &value) { - MStatus status; + MStatus status = MS::kSuccess; MObject node = objPath.node(&status); if (status) { MPlug plug(node, attr); @@ -205,7 +218,7 @@ static inline MStatus getNodeAttr(const MDagPath &objPath, const MObject &attr, static inline MStatus getNodeAttr(const MDagPath &objPath, const MObject &attr, MColor &value) { - MStatus status; + MStatus status = MS::kSuccess; MObject node = objPath.node(&status); if (status) { MPlug plug(node, attr); @@ -224,7 +237,7 @@ static inline MStatus getNodeAttr(const MDagPath &objPath, const MObject &attr, static inline MStatus getNodeAttr(const MDagPath &objPath, const MObject &attr, MMatrix &value) { - MStatus status; + MStatus status = MS::kSuccess; MObject node = objPath.node(&status); if (status) { MPlug plug(node, attr); @@ -240,7 +253,7 @@ static inline MStatus getNodeAttr(const MDagPath &objPath, const MObject &attr, static inline MStatus getNodeAttr(const MDagPath &objPath, const MObject &attr, MString &value) { - MStatus status; + MStatus status = MS::kSuccess; MObject node = objPath.node(&status); if (status) { MPlug plug(node, attr); diff --git a/src/mmSolver/node/MMImageSequenceFrameLogicNode.cpp b/src/mmSolver/node/MMImageSequenceFrameLogicNode.cpp new file mode 100644 index 000000000..c4fd7443c --- /dev/null +++ b/src/mmSolver/node/MMImageSequenceFrameLogicNode.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + * + */ + +#include "MMImageSequenceFrameLogicNode.h" + +// STL +#include +#include + +// Maya +#include +#include +#include +#include +#include +#include +#include + +// MM Solver +#include "mmSolver/mayahelper/maya_camera.h" +#include "mmSolver/mayahelper/maya_utils.h" +#include "mmSolver/nodeTypeIds.h" +#include "mmSolver/utilities/debug_utils.h" +#include "mmSolver/utilities/number_utils.h" + +namespace mmsolver { + +MTypeId MMImageSequenceFrameLogicNode::m_id( + MM_IMAGE_SEQUENCE_FRAME_LOGIC_TYPE_ID); + +// Input Attributes +MObject MMImageSequenceFrameLogicNode::a_inFrame; +MObject MMImageSequenceFrameLogicNode::a_firstFrame; +MObject MMImageSequenceFrameLogicNode::a_startFrame; +MObject MMImageSequenceFrameLogicNode::a_endFrame; + +// Output Attributes +MObject MMImageSequenceFrameLogicNode::a_outFrame; + +MMImageSequenceFrameLogicNode::MMImageSequenceFrameLogicNode() {} + +MMImageSequenceFrameLogicNode::~MMImageSequenceFrameLogicNode() {} + +MString MMImageSequenceFrameLogicNode::nodeName() { + return MString(MM_IMAGE_SEQUENCE_FRAME_LOGIC_TYPE_NAME); +} + +MStatus MMImageSequenceFrameLogicNode::compute(const MPlug &plug, + MDataBlock &data) { + MStatus status = MS::kUnknownParameter; + + if (plug == a_outFrame) { + // Get Data Handles + MDataHandle inFrameHandle = data.inputValue(a_inFrame); + MDataHandle firstFrameHandle = data.inputValue(a_firstFrame); + MDataHandle startFrameHandle = data.inputValue(a_startFrame); + MDataHandle endFrameHandle = data.inputValue(a_endFrame); + + // Get Values + double inFrame = inFrameHandle.asDouble(); + double firstFrame = firstFrameHandle.asDouble(); + double startFrame = startFrameHandle.asDouble(); + double endFrame = endFrameHandle.asDouble(); + + // Clamp to start and end frames. + double outFrame = (startFrame - firstFrame) + inFrame; + if (outFrame < startFrame) { + outFrame = startFrame; + } else if (outFrame > endFrame) { + outFrame = endFrame; + } + + // Output Frame + MDataHandle outFrameHandle = data.outputValue(a_outFrame); + outFrameHandle.setDouble(outFrame); + outFrameHandle.setClean(); + + status = MS::kSuccess; + } + return status; +} + +void *MMImageSequenceFrameLogicNode::creator() { + return (new MMImageSequenceFrameLogicNode()); +} + +MStatus MMImageSequenceFrameLogicNode::initialize() { + MStatus status; + MFnNumericAttribute numericAttr; + MFnEnumAttribute enumAttr; + MFnCompoundAttribute compoundAttr; + + // In Frame + a_inFrame = + numericAttr.create("inFrame", "ifrm", MFnNumericData::kDouble, 0.0); + CHECK_MSTATUS(numericAttr.setStorable(true)); + CHECK_MSTATUS(numericAttr.setKeyable(true)); + CHECK_MSTATUS(addAttribute(a_inFrame)); + + // Frame First + a_firstFrame = numericAttr.create("firstFrame", "fstfrm", + MFnNumericData::kDouble, 0.0); + CHECK_MSTATUS(numericAttr.setStorable(true)); + CHECK_MSTATUS(numericAttr.setKeyable(true)); + CHECK_MSTATUS(addAttribute(a_firstFrame)); + + // Frame Start + a_startFrame = + numericAttr.create("startFrame", "stfrm", MFnNumericData::kDouble, 0.0); + CHECK_MSTATUS(numericAttr.setStorable(true)); + CHECK_MSTATUS(numericAttr.setKeyable(true)); + CHECK_MSTATUS(addAttribute(a_startFrame)); + + // Frame End + a_endFrame = + numericAttr.create("endFrame", "edfrm", MFnNumericData::kDouble, 0.0); + CHECK_MSTATUS(numericAttr.setStorable(true)); + CHECK_MSTATUS(numericAttr.setKeyable(true)); + CHECK_MSTATUS(addAttribute(a_endFrame)); + + // Out Frame + a_outFrame = + numericAttr.create("outFrame", "ofrm", MFnNumericData::kDouble, 0.0); + CHECK_MSTATUS(numericAttr.setStorable(false)); + CHECK_MSTATUS(numericAttr.setKeyable(false)); + CHECK_MSTATUS(numericAttr.setReadable(true)); + CHECK_MSTATUS(numericAttr.setWritable(false)); + CHECK_MSTATUS(addAttribute(a_outFrame)); + + // Attribute Affects + MObjectArray inputAttrs; + inputAttrs.append(a_inFrame); + inputAttrs.append(a_firstFrame); + inputAttrs.append(a_startFrame); + inputAttrs.append(a_endFrame); + + MObjectArray outputAttrs; + outputAttrs.append(a_outFrame); + + CHECK_MSTATUS( + MMNodeInitUtils::attributeAffectsMulti(inputAttrs, outputAttrs)); + + return (MS::kSuccess); +} + +} // namespace mmsolver diff --git a/src/mmSolver/node/MMImageSequenceFrameLogicNode.h b/src/mmSolver/node/MMImageSequenceFrameLogicNode.h new file mode 100644 index 000000000..9216d78f9 --- /dev/null +++ b/src/mmSolver/node/MMImageSequenceFrameLogicNode.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + * Calculates the frame number of an image sequence. + */ + +#ifndef MM_IMAGE_SEQUENCE_FRAME_LOGIC_NODE_H +#define MM_IMAGE_SEQUENCE_FRAME_LOGIC_NODE_H + +// Maya +#include +#include +#include +#include +#include +#include + +namespace mmsolver { + +class MMImageSequenceFrameLogicNode : public MPxNode { +public: + MMImageSequenceFrameLogicNode(); + + virtual ~MMImageSequenceFrameLogicNode(); + + virtual MStatus compute(const MPlug &plug, MDataBlock &data); + + static void *creator(); + + static MStatus initialize(); + + static MString nodeName(); + + static MTypeId m_id; + + // Input Attributes + static MObject a_inFrame; + static MObject a_firstFrame; + static MObject a_startFrame; + static MObject a_endFrame; + + // Output Attributes + static MObject a_outFrame; +}; + +} // namespace mmsolver + +#endif // MM_IMAGE_SEQUENCE_FRAME_LOGIC_NODE_H diff --git a/src/mmSolver/pluginMain.cpp b/src/mmSolver/pluginMain.cpp index e47dd128a..fbe0c62de 100644 --- a/src/mmSolver/pluginMain.cpp +++ b/src/mmSolver/pluginMain.cpp @@ -39,8 +39,12 @@ #include "mmSolver/cmd/MMCameraPoseFromPointsCmd.h" #include "mmSolver/cmd/MMCameraRelativePoseCmd.h" #include "mmSolver/cmd/MMCameraSolveCmd.h" +#include "mmSolver/cmd/MMColorIOCmd.h" #include "mmSolver/cmd/MMConvertImageCmd.h" +#include "mmSolver/cmd/MMImageCacheCmd.h" #include "mmSolver/cmd/MMMarkerHomographyCmd.h" +#include "mmSolver/cmd/MMMemoryGPUCmd.h" +#include "mmSolver/cmd/MMMemorySystemCmd.h" #include "mmSolver/cmd/MMReadImageCmd.h" #include "mmSolver/cmd/MMReprojectionCmd.h" #include "mmSolver/cmd/MMSolver2Cmd.h" @@ -51,6 +55,7 @@ #include "mmSolver/cmd/MMTestCameraMatrixCmd.h" #include "mmSolver/node/MMCameraCalibrateNode.h" #include "mmSolver/node/MMImagePlaneTransformNode.h" +#include "mmSolver/node/MMImageSequenceFrameLogicNode.h" #include "mmSolver/node/MMLensData.h" #include "mmSolver/node/MMLensDeformerNode.h" #include "mmSolver/node/MMLensEvaluateNode.h" @@ -67,7 +72,9 @@ // Shape nodes. #include "mmSolver/shape/BundleDrawOverride.h" #include "mmSolver/shape/BundleShapeNode.h" +#include "mmSolver/shape/ImagePlaneGeometry2Override.h" #include "mmSolver/shape/ImagePlaneGeometryOverride.h" +#include "mmSolver/shape/ImagePlaneShape2Node.h" #include "mmSolver/shape/ImagePlaneShapeNode.h" #include "mmSolver/shape/LineDrawOverride.h" #include "mmSolver/shape/LineShapeNode.h" @@ -78,12 +85,12 @@ // MM Renderer #if MMSOLVER_BUILD_RENDERER == 1 -#include "mmSolver/render/MMRendererBasicCmd.h" #include "mmSolver/render/MMRendererSilhouetteCmd.h" -#include "mmSolver/render/RenderGlobalsBasicNode.h" +#include "mmSolver/render/MMRendererStandardCmd.h" #include "mmSolver/render/RenderGlobalsSilhouetteNode.h" -#include "mmSolver/render/RenderOverrideBasic.h" +#include "mmSolver/render/RenderGlobalsStandardNode.h" #include "mmSolver/render/RenderOverrideSilhouette.h" +#include "mmSolver/render/RenderOverrideStandard.h" #endif #define REGISTER_COMMAND(plugin, name, creator, syntax, stat) \ @@ -251,14 +258,30 @@ MStatus initializePlugin(MObject obj) { mmsolver::MMCameraSolveCmd::creator, mmsolver::MMCameraSolveCmd::newSyntax, status); + REGISTER_COMMAND(plugin, mmsolver::MMColorIOCmd::cmdName(), + mmsolver::MMColorIOCmd::creator, + mmsolver::MMColorIOCmd::newSyntax, status); + REGISTER_COMMAND(plugin, mmsolver::MMConvertImageCmd::cmdName(), mmsolver::MMConvertImageCmd::creator, mmsolver::MMConvertImageCmd::newSyntax, status); + REGISTER_COMMAND(plugin, mmsolver::MMImageCacheCmd::cmdName(), + mmsolver::MMImageCacheCmd::creator, + mmsolver::MMImageCacheCmd::newSyntax, status); + REGISTER_COMMAND(plugin, mmsolver::MMMarkerHomographyCmd::cmdName(), mmsolver::MMMarkerHomographyCmd::creator, mmsolver::MMMarkerHomographyCmd::newSyntax, status); + REGISTER_COMMAND(plugin, mmsolver::MMMemoryGPUCmd::cmdName(), + mmsolver::MMMemoryGPUCmd::creator, + mmsolver::MMMemoryGPUCmd::newSyntax, status); + + REGISTER_COMMAND(plugin, mmsolver::MMMemorySystemCmd::cmdName(), + mmsolver::MMMemorySystemCmd::creator, + mmsolver::MMMemorySystemCmd::newSyntax, status); + REGISTER_COMMAND(plugin, mmsolver::MMReadImageCmd::cmdName(), mmsolver::MMReadImageCmd::creator, mmsolver::MMReadImageCmd::newSyntax, status); @@ -268,6 +291,11 @@ MStatus initializePlugin(MObject obj) { mmsolver::MMMarkerScaleNode::creator, mmsolver::MMMarkerScaleNode::initialize, status); + REGISTER_NODE(plugin, mmsolver::MMImageSequenceFrameLogicNode::nodeName(), + mmsolver::MMImageSequenceFrameLogicNode::m_id, + mmsolver::MMImageSequenceFrameLogicNode::creator, + mmsolver::MMImageSequenceFrameLogicNode::initialize, status); + REGISTER_NODE(plugin, mmsolver::MMReprojectionNode::nodeName(), mmsolver::MMReprojectionNode::m_id, mmsolver::MMReprojectionNode::creator, @@ -314,12 +342,14 @@ MStatus initializePlugin(MObject obj) { mmsolver::MMLensModelToggleNode::creator, mmsolver::MMLensModelToggleNode::initialize, status); - const MString markerClassification = MM_MARKER_DRAW_CLASSIFY; const MString bundleClassification = MM_BUNDLE_DRAW_CLASSIFY; + const MString lineClassification = MM_LINE_DRAW_CLASSIFY; + const MString markerClassification = MM_MARKER_DRAW_CLASSIFY; + const MString skyDomeClassification = MM_SKY_DOME_DRAW_CLASSIFY; const MString imagePlaneShapeClassification = MM_IMAGE_PLANE_SHAPE_DRAW_CLASSIFY; - const MString skyDomeClassification = MM_SKY_DOME_DRAW_CLASSIFY; - const MString lineClassification = MM_LINE_DRAW_CLASSIFY; + const MString imagePlaneShape2Classification = + MM_IMAGE_PLANE_SHAPE_2_DRAW_CLASSIFY; REGISTER_LOCATOR_NODE(plugin, mmsolver::MarkerShapeNode::nodeName(), mmsolver::MarkerShapeNode::m_id, mmsolver::MarkerShapeNode::creator, @@ -336,6 +366,12 @@ MStatus initializePlugin(MObject obj) { mmsolver::ImagePlaneShapeNode::initialize, MPxNode::kLocatorNode, &imagePlaneShapeClassification, status); + REGISTER_LOCATOR_NODE(plugin, mmsolver::ImagePlaneShape2Node::nodeName(), + mmsolver::ImagePlaneShape2Node::m_id, + mmsolver::ImagePlaneShape2Node::creator, + mmsolver::ImagePlaneShape2Node::initialize, + MPxNode::kLocatorNode, + &imagePlaneShape2Classification, status); REGISTER_LOCATOR_NODE( plugin, mmsolver::SkyDomeShapeNode::nodeName(), mmsolver::SkyDomeShapeNode::m_id, mmsolver::SkyDomeShapeNode::creator, @@ -357,6 +393,10 @@ MStatus initializePlugin(MObject obj) { mmsolver::ImagePlaneShapeNode::m_draw_db_classification, mmsolver::ImagePlaneShapeNode::m_draw_registrant_id, mmsolver::ImagePlaneGeometryOverride::Creator, status); + REGISTER_GEOMETRY_OVERRIDE( + mmsolver::ImagePlaneShape2Node::m_draw_db_classification, + mmsolver::ImagePlaneShape2Node::m_draw_registrant_id, + mmsolver::ImagePlaneGeometry2Override::Creator, status); REGISTER_DRAW_OVERRIDE(mmsolver::SkyDomeShapeNode::m_draw_db_classification, mmsolver::SkyDomeShapeNode::m_draw_registrant_id, mmsolver::SkyDomeDrawOverride::Creator, status); @@ -396,18 +436,20 @@ MStatus initializePlugin(MObject obj) { markerTfmClassification, status); #if MMSOLVER_BUILD_RENDERER == 1 - REGISTER_COMMAND(plugin, mmsolver::render::MMRendererBasicCmd::cmdName(), - mmsolver::render::MMRendererBasicCmd::creator, - mmsolver::render::MMRendererBasicCmd::newSyntax, status); + REGISTER_COMMAND(plugin, mmsolver::render::MMRendererStandardCmd::cmdName(), + mmsolver::render::MMRendererStandardCmd::creator, + mmsolver::render::MMRendererStandardCmd::newSyntax, + status); REGISTER_COMMAND( plugin, mmsolver::render::MMRendererSilhouetteCmd::cmdName(), mmsolver::render::MMRendererSilhouetteCmd::creator, mmsolver::render::MMRendererSilhouetteCmd::newSyntax, status); - REGISTER_NODE(plugin, mmsolver::render::RenderGlobalsBasicNode::nodeName(), - mmsolver::render::RenderGlobalsBasicNode::m_id, - mmsolver::render::RenderGlobalsBasicNode::creator, - mmsolver::render::RenderGlobalsBasicNode::initialize, status); + REGISTER_NODE( + plugin, mmsolver::render::RenderGlobalsStandardNode::nodeName(), + mmsolver::render::RenderGlobalsStandardNode::m_id, + mmsolver::render::RenderGlobalsStandardNode::creator, + mmsolver::render::RenderGlobalsStandardNode::initialize, status); REGISTER_NODE( plugin, mmsolver::render::RenderGlobalsSilhouetteNode::nodeName(), mmsolver::render::RenderGlobalsSilhouetteNode::m_id, @@ -446,9 +488,9 @@ MStatus initializePlugin(MObject obj) { shader_location += MString("/shader"); shader_manager->addShaderPath(shader_location); - mmsolver::render::RenderOverrideBasic* default_renderer_ptr = - new mmsolver::render::RenderOverrideBasic( - MM_RENDERER_BASIC_NAME); + mmsolver::render::RenderOverrideStandard* default_renderer_ptr = + new mmsolver::render::RenderOverrideStandard( + MM_RENDERER_STANDARD_NAME); renderer->registerOverride(default_renderer_ptr); mmsolver::render::RenderOverrideSilhouette* @@ -460,52 +502,57 @@ MStatus initializePlugin(MObject obj) { } #endif - // TODO: Construct a single MEL command buffer and run all - // 'selectType' MEL commands at once. - MString mel_cmd = ""; - // Register a custom selection mask with priority 2 (same as // locators by default). + MString mel_cmd = ""; + auto selection_priority = 2; MSelectionMask::registerSelectionType( - mmsolver::MarkerShapeNode::m_selection_type_name, 2); - mel_cmd = "selectType -byName \""; + mmsolver::MarkerShapeNode::m_selection_type_name, selection_priority); + mel_cmd += "selectType -byName \""; mel_cmd += mmsolver::MarkerShapeNode::m_selection_type_name; - mel_cmd += "\" 1"; - status = MGlobal::executeCommand(mel_cmd); - CHECK_MSTATUS(status); + mel_cmd += "\" 1;"; MSelectionMask::registerSelectionType( - mmsolver::BundleShapeNode::m_selection_type_name, 2); - mel_cmd = "selectType -byName \""; + mmsolver::BundleShapeNode::m_selection_type_name, selection_priority); + mel_cmd += "selectType -byName \""; mel_cmd += mmsolver::BundleShapeNode::m_selection_type_name; - mel_cmd += "\" 1"; - status = MGlobal::executeCommand(mel_cmd); - CHECK_MSTATUS(status); + mel_cmd += "\" 1;"; MSelectionMask::registerSelectionType( - mmsolver::ImagePlaneShapeNode::m_selection_type_name, 2); - mel_cmd = "selectType -byName \""; + mmsolver::ImagePlaneShapeNode::m_selection_type_name, + selection_priority); + mel_cmd += "selectType -byName \""; mel_cmd += mmsolver::ImagePlaneShapeNode::m_selection_type_name; - mel_cmd += "\" 1"; - status = MGlobal::executeCommand(mel_cmd); - CHECK_MSTATUS(status); + mel_cmd += "\" 1;"; + + MSelectionMask::registerSelectionType( + mmsolver::ImagePlaneShape2Node::m_selection_type_name, + selection_priority); + mel_cmd = "selectType -byName \""; + mel_cmd += mmsolver::ImagePlaneShape2Node::m_selection_type_name; + mel_cmd += "\" 1;"; MSelectionMask::registerSelectionType( - mmsolver::SkyDomeShapeNode::m_selection_type_name, 2); + mmsolver::SkyDomeShapeNode::m_selection_type_name, selection_priority); mel_cmd = "selectType -byName \""; mel_cmd += mmsolver::SkyDomeShapeNode::m_selection_type_name; - mel_cmd += "\" 1"; - status = MGlobal::executeCommand(mel_cmd); - CHECK_MSTATUS(status); + mel_cmd += "\" 1;"; MSelectionMask::registerSelectionType( - mmsolver::LineShapeNode::m_selection_type_name, 2); + mmsolver::LineShapeNode::m_selection_type_name, selection_priority); mel_cmd = "selectType -byName \""; mel_cmd += mmsolver::LineShapeNode::m_selection_type_name; - mel_cmd += "\" 1"; + mel_cmd += "\" 1;"; + + // Register selection types. status = MGlobal::executeCommand(mel_cmd); CHECK_MSTATUS(status); + // Should we create a display filter for the 'mmImagePlaneShape'? + // We have a newer 'mmImagePlaneShape2' and it would be good to + // avoid having two almost identical display filter names. + const bool registerLegacyImagePlaneDisplayFilter = false; + // Register plugin display filter. // The filter is registered in both interactive and batch mode // (Hardware 2.0) @@ -517,10 +564,18 @@ MStatus initializePlugin(MObject obj) { mmsolver::BundleShapeNode::m_display_filter_name, mmsolver::BundleShapeNode::m_display_filter_label, mmsolver::BundleShapeNode::m_draw_db_classification); + if (registerLegacyImagePlaneDisplayFilter) { + plugin.registerDisplayFilter( + mmsolver::ImagePlaneShapeNode::m_display_filter_name, + mmsolver::ImagePlaneShapeNode::m_display_filter_label, + mmsolver::ImagePlaneShapeNode:: + m_display_filter_draw_db_classification); + } plugin.registerDisplayFilter( - mmsolver::ImagePlaneShapeNode::m_display_filter_name, - mmsolver::ImagePlaneShapeNode::m_display_filter_label, - mmsolver::ImagePlaneShapeNode::m_draw_db_classification); + mmsolver::ImagePlaneShape2Node::m_display_filter_name, + mmsolver::ImagePlaneShape2Node::m_display_filter_label, + mmsolver::ImagePlaneShape2Node:: + m_display_filter_draw_db_classification); plugin.registerDisplayFilter( mmsolver::SkyDomeShapeNode::m_display_filter_name, mmsolver::SkyDomeShapeNode::m_display_filter_label, @@ -530,15 +585,19 @@ MStatus initializePlugin(MObject obj) { mmsolver::LineShapeNode::m_display_filter_label, mmsolver::LineShapeNode::m_draw_db_classification); - // Run the Python startup function when the plug-in loads. + // Run the Python startup and image cache initialisation function + // when the plug-in loads. bool displayEnabled = true; bool undoEnabled = false; MString startup_cmd; startup_cmd += "global proc mmsolver_startup() "; startup_cmd += "{ "; + startup_cmd += "python(\""; startup_cmd += - "python(\"import mmSolver.startup; " - "mmSolver.startup.mmsolver_startup()\"); "; + "import mmSolver.startup; " + "mmSolver.startup.mmsolver_startup();" + "mmSolver.startup.mmsolver_image_cache_initialise();"; + startup_cmd += "\"); "; startup_cmd += "} "; startup_cmd += "evalDeferred(\"mmsolver_startup\");"; status = MGlobal::executeCommand(startup_cmd, displayEnabled, undoEnabled); @@ -562,7 +621,7 @@ MStatus uninitializePlugin(MObject obj) { if (renderer) { // Find override with the given name and deregister const MHWRender::MRenderOverride* default_renderer_ptr = - renderer->findRenderOverride(MM_RENDERER_BASIC_NAME); + renderer->findRenderOverride(MM_RENDERER_STANDARD_NAME); if (default_renderer_ptr) { renderer->deregisterOverride(default_renderer_ptr); delete default_renderer_ptr; @@ -576,14 +635,14 @@ MStatus uninitializePlugin(MObject obj) { } } - DEREGISTER_COMMAND(plugin, mmsolver::render::MMRendererBasicCmd::cmdName(), - status); + DEREGISTER_COMMAND( + plugin, mmsolver::render::MMRendererStandardCmd::cmdName(), status); DEREGISTER_COMMAND( plugin, mmsolver::render::MMRendererSilhouetteCmd::cmdName(), status); DEREGISTER_NODE(plugin, - mmsolver::render::RenderGlobalsBasicNode::nodeName(), - mmsolver::render::RenderGlobalsBasicNode::m_id, status); + mmsolver::render::RenderGlobalsStandardNode::nodeName(), + mmsolver::render::RenderGlobalsStandardNode::m_id, status); DEREGISTER_NODE( plugin, mmsolver::render::RenderGlobalsSilhouetteNode::nodeName(), mmsolver::render::RenderGlobalsSilhouetteNode::m_id, status); @@ -603,9 +662,13 @@ MStatus uninitializePlugin(MObject obj) { DEREGISTER_COMMAND(plugin, mmsolver::MMCameraRelativePoseCmd::cmdName(), status); DEREGISTER_COMMAND(plugin, mmsolver::MMCameraSolveCmd::cmdName(), status); + DEREGISTER_COMMAND(plugin, mmsolver::MMColorIOCmd::cmdName(), status); + DEREGISTER_COMMAND(plugin, mmsolver::MMImageCacheCmd::cmdName(), status); DEREGISTER_COMMAND(plugin, mmsolver::MMConvertImageCmd::cmdName(), status); DEREGISTER_COMMAND(plugin, mmsolver::MMMarkerHomographyCmd::cmdName(), status); + DEREGISTER_COMMAND(plugin, mmsolver::MMMemoryGPUCmd::cmdName(), status); + DEREGISTER_COMMAND(plugin, mmsolver::MMMemorySystemCmd::cmdName(), status); DEREGISTER_COMMAND(plugin, mmsolver::MMReadImageCmd::cmdName(), status); DEREGISTER_DRAW_OVERRIDE( @@ -617,6 +680,9 @@ MStatus uninitializePlugin(MObject obj) { DEREGISTER_GEOMETRY_OVERRIDE( mmsolver::ImagePlaneShapeNode::m_draw_db_classification, mmsolver::ImagePlaneShapeNode::m_draw_registrant_id, status); + DEREGISTER_GEOMETRY_OVERRIDE( + mmsolver::ImagePlaneShape2Node::m_draw_db_classification, + mmsolver::ImagePlaneShape2Node::m_draw_registrant_id, status); DEREGISTER_DRAW_OVERRIDE( mmsolver::SkyDomeShapeNode::m_draw_db_classification, mmsolver::SkyDomeShapeNode::m_draw_registrant_id, status); @@ -630,6 +696,8 @@ MStatus uninitializePlugin(MObject obj) { mmsolver::BundleShapeNode::m_id, status); DEREGISTER_LOCATOR_NODE(plugin, mmsolver::ImagePlaneShapeNode::nodeName(), mmsolver::ImagePlaneShapeNode::m_id, status); + DEREGISTER_LOCATOR_NODE(plugin, mmsolver::ImagePlaneShape2Node::nodeName(), + mmsolver::ImagePlaneShape2Node::m_id, status); DEREGISTER_LOCATOR_NODE(plugin, mmsolver::SkyDomeShapeNode::nodeName(), mmsolver::SkyDomeShapeNode::m_id, status); DEREGISTER_LOCATOR_NODE(plugin, mmsolver::LineShapeNode::nodeName(), @@ -656,6 +724,9 @@ MStatus uninitializePlugin(MObject obj) { DEREGISTER_NODE(plugin, mmsolver::MMMarkerGroupTransformNode::nodeName(), mmsolver::MMMarkerGroupTransformNode::m_id, status); + DEREGISTER_NODE(plugin, mmsolver::MMImageSequenceFrameLogicNode::nodeName(), + mmsolver::MMImageSequenceFrameLogicNode::m_id, status); + DEREGISTER_NODE(plugin, mmsolver::MMImagePlaneTransformNode::nodeName(), mmsolver::MMImagePlaneTransformNode::m_id, status); diff --git a/src/mmSolver/render/MMRendererBasicCmd.cpp b/src/mmSolver/render/MMRendererStandardCmd.cpp similarity index 73% rename from src/mmSolver/render/MMRendererBasicCmd.cpp rename to src/mmSolver/render/MMRendererStandardCmd.cpp index 86688b561..d6868c1b1 100644 --- a/src/mmSolver/render/MMRendererBasicCmd.cpp +++ b/src/mmSolver/render/MMRendererStandardCmd.cpp @@ -19,7 +19,7 @@ * */ -#include "MMRendererBasicCmd.h" +#include "MMRendererStandardCmd.h" // Maya #include @@ -29,26 +29,28 @@ #include // MM Solver -#include "RenderOverrideBasic.h" +#include "RenderOverrideStandard.h" namespace mmsolver { namespace render { -MMRendererBasicCmd::MMRendererBasicCmd() {} +MMRendererStandardCmd::MMRendererStandardCmd() {} -MMRendererBasicCmd::~MMRendererBasicCmd() {} +MMRendererStandardCmd::~MMRendererStandardCmd() {} -void *MMRendererBasicCmd::creator() { return (void *)(new MMRendererBasicCmd); } +void *MMRendererStandardCmd::creator() { + return (void *)(new MMRendererStandardCmd); +} -MString MMRendererBasicCmd::cmdName() { return kRendererBasicCmdName; } +MString MMRendererStandardCmd::cmdName() { return kRendererStandardCmdName; } -MSyntax MMRendererBasicCmd::newSyntax() { +MSyntax MMRendererStandardCmd::newSyntax() { MSyntax syntax; syntax.enableQuery(true); return syntax; } -MStatus MMRendererBasicCmd::doIt(const MArgList &args) { +MStatus MMRendererStandardCmd::doIt(const MArgList &args) { MStatus status = MStatus::kFailure; MHWRender::MRenderer *renderer = MHWRender::MRenderer::theRenderer(); @@ -57,10 +59,11 @@ MStatus MMRendererBasicCmd::doIt(const MArgList &args) { return status; } - RenderOverrideBasic *override_ptr = - (RenderOverrideBasic *)renderer->findRenderOverride(kRendererBasicName); + RenderOverrideStandard *override_ptr = + (RenderOverrideStandard *)renderer->findRenderOverride( + kRendererStandardName); if (override_ptr == nullptr) { - MGlobal::displayError(kRendererBasicCmdName + " is not registered."); + MGlobal::displayError(kRendererStandardCmdName + " is not registered."); return status; } diff --git a/src/mmSolver/render/MMRendererBasicCmd.h b/src/mmSolver/render/MMRendererStandardCmd.h similarity index 74% rename from src/mmSolver/render/MMRendererBasicCmd.h rename to src/mmSolver/render/MMRendererStandardCmd.h index 92401ab1a..1ff06bb43 100644 --- a/src/mmSolver/render/MMRendererBasicCmd.h +++ b/src/mmSolver/render/MMRendererStandardCmd.h @@ -17,17 +17,17 @@ * along with mmSolver. If not, see . * ==================================================================== * - * mmSolver Basic Renderer command. + * mmSolver Standard Renderer command. * * This command is used to query and edit values for the mmSolver * Renderer. * - * Create a 'mmRendererBasic' node used to store the attributes for the + * Create a 'mmRendererStandard' node used to store the attributes for the * renderer in the scene. */ -#ifndef MM_SOLVER_RENDER_MM_RENDERER_BASIC_CMD_H -#define MM_SOLVER_RENDER_MM_RENDERER_BASIC_CMD_H +#ifndef MM_SOLVER_RENDER_MM_RENDERER_STANDARD_CMD_H +#define MM_SOLVER_RENDER_MM_RENDERER_STANDARD_CMD_H // Maya #include @@ -36,13 +36,13 @@ namespace mmsolver { namespace render { /* - * MM Basic Renderer command. + * MM Standard Renderer command. */ -class MMRendererBasicCmd : public MPxCommand { +class MMRendererStandardCmd : public MPxCommand { public: - MMRendererBasicCmd(); + MMRendererStandardCmd(); - ~MMRendererBasicCmd() override; + ~MMRendererStandardCmd() override; MStatus doIt(const MArgList &args) override; @@ -56,4 +56,4 @@ class MMRendererBasicCmd : public MPxCommand { } // namespace render } // namespace mmsolver -#endif // MAYA_MM_SOLVER_RENDER_MM_RENDERER_BASIC_CMD_H +#endif // MAYA_MM_SOLVER_RENDER_MM_RENDERER_STANDARD_CMD_H diff --git a/src/mmSolver/render/RenderGlobalsSilhouetteNode.cpp b/src/mmSolver/render/RenderGlobalsSilhouetteNode.cpp index 4b7717654..67a57e012 100644 --- a/src/mmSolver/render/RenderGlobalsSilhouetteNode.cpp +++ b/src/mmSolver/render/RenderGlobalsSilhouetteNode.cpp @@ -52,6 +52,7 @@ MTypeId RenderGlobalsSilhouetteNode::m_id(MM_RENDER_GLOBALS_SILHOUETTE_TYPE_ID); // Input Attributes MObject RenderGlobalsSilhouetteNode::a_enable; +MObject RenderGlobalsSilhouetteNode::a_overrideColor; MObject RenderGlobalsSilhouetteNode::a_depthOffset; MObject RenderGlobalsSilhouetteNode::a_width; MObject RenderGlobalsSilhouetteNode::a_color; @@ -152,6 +153,18 @@ MStatus RenderGlobalsSilhouetteNode::initialize() { CHECK_MSTATUS(addAttribute(a_enable)); } + // Silhouette Override Color + { + a_overrideColor = numeric_attribute.create( + kAttrNameSilhouetteOverrideColor, "ovrdcol", + MFnNumericData::kBoolean, + static_cast(kSilhouetteOverrideColorDefault)); + CHECK_MSTATUS(numeric_attribute.setStorable(true)); + CHECK_MSTATUS(numeric_attribute.setConnectable(true)); + CHECK_MSTATUS(numeric_attribute.setKeyable(true)); + CHECK_MSTATUS(addAttribute(a_overrideColor)); + } + // Silhouette Depth Offset { auto depth_offset_max = 0.0; diff --git a/src/mmSolver/render/RenderGlobalsSilhouetteNode.h b/src/mmSolver/render/RenderGlobalsSilhouetteNode.h index 909309aea..55f9ef6cd 100644 --- a/src/mmSolver/render/RenderGlobalsSilhouetteNode.h +++ b/src/mmSolver/render/RenderGlobalsSilhouetteNode.h @@ -56,6 +56,7 @@ class RenderGlobalsSilhouetteNode : public MPxNode { static MObject a_enable; static MObject a_depthOffset; static MObject a_width; + static MObject a_overrideColor; static MObject a_color; static MObject a_alpha; static MObject a_cullFace; diff --git a/src/mmSolver/render/RenderGlobalsBasicNode.cpp b/src/mmSolver/render/RenderGlobalsStandardNode.cpp similarity index 74% rename from src/mmSolver/render/RenderGlobalsBasicNode.cpp rename to src/mmSolver/render/RenderGlobalsStandardNode.cpp index b2456aa93..a97a3c678 100644 --- a/src/mmSolver/render/RenderGlobalsBasicNode.cpp +++ b/src/mmSolver/render/RenderGlobalsStandardNode.cpp @@ -20,7 +20,7 @@ * Stores global values for the mmSolver viewport renderer. */ -#include "RenderGlobalsBasicNode.h" +#include "RenderGlobalsStandardNode.h" // Maya #include @@ -37,7 +37,7 @@ #include // MM Solver -#include "RenderOverrideBasic.h" +#include "RenderOverrideStandard.h" #include "mmSolver/nodeTypeIds.h" #include "mmSolver/render/data/RenderColorFormat.h" #include "mmSolver/utilities/debug_utils.h" @@ -45,21 +45,22 @@ namespace mmsolver { namespace render { -MTypeId RenderGlobalsBasicNode::m_id(MM_RENDER_GLOBALS_BASIC_TYPE_ID); +MTypeId RenderGlobalsStandardNode::m_id(MM_RENDER_GLOBALS_STANDARD_TYPE_ID); -RenderGlobalsBasicNode::RenderGlobalsBasicNode() : m_attr_change_callback(0) {} +RenderGlobalsStandardNode::RenderGlobalsStandardNode() + : m_attr_change_callback(0) {} -RenderGlobalsBasicNode::~RenderGlobalsBasicNode() { +RenderGlobalsStandardNode::~RenderGlobalsStandardNode() { if (m_attr_change_callback) { MMessage::removeCallback(m_attr_change_callback); } } -MString RenderGlobalsBasicNode::nodeName() { - return MString(MM_RENDER_GLOBALS_BASIC_TYPE_NAME); +MString RenderGlobalsStandardNode::nodeName() { + return MString(MM_RENDER_GLOBALS_STANDARD_TYPE_NAME); } -void RenderGlobalsBasicNode::postConstructor() { +void RenderGlobalsStandardNode::postConstructor() { MObject obj = thisMObject(); if ((m_attr_change_callback == 0) && (!obj.isNull())) { m_attr_change_callback = @@ -67,7 +68,7 @@ void RenderGlobalsBasicNode::postConstructor() { } } -void RenderGlobalsBasicNode::attr_change_func( +void RenderGlobalsStandardNode::attr_change_func( MNodeMessage::AttributeMessage msg, MPlug &plug, MPlug & /*other_plug*/, void * /*client_data*/) { const bool verbose = false; @@ -95,11 +96,11 @@ void RenderGlobalsBasicNode::attr_change_func( return; } - RenderOverrideBasic *override_ptr = - (RenderOverrideBasic *)renderer->findRenderOverride( - MM_RENDERER_BASIC_NAME); + RenderOverrideStandard *override_ptr = + (RenderOverrideStandard *)renderer->findRenderOverride( + MM_RENDERER_STANDARD_NAME); if (override_ptr == nullptr) { - MGlobal::displayError(kRendererBasicCmdName + " is not registered."); + MGlobal::displayError(kRendererStandardCmdName + " is not registered."); return; } @@ -112,17 +113,17 @@ void RenderGlobalsBasicNode::attr_change_func( view.refresh(/*all=*/false, /*force=*/true); } -MStatus RenderGlobalsBasicNode::compute(const MPlug & /*plug*/, - MDataBlock & /*data*/) { +MStatus RenderGlobalsStandardNode::compute(const MPlug & /*plug*/, + MDataBlock & /*data*/) { // This node does not compute any values. return MS::kUnknownParameter; } -void *RenderGlobalsBasicNode::creator() { - return (new RenderGlobalsBasicNode()); +void *RenderGlobalsStandardNode::creator() { + return (new RenderGlobalsStandardNode()); } -MStatus RenderGlobalsBasicNode::initialize() { return MS::kSuccess; } +MStatus RenderGlobalsStandardNode::initialize() { return MS::kSuccess; } } // namespace render } // namespace mmsolver diff --git a/src/mmSolver/render/RenderGlobalsBasicNode.h b/src/mmSolver/render/RenderGlobalsStandardNode.h similarity index 85% rename from src/mmSolver/render/RenderGlobalsBasicNode.h rename to src/mmSolver/render/RenderGlobalsStandardNode.h index 0057876e1..099ad01b2 100644 --- a/src/mmSolver/render/RenderGlobalsBasicNode.h +++ b/src/mmSolver/render/RenderGlobalsStandardNode.h @@ -20,8 +20,8 @@ * Stores global values for the mmSolver viewport renderer. */ -#ifndef MM_SOLVER_RENDER_RENDER_GLOBALS_BASIC_NODE_H -#define MM_SOLVER_RENDER_RENDER_GLOBALS_BASIC_NODE_H +#ifndef MM_SOLVER_RENDER_RENDER_GLOBALS_STANDARD_NODE_H +#define MM_SOLVER_RENDER_RENDER_GLOBALS_STANDARD_NODE_H // Maya #include @@ -34,11 +34,11 @@ namespace mmsolver { namespace render { -class RenderGlobalsBasicNode : public MPxNode { +class RenderGlobalsStandardNode : public MPxNode { public: - RenderGlobalsBasicNode(); + RenderGlobalsStandardNode(); - ~RenderGlobalsBasicNode() override; + ~RenderGlobalsStandardNode() override; MStatus compute(const MPlug &plug, MDataBlock &data) override; @@ -63,4 +63,4 @@ class RenderGlobalsBasicNode : public MPxNode { } // namespace render } // namespace mmsolver -#endif // MM_SOLVER_RENDER_RENDER_GLOBALS_BASIC_NODE_H +#endif // MM_SOLVER_RENDER_RENDER_GLOBALS_STANDARD_NODE_H diff --git a/src/mmSolver/render/RenderOverrideSilhouette.cpp b/src/mmSolver/render/RenderOverrideSilhouette.cpp index 84b7dd26b..b163d206d 100644 --- a/src/mmSolver/render/RenderOverrideSilhouette.cpp +++ b/src/mmSolver/render/RenderOverrideSilhouette.cpp @@ -108,6 +108,7 @@ RenderOverrideSilhouette::RenderOverrideSilhouette(const MString &name) , m_render_override_change_callback(0) , m_globals_node() , m_enable(kSilhouetteEnableDefault) + , m_override_color(kSilhouetteOverrideColorDefault) , m_depth_offset(kSilhouetteDepthOffsetDefault) , m_width(kSilhouetteWidthDefault) , m_color{kSilhouetteColorDefault[0], kSilhouetteColorDefault[1], @@ -319,10 +320,11 @@ MHWRender::DrawAPI RenderOverrideSilhouette::supportedDrawAPIs() const { // Read node plug attributes and set the values. MStatus update_parameters_silhouette( MObjectHandle &out_globals_node, bool &out_silhouette_enable, - float &out_silhouette_depth_offset, float &out_silhouette_width, - float &out_silhouette_color_r, float &out_silhouette_color_g, - float &out_silhouette_color_b, float &out_silhouette_alpha, - CullFace &out_silhouette_cull_face, uint8_t &out_operation_num) { + bool &out_silhouette_override_color, float &out_silhouette_depth_offset, + float &out_silhouette_width, float &out_silhouette_color_r, + float &out_silhouette_color_g, float &out_silhouette_color_b, + float &out_silhouette_alpha, CullFace &out_silhouette_cull_face, + uint8_t &out_operation_num) { const bool verbose = false; MStatus status = MS::kSuccess; MMSOLVER_MAYA_VRB("RenderOverrideSilhouette::update_parameters_silhouette"); @@ -385,6 +387,16 @@ MStatus update_parameters_silhouette( MMSOLVER_MAYA_VRB("RenderOverrideSilhouette Silhouette Enable: " << static_cast(out_silhouette_enable)); + // Override Color Silhouette render. + out_silhouette_override_color = kSilhouetteOverrideColorDefault; + MPlug silhouette_override_color_plug = depends_node.findPlug( + kAttrNameSilhouetteOverrideColor, want_networked_plug, &status); + if (status == MStatus::kSuccess) { + out_silhouette_override_color = silhouette_override_color_plug.asBool(); + } + MMSOLVER_MAYA_VRB("RenderOverrideSilhouette Silhouette Override Color: " + << static_cast(out_silhouette_override_color)); + // Silhouette Depth Offset out_silhouette_depth_offset = kSilhouetteDepthOffsetDefault; MPlug silhouette_depth_offset_plug = depends_node.findPlug( @@ -562,8 +574,9 @@ MStatus RenderOverrideSilhouette::setup(const MString &destination) { // Get override values. status = update_parameters_silhouette( - m_globals_node, m_enable, m_depth_offset, m_width, m_color[0], - m_color[1], m_color[2], m_alpha, m_cull_face, m_operation_num); + m_globals_node, m_enable, m_override_color, m_depth_offset, m_width, + m_color[0], m_color[1], m_color[2], m_alpha, m_cull_face, + m_operation_num); CHECK_MSTATUS(status); MMSOLVER_MAYA_VRB( @@ -587,6 +600,7 @@ MStatus RenderOverrideSilhouette::setup(const MString &destination) { m_silhouetteOp->setPanelName(destination); m_silhouetteOp->setEnabled(m_enable); + m_silhouetteOp->setSilhouetteOverrideColor(m_override_color); m_silhouetteOp->setSilhouetteDepthOffset(m_depth_offset); m_silhouetteOp->setSilhouetteWidth(m_width); m_silhouetteOp->setSilhouetteColor(m_color[0], m_color[1], m_color[2]); diff --git a/src/mmSolver/render/RenderOverrideSilhouette.h b/src/mmSolver/render/RenderOverrideSilhouette.h index 16638df0a..e79b32c1a 100644 --- a/src/mmSolver/render/RenderOverrideSilhouette.h +++ b/src/mmSolver/render/RenderOverrideSilhouette.h @@ -31,13 +31,15 @@ #include #include #include -#include #include #include + +// Maya Viewport 2.0 +#include #include // MM Solver -#include "RenderGlobalsBasicNode.h" +#include "RenderGlobalsStandardNode.h" #include "mmSolver/render/data/RenderMode.h" #include "mmSolver/render/data/constants.h" #include "mmSolver/render/ops/ClearOperation.h" @@ -105,12 +107,13 @@ class RenderOverrideSilhouette : public MHWRender::MRenderOverride { HudRender* m_hudOp; PresentTarget* m_presentOp; - // A handle to the 'mmRenderGlobals' node. + // A handle to the 'mmRenderGlobalsSilhouette' node. MObjectHandle m_globals_node; MSelectionList m_image_plane_nodes; bool m_enable; + bool m_override_color; float m_depth_offset; float m_width; float m_color[3]; diff --git a/src/mmSolver/render/RenderOverrideBasic.cpp b/src/mmSolver/render/RenderOverrideStandard.cpp similarity index 82% rename from src/mmSolver/render/RenderOverrideBasic.cpp rename to src/mmSolver/render/RenderOverrideStandard.cpp index 5ff368616..afecf6def 100644 --- a/src/mmSolver/render/RenderOverrideBasic.cpp +++ b/src/mmSolver/render/RenderOverrideStandard.cpp @@ -21,7 +21,7 @@ * */ -#include "RenderOverrideBasic.h" +#include "RenderOverrideStandard.h" // Maya #include @@ -49,30 +49,30 @@ namespace mmsolver { namespace render { static MStatus create_render_globals_node() { - return MGlobal::executeCommand(kRendererBasicCreateNodeCommand, + return MGlobal::executeCommand(kRendererStandardCreateNodeCommand, /*displayEnabled*/ true, /*undoEnabled*/ true); } // Callback for tracking renderer changes -void RenderOverrideBasic::renderer_change_func(const MString &panel_name, - const MString &old_renderer, - const MString &new_renderer, - void * /*client_data*/) { +void RenderOverrideStandard::renderer_change_func(const MString &panel_name, + const MString &old_renderer, + const MString &new_renderer, + void * /*client_data*/) { const bool verbose = false; MMSOLVER_MAYA_VRB("Renderer changed for panel '" << panel_name.asChar() << "'. " << "New renderer is '" << new_renderer.asChar() << "', " << "old was '" << old_renderer.asChar() << "'."); - if (new_renderer == MM_RENDERER_BASIC_NAME) { + if (new_renderer == MM_RENDERER_STANDARD_NAME) { MStatus status = create_render_globals_node(); CHECK_MSTATUS(status); } } // Callback for tracking render override changes -void RenderOverrideBasic::render_override_change_func( +void RenderOverrideStandard::render_override_change_func( const MString &panel_name, const MString &old_renderer, const MString &new_renderer, void * /*client_data*/) { const bool verbose = false; @@ -81,27 +81,28 @@ void RenderOverrideBasic::render_override_change_func( << "New override is '" << new_renderer.asChar() << "', " << "old was '" << old_renderer.asChar() << "'."); - if (new_renderer == MM_RENDERER_BASIC_NAME) { + if (new_renderer == MM_RENDERER_STANDARD_NAME) { MStatus status = create_render_globals_node(); CHECK_MSTATUS(status); } } // Set up operations -RenderOverrideBasic::RenderOverrideBasic(const MString &name) +RenderOverrideStandard::RenderOverrideStandard(const MString &name) : MRenderOverride(name) - , m_ui_name(kRendererBasicUiName) + , m_ui_name(kRendererStandardUiName) , m_renderer_change_callback(0) , m_render_override_change_callback(0) , m_globals_node() { MHWRender::MRenderer *renderer = MHWRender::MRenderer::theRenderer(); if (!renderer) { MMSOLVER_MAYA_ERR( - "MM Renderer Basic Render Override: " + "MM Renderer Standard Render Override: " "Failed to get renderer."); } - const MString kBackgroundOpName = MString("mmRendererBasic_backgroundPass"); + const MString kBackgroundOpName = + MString("mmRendererStandard_backgroundPass"); m_backgroundOp = new SceneRender(kBackgroundOpName); m_backgroundOp->setEnabled(true); @@ -143,10 +144,10 @@ RenderOverrideBasic::RenderOverrideBasic(const MString &name) MHWRender::MRenderOperation::kStandardBackgroundName, m_backgroundOp); } -RenderOverrideBasic::~RenderOverrideBasic() { +RenderOverrideStandard::~RenderOverrideStandard() { m_backgroundOp = nullptr; - RenderOverrideBasic::cleanup(); + RenderOverrideStandard::cleanup(); // Clean up callbacks if (m_renderer_change_callback) { @@ -157,16 +158,16 @@ RenderOverrideBasic::~RenderOverrideBasic() { } } -MHWRender::DrawAPI RenderOverrideBasic::supportedDrawAPIs() const { +MHWRender::DrawAPI RenderOverrideStandard::supportedDrawAPIs() const { // The SilhouetteRender only works on OpenGL, so we cannot support // DirectX on Windows or Metal on Apple. return MHWRender::kOpenGLCoreProfile; } // Perform any setup required before render operations are to be executed. -MStatus RenderOverrideBasic::setup(const MString &destination) { +MStatus RenderOverrideStandard::setup(const MString &destination) { const bool verbose = false; - MMSOLVER_MAYA_VRB("RenderOverrideBasic::setup: start " + MMSOLVER_MAYA_VRB("RenderOverrideStandard::setup: start " << destination.asChar()); MStatus status = MS::kSuccess; @@ -187,19 +188,19 @@ MStatus RenderOverrideBasic::setup(const MString &destination) { } MMSOLVER_MAYA_VRB( - "RenderOverrideBasic::setup: m_backgroundOp=" << m_backgroundOp); + "RenderOverrideStandard::setup: m_backgroundOp=" << m_backgroundOp); if (m_backgroundOp) { m_image_plane_nodes.clear(); status = add_all_image_planes(m_image_plane_nodes); MMSOLVER_MAYA_VRB( - "RenderOverrideBasic::setup: m_image_plane_nodes.length()=" + "RenderOverrideStandard::setup: m_image_plane_nodes.length()=" << m_image_plane_nodes.length()); CHECK_MSTATUS_AND_RETURN_IT(status); m_backgroundOp->setObjectSetOverride(&m_image_plane_nodes); } - MMSOLVER_MAYA_VRB("RenderOverrideBasic::setup: end " + MMSOLVER_MAYA_VRB("RenderOverrideStandard::setup: end " << destination.asChar()); return MRenderOverride::setup(destination); } @@ -209,9 +210,9 @@ MStatus RenderOverrideBasic::setup(const MString &destination) { // // End of frame cleanup. Clears out any data on operations which may // change from frame to frame (render target, output panel name etc). -MStatus RenderOverrideBasic::cleanup() { +MStatus RenderOverrideStandard::cleanup() { const bool verbose = false; - MMSOLVER_MAYA_VRB("RenderOverrideBasic::cleanup: "); + MMSOLVER_MAYA_VRB("RenderOverrideStandard::cleanup: "); return MRenderOverride::cleanup(); } diff --git a/src/mmSolver/render/RenderOverrideBasic.h b/src/mmSolver/render/RenderOverrideStandard.h similarity index 86% rename from src/mmSolver/render/RenderOverrideBasic.h rename to src/mmSolver/render/RenderOverrideStandard.h index 081d5ccb1..dac16f4b6 100644 --- a/src/mmSolver/render/RenderOverrideBasic.h +++ b/src/mmSolver/render/RenderOverrideStandard.h @@ -21,8 +21,8 @@ * */ -#ifndef MM_SOLVER_RENDER_BASIC_RENDER_OVERRIDE_H -#define MM_SOLVER_RENDER_BASIC_RENDER_OVERRIDE_H +#ifndef MM_SOLVER_RENDER_STANDARD_RENDER_OVERRIDE_H +#define MM_SOLVER_RENDER_STANDARD_RENDER_OVERRIDE_H // STL #include @@ -37,7 +37,7 @@ #include // MM Solver -#include "RenderGlobalsBasicNode.h" +#include "RenderGlobalsStandardNode.h" #include "mmSolver/render/data/RenderMode.h" #include "mmSolver/render/data/constants.h" #include "mmSolver/render/ops/SceneRender.h" @@ -46,10 +46,10 @@ namespace mmsolver { namespace render { -class RenderOverrideBasic : public MHWRender::MRenderOverride { +class RenderOverrideStandard : public MHWRender::MRenderOverride { public: - RenderOverrideBasic(const MString& name); - ~RenderOverrideBasic() override; + RenderOverrideStandard(const MString& name); + ~RenderOverrideStandard() override; MHWRender::DrawAPI supportedDrawAPIs() const override; @@ -81,7 +81,7 @@ class RenderOverrideBasic : public MHWRender::MRenderOverride { private: SceneRender* m_backgroundOp; - // A handle to the 'mmRenderGlobals' node. + // A handle to the 'mmRenderGlobalsStandard' node. MObjectHandle m_globals_node; MSelectionList m_image_plane_nodes; @@ -90,4 +90,4 @@ class RenderOverrideBasic : public MHWRender::MRenderOverride { } // namespace render } // namespace mmsolver -#endif // MAYA_MM_SOLVER_RENDER_BASIC_RENDER_OVERRIDE_H +#endif // MAYA_MM_SOLVER_RENDER_STANDARD_RENDER_OVERRIDE_H diff --git a/src/mmSolver/render/data/constants.h b/src/mmSolver/render/data/constants.h index 8626b22f4..05a54b037 100644 --- a/src/mmSolver/render/data/constants.h +++ b/src/mmSolver/render/data/constants.h @@ -92,23 +92,23 @@ const BackgroundStyle kBackgroundStyleDefault = BackgroundStyle::kMayaDefault; const RenderColorFormat kRenderColorFormatDefault = RenderColorFormat::kRGBA16BitFloat; -// Basic Constants -const MString kRenderGlobalsBasicNodeName = "mmRenderGlobals"; -const MString kRendererBasicCmdName = "mmRenderer"; -const char kRendererBasicName[] = "mmRenderer"; -const char kRendererBasicUiName[] = "mmRenderer"; -const MString kRendererBasicCreateNodeCommand = +// Standard Renderer Constants +const MString kRenderGlobalsStandardNodeName = "mmRenderGlobalsStandard"; +const MString kRendererStandardCmdName = "mmRendererStandard"; +const char kRendererStandardName[] = "mmRendererStandard"; +const char kRendererStandardUiName[] = "MM Standard Renderer"; +const MString kRendererStandardCreateNodeCommand = "string $mm_globals_node = `createNode \"" + - MString(MM_RENDER_GLOBALS_BASIC_TYPE_NAME) + "\" -name \"" + - MString(kRenderGlobalsBasicNodeName) + "\" -shared -skipSelect`;\n" + + MString(MM_RENDER_GLOBALS_STANDARD_TYPE_NAME) + "\" -name \"" + + MString(kRenderGlobalsStandardNodeName) + "\" -shared -skipSelect`;\n" + "if (size($mm_globals_node) > 0) {\n" + " lockNode -lock on $mm_globals_node;\n" + "}\n"; -// Silhouette Constants +// Silhouette Renderer Constants const MString kRenderGlobalsSilhouetteNodeName = "mmRenderGlobalsSilhouette"; const MString kRendererSilhouetteCmdName = "mmRendererSilhouette"; const char kRendererSilhouetteName[] = "mmRendererSilhouette"; -const char kRendererSilhouetteUiName[] = "mmRenderer (silhouette)"; +const char kRendererSilhouetteUiName[] = "MM Silhouette Renderer"; const MString kRendererSilhouetteCreateNodeCommand = "string $mm_globals_node = `createNode \"" + MString(MM_RENDER_GLOBALS_SILHOUETTE_TYPE_NAME) + "\" -name \"" + @@ -118,6 +118,7 @@ const MString kRendererSilhouetteCreateNodeCommand = // Silhouette Attribute Names const MString kAttrNameSilhouetteEnable = "enable"; +const MString kAttrNameSilhouetteOverrideColor = "overrideColor"; const MString kAttrNameSilhouetteDepthOffset = "depthOffset"; const MString kAttrNameSilhouetteWidth = "width"; const MString kAttrNameSilhouetteColor = "color"; @@ -130,6 +131,9 @@ const MString kAttrNameSilhouetteOperationNum = "operationNum"; // Silhouette Renderer Attribute Default Values const bool kSilhouetteEnableDefault = true; +// We enable 'override color' by default, so that users with wireframe +// on shaded can see the obvious effect right away. +const bool kSilhouetteOverrideColorDefault = true; const float kSilhouetteDepthOffsetDefault = -1.0f; const float kSilhouetteWidthDefault = 2.0f; const float kSilhouetteColorDefault[] = {0.0f, 1.0f, 0.0f}; diff --git a/src/mmSolver/render/ops/SilhouetteRender.cpp b/src/mmSolver/render/ops/SilhouetteRender.cpp index e2f1fa18f..44d21306c 100644 --- a/src/mmSolver/render/ops/SilhouetteRender.cpp +++ b/src/mmSolver/render/ops/SilhouetteRender.cpp @@ -28,20 +28,25 @@ // Maya #include #include -#include #include -#include -#include #include #include +#include +#include +#include + +// Maya Viewport 1.0 +#include +#include #include #include -#include + +// Maya Viewport 2.0 +#include +#include #include -#include #include #include -#include #include #include @@ -57,6 +62,7 @@ SilhouetteRender::SilhouetteRender(const MString& name) , m_shader_program(0) , m_output_targets(nullptr) , m_silhouette_cull_face(GL_BACK) + , m_silhouette_override_color(false) , gGLFT(nullptr) {} SilhouetteRender::~SilhouetteRender() { @@ -149,11 +155,14 @@ GLuint build_shader_program(MGLFunctionTable* gGLFT) { } MStatus calculate_model_view_projection_matrix( - M3dView view, MDagPath dag_path, MMatrix& out_model_view_projection) { + M3dView view, const MDagPath dag_path, const MMatrix& projection_matrix, + MMatrix& out_model_view_projection) { const MMatrix inclusive_matrix = dag_path.inclusiveMatrix(); - MMatrix projection_matrix; - MStatus status = view.projectionMatrix(projection_matrix); + // The camera and geometry matrices must be updated each frame, + // when playblasting. This is not obvious when viewing in the + MStatus status = MStatus::kSuccess; + status = view.updateViewingParameters(); CHECK_MSTATUS_AND_RETURN_IT(status); MMatrix model_view_matrix; @@ -331,6 +340,9 @@ MStatus SilhouetteRender::execute(const MHWRender::MDrawContext& drawContext) { const float default_line_width = drawContext.getGlobalLineWidth(); + float silhouette_color[3] = {m_silhouette_color[0], m_silhouette_color[1], + m_silhouette_color[2]}; + // Extract OpenGL buffers from Maya mesh nodes, then render the // buffers using our own OpenGL pipeline. MItDag dag_iter = MItDag(MItDag::kDepthFirst, MFn::kMesh); @@ -352,12 +364,29 @@ MStatus SilhouetteRender::execute(const MHWRender::MDrawContext& drawContext) { continue; } + if (!m_silhouette_override_color) { + // TODO: Write our own version of + // 'MGeometryUtilities::wireframeColor', that will do the same + // logic, but will not take into account the current selection + // status of the DagPath. This would avoid the need to save + // the selection list, de-select and re-select. + const MColor wireframe_color = + MHWRender::MGeometryUtilities::wireframeColor(dag_path); + silhouette_color[0] = wireframe_color.r; + silhouette_color[1] = wireframe_color.g; + silhouette_color[2] = wireframe_color.b; + } + // TODO: Check if an attribute exists on the shape node, and // check the value of the shape node. + const MMatrix projection_matrix = drawContext.getMatrix( + MHWRender::MFrameContext::kProjectionMtx, &status); + CHECK_MSTATUS_AND_RETURN_IT(status); + MMatrix model_view_projection; - status = calculate_model_view_projection_matrix(view, dag_path, - model_view_projection); + status = calculate_model_view_projection_matrix( + view, dag_path, projection_matrix, model_view_projection); // Create vertex buffer desc for position const MString empty_string = ""; @@ -397,6 +426,8 @@ MStatus SilhouetteRender::execute(const MHWRender::MDrawContext& drawContext) { vertex_buffer.commit(vertices_ptr); // Fill edge index buffer with data + // + // Edges have 2 indices for each edge. unsigned int edge_count = geometry_extractor.primitiveCount(edge_description); void* edge_indices_ptr = @@ -406,6 +437,8 @@ MStatus SilhouetteRender::execute(const MHWRender::MDrawContext& drawContext) { edge_index_buffer.commit(edge_indices_ptr); // Fill tri index buffer with data + // + // Triangles have 3 indices for each triangle. unsigned int triangles_count = geometry_extractor.primitiveCount(triangle_description); void* triangles_indices_ptr = @@ -434,7 +467,7 @@ MStatus SilhouetteRender::execute(const MHWRender::MDrawContext& drawContext) { const MGLuint* triangles_index_buffer_handle = static_cast(triangles_index_buffer_handle_ptr); draw_buffers( - gGLFT, m_silhouette_color, m_silhouette_alpha, m_silhouette_width, + gGLFT, silhouette_color, m_silhouette_alpha, m_silhouette_width, m_silhouette_depth_offset, m_silhouette_cull_face, default_line_width, model_view_projection, vertex_buffer_handle, edge_index_buffer_handle, triangles_index_buffer_handle, diff --git a/src/mmSolver/render/ops/SilhouetteRender.h b/src/mmSolver/render/ops/SilhouetteRender.h index 716b84f29..d7e2b0a92 100644 --- a/src/mmSolver/render/ops/SilhouetteRender.h +++ b/src/mmSolver/render/ops/SilhouetteRender.h @@ -28,11 +28,15 @@ // Maya #include +#include +#include + +// Maya Viewport 1.0 #include #include + +// Maya Viewport 2.0 #include -#include -#include #include // MM Solver @@ -62,6 +66,9 @@ class SilhouetteRender : public MHWRender::MUserRenderOperation { void setPanelName(MString value) { m_panel_name = value; } void setSilhouetteEnable(const bool value) { m_silhouette_enable = value; } + void setSilhouetteOverrideColor(const bool value) { + m_silhouette_override_color = value; + } void setSilhouetteDepthOffset(const float value) { m_silhouette_depth_offset = value; } @@ -98,6 +105,7 @@ class SilhouetteRender : public MHWRender::MUserRenderOperation { MGLFunctionTable *gGLFT; bool m_silhouette_enable; + bool m_silhouette_override_color; float m_silhouette_depth_offset; float m_silhouette_width; float m_silhouette_color[3]; diff --git a/src/mmSolver/render/ops/scene_utils.cpp b/src/mmSolver/render/ops/scene_utils.cpp index 2f2597712..9df8e95a5 100644 --- a/src/mmSolver/render/ops/scene_utils.cpp +++ b/src/mmSolver/render/ops/scene_utils.cpp @@ -361,9 +361,10 @@ bool set_background_clear_operation( MHWRender::MClearOperation& out_clear_operation) { if (background_style == BackgroundStyle::kTransparentBlack) { float val[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + out_clear_operation.setOverridesColors(false); out_clear_operation.setClearColor(val); out_clear_operation.setClearColor2(val); - out_clear_operation.setClearGradient(false); + out_clear_operation.setClearGradient(true); out_clear_operation.setClearStencil(0); // A depth value of 1.0f represents the 'most distant' // object. As objects draw, they draw darker pixels on top of @@ -374,14 +375,23 @@ bool set_background_clear_operation( // preferences. MRenderer provides us a way to get these // values automatically. MHWRender::MRenderer* renderer = MHWRender::MRenderer::theRenderer(); - bool gradient = renderer->useGradient(); - MColor color1 = renderer->clearColor(); - MColor color2 = renderer->clearColor2(); + const bool gradient = renderer->useGradient(); + const MColor color1 = renderer->clearColor(); + const MColor color2 = renderer->clearColor2(); + float val1[4] = {color1[0], color1[1], color1[2], 1.0f}; float val2[4] = {color2[0], color2[1], color2[2], 1.0f}; + + out_clear_operation.setOverridesColors(true); out_clear_operation.setClearColor(val1); out_clear_operation.setClearColor2(val2); - out_clear_operation.setClearGradient(gradient); + // NOTE: The gradient is forced to enabled because + // MRenderer::useGradient() appears to return zero when + // playblasting, but correctly returns values when rendering + // in the interactive Maya viewport. If we force the clear + // gradient enabled, the 'clearColor's will be set to solid + // colours when a non-gradient is waned. + out_clear_operation.setClearGradient(true); out_clear_operation.setClearStencil(0); out_clear_operation.setClearDepth(1.0f); } else { diff --git a/src/mmSolver/render/shader/shader_utils.cpp b/src/mmSolver/render/shader/shader_utils.cpp index ddcb9830f..d71cfd2e2 100644 --- a/src/mmSolver/render/shader/shader_utils.cpp +++ b/src/mmSolver/render/shader/shader_utils.cpp @@ -21,6 +21,10 @@ #include "shader_utils.h" +// STL +#include +#include + // Maya #include #include @@ -31,6 +35,7 @@ // MM Solver #include "mmSolver/utilities/debug_utils.h" +#include "mmSolver/utilities/path_utils.h" namespace mmsolver { namespace render { @@ -70,12 +75,70 @@ const MHWRender::MShaderManager *get_shader_manager() { return shader_manager; } +MString find_shader_file_path(const MString &shader_file_name) { + const bool verbose = false; + MStatus status = MS::kSuccess; + + MMSOLVER_MAYA_VRB("MM Renderer finding shader file..." + << " shader_file_name=" << shader_file_name.asChar()); + + MString shader_file_path = ""; + const MHWRender::MShaderManager *shader_manager = get_shader_manager(); + if (!shader_manager) { + return shader_file_path; + } + + MStringArray shader_paths; + status = shader_manager->shaderPaths(shader_paths); + if (status != MStatus::kSuccess) { + return shader_file_path; + } + + for (auto i = 0; i < shader_paths.length(); i++) { + MMSOLVER_MAYA_VRB("MM Renderer look for shader in: " + << " shader_paths[" << i + << "]=" << shader_paths[i].asChar()); + + MString test_file_path = shader_paths[i]; + const auto found_forward_slash_index = test_file_path.rindex('/'); + MMSOLVER_MAYA_VRB( + "MM Renderer test_file_path.length(): " << test_file_path.length()); + MMSOLVER_MAYA_VRB("MM Renderer found_forward_slash_index: " + << found_forward_slash_index); + if (found_forward_slash_index != (test_file_path.length() - 1)) { + test_file_path += MString("/"); + } + test_file_path += shader_file_name; + // TODO: Should we automatically try and find the file name + // with the '.ogsfx' appended? + MMSOLVER_MAYA_VRB( + "MM Renderer test_file_path: " << test_file_path.asChar()); + + status = mmpath::resolve_input_file_path(test_file_path); + if ((status == MStatus::kSuccess) && (test_file_path.length() > 0)) { + shader_file_path = test_file_path; + break; + } + } + + return shader_file_path; +} + +MString read_shader_file(const MString &shader_file_path) { + std::ifstream file_stream(shader_file_path.asChar()); + const std::string content((std::istreambuf_iterator(file_stream)), + (std::istreambuf_iterator())); + return MString(content.c_str()); +} + MHWRender::MShaderInstance *compile_shader_file(const MString &shader_file_name, const MString &technique_name) { const bool verbose = false; MStatus status = MS::kSuccess; - MMSOLVER_MAYA_VRB("MM Renderer compiling shader file..."); + MMSOLVER_MAYA_VRB("MM Renderer compiling shader file..." + << " shader_file_name=" << shader_file_name.asChar() + << " technique_name=" << technique_name.asChar()); const MHWRender::MShaderManager *shader_manager = get_shader_manager(); if (!shader_manager) { @@ -84,8 +147,8 @@ MHWRender::MShaderInstance *compile_shader_file(const MString &shader_file_name, // Shader compiling options. MShaderCompileMacro *macros = nullptr; - unsigned int number_of_macros = 0; - bool use_effect_cache = true; + const unsigned int number_of_macros = 0; + const bool use_effect_cache = true; // Get Techniques. MMSOLVER_MAYA_VRB("MM Renderer: Get techniques..."); @@ -136,5 +199,63 @@ MHWRender::MShaderInstance *compile_shader_file(const MString &shader_file_name, return shader_instance; } +MHWRender::MShaderInstance *compile_shader_text(const MString &shader_text, + const MString &technique_name) { + const bool verbose = false; + MStatus status = MS::kSuccess; + + MMSOLVER_MAYA_VRB("MM Renderer compiling shader file..." + << " technique_name=" << technique_name.asChar()); + MMSOLVER_MAYA_VRB("Shader text: "); + MMSOLVER_MAYA_VRB(shader_text.asChar()); + + const MHWRender::MShaderManager *shader_manager = get_shader_manager(); + if (!shader_manager) { + return nullptr; + } + + const void *text_buffer = static_cast(shader_text.asChar()); + const unsigned int text_buffer_size = + static_cast(shader_text.length()); + + // Shader compiling options. + MShaderCompileMacro *macros = nullptr; + const unsigned int number_of_macros = 0; + const bool use_effect_cache = true; + + // Compile shader. + MMSOLVER_MAYA_VRB("MM Renderer: Compiling shader..."); + MHWRender::MShaderInstance *shader_instance = + shader_manager->getEffectsBufferShader( + text_buffer, text_buffer_size, technique_name, macros, + number_of_macros, use_effect_cache); + if (!shader_instance) { + MString error_message = + MString("MM Renderer failed to compile shader."); + bool display_line_number = true; + bool filter_source = true; + uint32_t num_lines = 3; + MGlobal::displayError(error_message); + MGlobal::displayError(MHWRender::MShaderManager::getLastError()); + MGlobal::displayError(MHWRender::MShaderManager::getLastErrorSource( + display_line_number, filter_source, num_lines)); + MMSOLVER_MAYA_ERR("MM Renderer failed to compile shader."); + MMSOLVER_MAYA_ERR(MHWRender::MShaderManager::getLastError().asChar()); + MMSOLVER_MAYA_ERR(MHWRender::MShaderManager::getLastErrorSource( + display_line_number, filter_source, num_lines) + .asChar()); + return nullptr; + } + + MStringArray parameter_list; + shader_instance->parameterList(parameter_list); + for (uint32_t i = 0; i < parameter_list.length(); ++i) { + MMSOLVER_MAYA_VRB("MM Renderer: param " << i << ": " + << parameter_list[i].asChar()); + } + + return shader_instance; +} + } // namespace render } // namespace mmsolver diff --git a/src/mmSolver/render/shader/shader_utils.h b/src/mmSolver/render/shader/shader_utils.h index 879dca355..17f58da66 100644 --- a/src/mmSolver/render/shader/shader_utils.h +++ b/src/mmSolver/render/shader/shader_utils.h @@ -31,9 +31,16 @@ namespace render { const MHWRender::MShaderManager *get_shader_manager(); +MString find_shader_file_path(const MString &shader_file_name); + +MString read_shader_file(const MString &shader_file_path); + MHWRender::MShaderInstance *compile_shader_file(const MString &shader_file_name, const MString &technique_name); +MHWRender::MShaderInstance *compile_shader_text(const MString &shader_text, + const MString &technique_name); + } // namespace render } // namespace mmsolver diff --git a/src/mmSolver/shape/BundleDrawOverride.h b/src/mmSolver/shape/BundleDrawOverride.h index d2ce7f11f..71fc76597 100644 --- a/src/mmSolver/shape/BundleDrawOverride.h +++ b/src/mmSolver/shape/BundleDrawOverride.h @@ -48,13 +48,20 @@ namespace mmsolver { class BundleDrawData : public MUserData { public: BundleDrawData() +#if MAYA_API_VERSION >= 20220000 + : MUserData() +#else + // MUserData(bool) constructor is deprecated in Maya 2022+ + // because 'deleteAfterUse' is no longer needed. : MUserData(/*deleteAfterUse=*/true) // let Maya clean up +#endif , m_depth_priority(0) , m_line_width(1.0) , m_point_size(1.0) , m_icon_size(1.0) , m_active(false) - , m_draw_name(false) {} + , m_draw_name(false) { + } ~BundleDrawData() override {} diff --git a/src/mmSolver/shape/BundleShapeNode.cpp b/src/mmSolver/shape/BundleShapeNode.cpp index 590a31b4d..8b209f579 100644 --- a/src/mmSolver/shape/BundleShapeNode.cpp +++ b/src/mmSolver/shape/BundleShapeNode.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -136,7 +135,6 @@ MStatus BundleShapeNode::initialize() { MStatus status; MFnUnitAttribute uAttr; MFnNumericAttribute nAttr; - MFnEnumAttribute eAttr; // Color m_color = nAttr.createColor("color", "clr"); diff --git a/src/mmSolver/shape/ImagePlaneGeometry2Override.cpp b/src/mmSolver/shape/ImagePlaneGeometry2Override.cpp new file mode 100644 index 000000000..6bafe440e --- /dev/null +++ b/src/mmSolver/shape/ImagePlaneGeometry2Override.cpp @@ -0,0 +1,960 @@ +/* + * Copyright (C) 2022, 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#include "ImagePlaneGeometry2Override.h" + +// Get M_PI constant +#define _USE_MATH_DEFINES +#include + +// STL +#include +#include +#include + +// Maya +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Maya Viewport 2.0 +#include +#include +#include +#include +#include + +// MM Solver +#include +#include + +#include "ImagePlaneShape2Node.h" +#include "ImagePlaneUtils.h" +#include "mmSolver/image/ImageCache.h" +#include "mmSolver/mayahelper/maya_utils.h" +#include "mmSolver/render/shader/shader_utils.h" +#include "mmSolver/shape/constant_texture_data.h" +#include "mmSolver/utilities/number_utils.h" + +namespace mmsolver { + +const MString renderItemName_imagePlaneWireframe = + MString("imagePlaneWireframe"); +const MString renderItemName_imagePlaneShaded = MString("imagePlaneShaded"); + +ImagePlaneGeometry2Override::ImagePlaneGeometry2Override(const MObject &obj) + : MHWRender::MPxGeometryOverride(obj) + , m_this_node(obj) + , m_visible(true) + , m_draw_hud(false) + , m_draw_image_size(false) + , m_draw_camera_size(false) + , m_geometry_node_type(MFn::kInvalid) + , m_image_display_channel(ImageDisplayChannel::kRGBA) + , m_color_gain(1.0, 1.0, 1.0, 1.0) + , m_color_exposure(0.0f) + , m_color_gamma(1.0f) + , m_color_saturation(1.0f) + , m_color_soft_clip(0.0f) + , m_alpha_gain(1.0f) + , m_default_color(0.3, 0.0, 0.0, 1.0) + , m_ignore_alpha(false) + , m_flip(false) + , m_flop(false) + , m_is_transparent(false) + , m_frame(0) + , m_file_path() + , m_input_color_space_name() + , m_output_color_space_name() + , m_shader(nullptr) + , m_update_shader(false) + , m_color_texture(nullptr) + , m_texture_sampler(nullptr) { + m_model_editor_changed_callback_id = MEventMessage::addEventCallback( + "modelEditorChanged", + ImagePlaneGeometry2Override::on_model_editor_changed_func, this); +#if MAYA_API_VERSION >= 20220000 + m_shader_link_lost_user_data_ptr = + ShaderLinkLostUserData2Ptr(new ShaderLinkLostUserData2()); +#else + m_shader_link_lost_user_data = ShaderLinkLostUserData2(); +#endif +} + +ImagePlaneGeometry2Override::~ImagePlaneGeometry2Override() { + if (m_model_editor_changed_callback_id != 0) { + MMessage::removeCallback(m_model_editor_changed_callback_id); + m_model_editor_changed_callback_id = 0; + } + + if (m_color_texture) { + MHWRender::MRenderer *renderer = MHWRender::MRenderer::theRenderer(); + MHWRender::MTextureManager *texture_manager = + renderer ? renderer->getTextureManager() : nullptr; + if (texture_manager) { + texture_manager->releaseTexture(m_color_texture); + m_color_texture = nullptr; + } + } + + if (m_texture_sampler) { + MHWRender::MStateManager::releaseSamplerState(m_texture_sampler); + m_texture_sampler = nullptr; + } + + if (m_shader) { + MHWRender::MRenderer *renderer = MHWRender::MRenderer::theRenderer(); + const MHWRender::MShaderManager *shaderManager = + renderer ? renderer->getShaderManager() : nullptr; + if (shaderManager) { + shaderManager->releaseShader(m_shader); + } + } +} + +void ImagePlaneGeometry2Override::on_model_editor_changed_func( + void *clientData) { + // Mark the node as being dirty so that it can update on display + // appearance switch among wireframe and shaded. + ImagePlaneGeometry2Override *ovr = + static_cast(clientData); + if (ovr && !ovr->m_this_node.isNull()) { + MHWRender::MRenderer::setGeometryDrawDirty(ovr->m_this_node); + } +} + +void ImagePlaneGeometry2Override::shader_link_lost_func( + ShaderLinkLostUserData2 *userData) { + // TODO: What should this function do? Does it need to do anything? + MMSOLVER_MAYA_DBG( + "mmImagePlaneGeometry2Override: " + "shader_link_lost_func: " + "link_lost_count=" + << (*userData).link_lost_count + << " set_shader_count=" << (*userData).set_shader_count); + (*userData).link_lost_count += 1; +} + +MHWRender::DrawAPI ImagePlaneGeometry2Override::supportedDrawAPIs() const { + // TODO: We cannot support DirectX, unless we also write another + // mmImagePlane for DirectX. Legacy OpenGL is also unsupported. + return MHWRender::kOpenGLCoreProfile; +} + +void ImagePlaneGeometry2Override::query_node_attributes( + MObject &node, MDagPath &out_camera_node_path, bool &out_visible, + bool &out_visible_to_camera_only, bool &out_is_under_camera, + bool &out_draw_hud, bool &out_draw_image_size, MString &out_image_size, + bool &out_draw_camera_size, MString &out_camera_size, + ImageDisplayChannel &out_image_display_channel, MColor &out_color_gain, + float &out_color_exposure, float &out_color_gamma, + float &out_color_saturation, float &out_color_soft_clip, + float &out_alpha_gain, MColor &out_default_color, bool &out_ignore_alpha, + bool &out_flip, bool &out_flop, bool &out_is_transparent, + mmcore::FrameValue &out_frame, MString &out_file_path, + MString &out_input_color_space_name, MString &out_output_color_space_name) { + const bool verbose = false; + + MDagPath objPath; + MDagPath::getAPathTo(node, objPath); + + if (!objPath.isValid()) { + return; + } + MStatus status; + + auto frame_context = MPxGeometryOverride::getFrameContext(); + MDagPath camera_node_path = frame_context->getCurrentCameraPath(&status); + CHECK_MSTATUS(status); + + // By default the draw is visible, unless overridden by + // out_visible_to_camera_only or out_is_under_camera. + out_visible = true; + + status = + getNodeAttr(objPath, ImagePlaneShape2Node::m_visible_to_camera_only, + out_visible_to_camera_only); + CHECK_MSTATUS(status); + + status = + getNodeAttr(objPath, ImagePlaneShape2Node::m_draw_hud, out_draw_hud); + CHECK_MSTATUS(status); + + out_is_under_camera = true; + if (camera_node_path.isValid() && out_camera_node_path.isValid()) { + // Using an explicit camera node path to compare + // against ensures that if a rouge camera is parented + // under the attached camera, the node will be + // invisible. + out_is_under_camera = out_camera_node_path == camera_node_path; + } + + if (!out_is_under_camera) { + if (out_visible_to_camera_only) { + out_visible = false; + } + // Do not draw the HUD if we are not under the camera, + // the HUD must only be visible from the point of view + // of the intended camera, otherwise it will look + // wrong. + out_draw_hud = false; + } + + const auto int_precision = 0; + const auto double_precision = 3; + calculate_node_image_size_string( + objPath, ImagePlaneShape2Node::m_draw_image_size, + ImagePlaneShape2Node::m_image_width, + ImagePlaneShape2Node::m_image_height, + ImagePlaneShape2Node::m_image_pixel_aspect, int_precision, + double_precision, out_draw_image_size, out_image_size); + calculate_node_camera_size_string( + objPath, ImagePlaneShape2Node::m_draw_camera_size, + ImagePlaneShape2Node::m_camera_width_inch, + ImagePlaneShape2Node::m_camera_height_inch, double_precision, + out_draw_camera_size, out_camera_size); + + status = getNodeAttr(objPath, ImagePlaneShape2Node::m_image_color_gain, + out_color_gain); + CHECK_MSTATUS(status); + + status = getNodeAttr(objPath, ImagePlaneShape2Node::m_image_color_exposure, + out_color_exposure); + CHECK_MSTATUS(status); + + status = getNodeAttr(objPath, ImagePlaneShape2Node::m_image_color_gamma, + out_color_gamma); + CHECK_MSTATUS(status); + + status = + getNodeAttr(objPath, ImagePlaneShape2Node::m_image_color_saturation, + out_color_saturation); + CHECK_MSTATUS(status); + + status = getNodeAttr(objPath, ImagePlaneShape2Node::m_image_color_soft_clip, + out_color_soft_clip); + CHECK_MSTATUS(status); + + status = getNodeAttr(objPath, ImagePlaneShape2Node::m_image_alpha_gain, + out_alpha_gain); + CHECK_MSTATUS(status); + + short image_display_channel_value = 0; + status = getNodeAttr(objPath, ImagePlaneShape2Node::m_image_display_channel, + image_display_channel_value); + CHECK_MSTATUS(status); + out_image_display_channel = + static_cast(image_display_channel_value); + + status = getNodeAttr(objPath, ImagePlaneShape2Node::m_image_ignore_alpha, + out_ignore_alpha); + CHECK_MSTATUS(status); + + status = getNodeAttr(objPath, ImagePlaneShape2Node::m_image_flip, out_flip); + CHECK_MSTATUS(status); + + status = getNodeAttr(objPath, ImagePlaneShape2Node::m_image_flop, out_flop); + CHECK_MSTATUS(status); + + status = getNodeAttr(objPath, ImagePlaneShape2Node::m_shader_is_transparent, + out_is_transparent); + CHECK_MSTATUS(status); + + status = getNodeAttr(objPath, ImagePlaneShape2Node::m_image_frame_number, + out_frame); + CHECK_MSTATUS(status); + + status = getNodeAttr(objPath, ImagePlaneShape2Node::m_image_file_path, + out_file_path); + CHECK_MSTATUS(status); + + status = + getNodeAttr(objPath, ImagePlaneShape2Node::m_image_input_color_space, + out_input_color_space_name); + CHECK_MSTATUS(status); + + status = + getNodeAttr(objPath, ImagePlaneShape2Node::m_image_output_color_space, + out_output_color_space_name); + CHECK_MSTATUS(status); + + // Find the input/output file color spaces. + // + // TODO: Do not re-calculate this each update. Compute once and + // cache the results. + const char *file_color_space_name = + mmcolorio::guess_color_space_name_from_file_path( + out_file_path.asChar()); + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: " + "query_node_attributes:" + << " file_color_space_name=\"" << file_color_space_name << "\"."); + + const char *output_color_space_name = mmcolorio::get_role_color_space_name( + mmcolorio::ColorSpaceRole::kSceneLinear); + out_output_color_space_name = MString(output_color_space_name); + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: " + "query_node_attributes:" + << " out_output_color_space_name=\"" + << out_output_color_space_name.asChar() << "\"."); +} + +void ImagePlaneGeometry2Override::updateDG() { + if (!m_geometry_node_path.isValid()) { + MString attr_name = "geometryNode"; + find_geometry_node_path(m_this_node, attr_name, m_geometry_node_path, + m_geometry_node_type); + } + + if (!m_camera_node_path.isValid()) { + MString attr_name = "cameraNode"; + find_camera_node_path(m_this_node, attr_name, m_camera_node_path, + m_camera_node_type); + } + + // Query Attributes from the base node. + MString temp_input_color_space_name = ""; + MString temp_output_color_space_name = ""; + ImagePlaneGeometry2Override::query_node_attributes( + m_this_node, m_camera_node_path, m_visible, m_visible_to_camera_only, + m_is_under_camera, m_draw_hud, m_draw_image_size, m_image_size, + m_draw_camera_size, m_camera_size, m_image_display_channel, + m_color_gain, m_color_exposure, m_color_gamma, m_color_saturation, + m_color_soft_clip, m_alpha_gain, m_default_color, m_ignore_alpha, + m_flip, m_flop, m_is_transparent, m_frame, m_file_path, + temp_input_color_space_name, temp_output_color_space_name); + + if ((m_input_color_space_name.asChar() != + temp_input_color_space_name.asChar()) || + (m_input_color_space_name.asChar() != + temp_input_color_space_name.asChar())) { + m_input_color_space_name = temp_input_color_space_name; + m_output_color_space_name = temp_output_color_space_name; + m_update_shader = true; + } +} + +inline MFloatMatrix create_saturation_matrix(const float saturation) { + // Luminance weights + // + // From Mozilla: + // https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_Colors_and_Luminance + const float kLuminanceRed = 0.2126f; + const float kLuminanceGreen = 0.7152f; + const float kLuminanceBlue = 0.0722f; + + const float r_weight = kLuminanceRed; + const float g_weight = kLuminanceGreen; + const float b_weight = kLuminanceBlue; + + const float r1 = (1.0 - saturation) * r_weight + saturation; + const float r2 = (1.0 - saturation) * r_weight; + const float r3 = (1.0 - saturation) * r_weight; + + const float g1 = (1.0 - saturation) * g_weight; + const float g2 = (1.0 - saturation) * g_weight + saturation; + const float g3 = (1.0 - saturation) * g_weight; + + const float b1 = (1.0 - saturation) * b_weight; + const float b2 = (1.0 - saturation) * b_weight; + const float b3 = (1.0 - saturation) * b_weight + saturation; + + const float saturation_matrix_values[4][4] = { + // Column 0 + {r1, g1, b1, 0.0}, + // Column 1 + {r2, g2, b2, 0.0}, + // Column 2 + {r3, g3, b3, 0.0}, + // Column 3 + {0.0, 0.0, 0.0, 1.0}, + }; + + return MFloatMatrix(saturation_matrix_values); +} + +void ImagePlaneGeometry2Override::set_shader_instance_parameters( + MShaderInstance *shader, MHWRender::MTextureManager *texture_manager, + const MColor &color_gain, const float color_exposure, + const float color_gamma, const float color_saturation, + const float color_soft_clip, const float alpha_gain, + const MColor &default_color, const bool ignore_alpha, const bool flip, + const bool flop, const bool is_transparent, + const ImageDisplayChannel image_display_channel, + const mmcore::FrameValue frame, const MString &file_path, + const MString &input_color_space_name, + const MString &output_color_space_name, + MHWRender::MTexture *out_color_texture, + const MHWRender::MSamplerState *out_texture_sampler) { + MStatus status = MStatus::kSuccess; + const bool verbose = false; + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: set_shader_instance_parameters."); + + const float color[] = {color_gain[0], color_gain[1], color_gain[2], 1.0f}; + status = shader->setParameter("gColorGain", color); + CHECK_MSTATUS(status); + + status = shader->setParameter("gColorExposure", color_exposure); + CHECK_MSTATUS(status); + + status = shader->setParameter("gColorGamma", color_gamma); + CHECK_MSTATUS(status); + + MFloatMatrix saturation_matrix = create_saturation_matrix(color_saturation); + status = shader->setParameter("gColorSaturationMatrix", saturation_matrix); + CHECK_MSTATUS(status); + + status = shader->setParameter("gColorSoftClip", color_soft_clip); + CHECK_MSTATUS(status); + + status = shader->setParameter("gAlphaGain", alpha_gain); + CHECK_MSTATUS(status); + + const float temp_default_color[] = {default_color[0], default_color[1], + default_color[2], 1.0f}; + status = shader->setParameter("gFallbackColor", temp_default_color); + CHECK_MSTATUS(status); + + status = shader->setParameter("gFlip", flip); + CHECK_MSTATUS(status); + + status = shader->setParameter("gFlop", flop); + CHECK_MSTATUS(status); + + status = shader->setParameter("gIgnoreAlpha", m_ignore_alpha); + CHECK_MSTATUS(status); + + status = shader->setParameter("gDisplayChannel", + static_cast(image_display_channel)); + CHECK_MSTATUS(status); + + status = shader->setIsTransparent(is_transparent); + MMSOLVER_MAYA_VRB("mmImagePlaneGeometry2Override: shader->isTransparent()=" + << shader->isTransparent()); + CHECK_MSTATUS(status); + + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: file_path=" << file_path.asChar()); + + rust::Str file_path_rust_str = rust::Str(file_path.asChar()); + rust::String expanded_file_path_rust_string = + mmcore::expand_file_path_string(file_path_rust_str, frame); + MString expanded_file_path(expanded_file_path_rust_string.data(), + expanded_file_path_rust_string.length()); + MMSOLVER_MAYA_VRB("mmImagePlaneGeometry2Override: expanded_file_path=" + << expanded_file_path.asChar()); + + MMSOLVER_MAYA_VRB("mmImagePlaneGeometry2Override: start out_color_texture=" + << out_color_texture); + + if (!out_color_texture) { + MMSOLVER_MAYA_VRB("mmImagePlaneGeometry2Override: use image read"); + + const bool do_texture_update = false; + image::ImageCache &image_cache = image::ImageCache::getInstance(); + out_color_texture = image::read_texture_image_file( + texture_manager, image_cache, m_temp_image, m_temp_pixel_buffer, + m_temp_meta_data, file_path, expanded_file_path, do_texture_update); + + if (out_color_texture) { + MMSOLVER_MAYA_VRB("mmImagePlaneGeometry2Override: texture->name()=" + << out_color_texture->name().asChar()); + const void *resource_handle = out_color_texture->resourceHandle(); + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: texture->resourceHandle()=" + << resource_handle); + if (resource_handle) { + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: *texture->resourceHandle()=" + << *(uint32_t *)resource_handle); + } + + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: texture->hasAlpha()=" + << out_color_texture->hasAlpha()); + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: texture->hasZeroAlpha()=" + << out_color_texture->hasZeroAlpha()); + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: texture->hasTransparentAlpha()=" + << out_color_texture->hasTransparentAlpha()); + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: texture->bytesPerPixel()=" + << out_color_texture->bytesPerPixel()); + + MTextureDescription texture_desc; + out_color_texture->textureDescription(texture_desc); + + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: texture_desc.fWidth=" + << texture_desc.fWidth); + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: texture_desc.fHeight=" + << texture_desc.fHeight); + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: texture_desc.fDepth=" + << texture_desc.fDepth); + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: texture_desc.fBytesPerRow=" + << texture_desc.fBytesPerRow); + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: texture_desc.fBytesPerSlice=" + << texture_desc.fBytesPerSlice); + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: texture_desc.fMipmaps=" + << texture_desc.fMipmaps); + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: texture_desc.fArraySlices=" + << texture_desc.fArraySlices); + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: texture_desc.fFormat=" + << texture_desc.fFormat); + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: texture_desc.fTextureType=" + << texture_desc.fTextureType); + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: texture_desc.fEnvMapType=" + << texture_desc.fEnvMapType); + } + } + + if (!out_texture_sampler) { + MHWRender::MSamplerStateDesc sampler_desc; + sampler_desc.addressU = MHWRender::MSamplerState::kTexWrap; + sampler_desc.addressV = MHWRender::MSamplerState::kTexWrap; + sampler_desc.addressW = MHWRender::MSamplerState::kTexWrap; + // kMinMagMipPoint is "nearest pixel" filtering. + sampler_desc.filter = MHWRender::MSamplerState::kMinMagMipPoint; + out_texture_sampler = + MHWRender::MStateManager::acquireSamplerState(sampler_desc); + } + + if (out_texture_sampler) { + status = + shader->setParameter("gImageTextureSampler", *out_texture_sampler); + CHECK_MSTATUS(status); + } else { + MMSOLVER_MAYA_WRN( + "mmImagePlaneGeometry2Override: " + "Could not get texture sampler." + << " out_texture_sampler=" << out_texture_sampler); + } + + if (out_color_texture) { + MHWRender::MTextureAssignment texture_assignment; + texture_assignment.texture = out_color_texture; + status = shader->setParameter("gImageTexture", texture_assignment); + CHECK_MSTATUS(status); + + out_color_texture = nullptr; + } else { + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: " + "Could not get color texture; did not assign texture." + << " out_color_texture=" << out_color_texture); + } + + return; +} + +void ImagePlaneGeometry2Override::updateRenderItems(const MDagPath &path, + MRenderItemList &list) { + const bool verbose = false; + if (!m_geometry_node_path.isValid()) { + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: " + "Geometry node DAG path is not valid."); + return; + } + + MHWRender::MRenderer *renderer = MRenderer::theRenderer(); + if (!renderer) { + MMSOLVER_MAYA_WRN( + "mmImagePlaneGeometry2Override: " + "Could not get MRenderer."); + return; + } + + const MHWRender::MShaderManager *shaderManager = + renderer->getShaderManager(); + if (!shaderManager) { + MMSOLVER_MAYA_WRN( + "mmImagePlaneGeometry2Override: " + "Could not get MShaderManager."); + return; + } + + if (m_geometry_node_type != MFn::kMesh) { + MMSOLVER_MAYA_WRN("mmImagePlaneGeometry2Override: " + << "Only Meshes are supported, geometry node " + "given is not a mesh."); + return; + } + + bool draw_wireframe = false; + int index = 0; + + MRenderItem *wireframeItem = nullptr; + if (draw_wireframe) { + // Add render item for drawing wireframe on the mesh + index = list.indexOf(renderItemName_imagePlaneWireframe); + if (index >= 0) { + wireframeItem = list.itemAt(index); + } else { + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: " + "Generate wireframe MRenderItem..."); + wireframeItem = MRenderItem::Create( + renderItemName_imagePlaneWireframe, MRenderItem::DecorationItem, + MGeometry::kLines); + + auto draw_mode = MGeometry::kAll; // Draw in all visible modes. + auto depth_priority = MRenderItem::sActiveWireDepthPriority; + + wireframeItem->setDrawMode(draw_mode); + wireframeItem->depthPriority(depth_priority); + + list.append(wireframeItem); + } + } + + // Add render item for drawing shaded on the mesh + MRenderItem *shadedItem = nullptr; + index = list.indexOf(renderItemName_imagePlaneShaded); + if (index >= 0) { + shadedItem = list.itemAt(index); + } else { + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: " + "Generate shaded MRenderItem..."); + shadedItem = MRenderItem::Create(renderItemName_imagePlaneShaded, + MRenderItem::NonMaterialSceneItem, + MGeometry::kTriangles); + + auto draw_mode = MGeometry::kAll; // Draw in all visible modes. + auto depth_priority = MRenderItem::sDormantWireDepthPriority; + + shadedItem->setDrawMode(draw_mode); + shadedItem->depthPriority(depth_priority); + + list.append(shadedItem); + } + + if (wireframeItem) { + wireframeItem->enable(m_visible); + + MShaderInstance *shader = + shaderManager->getStockShader(MShaderManager::k3dSolidShader); + if (shader) { + static const float color[] = {1.0f, 0.0f, 0.0f, 1.0f}; + MStatus status = shader->setParameter("solidColor", color); + CHECK_MSTATUS(status); + wireframeItem->setShader(shader); + shaderManager->releaseShader(shader); + } + } + + if (shadedItem) { + shadedItem->enable(m_visible); + + MMSOLVER_MAYA_VRB("mmImagePlaneGeometry2Override: " + << "shadedItem->isEnabled()=" + << shadedItem->isEnabled()); + MMSOLVER_MAYA_VRB("mmImagePlaneGeometry2Override: " + << "shadedItem->isShaderFromNode()=" + << shadedItem->isShaderFromNode()); + MMSOLVER_MAYA_VRB("mmImagePlaneGeometry2Override: " + << "shadedItem->isMultiDraw()=" + << shadedItem->isMultiDraw()); + MMSOLVER_MAYA_VRB("mmImagePlaneGeometry2Override: " + << "shadedItem->isConsolidated()=" + << shadedItem->isConsolidated()); + MMSOLVER_MAYA_VRB("mmImagePlaneGeometry2Override: " + << "shadedItem->wantConsolidation()=" + << shadedItem->wantConsolidation()); + MMSOLVER_MAYA_VRB("mmImagePlaneGeometry2Override: " + << "shadedItem->castsShadows()=" + << shadedItem->castsShadows()); + MMSOLVER_MAYA_VRB("mmImagePlaneGeometry2Override: " + << "shadedItem->receivesShadows()=" + << shadedItem->receivesShadows()); + MMSOLVER_MAYA_VRB("mmImagePlaneGeometry2Override: " + << "shadedItem->excludedFromPostEffects()=" + << shadedItem->excludedFromPostEffects()); + MMSOLVER_MAYA_VRB("mmImagePlaneGeometry2Override: " + << "shadedItem->supportsAdvancedTransparency()=" + << shadedItem->supportsAdvancedTransparency()); + + if (!m_shader || m_update_shader) { + if (m_shader) { + shaderManager->releaseShader(m_shader); + } + + const MString shader_file_path = + mmsolver::render::find_shader_file_path("mmImagePlane.ogsfx"); + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: " + "found shader_file_path=\"" + << shader_file_path << "\""); + + if (shader_file_path.length() > 0) { + MString shader_text = + mmsolver::render::read_shader_file(shader_file_path); + + std::string ocio_shader_text; + mmcolorio::generate_shader_text( + m_input_color_space_name.asChar(), + m_output_color_space_name.asChar(), ocio_shader_text); + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: " + "ocio_shader_text=\"" + << ocio_shader_text << "\""); + if (ocio_shader_text.size() > 0) { + const MString ocio_function_declare_text = MString( + "vec4 OCIODisplay(vec4 passthrough) { return " + "passthrough; " + "}"); + MStatus status = shader_text.substitute( + ocio_function_declare_text, + MString(ocio_shader_text.c_str())); + CHECK_MSTATUS(status); + } + + m_shader = + mmsolver::render::compile_shader_text(shader_text, "Main"); + } + + if (m_shader) { + m_update_shader = false; + } + } + + if (m_shader) { + MHWRender::MTextureManager *texture_manager = + renderer->getTextureManager(); + if (!texture_manager) { + MMSOLVER_MAYA_WRN( + "mmImagePlaneGeometry2Override: " + "Could not get MTextureManager."); + return; + } + + set_shader_instance_parameters( + m_shader, texture_manager, m_color_gain, m_color_exposure, + m_color_gamma, m_color_saturation, m_color_soft_clip, + m_alpha_gain, m_default_color, m_ignore_alpha, m_flip, m_flop, + m_is_transparent, m_image_display_channel, m_frame, m_file_path, + m_input_color_space_name, m_output_color_space_name, + m_color_texture, m_texture_sampler); + + shadedItem->setShader(m_shader); + } + } +} + +void ImagePlaneGeometry2Override::populateGeometry( + const MGeometryRequirements &requirements, + const MRenderItemList &renderItems, MGeometry &data) { + const bool verbose = false; + if (!m_geometry_node_path.isValid()) { + MMSOLVER_MAYA_VRB( + "mmImagePlaneGeometry2Override: " + "Geometry node DAG path is not valid."); + return; + } + + MStatus status; + + // kPolyGeom_Normal = Normal Indicates the polygon performs the + // default geometry. + // + // kPolyGeom_NotSharing = NotSharing Indicates if you don't want + // vertex sharing to be computed by the extractor. Vertex buffer + // size will not be reduced if sharing can be performed. + // + // kPolyGeom_BaseMesh = BaseMesh Indicates if you want the base + // geometry in smoothCage mode. The geometry in extractor is + // always the base geometry in normal mode. + MHWRender::MPolyGeomOptions polygon_geometry_options = + MHWRender::kPolyGeom_Normal | MHWRender::kPolyGeom_BaseMesh; + + MGeometryExtractor extractor(requirements, m_geometry_node_path, + polygon_geometry_options, &status); + if (status == MS::kFailure) { + CHECK_MSTATUS(status); + return; + } + + const MVertexBufferDescriptorList &descList = + requirements.vertexRequirements(); + for (int reqNum = 0; reqNum < descList.length(); ++reqNum) { + MVertexBufferDescriptor desc; + if (!descList.getDescriptor(reqNum, desc)) { + continue; + } + + auto desc_semantic = desc.semantic(); + if ((desc_semantic == MGeometry::kPosition) || + (desc_semantic == MGeometry::kNormal) || + (desc_semantic == MGeometry::kTexture) || + (desc_semantic == MGeometry::kTangent) || + (desc_semantic == MGeometry::kBitangent) || + (desc_semantic == MGeometry::kColor)) { + MVertexBuffer *vertexBuffer = data.createVertexBuffer(desc); + if (vertexBuffer) { + uint32_t vertexCount = extractor.vertexCount(); + bool writeOnly = + true; // We don't need the current buffer values. + float *data = static_cast( + vertexBuffer->acquire(vertexCount, writeOnly)); + if (data) { + status = + extractor.populateVertexBuffer(data, vertexCount, desc); + if (status == MS::kFailure) return; + vertexBuffer->commit(data); + } + } + } + } + + for (int i = 0; i < renderItems.length(); ++i) { + const MRenderItem *item = renderItems.itemAt(i); + if (!item) { + continue; + } + + MIndexBuffer *indexBuffer = + data.createIndexBuffer(MGeometry::kUnsignedInt32); + if (!indexBuffer) { + continue; + } + + if (item->primitive() == MGeometry::kTriangles) { + MIndexBufferDescriptor triangleDesc( + MIndexBufferDescriptor::kTriangle, MString(), + MGeometry::kTriangles, 3); + uint32_t numTriangles = extractor.primitiveCount(triangleDesc); + bool writeOnly = true; // We don't need the current buffer values. + uint32_t *indices = static_cast( + indexBuffer->acquire(3 * numTriangles, writeOnly)); + + status = extractor.populateIndexBuffer(indices, numTriangles, + triangleDesc); + if (status == MS::kFailure) { + return; + } + indexBuffer->commit(indices); + } else if (item->primitive() == MGeometry::kLines) { + MIndexBufferDescriptor edgeDesc(MIndexBufferDescriptor::kEdgeLine, + MString(), MGeometry::kLines, 2); + uint32_t numEdges = extractor.primitiveCount(edgeDesc); + bool writeOnly = true; // We don't need the current buffer values. + uint32_t *indices = static_cast( + indexBuffer->acquire(2 * numEdges, writeOnly)); + + status = extractor.populateIndexBuffer(indices, numEdges, edgeDesc); + if (status == MS::kFailure) { + return; + } + indexBuffer->commit(indices); + } + + item->associateWithIndexBuffer(indexBuffer); + } +} + +void ImagePlaneGeometry2Override::cleanUp() {} + +#if MAYA_API_VERSION >= 20190000 +bool ImagePlaneGeometry2Override::requiresGeometryUpdate() const { + const bool verbose = false; + if (m_geometry_node_path.isValid()) { + MMSOLVER_MAYA_VRB( + "ImagePlaneGeometry2Override::requiresGeometryUpdate: false"); + return false; + } + MMSOLVER_MAYA_VRB( + "ImagePlaneGeometry2Override::requiresGeometryUpdate: true"); + return true; +} + +bool ImagePlaneGeometry2Override::requiresUpdateRenderItems( + const MDagPath &path) const { + const bool verbose = false; + MMSOLVER_MAYA_VRB( + "ImagePlaneGeometry2Override::requiresUpdateRenderItems: true: " + << path.fullPathName().asChar()); + return true; // Always update the render items. +} +#endif + +bool ImagePlaneGeometry2Override::hasUIDrawables() const { return true; } + +void ImagePlaneGeometry2Override::addUIDrawables( + const MDagPath &path, MUIDrawManager &drawManager, + const MFrameContext &frameContext) { + if (!m_draw_hud) { + return; + } + + const float pos_coord_x = 0.48f; + const float pos_coord_y = 0.52f; + const MColor text_color = MColor(1.0f, 0.0f, 0.0f); + const uint32_t font_size = 12; + const int *background_size = nullptr; + const MColor *background_color = nullptr; + auto dynamic = false; + + if (m_draw_image_size) { + auto text_position = MPoint(pos_coord_x, pos_coord_y, 0.0); + auto font_alignment = MUIDrawManager::kRight; + + drawManager.beginDrawable(); + drawManager.setColor(text_color); + drawManager.setFontSize(font_size); + drawManager.text(text_position, m_image_size, font_alignment, + background_size, background_color, dynamic); + drawManager.endDrawable(); + } + + if (m_draw_camera_size) { + auto text_position = MPoint(-pos_coord_x, pos_coord_y, 0.0); + auto font_alignment = MUIDrawManager::kLeft; + + drawManager.beginDrawable(); + drawManager.setColor(text_color); + drawManager.setFontSize(font_size); + drawManager.text(text_position, m_camera_size, font_alignment, + background_size, background_color, dynamic); + drawManager.endDrawable(); + } +} + +} // namespace mmsolver diff --git a/src/mmSolver/shape/ImagePlaneGeometry2Override.h b/src/mmSolver/shape/ImagePlaneGeometry2Override.h new file mode 100644 index 000000000..3173405f0 --- /dev/null +++ b/src/mmSolver/shape/ImagePlaneGeometry2Override.h @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2022, 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#ifndef MM_IMAGE_PLANE_GEOMETRY_2_OVERRIDE_H +#define MM_IMAGE_PLANE_GEOMETRY_2_OVERRIDE_H + +// Maya +#include +#include +#include +#include +#include +#include +#include + +// Maya Viewport 2.0 +#include +#include +#include +#include +#include + +#if MAYA_API_VERSION >= 20220000 +#include +#endif + +// MM Solver +#include +#include + +#include "ImagePlaneShape2Node.h" +#include "ImagePlaneUtils.h" +#include "mmSolver/image/ImageCache.h" +#include "mmSolver/utilities/debug_utils.h" + +namespace mmsolver { + +class ShaderLinkLostUserData2 : public MUserData { +public: + ShaderLinkLostUserData2() +#if MAYA_API_VERSION >= 20220000 + : MUserData() +#else + // MUserData(bool) constructor is deprecated in Maya 2022+ + // because 'deleteAfterUse' is no longer needed. + : MUserData(/*deleteAfterUse=*/true) // let Maya clean up +#endif + , link_lost_count(0) + , set_shader_count(0) { + } + + // Keep track of the number of times stuff happens, just for + // interest sake (maybe to help debugging?) - doesn't really mean + // or do anything special. + uint32_t link_lost_count; + uint32_t set_shader_count; +}; + +#if MAYA_API_VERSION >= 20220000 +using ShaderLinkLostUserData2Ptr = MSharedPtr; +#endif + +class ImagePlaneGeometry2Override : public MPxGeometryOverride { +public: + static MPxGeometryOverride *Creator(const MObject &obj) { + return new ImagePlaneGeometry2Override(obj); + } + + ~ImagePlaneGeometry2Override() override; + + MHWRender::DrawAPI supportedDrawAPIs() const override; + + bool hasUIDrawables() const override; + void addUIDrawables(const MDagPath &path, MUIDrawManager &drawManager, + const MFrameContext &frameContext) override; + + void updateDG() override; + void updateRenderItems(const MDagPath &path, + MRenderItemList &list) override; + void populateGeometry(const MGeometryRequirements &requirements, + const MRenderItemList &renderItems, + MGeometry &data) override; + void cleanUp() override; + +#if MAYA_API_VERSION >= 20190000 + bool requiresGeometryUpdate() const override; + bool requiresUpdateRenderItems(const MDagPath &path) const; +#endif + + bool traceCallSequence() const override { + // Return true if internal tracing is desired. + return false; + } + + void handleTraceMessage(const MString &message) const override { + MGlobal::displayInfo("ImagePlaneGeometry2Override: " + message); + MMSOLVER_MAYA_INFO("ImagePlaneGeometry2Override: " << message.asChar()); + } + +protected: + ImagePlaneGeometry2Override(const MObject &obj); + + static void on_model_editor_changed_func(void *clientData); + static void shader_link_lost_func(ShaderLinkLostUserData2 *userData); + + void query_node_attributes( + MObject &node, MDagPath &out_camera_node_path, bool &out_visible, + bool &out_visible_to_camera_only, bool &out_is_under_camera, + bool &out_draw_hud, bool &out_draw_image_size, MString &out_image_size, + bool &out_draw_camera_size, MString &out_camera_size, + ImageDisplayChannel &out_image_display_channel, MColor &out_color_gain, + float &out_color_exposure, float &out_color_gamma, + float &out_color_saturation, float &out_color_soft_clip, + float &out_alpha_gain, MColor &out_default_color, + bool &out_ignore_alpha, bool &out_flip, bool &out_flop, + bool &out_is_transparent, mmcore::FrameValue &out_frame, + MString &out_file_path, MString &out_input_color_space_name, + MString &out_output_color_space_name); + + void set_shader_instance_parameters( + MShaderInstance *shader, MHWRender::MTextureManager *textureManager, + const MColor &color_gain, const float color_exposure, + const float color_gamma, const float color_saturation, + const float color_soft_clip, const float alpha_gain, + const MColor &default_color, const bool ignore_alpha, const bool flip, + const bool flop, const bool is_transparent, + const ImageDisplayChannel image_display_channel, + const mmcore::FrameValue frame, const MString &file_path, + const MString &input_color_space_name, + const MString &output_color_space_name, + MHWRender::MTexture *out_color_texture, + const MHWRender::MSamplerState *out_texture_sampler); + + MObject m_this_node; + MDagPath m_geometry_node_path; + MDagPath m_camera_node_path; + MFn::Type m_geometry_node_type; + MFn::Type m_camera_node_type; + + bool m_visible; + bool m_visible_to_camera_only; + bool m_is_under_camera; + bool m_draw_hud; + bool m_draw_image_size; + bool m_draw_camera_size; + bool m_update_shader; + MString m_image_size; + MString m_camera_size; + MCallbackId m_model_editor_changed_callback_id; + + // Shader attributes. + MShaderInstance *m_shader; + ImageDisplayChannel m_image_display_channel; + MColor m_color_gain; + float m_alpha_gain; + float m_color_exposure; + float m_color_gamma; + float m_color_saturation; + float m_color_soft_clip; + MColor m_default_color; + bool m_ignore_alpha; + bool m_flip; + bool m_flop; + bool m_is_transparent; + mmcore::FrameValue m_frame; + MString m_file_path; + MString m_input_color_space_name; + MString m_output_color_space_name; + + // Texture caching + MImage m_temp_image; + mmimage::ImagePixelBuffer m_temp_pixel_buffer; + mmimage::ImageMetaData m_temp_meta_data; + MHWRender::MTexture *m_color_texture; + const MHWRender::MSamplerState *m_texture_sampler; + +#if MAYA_API_VERSION >= 20220000 + ShaderLinkLostUserData2Ptr m_shader_link_lost_user_data_ptr; +#else + ShaderLinkLostUserData2 m_shader_link_lost_user_data; +#endif +}; + +} // namespace mmsolver + +#endif // MM_IMAGE_PLANE_GEOMETRY_2_OVERRIDE_H diff --git a/src/mmSolver/shape/ImagePlaneGeometryOverride.cpp b/src/mmSolver/shape/ImagePlaneGeometryOverride.cpp index 082db7474..95298be7a 100644 --- a/src/mmSolver/shape/ImagePlaneGeometryOverride.cpp +++ b/src/mmSolver/shape/ImagePlaneGeometryOverride.cpp @@ -44,6 +44,8 @@ #include // MM Solver +#include "ImagePlaneShapeNode.h" +#include "ImagePlaneUtils.h" #include "mmSolver/mayahelper/maya_utils.h" #include "mmSolver/utilities/number_utils.h" @@ -88,140 +90,25 @@ MHWRender::DrawAPI ImagePlaneGeometryOverride::supportedDrawAPIs() const { MHWRender::kOpenGLCoreProfile); } -bool getUpstreamNodeFromConnection(const MObject &this_node, - const MString &attr_name, - MPlugArray &out_connections) { - MStatus status; - MFnDependencyNode mfn_depend_node(this_node); - - bool wantNetworkedPlug = true; - MPlug plug = - mfn_depend_node.findPlug(attr_name, wantNetworkedPlug, &status); - if (status != MStatus::kSuccess) { - CHECK_MSTATUS(status); - return false; - } - if (plug.isNull()) { - MMSOLVER_MAYA_WRN("Could not get plug for \"" - << mfn_depend_node.name().asChar() << "." - << attr_name.asChar() << "\" node."); - return false; - } - - bool as_destination = true; - bool as_source = false; - // Ask for plugs connecting to this node's ".shaderNode" - // attribute. - plug.connectedTo(out_connections, as_destination, as_source, &status); - if (status != MStatus::kSuccess) { - CHECK_MSTATUS(status); - return false; - } - if (out_connections.length() == 0) { - MMSOLVER_MAYA_WRN("No connections to the \"" - << mfn_depend_node.name().asChar() << "." - << attr_name.asChar() << "\" attribute."); - return false; - } - return true; -} - void ImagePlaneGeometryOverride::updateDG() { const auto verbose = false; + if (!m_geometry_node_path.isValid()) { MString attr_name = "geometryNode"; - MPlugArray connections; - bool ok = - getUpstreamNodeFromConnection(m_this_node, attr_name, connections); - - if (ok) { - for (uint32_t i = 0; i < connections.length(); ++i) { - MObject node = connections[i].node(); - - if (node.hasFn(MFn::kMesh)) { - MDagPath path; - MDagPath::getAPathTo(node, path); - m_geometry_node_path = path; - m_geometry_node_type = path.apiType(); - MMSOLVER_MAYA_VRB( - "Validated geometry node: " - << " path=" - << m_geometry_node_path.fullPathName().asChar() - << " type=" << node.apiTypeStr()); - break; - } else { - MMSOLVER_MAYA_WRN( - "Geometry node is not correct type:" - << " path=" - << m_geometry_node_path.fullPathName().asChar() - << " type=" << node.apiTypeStr()); - } - } - } + find_geometry_node_path(m_this_node, attr_name, m_geometry_node_path, + m_geometry_node_type); } if (m_shader_node.isNull()) { MString attr_name = "shaderNode"; - MPlugArray connections; - bool ok = - getUpstreamNodeFromConnection(m_this_node, attr_name, connections); - - if (ok) { - for (uint32_t i = 0; i < connections.length(); ++i) { - MObject node = connections[i].node(); - - MFnDependencyNode mfn_depend_node(node); - if (node.hasFn(MFn::kSurfaceShader) || - node.hasFn(MFn::kHwShaderNode) || - node.hasFn(MFn::kPluginHardwareShader) || - node.hasFn(MFn::kPluginHwShaderNode)) { - m_shader_node = node; - m_shader_node_type = node.apiType(); - MMSOLVER_MAYA_VRB("Validated shader node:" - << " name=" - << mfn_depend_node.name().asChar() - << " type=" << node.apiTypeStr()); - break; - } else { - MMSOLVER_MAYA_WRN("Shader node is not correct type: " - << " name=" - << mfn_depend_node.name().asChar() - << " type=" << node.apiTypeStr()); - } - } - } + find_shader_node_path(m_this_node, attr_name, m_shader_node, + m_shader_node_type); } if (!m_camera_node_path.isValid()) { MString attr_name = "cameraNode"; - MPlugArray connections; - bool ok = - getUpstreamNodeFromConnection(m_this_node, attr_name, connections); - - if (ok) { - for (uint32_t i = 0; i < connections.length(); ++i) { - MObject node = connections[i].node(); - - if (node.hasFn(MFn::kCamera)) { - MDagPath path; - MDagPath::getAPathTo(node, path); - m_camera_node_path = path; - m_camera_node_type = path.apiType(); - MMSOLVER_MAYA_VRB( - "Validated camera node: " - << " path=" - << m_camera_node_path.fullPathName().asChar() - << " type=" << node.apiTypeStr()); - break; - } else { - MMSOLVER_MAYA_WRN( - "Camera node is not correct type:" - << " path=" - << m_camera_node_path.fullPathName().asChar() - << " type=" << node.apiTypeStr()); - } - } - } + find_camera_node_path(m_this_node, attr_name, m_camera_node_path, + m_camera_node_type); } // Query Attributes from the base node. @@ -272,78 +159,19 @@ void ImagePlaneGeometryOverride::updateDG() { const auto int_precision = 0; const auto double_precision = 3; - { - double width = 1.0; - double height = 1.0; - double pixel_aspect = 1.0; - - status = - getNodeAttr(objPath, ImagePlaneShapeNode::m_draw_image_size, - m_draw_image_size); - CHECK_MSTATUS(status); - - status = getNodeAttr(objPath, - ImagePlaneShapeNode::m_image_width, width); - CHECK_MSTATUS(status); - - status = getNodeAttr( - objPath, ImagePlaneShapeNode::m_image_height, height); - CHECK_MSTATUS(status); - - status = getNodeAttr(objPath, - ImagePlaneShapeNode::m_image_pixel_aspect, - pixel_aspect); - CHECK_MSTATUS(status); - - double aspect = (width * pixel_aspect) / height; - - MString width_string; - MString height_string; - MString pixel_aspect_string; - MString aspect_string; - - width_string.set(width, int_precision); - height_string.set(height, int_precision); - pixel_aspect_string.set(pixel_aspect, double_precision); - aspect_string.set(aspect, double_precision); - - m_image_size = MString("Image: ") + width_string + - MString(" x ") + height_string + - MString(" | PAR ") + pixel_aspect_string + - MString(" | ") + aspect_string; - } - - { - double width = 0.0; - double height = 0.0; - - status = getNodeAttr(objPath, - ImagePlaneShapeNode::m_draw_camera_size, - m_draw_camera_size); - CHECK_MSTATUS(status); - - status = getNodeAttr( - objPath, ImagePlaneShapeNode::m_camera_width_inch, width); - CHECK_MSTATUS(status); - - status = getNodeAttr( - objPath, ImagePlaneShapeNode::m_camera_height_inch, height); - CHECK_MSTATUS(status); - - double aspect = width / height; - - MString width_string; - MString height_string; - MString aspect_string; - - width_string.set(width * INCH_TO_MM, double_precision); - height_string.set(height * INCH_TO_MM, double_precision); - aspect_string.set(aspect, double_precision); - - m_camera_size = MString("Camera: ") + width_string + - MString("mm x ") + height_string + - MString("mm | ") + aspect_string; - } + calculate_node_image_size_string( + objPath, ImagePlaneShapeNode::m_draw_image_size, + ImagePlaneShapeNode::m_image_width, + ImagePlaneShapeNode::m_image_height, + ImagePlaneShapeNode::m_image_pixel_aspect, int_precision, + double_precision, + + m_draw_image_size, m_image_size); + calculate_node_camera_size_string( + objPath, ImagePlaneShapeNode::m_draw_camera_size, + ImagePlaneShapeNode::m_camera_width_inch, + ImagePlaneShapeNode::m_camera_height_inch, double_precision, + m_draw_camera_size, m_camera_size); } } } @@ -443,10 +271,16 @@ void ImagePlaneGeometryOverride::updateRenderItems(const MDagPath &path, // needs to be re-compiled. auto linkLostCb = nullptr; auto linkLostUserData = nullptr; - bool nonTextured = false; + const bool nonTextured = false; +#if MAYA_API_VERSION >= 20220000 + shadedItem->setShaderFromNode2(m_shader_node, m_geometry_node_path, + linkLostCb, linkLostUserData, + nonTextured); +#else shadedItem->setShaderFromNode(m_shader_node, m_geometry_node_path, linkLostCb, linkLostUserData, nonTextured); +#endif } else { MMSOLVER_MAYA_WRN( "mmImagePlaneShape: " diff --git a/src/mmSolver/shape/ImagePlaneShape2Node.cpp b/src/mmSolver/shape/ImagePlaneShape2Node.cpp new file mode 100644 index 000000000..a4111cd2b --- /dev/null +++ b/src/mmSolver/shape/ImagePlaneShape2Node.cpp @@ -0,0 +1,477 @@ +/* + * Copyright (C) 2022, 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#include "ImagePlaneShape2Node.h" + +// STL +#include + +// Maya +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if MAYA_API_VERSION >= 20190000 +#include +#endif + +// Maya Viewport 2.0 +#include + +// MM Solver +#include "ImagePlaneUtils.h" +#include "mmSolver/mayahelper/maya_utils.h" +#include "mmSolver/nodeTypeIds.h" + +namespace mmsolver { + +MTypeId ImagePlaneShape2Node::m_id(MM_IMAGE_PLANE_SHAPE_2_TYPE_ID); +MString ImagePlaneShape2Node::m_draw_db_classification( + MM_IMAGE_PLANE_SHAPE_2_DRAW_CLASSIFY); +MString ImagePlaneShape2Node::m_draw_registrant_id( + MM_IMAGE_PLANE_SHAPE_2_DRAW_REGISTRANT_ID); +MString ImagePlaneShape2Node::m_selection_type_name( + MM_IMAGE_PLANE_SHAPE_2_SELECTION_TYPE_NAME); +MString ImagePlaneShape2Node::m_display_filter_draw_db_classification( + MM_IMAGE_PLANE_SHAPE_2_DISPLAY_FILTER_DRAW_DB_CLASSIFICATION); +MString ImagePlaneShape2Node::m_display_filter_name( + MM_IMAGE_PLANE_SHAPE_2_DISPLAY_FILTER_NAME); +MString ImagePlaneShape2Node::m_display_filter_label( + MM_IMAGE_PLANE_SHAPE_2_DISPLAY_FILTER_LABEL); + +// Attributes +MObject ImagePlaneShape2Node::m_visible_to_camera_only; +MObject ImagePlaneShape2Node::m_draw_hud; +MObject ImagePlaneShape2Node::m_draw_image_size; +MObject ImagePlaneShape2Node::m_draw_camera_size; +MObject ImagePlaneShape2Node::m_image_width; +MObject ImagePlaneShape2Node::m_image_height; +MObject ImagePlaneShape2Node::m_image_num_channels; +MObject ImagePlaneShape2Node::m_image_bytes_per_channel; +MObject ImagePlaneShape2Node::m_image_size_bytes; +MObject ImagePlaneShape2Node::m_image_pixel_aspect; +MObject ImagePlaneShape2Node::m_camera_width_inch; +MObject ImagePlaneShape2Node::m_camera_height_inch; +MObject ImagePlaneShape2Node::m_lens_hash_current; +MObject ImagePlaneShape2Node::m_lens_hash_previous; +MObject ImagePlaneShape2Node::m_geometry_node; +MObject ImagePlaneShape2Node::m_camera_node; + +// Image Attributes +MObject ImagePlaneShape2Node::m_image_display_channel; +MObject ImagePlaneShape2Node::m_image_color_gain; +MObject ImagePlaneShape2Node::m_image_color_exposure; +MObject ImagePlaneShape2Node::m_image_color_gamma; +MObject ImagePlaneShape2Node::m_image_color_saturation; +MObject ImagePlaneShape2Node::m_image_color_soft_clip; +MObject ImagePlaneShape2Node::m_image_alpha_gain; +MObject ImagePlaneShape2Node::m_image_default_color; +MObject ImagePlaneShape2Node::m_image_ignore_alpha; +MObject ImagePlaneShape2Node::m_image_flip; +MObject ImagePlaneShape2Node::m_image_flop; +MObject ImagePlaneShape2Node::m_image_frame_number; +MObject ImagePlaneShape2Node::m_image_file_path; +MObject ImagePlaneShape2Node::m_image_input_color_space; +MObject ImagePlaneShape2Node::m_image_output_color_space; +MObject ImagePlaneShape2Node::m_shader_is_transparent; + +ImagePlaneShape2Node::ImagePlaneShape2Node() {} + +ImagePlaneShape2Node::~ImagePlaneShape2Node() {} + +MString ImagePlaneShape2Node::nodeName() { + return MString(MM_IMAGE_PLANE_SHAPE_2_TYPE_NAME); +} + +MStatus ImagePlaneShape2Node::compute(const MPlug & /*plug*/, + MDataBlock & /*dataBlock*/ +) { + return MS::kUnknownParameter; +} + +bool ImagePlaneShape2Node::isBounded() const { return true; } + +MBoundingBox ImagePlaneShape2Node::boundingBox() const { + MObject this_node = thisMObject(); + + MPlug current_plug(this_node, m_lens_hash_current); + int64_t current_hash = current_plug.asInt64(); + + MPlug previous_plug(this_node, m_lens_hash_previous); + int64_t previous_hash = previous_plug.asInt64(); + + // Limit the number of calls to + // 'setGeometryDrawDirty', because this causes the viewport update + // to run constantly, running up CPU for no reason. + if (current_hash != previous_hash) { + MHWRender::MRenderer::setGeometryDrawDirty(this_node); + previous_plug.setInt64(current_hash); + } + + MPoint corner1(-1.0, -1.0, -1.0); + MPoint corner2(1.0, 1.0, 1.0); + return MBoundingBox(corner1, corner2); +} + +bool ImagePlaneShape2Node::excludeAsLocator() const { + // Returning 'false' here means that when the user toggles + // locators on/off with the (per-viewport) "Show" menu, this shape + // node will not be affected. + return false; +} + +// Called before this node is evaluated by Evaluation Manager. +#if MAYA_API_VERSION >= 20190000 +MStatus ImagePlaneShape2Node::preEvaluation( + const MDGContext &context, const MEvaluationNode &evaluationNode) { + if (context.isNormal()) { + MHWRender::MRenderer::setGeometryDrawDirty(thisMObject()); + } + + return MStatus::kSuccess; +} +#endif + +#if MAYA_API_VERSION >= 20200000 +void ImagePlaneShape2Node::getCacheSetup( + const MEvaluationNode &evalNode, MNodeCacheDisablingInfo &disablingInfo, + MNodeCacheSetupInfo &cacheSetupInfo, + MObjectArray &monitoredAttributes) const { + MPxLocatorNode::getCacheSetup(evalNode, disablingInfo, cacheSetupInfo, + monitoredAttributes); + assert(!disablingInfo.getCacheDisabled()); + cacheSetupInfo.setPreference(MNodeCacheSetupInfo::kWantToCacheByDefault, + false); +} +#endif + +void *ImagePlaneShape2Node::creator() { return new ImagePlaneShape2Node(); } + +MStatus ImagePlaneShape2Node::initialize() { + MStatus status; + MFnNumericAttribute nAttr; + MFnTypedAttribute tAttr; + MFnEnumAttribute eAttr; + MFnMessageAttribute msgAttr; + + m_visible_to_camera_only = nAttr.create("visibleToCameraOnly", "viscamony", + MFnNumericData::kBoolean, 0); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(addAttribute(m_visible_to_camera_only)); + + m_draw_hud = nAttr.create("drawHud", "enbhud", MFnNumericData::kBoolean, 1); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(addAttribute(m_draw_hud)); + + m_draw_image_size = + nAttr.create("drawImageSize", "enbimgsz", MFnNumericData::kBoolean, 1); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(addAttribute(m_draw_image_size)); + + m_draw_camera_size = + nAttr.create("drawCameraSize", "enbcamsz", MFnNumericData::kBoolean, 1); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(addAttribute(m_draw_camera_size)); + + m_image_width = + nAttr.create("imageWidth", "imgwdth", MFnNumericData::kInt, 1920); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(nAttr.setMin(1)); + CHECK_MSTATUS(addAttribute(m_image_width)); + + m_image_height = + nAttr.create("imageHeight", "imghght", MFnNumericData::kInt, 1080); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(nAttr.setMin(1)); + CHECK_MSTATUS(addAttribute(m_image_height)); + + m_image_num_channels = + nAttr.create("imageNumChannels", "imgnchan", MFnNumericData::kInt, 4); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(nAttr.setMin(0)); + CHECK_MSTATUS(addAttribute(m_image_num_channels)); + + m_image_bytes_per_channel = nAttr.create( + "imageBytesPerChannel", "imgbtyprchan", MFnNumericData::kInt, 1); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(nAttr.setMin(0)); + CHECK_MSTATUS(addAttribute(m_image_bytes_per_channel)); + + // Create empty string data to be used as attribute default + // (string) value. + MFnStringData zero_string_data; + MObject zero_string_data_obj = zero_string_data.create("0"); + + m_image_size_bytes = tAttr.create("imageSizeBytes", "imgszbyt", + MFnData::kString, zero_string_data_obj); + CHECK_MSTATUS(tAttr.setStorable(true)); + CHECK_MSTATUS(tAttr.setUsedAsFilename(false)); + CHECK_MSTATUS(addAttribute(m_image_size_bytes)); + + m_image_pixel_aspect = nAttr.create("imagePixelAspect", "imgpxasp", + MFnNumericData::kDouble, 1.0); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(nAttr.setMin(0.1)); + CHECK_MSTATUS(nAttr.setMax(4.0)); + CHECK_MSTATUS(addAttribute(m_image_pixel_aspect)); + + m_camera_width_inch = nAttr.create("cameraWidthInch", "camwdthin", + MFnNumericData::kDouble, 1.0); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setConnectable(true)); + CHECK_MSTATUS(nAttr.setKeyable(false)); + CHECK_MSTATUS(nAttr.setMin(0)); + CHECK_MSTATUS(nAttr.setNiceNameOverride(MString("Camera Width (inches)"))); + CHECK_MSTATUS(addAttribute(m_camera_width_inch)); + + m_camera_height_inch = nAttr.create("cameraHeightInch", "camhghtin", + MFnNumericData::kDouble, 1.0); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setConnectable(true)); + CHECK_MSTATUS(nAttr.setKeyable(false)); + CHECK_MSTATUS(nAttr.setMin(0)); + CHECK_MSTATUS(nAttr.setNiceNameOverride(MString("Camera Height (inches)"))); + CHECK_MSTATUS(addAttribute(m_camera_height_inch)); + + m_lens_hash_current = + nAttr.create("lensHashCurrent", "lnshshcur", MFnNumericData::kInt64, 0); + CHECK_MSTATUS(nAttr.setStorable(false)); + CHECK_MSTATUS(nAttr.setConnectable(true)); + CHECK_MSTATUS(nAttr.setKeyable(false)); + CHECK_MSTATUS(nAttr.setHidden(true)); + CHECK_MSTATUS(addAttribute(m_lens_hash_current)); + + m_lens_hash_previous = nAttr.create("lensHashPrevious", "lnshshprv", + MFnNumericData::kInt64, 0); + CHECK_MSTATUS(nAttr.setStorable(false)); + CHECK_MSTATUS(nAttr.setConnectable(false)); + CHECK_MSTATUS(nAttr.setKeyable(false)); + CHECK_MSTATUS(nAttr.setHidden(true)); + CHECK_MSTATUS(addAttribute(m_lens_hash_previous)); + + m_geometry_node = msgAttr.create("geometryNode", "geond", &status); + CHECK_MSTATUS(status); + CHECK_MSTATUS(msgAttr.setStorable(true)); + CHECK_MSTATUS(msgAttr.setConnectable(true)); + CHECK_MSTATUS(msgAttr.setKeyable(false)); + CHECK_MSTATUS(addAttribute(m_geometry_node)); + + m_camera_node = msgAttr.create("cameraNode", "camnd", &status); + CHECK_MSTATUS(status); + CHECK_MSTATUS(msgAttr.setStorable(true)); + CHECK_MSTATUS(msgAttr.setConnectable(true)); + CHECK_MSTATUS(msgAttr.setKeyable(false)); + CHECK_MSTATUS(addAttribute(m_camera_node)); + + m_image_color_gain = nAttr.createColor("colorGain", "colgn"); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setReadable(true)); + CHECK_MSTATUS(nAttr.setWritable(true)); + CHECK_MSTATUS(nAttr.setDefault(1.0f, 1.0f, 1.0f)); + CHECK_MSTATUS(nAttr.setNiceNameOverride(MString("Gain"))); + CHECK_MSTATUS(addAttribute(m_image_color_gain)); + + const float exposure_soft_min = -9.0f; + const float exposure_soft_max = +9.0f; + const float exposure_default = 0.0f; + m_image_color_exposure = nAttr.create( + "colorExposure", "colexpsr", MFnNumericData::kFloat, exposure_default); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(nAttr.setSoftMin(exposure_soft_min)); + CHECK_MSTATUS(nAttr.setSoftMax(exposure_soft_max)); + CHECK_MSTATUS(nAttr.setNiceNameOverride(MString("Exposure"))); + CHECK_MSTATUS(addAttribute(m_image_color_exposure)); + + const float gamma_min = 0.0f; + const float gamma_soft_max = +2.0f; + const float gamma_default = 1.0f; + m_image_color_gamma = nAttr.create("colorGamma", "colgmma", + MFnNumericData::kFloat, gamma_default); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(nAttr.setMin(gamma_min)); + CHECK_MSTATUS(nAttr.setSoftMax(gamma_soft_max)); + CHECK_MSTATUS(nAttr.setNiceNameOverride(MString("Gamma"))); + CHECK_MSTATUS(addAttribute(m_image_color_gamma)); + + const float saturation_min = 0.0f; + const float saturation_soft_max = 2.0f; + const float saturation_default = 1.0f; + m_image_color_saturation = + nAttr.create("colorSaturation", "colstrtn", MFnNumericData::kFloat, + saturation_default); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(nAttr.setMin(saturation_min)); + CHECK_MSTATUS(nAttr.setSoftMax(saturation_soft_max)); + CHECK_MSTATUS(nAttr.setNiceNameOverride(MString("Saturation"))); + CHECK_MSTATUS(addAttribute(m_image_color_saturation)); + + const float soft_clip_min = 0.0f; + const float soft_clip_max = 1.0f; + const float soft_clip_default = 0.0f; + m_image_color_soft_clip = + nAttr.create("colorSoftClip", "colsftclp", MFnNumericData::kFloat, + soft_clip_default); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(nAttr.setMin(soft_clip_min)); + CHECK_MSTATUS(nAttr.setMax(soft_clip_max)); + CHECK_MSTATUS(nAttr.setNiceNameOverride(MString("SoftClip"))); + CHECK_MSTATUS(addAttribute(m_image_color_soft_clip)); + + const double alpha_min = 0.0; + const double alpha_max = 1.0; + const double alpha_default = 1.0; + m_image_alpha_gain = nAttr.create("alphaGain", "alpgn", + MFnNumericData::kDouble, alpha_default); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setConnectable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(nAttr.setMin(alpha_min)); + CHECK_MSTATUS(nAttr.setMax(alpha_max)); + CHECK_MSTATUS(addAttribute(m_image_alpha_gain)); + + // Which channel of the image should be displayed? + const short value_rgba = static_cast(ImageDisplayChannel::kRGBA); + const short value_rgb = static_cast(ImageDisplayChannel::kRGB); + const short value_red = static_cast(ImageDisplayChannel::kRed); + const short value_green = static_cast(ImageDisplayChannel::kGreen); + const short value_blue = static_cast(ImageDisplayChannel::kBlue); + const short value_alpha = static_cast(ImageDisplayChannel::kAlpha); + const short value_luminance = + static_cast(ImageDisplayChannel::kLuminance); + m_image_display_channel = + eAttr.create("displayChannel", "dspchan", value_rgba, &status); + CHECK_MSTATUS(status); + CHECK_MSTATUS(eAttr.addField("RGBA", value_rgba)); + CHECK_MSTATUS(eAttr.addField("RGB", value_rgb)); + CHECK_MSTATUS(eAttr.addField("Red", value_red)); + CHECK_MSTATUS(eAttr.addField("Green", value_green)); + CHECK_MSTATUS(eAttr.addField("Blue", value_blue)); + CHECK_MSTATUS(eAttr.addField("Alpha", value_alpha)); + CHECK_MSTATUS(eAttr.addField("Luminance", value_luminance)); + CHECK_MSTATUS(eAttr.setStorable(true)); + CHECK_MSTATUS(eAttr.setKeyable(true)); + CHECK_MSTATUS(addAttribute(m_image_display_channel)); + + m_image_ignore_alpha = nAttr.create("imageIgnoreAlpha", "imgignalp", + MFnNumericData::kBoolean, false); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setConnectable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(addAttribute(m_image_ignore_alpha)); + + m_image_flip = + nAttr.create("imageFlip", "imgflip", MFnNumericData::kBoolean, false); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setConnectable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(nAttr.setNiceNameOverride(MString("Image Flip (Vertical)"))); + CHECK_MSTATUS(addAttribute(m_image_flip)); + + m_image_flop = + nAttr.create("imageFlop", "imgflop", MFnNumericData::kBoolean, false); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setConnectable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS( + nAttr.setNiceNameOverride(MString("Image Flop (Horizontal)"))); + CHECK_MSTATUS(addAttribute(m_image_flop)); + + m_shader_is_transparent = nAttr.create("shaderIsTransparent", "shdistrnsp", + MFnNumericData::kBoolean, false); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setConnectable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS( + nAttr.setNiceNameOverride(MString("Shader Is Transparent (Debug)"))); + CHECK_MSTATUS(addAttribute(m_shader_is_transparent)); + + m_image_frame_number = + nAttr.create("imageFrameNumber", "imgfrmnmb", MFnNumericData::kInt, 1); + CHECK_MSTATUS(nAttr.setStorable(true)); + CHECK_MSTATUS(nAttr.setKeyable(true)); + CHECK_MSTATUS(addAttribute(m_image_frame_number)); + + // // Pixel Data Type + // m_cache_pixel_data_type = eAttr.create( + // "cachePixelDataType", "cchpxldtyp", + // kDataTypeUnknown); + // CHECK_MSTATUS(eAttr.addField("auto", kDataTypeUnknown)); + // CHECK_MSTATUS(eAttr.addField("uint8", kDataTypeUInt8)); + // CHECK_MSTATUS(eAttr.addField("uint16", kDataTypeUInt16)); + // CHECK_MSTATUS(eAttr.addField("half16", kDataTypeHalf16)); + // CHECK_MSTATUS(eAttr.addField("float32", kDataTypeFloat32)); + // CHECK_MSTATUS(eAttr.setStorable(true)); + + // Create empty string data to be used as attribute default + // (string) value. + MFnStringData empty_string_data; + MObject empty_string_data_obj = empty_string_data.create(""); + + m_image_file_path = tAttr.create("imageFilePath", "imgflpth", + MFnData::kString, empty_string_data_obj); + CHECK_MSTATUS(tAttr.setStorable(true)); + CHECK_MSTATUS(tAttr.setUsedAsFilename(true)); + CHECK_MSTATUS(addAttribute(m_image_file_path)); + + m_image_input_color_space = tAttr.create( + "inputColorSpace", "incolspc", MFnData::kString, empty_string_data_obj); + CHECK_MSTATUS(tAttr.setStorable(true)); + CHECK_MSTATUS(tAttr.setUsedAsFilename(false)); + CHECK_MSTATUS(addAttribute(m_image_input_color_space)); + + m_image_output_color_space = + tAttr.create("outputColorSpace", "outcolspc", MFnData::kString, + empty_string_data_obj); + CHECK_MSTATUS(tAttr.setStorable(true)); + CHECK_MSTATUS(tAttr.setUsedAsFilename(false)); + CHECK_MSTATUS(addAttribute(m_image_output_color_space)); + + return MS::kSuccess; +} + +} // namespace mmsolver diff --git a/src/mmSolver/shape/ImagePlaneShape2Node.h b/src/mmSolver/shape/ImagePlaneShape2Node.h new file mode 100644 index 000000000..0af176bd8 --- /dev/null +++ b/src/mmSolver/shape/ImagePlaneShape2Node.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2022, 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#ifndef MM_IMAGE_PLANE_SHAPE_2_NODE_H +#define MM_IMAGE_PLANE_SHAPE_2_NODE_H + +// Maya +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if MAYA_API_VERSION >= 20190000 +#include +#endif + +namespace mmsolver { + +class ImagePlaneShape2Node : public MPxLocatorNode { +public: + ImagePlaneShape2Node(); + + ~ImagePlaneShape2Node() override; + + MStatus compute(const MPlug &plug, MDataBlock &data) override; + + bool isBounded() const override; + + MBoundingBox boundingBox() const override; + + bool excludeAsLocator() const; + +#if MAYA_API_VERSION >= 20190000 + MStatus preEvaluation(const MDGContext &context, + const MEvaluationNode &evaluationNode) override; +#endif + +#if MAYA_API_VERSION >= 20200000 + void getCacheSetup(const MEvaluationNode &evalNode, + MNodeCacheDisablingInfo &disablingInfo, + MNodeCacheSetupInfo &cacheSetupInfo, + MObjectArray &monitoredAttributes) const override; +#endif + + static void *creator(); + + static MStatus initialize(); + + static MString nodeName(); + + // Node specific meta-data. + static MTypeId m_id; + static MString m_draw_db_classification; + static MString m_draw_registrant_id; + static MString m_selection_type_name; + static MString m_display_filter_draw_db_classification; + static MString m_display_filter_name; + static MString m_display_filter_label; + + // Attributes + static MObject m_visible_to_camera_only; + static MObject m_draw_hud; + static MObject m_draw_image_size; + static MObject m_draw_camera_size; + static MObject m_image_width; + static MObject m_image_height; + static MObject m_image_num_channels; + static MObject m_image_bytes_per_channel; + static MObject m_image_size_bytes; + static MObject m_image_pixel_aspect; + static MObject m_camera_width_inch; + static MObject m_camera_height_inch; + static MObject m_lens_hash_current; + static MObject m_lens_hash_previous; + static MObject m_geometry_node; + static MObject m_camera_node; + static MObject m_shader_is_transparent; + + // Image Attributes + static MObject m_image_display_channel; + static MObject m_image_color_gain; + static MObject m_image_color_exposure; + static MObject m_image_color_gamma; + static MObject m_image_color_saturation; + static MObject m_image_color_soft_clip; + static MObject m_image_alpha_gain; + static MObject m_image_default_color; + static MObject m_image_ignore_alpha; + static MObject m_image_flip; + static MObject m_image_flop; + static MObject m_image_file_path; + static MObject m_image_frame_number; + static MObject m_image_input_color_space; + static MObject m_image_output_color_space; +}; + +} // namespace mmsolver + +#endif // MM_IMAGE_PLANE_SHAPE_2_NODE_H diff --git a/src/mmSolver/shape/ImagePlaneShapeNode.cpp b/src/mmSolver/shape/ImagePlaneShapeNode.cpp index 5bed68699..6a439656d 100644 --- a/src/mmSolver/shape/ImagePlaneShapeNode.cpp +++ b/src/mmSolver/shape/ImagePlaneShapeNode.cpp @@ -21,6 +21,9 @@ #include "ImagePlaneShapeNode.h" +// STL +#include + // Maya #include #include @@ -42,8 +45,6 @@ #include #endif -#include - // MM Solver #include "mmSolver/mayahelper/maya_utils.h" #include "mmSolver/nodeTypeIds.h" @@ -57,6 +58,8 @@ MString ImagePlaneShapeNode::m_draw_registrant_id( MM_IMAGE_PLANE_SHAPE_DRAW_REGISTRANT_ID); MString ImagePlaneShapeNode::m_selection_type_name( MM_IMAGE_PLANE_SHAPE_SELECTION_TYPE_NAME); +MString ImagePlaneShapeNode::m_display_filter_draw_db_classification( + MM_IMAGE_PLANE_SHAPE_DISPLAY_FILTER_DRAW_DB_CLASSIFICATION); MString ImagePlaneShapeNode::m_display_filter_name( MM_IMAGE_PLANE_SHAPE_DISPLAY_FILTER_NAME); MString ImagePlaneShapeNode::m_display_filter_label( diff --git a/src/mmSolver/shape/ImagePlaneShapeNode.h b/src/mmSolver/shape/ImagePlaneShapeNode.h index ca2dfd6cf..c253ef472 100644 --- a/src/mmSolver/shape/ImagePlaneShapeNode.h +++ b/src/mmSolver/shape/ImagePlaneShapeNode.h @@ -22,6 +22,9 @@ #ifndef MM_IMAGE_PLANE_SHAPE_NODE_H #define MM_IMAGE_PLANE_SHAPE_NODE_H +// STL +#include + // Maya #include #include @@ -40,8 +43,6 @@ #include #endif -#include - namespace mmsolver { class ImagePlaneShapeNode : public MPxLocatorNode { @@ -81,6 +82,7 @@ class ImagePlaneShapeNode : public MPxLocatorNode { static MString m_draw_db_classification; static MString m_draw_registrant_id; static MString m_selection_type_name; + static MString m_display_filter_draw_db_classification; static MString m_display_filter_name; static MString m_display_filter_label; diff --git a/src/mmSolver/shape/ImagePlaneUtils.cpp b/src/mmSolver/shape/ImagePlaneUtils.cpp new file mode 100644 index 000000000..826cec596 --- /dev/null +++ b/src/mmSolver/shape/ImagePlaneUtils.cpp @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2022, 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#include "ImagePlaneUtils.h" + +// Get M_PI constant +#define _USE_MATH_DEFINES +#include + +// STL +#include +#include +#include + +// Maya +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Maya Viewport 2.0 +#include +#include +#include +#include +#include + +// MM Solver +#include +#include + +#include "mmSolver/mayahelper/maya_utils.h" +#include "mmSolver/render/shader/shader_utils.h" +#include "mmSolver/shape/constant_texture_data.h" +#include "mmSolver/utilities/number_utils.h" + +namespace mmsolver { + +bool getUpstreamNodeFromConnection(const MObject &this_node, + const MString &attr_name, + MPlugArray &out_connections) { + MStatus status; + MFnDependencyNode mfn_depend_node(this_node); + + bool wantNetworkedPlug = true; + MPlug plug = + mfn_depend_node.findPlug(attr_name, wantNetworkedPlug, &status); + if (status != MStatus::kSuccess) { + CHECK_MSTATUS(status); + return false; + } + if (plug.isNull()) { + MMSOLVER_MAYA_WRN("Could not get plug for \"" + << mfn_depend_node.name().asChar() << "." + << attr_name.asChar() << "\" node."); + return false; + } + + bool as_destination = true; + bool as_source = false; + // Ask for plugs connecting to this node's ".shaderNode" + // attribute. + plug.connectedTo(out_connections, as_destination, as_source, &status); + if (status != MStatus::kSuccess) { + CHECK_MSTATUS(status); + return false; + } + if (out_connections.length() == 0) { + MMSOLVER_MAYA_WRN("No connections to the \"" + << mfn_depend_node.name().asChar() << "." + << attr_name.asChar() << "\" attribute."); + return false; + } + return true; +} + +void calculate_node_image_size_string( + MDagPath &objPath, MObject &draw_image_size_attr, MObject &image_width_attr, + MObject &image_height_attr, MObject &image_pixel_aspect_attr, + const uint32_t int_precision, const uint32_t double_precision, + bool &out_draw_image_size, MString &out_image_size) { + MStatus status = MS::kSuccess; + + double width = 1.0; + double height = 1.0; + double pixel_aspect = 1.0; + + status = getNodeAttr(objPath, draw_image_size_attr, out_draw_image_size); + CHECK_MSTATUS(status); + + status = getNodeAttr(objPath, image_width_attr, width); + CHECK_MSTATUS(status); + + status = getNodeAttr(objPath, image_height_attr, height); + CHECK_MSTATUS(status); + + status = getNodeAttr(objPath, image_pixel_aspect_attr, pixel_aspect); + CHECK_MSTATUS(status); + + double aspect = (width * pixel_aspect) / height; + + MString width_string; + MString height_string; + MString pixel_aspect_string; + MString aspect_string; + + width_string.set(width, int_precision); + height_string.set(height, int_precision); + pixel_aspect_string.set(pixel_aspect, double_precision); + aspect_string.set(aspect, double_precision); + + out_image_size = MString("Image: ") + width_string + MString(" x ") + + height_string + MString(" | PAR ") + pixel_aspect_string + + MString(" | ") + aspect_string; + return; +} + +void calculate_node_camera_size_string(MDagPath &objPath, + MObject &draw_camera_size_attr, + MObject &camera_width_inch_attr, + MObject &camera_height_inch_attr, + const uint32_t double_precision, + bool &out_draw_camera_size, + MString &out_camera_size) { + MStatus status = MS::kSuccess; + + double width = 0.0; + double height = 0.0; + + status = getNodeAttr(objPath, draw_camera_size_attr, out_draw_camera_size); + CHECK_MSTATUS(status); + + status = getNodeAttr(objPath, camera_width_inch_attr, width); + CHECK_MSTATUS(status); + + status = getNodeAttr(objPath, camera_height_inch_attr, height); + CHECK_MSTATUS(status); + + double aspect = width / height; + + MString width_string; + MString height_string; + MString aspect_string; + + width_string.set(width * INCH_TO_MM, double_precision); + height_string.set(height * INCH_TO_MM, double_precision); + aspect_string.set(aspect, double_precision); + + out_camera_size = MString("Camera: ") + width_string + MString("mm x ") + + height_string + MString("mm | ") + aspect_string; +} + +void find_geometry_node_path(MObject &node, MString &attr_name, + MDagPath &out_geometry_node_path, + MFn::Type &out_geometry_node_type) { + const auto verbose = false; + + MPlugArray connections; + bool ok = getUpstreamNodeFromConnection(node, attr_name, connections); + if (!ok) { + return; + } + + for (uint32_t i = 0; i < connections.length(); ++i) { + MObject node = connections[i].node(); + + if (node.hasFn(MFn::kMesh)) { + MDagPath path; + MDagPath::getAPathTo(node, path); + out_geometry_node_path = path; + out_geometry_node_type = path.apiType(); + MMSOLVER_MAYA_VRB("Validated geometry node: " + << " path=" + << out_geometry_node_path.fullPathName().asChar() + << " type=" << node.apiTypeStr()); + break; + } else { + MMSOLVER_MAYA_WRN("Geometry node is not correct type:" + << " path=" + << out_geometry_node_path.fullPathName().asChar() + << " type=" << node.apiTypeStr()); + } + } +} + +void find_shader_node_path(MObject &node, MString &attr_name, + MObject &out_shader_node, + MFn::Type &out_shader_node_type) { + const auto verbose = false; + + MPlugArray connections; + bool ok = getUpstreamNodeFromConnection(node, attr_name, connections); + if (!ok) { + return; + } + + for (uint32_t i = 0; i < connections.length(); ++i) { + MObject node = connections[i].node(); + + MFnDependencyNode mfn_depend_node(node); + if (node.hasFn(MFn::kSurfaceShader) || node.hasFn(MFn::kHwShaderNode) || + node.hasFn(MFn::kPluginHardwareShader) || + node.hasFn(MFn::kPluginHwShaderNode)) { + out_shader_node = node; + out_shader_node_type = node.apiType(); + MMSOLVER_MAYA_VRB("Validated shader node: " + << " path=" << mfn_depend_node.name().asChar() + << " type=" << node.apiTypeStr()); + break; + } else { + MMSOLVER_MAYA_WRN("Shader node is not correct type:" + << " path=" << mfn_depend_node.name().asChar() + << " type=" << node.apiTypeStr()); + } + } +} + +void find_camera_node_path(MObject &node, MString &attr_name, + MDagPath &out_camera_node_path, + MFn::Type &out_camera_node_type) { + const auto verbose = false; + + MPlugArray connections; + bool ok = getUpstreamNodeFromConnection(node, attr_name, connections); + if (!ok) { + return; + } + + for (uint32_t i = 0; i < connections.length(); ++i) { + MObject node = connections[i].node(); + + if (node.hasFn(MFn::kCamera)) { + MDagPath path; + MDagPath::getAPathTo(node, path); + out_camera_node_path = path; + out_camera_node_type = path.apiType(); + MMSOLVER_MAYA_VRB("Validated camera node: " + << " path=" + << out_camera_node_path.fullPathName().asChar() + << " type=" << node.apiTypeStr()); + break; + } else { + MMSOLVER_MAYA_WRN("Camera node is not correct type:" + << " path=" + << out_camera_node_path.fullPathName().asChar() + << " type=" << node.apiTypeStr()); + } + } +} + +} // namespace mmsolver diff --git a/src/mmSolver/shape/ImagePlaneUtils.h b/src/mmSolver/shape/ImagePlaneUtils.h new file mode 100644 index 000000000..79c257fc8 --- /dev/null +++ b/src/mmSolver/shape/ImagePlaneUtils.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2022, 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#ifndef MM_IMAGE_PLANE_UTILS_H +#define MM_IMAGE_PLANE_UTILS_H + +// Maya +#include +#include +#include +#include +#include +#include +#include + +// Maya Viewport 2.0 +#include +#include +#include +#include +#include + +// MM Solver +#include + +#include "ImagePlaneShapeNode.h" +#include "mmSolver/utilities/debug_utils.h" + +namespace mmsolver { + +enum class ImageDisplayChannel { + kRGBA = 0, + kRGB, + kRed, + kGreen, + kBlue, + kAlpha, + kLuminance +}; + +bool getUpstreamNodeFromConnection(const MObject &this_node, + const MString &attr_name, + MPlugArray &out_connections); + +void calculate_node_image_size_string( + MDagPath &objPath, MObject &draw_image_size_attr, MObject &image_width_attr, + MObject &image_height_attr, MObject &image_pixel_aspect_attr, + const uint32_t int_precision, const uint32_t double_precision, + bool &out_draw_image_size, MString &out_image_size); + +void calculate_node_camera_size_string(MDagPath &objPath, + MObject &draw_camera_size_attr, + MObject &camera_width_inch_attr, + MObject &camera_height_inch_attr, + const uint32_t double_precision, + bool &out_draw_camera_size, + MString &out_camera_size); + +void find_geometry_node_path(MObject &node, MString &attr_name, + MDagPath &out_geometry_node_path, + MFn::Type &out_geometry_node_type); + +void find_shader_node_path(MObject &node, MString &attr_name, + MObject &out_shader_node, + MFn::Type &out_shader_node_type); + +void find_camera_node_path(MObject &node, MString &attr_name, + MDagPath &out_camera_node_path, + MFn::Type &out_camera_node_type); + +} // namespace mmsolver + +#endif // MM_IMAGE_PLANE_UTILS_H diff --git a/src/mmSolver/shape/LineDrawOverride.h b/src/mmSolver/shape/LineDrawOverride.h index 6486f1589..11c9fb719 100644 --- a/src/mmSolver/shape/LineDrawOverride.h +++ b/src/mmSolver/shape/LineDrawOverride.h @@ -49,7 +49,13 @@ namespace mmsolver { class LineDrawData : public MUserData { public: LineDrawData() +#if MAYA_API_VERSION >= 20220000 + : MUserData() +#else + // MUserData(bool) constructor is deprecated in Maya 2022+ + // because 'deleteAfterUse' is no longer needed. : MUserData(/*deleteAfterUse=*/true) // let Maya clean up +#endif , m_depth_priority(0) , m_inner_line_width(1.0) , m_middle_line_width(1.0) @@ -58,7 +64,8 @@ class LineDrawData : public MUserData { , m_point_list() , m_active(false) , m_draw_name(false) - , m_draw_middle(false) {} + , m_draw_middle(false) { + } ~LineDrawData() override {} diff --git a/src/mmSolver/shape/LineShapeNode.cpp b/src/mmSolver/shape/LineShapeNode.cpp index 89437b9f1..2b8d490f6 100644 --- a/src/mmSolver/shape/LineShapeNode.cpp +++ b/src/mmSolver/shape/LineShapeNode.cpp @@ -34,7 +34,6 @@ #include #include #include -#include #include #include #include @@ -270,7 +269,6 @@ MStatus LineShapeNode::initialize() { MStatus status; MFnUnitAttribute uAttr; MFnNumericAttribute nAttr; - MFnEnumAttribute eAttr; MFnMessageAttribute msgAttr; MFnCompoundAttribute compoundAttr; MFnMatrixAttribute matrixAttr; diff --git a/src/mmSolver/shape/MarkerDrawOverride.h b/src/mmSolver/shape/MarkerDrawOverride.h index 8d2c29444..fc0085b8c 100644 --- a/src/mmSolver/shape/MarkerDrawOverride.h +++ b/src/mmSolver/shape/MarkerDrawOverride.h @@ -46,7 +46,13 @@ namespace mmsolver { class MarkerDrawData : public MUserData { public: MarkerDrawData() +#if MAYA_API_VERSION >= 20220000 + : MUserData() +#else + // MUserData(bool) constructor is deprecated in Maya 2022+ + // because 'deleteAfterUse' is no longer needed. : MUserData(/*deleteAfterUse=*/true) // let Maya clean up +#endif , m_depth_priority(0) , m_line_width(1.0) , m_point_size(1.0) @@ -54,7 +60,8 @@ class MarkerDrawData : public MUserData { , m_locked(false) , m_active(false) , m_draw_name(false) - , m_visible(true) {} + , m_visible(true) { + } ~MarkerDrawData() override {} diff --git a/src/mmSolver/shape/MarkerShapeNode.cpp b/src/mmSolver/shape/MarkerShapeNode.cpp index 725bb8a00..0c7cf333a 100644 --- a/src/mmSolver/shape/MarkerShapeNode.cpp +++ b/src/mmSolver/shape/MarkerShapeNode.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -147,7 +146,6 @@ MStatus MarkerShapeNode::initialize() { MStatus status; MFnUnitAttribute uAttr; MFnNumericAttribute nAttr; - MFnEnumAttribute eAttr; // Color m_color = nAttr.createColor("color", "clr"); diff --git a/src/mmSolver/shape/SkyDomeDrawOverride.h b/src/mmSolver/shape/SkyDomeDrawOverride.h index a829a0f07..9ebfc932e 100644 --- a/src/mmSolver/shape/SkyDomeDrawOverride.h +++ b/src/mmSolver/shape/SkyDomeDrawOverride.h @@ -46,7 +46,13 @@ namespace mmsolver { class SkyDomeDrawData : public MUserData { public: SkyDomeDrawData() +#if MAYA_API_VERSION >= 20220000 + : MUserData() +#else + // MUserData(bool) constructor is deprecated in Maya 2022+ + // because 'deleteAfterUse' is no longer needed. : MUserData(/*deleteAfterUse=*/true) // let Maya clean up +#endif , m_draw_mode(static_cast(DrawMode::kDrawOnTop)) , m_transform_mode(static_cast(TransformMode::kNoOffset)) , m_line_width(1.0) @@ -77,7 +83,8 @@ class SkyDomeDrawData : public MUserData { , m_grid_lat_line_width(1.0f) , m_grid_long_line_width(1.0f) , m_grid_lat_divisions(6) - , m_grid_long_divisions(6) {} + , m_grid_long_divisions(6) { + } ~SkyDomeDrawData() override {} diff --git a/src/mmSolver/shape/constant_texture_data.h b/src/mmSolver/shape/constant_texture_data.h new file mode 100644 index 000000000..e9c982f55 --- /dev/null +++ b/src/mmSolver/shape/constant_texture_data.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2020, 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + * Unchanging texture data to be used as hard-coded images that are + * embedded in mmSolver. + */ + +#ifndef MM_SOLVER_CONSTANT_TEXTURE_DATA_H +#define MM_SOLVER_CONSTANT_TEXTURE_DATA_H + +namespace mmsolver { + +/* + * Color Bars Texture, for debug. + * + * https://en.wikipedia.org/wiki/SMPTE_color_bars + * + * ------------------------------ + * R - G - B - COLOR NAME + * ------------------------------ + * 235 - 235 - 235 - 100% White + * 180 - 180 - 180 - 75% White + * 235 - 235 - 16 - Yellow + * 16 - 235 - 235 - Cyan + * 16 - 235 - 16 - Green + * 235 - 16 - 235 - Magenta + * 235 - 16 - 16 - Red + * 16 - 16 - 235 - Blue + * 16 - 16 - 16 - Black + * ------------------------------ + * + * The texture block (below) starts at the lower-left (zeroth index) + * and continues the upper-right (last index). + * + * Note: To make things even (only 8 entries), we skip the "75% white" + * value. + */ +static const float COLOR_BARS_F32_4X4[] = { + // Row 0 + // + // 235, 16, 235 - Magenta + 0.9215f, 0.0627f, 0.9215f, 1.0f, + + // 235, 16, 16 - Red + 0.9215f, 0.0627f, 0.0627f, 1.0f, + + // 16, 16, 235 - Blue + 0.0627f, 0.0627f, 0.9215f, 1.0f, + + // 16, 16, 16 - Black + 0.0627f, 0.0627f, 0.0627f, 1.0f, + + // Row 1 + // + // 235, 16, 235 - Magenta + 0.9215f, 0.0627f, 0.9215f, 0.8f, + + // 235, 16, 16 - Red + 0.9215f, 0.0627f, 0.0627f, 0.6f, + + // 16, 16, 235 - Blue + 0.0627f, 0.0627f, 0.9215f, 0.4f, + + // 16, 16, 16 - Black + 0.0627f, 0.0627f, 0.0627f, 0.2f, + + // Row 2 + // + // 235, 235, 235 - 100% White + 0.9215f, 0.9215f, 0.9215f, 1.0f, + + // 235, 235, 16 - Yellow + 0.9215f, 0.9215f, 0.0627f, 1.0f, + + // 16, 235, 235 - Cyan + 0.0627f, 0.9215f, 0.9215f, 1.0f, + + // 16, 235, 16 - Green + 0.0627f, 0.9215f, 0.0627f, 1.0f, + + // Row 3 + // + // 235, 235, 235 - 100% White + 0.9215f, 0.9215f, 0.9215f, 0.2f, + + // 235, 235, 16 - Yellow + 0.9215f, 0.9215f, 0.0627f, 0.4f, + + // 16, 235, 235 - Cyan + 0.0627f, 0.9215f, 0.9215f, 0.6f, + + // 16, 235, 16 - Green + 0.0627f, 0.9215f, 0.0627f, 0.8f}; +static const uint32_t COLOR_BARS_F32_4X4_PIXEL_WIDTH = 4; +static const uint32_t COLOR_BARS_F32_4X4_PIXEL_HEIGHT = 4; +static const uint32_t COLOR_BARS_F32_4X4_PIXEL_COUNT = + COLOR_BARS_F32_4X4_PIXEL_WIDTH * COLOR_BARS_F32_4X4_PIXEL_HEIGHT; +static const uint32_t COLOR_BARS_F32_4X4_CHANNEL_COUNT = 4; +// float data type has 4 bytes. +static const uint32_t COLOR_BARS_F32_4X4_CHANNEL_BYTE_COUNT = 4; +static const uint32_t COLOR_BARS_F32_4X4_PIXEL_BYTE_COUNT = + COLOR_BARS_F32_4X4_CHANNEL_COUNT * COLOR_BARS_F32_4X4_CHANNEL_BYTE_COUNT; + +} // namespace mmsolver + +#endif // MM_SOLVER_CONSTANT_TEXTURE_DATA_H diff --git a/src/mmSolver/utilities/hash_utils.h b/src/mmSolver/utilities/hash_utils.h new file mode 100644 index 000000000..cc0b17ee8 --- /dev/null +++ b/src/mmSolver/utilities/hash_utils.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + * Implements simple hashing on generic data. + * + * See this page for an interesting article on hash collisions: + * http://softwareengineering.stackexchange.com/questions/49550/which-hashing-algorithm-is-best-for-uniqueness-and-speed + * + */ + +#ifndef HASH_UTILS_H +#define HASH_UTILS_H + +// STL +#include // size_t +#include // string +#include // vector + +// Platform-specific functions and macros +#if defined(_MSC_VER) +// Microsoft Visual Studio +#define BIG_CONSTANT(x) (x) +#else +// Other compilers +#define BIG_CONSTANT(x) (x##LLU) +#endif + +#if defined(_MSC_VER) && (_MSC_VER < 1600) +// Microsoft Visual Studio +typedef unsigned char uint8_t; +typedef unsigned int uint32_t; +typedef unsigned __int64 uint64_t; +#else +// Other compilers +#include +#endif + +// typedef unsigned long long int CacheKey; + +namespace mmsolver { +namespace hash { + +// MurmurHash2 hash, for 64-bit versions +// +// From: +// https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L96 +// +// MurmurHash2, 64-bit versions, by Austin Appleby +// +// MurmurHash2 was written by Austin Appleby, and is placed in the +// public domain. The author hereby disclaims copyright to the +// MurmurHash2 source code. +// +// The same caveats as 32-bit MurmurHash2 apply here - beware of alignment +// and endian-ness issues if used across multiple platforms. +// +// 64-bit hash for 64-bit platforms +inline uint64_t MurmurHash64A(const void *key, int len, uint64_t seed) { + const uint64_t m = BIG_CONSTANT(0xc6a4a7935bd1e995); + const int r = 47; + + uint64_t h = seed ^ (len * m); + + const uint64_t *data = (const uint64_t *)key; + const uint64_t *end = data + (len / 8); + + while (data != end) { + uint64_t k = *data++; + + k *= m; + k ^= k >> r; + k *= m; + + h ^= k; + h *= m; + } + + const unsigned char *data2 = (const unsigned char *)data; + + switch (len & 7) { + case 7: + h ^= uint64_t(data2[6]) << 48; + case 6: + h ^= uint64_t(data2[5]) << 40; + case 5: + h ^= uint64_t(data2[4]) << 32; + case 4: + h ^= uint64_t(data2[3]) << 24; + case 3: + h ^= uint64_t(data2[2]) << 16; + case 2: + h ^= uint64_t(data2[1]) << 8; + case 1: + h ^= uint64_t(data2[0]); + h *= m; + default: + break; + }; + + h ^= h >> r; + h *= m; + h ^= h >> r; + + return h; +} + +template +inline R hashMurmur2(const K *key, int len) { + const void *p = reinterpret_cast(key); + R h = (R)MurmurHash64A(p, len, 0); + return h; +} + +// Hashing functions, added to the class for convenience. +inline uint64_t make_hash(const std::string &value) { + return hash::hashMurmur2(value.c_str(), value.size()); +} + +// Merges multiple hashes together into a single hash. +template +inline R mergeHashes(const std::vector &hashes, R seed = 0) { + typename std::vector::const_iterator it; + R result = seed; + for (it = hashes.begin(); it != hashes.end(); ++it) { + result = 16777619 * result ^ *it; + } + return result; +} + +// Combines a hash with another hash. +// +// Similar to 'boost::combine_hash'. +template +inline void combineHash(K &seed, K key) { + seed = 16777619 * seed ^ key; +} + +} // namespace hash +} // namespace mmsolver + +#endif // HASH_UTILS_H diff --git a/src/mmSolver/utilities/memory_gpu_utils.cpp b/src/mmSolver/utilities/memory_gpu_utils.cpp new file mode 100644 index 000000000..9b4ca7632 --- /dev/null +++ b/src/mmSolver/utilities/memory_gpu_utils.cpp @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#include "memory_gpu_utils.h" + +// STL +#include + +// Maya +#include + +// Maya Viewport 1.0 (Legacy) +#include +#include +#include + +// Maya Viewport 2.0 +#include + +// MM Solver +#include "debug_utils.h" + +namespace mmmemorygpu { + +MStatus memory_total_size_in_bytes(size_t &out_size_in_bytes) { + out_size_in_bytes = 0; + MHWRender::MRenderer *renderer = MHWRender::MRenderer::theRenderer(); + if (!renderer) { + MMSOLVER_MAYA_WRN( + "mmmemorygpu::memory_total_size_in_bytes: " + "Failed to get Maya MRenderer!"); + return MStatus::kFailure; + } + out_size_in_bytes = static_cast(renderer->GPUtotalMemorySize()); + return MStatus::kSuccess; +} + +MStatus gpu_memory_usage(size_t &total_memory, size_t &free_memory, + size_t &used_memory) { + const bool verbose = false; + MStatus status = MStatus::kSuccess; + + total_memory = 0; + free_memory = 0; + used_memory = 0; + + MGLFunctionTable *gGLFT = nullptr; + const MHardwareRenderer *hardware_renderer_ptr = + MHardwareRenderer::theRenderer(); + if (hardware_renderer_ptr) { + gGLFT = hardware_renderer_ptr->glFunctionTable(); + MMSOLVER_MAYA_VRB( + "mmmemorygpu::gpu_memory_usage: " + "gGLFT=" + << gGLFT); + } + + if (!gGLFT) { + MMSOLVER_MAYA_ERR( + "mmmemorygpu::gpu_memory_usage: " + "Could not get OpenGL Function Table!"); + return MStatus::kFailure; + } + + const bool has_extension_nvidia = + gGLFT->extensionExists(MGLExtension::kMGLext_NVX_gpu_memory_info); + const bool has_extension_ati = + gGLFT->extensionExists(MGLExtension::kMGLext_ATI_meminfo); + MMSOLVER_MAYA_VRB( + "mmmemorygpu::gpu_memory_usage: " + "has_extension_nvidia=" + << has_extension_nvidia); + MMSOLVER_MAYA_VRB( + "mmmemorygpu::gpu_memory_usage: " + "has_extension_ati=" + << has_extension_ati); + + const size_t kilobytes_to_bytes = 1024; + if (has_extension_nvidia) { + // https://registry.khronos.org/OpenGL/extensions/NVX/NVX_gpu_memory_info.txt + MGLint nvidia_total_memory_as_kilobytes = 0; + MGLint nvidia_free_memory_as_kilobytes = 0; + gGLFT->glGetIntegerv(MGL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, + &nvidia_total_memory_as_kilobytes); + gGLFT->glGetIntegerv(MGL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, + &nvidia_free_memory_as_kilobytes); + total_memory = static_cast(nvidia_total_memory_as_kilobytes) * + kilobytes_to_bytes; + free_memory = static_cast(nvidia_free_memory_as_kilobytes) * + kilobytes_to_bytes; + used_memory = total_memory - free_memory; + } else if (has_extension_ati) { + // https://registry.khronos.org/OpenGL/extensions/ATI/ATI_meminfo.txt + MGLint ati_vbo_free_memory[4]; + gGLFT->glGetIntegerv(MGL_VBO_FREE_MEMORY_ATI, ati_vbo_free_memory); + + MGLint ati_free_memory_as_kilobytes = ati_vbo_free_memory[0]; + MGLint ati_free_memory_largest_block_as_kilobytes = + ati_vbo_free_memory[1]; + + // Auxiliary memory is memory that an implementation may use + // as a backup to its primary pool for a certain type of + // allocation. + MGLint ati_free_auxiliary_memory_as_kilobytes = ati_vbo_free_memory[2]; + MGLint ati_free_auxiliary_memory_largest_block_as_kilobytes = + ati_vbo_free_memory[3]; + + // ATL cards (using this extension or the extensions supported + // by Maya) do not allow getting the total amount of memory on + // the device, so we must call our function. + size_t ati_total_memory_as_bytes = 0; + status = memory_total_size_in_bytes(ati_total_memory_as_bytes); + CHECK_MSTATUS_AND_RETURN_IT(status); + + total_memory = ati_total_memory_as_bytes; + free_memory = static_cast(ati_free_memory_as_kilobytes) * + kilobytes_to_bytes; + used_memory = total_memory - free_memory; + } else { + MMSOLVER_MAYA_ERR( + "mmmemorygpu::gpu_memory_usage: " + "Neither GL_NVX_gpu_memory_info nor GL_ATI_meminfo " + "extensions are supported on this system."); + status = MStatus::kFailure; + } + + MMSOLVER_MAYA_VRB( + "mmmemorygpu::gpu_memory_usage: " + "total_memory=" + << total_memory); + MMSOLVER_MAYA_VRB( + "mmmemorygpu::gpu_memory_usage: " + "free_memory=" + << free_memory); + MMSOLVER_MAYA_VRB( + "mmmemorygpu::gpu_memory_usage: " + "used_memory=" + << used_memory); + + return status; +} + +MStatus memory_used_size_in_bytes(size_t &out_size_in_bytes) { + out_size_in_bytes = 0; + + size_t total_memory = 0; + size_t free_memory = 0; + size_t used_memory = 0; + + MStatus status = gpu_memory_usage(total_memory, free_memory, used_memory); + CHECK_MSTATUS_AND_RETURN_IT(status); + + out_size_in_bytes = used_memory; + + return MStatus::kSuccess; +} + +MStatus memory_free_size_in_bytes(size_t &out_size_in_bytes) { + out_size_in_bytes = 0; + + size_t total_memory = 0; + size_t free_memory = 0; + size_t used_memory = 0; + + MStatus status = gpu_memory_usage(total_memory, free_memory, used_memory); + CHECK_MSTATUS_AND_RETURN_IT(status); + + out_size_in_bytes = free_memory; + + return MStatus::kSuccess; +} + +// Gets the GPU memory used by the current Maya process. +// +// NOTE: This is not all the memory used by the GPU, only by the +// current Maya instance. +MStatus current_maya_process_memory_used_size_in_bytes( + size_t &out_size_in_bytes) { + out_size_in_bytes = 0; + MHWRender::MRenderer *renderer = MHWRender::MRenderer::theRenderer(); + if (!renderer) { + MMSOLVER_MAYA_WRN( + "mmmemorygpu::current_maya_process_memory_used_size_in_bytes: " + "Failed to get Maya MRenderer!"); + return MStatus::kFailure; + } + out_size_in_bytes = static_cast(renderer->GPUUsedMemorySize( + MHWRender::MRenderer::MGPUMemType::kMemAll)); + return MStatus::kSuccess; +} + +// These methods can be used to inform Maya's internal system of any +// GPU memory that we allocate/de-allocate. +MStatus register_allocated_memory_size_in_bytes(const size_t size_in_bytes) { + MHWRender::MRenderer *renderer = MHWRender::MRenderer::theRenderer(); + if (!renderer) { + MMSOLVER_MAYA_WRN( + "mmmemorygpu::register_allocated_memory_size_in_bytes: " + "Failed to get Maya MRenderer!"); + return MStatus::kFailure; + } + MInt64 *evictedGPUMemSize = nullptr; + return renderer->holdGPUMemory(size_in_bytes, evictedGPUMemSize); +} + +MStatus register_deallocated_memory_size_in_bytes(const size_t size_in_bytes) { + MHWRender::MRenderer *renderer = MHWRender::MRenderer::theRenderer(); + if (!renderer) { + MMSOLVER_MAYA_WRN( + "mmmemorygpu::register_deallocated_memory_size_in_bytes: " + "Failed to get Maya MRenderer!"); + return MStatus::kFailure; + } + return renderer->releaseGPUMemory(size_in_bytes); +} + +} // namespace mmmemorygpu diff --git a/src/mmSolver/utilities/memory_gpu_utils.h b/src/mmSolver/utilities/memory_gpu_utils.h new file mode 100644 index 000000000..549aa510e --- /dev/null +++ b/src/mmSolver/utilities/memory_gpu_utils.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + * Get details about the GPU memory. + */ + +#ifndef MEMORY_GPU_UTILS_H +#define MEMORY_GPU_UTILS_H + +// Maya +#include + +namespace mmmemorygpu { + +MStatus memory_total_size_in_bytes(size_t &out_size_in_bytes); +MStatus memory_used_size_in_bytes(size_t &out_size_in_bytes); +MStatus memory_free_size_in_bytes(size_t &out_size_in_bytes); + +MStatus current_maya_process_memory_used_size_in_bytes( + size_t &out_size_in_bytes); + +MStatus register_allocated_memory_size_in_bytes(const size_t size_in_bytes); +MStatus register_deallocated_memory_size_in_bytes(const size_t size_in_bytes); + +} // namespace mmmemorygpu + +#endif // MEMORY_GPU_UTILS_H diff --git a/src/mmSolver/utilities/memory_system_utils.cpp b/src/mmSolver/utilities/memory_system_utils.cpp new file mode 100644 index 000000000..7d9a10753 --- /dev/null +++ b/src/mmSolver/utilities/memory_system_utils.cpp @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#include "memory_system_utils.h" + +// STL +#include // ifstream +#include // ios_base +#include // cout, cerr, endl +#include // stringstream + +// OS-specific headers. +#ifdef _WIN32 // Windows MSVC +#include +// windows.h must be defined first. +#include +#else // Linux and MacOS +#include // stat (check a file exists) +#include // sysinfo +#include // uint32_t, uint64_t, etc +#include // sysconf +#endif + +namespace mmmemorysystem { + +// Get the current process's memory usage. +// +// Takes two size_t by reference, attempts to read the +// system-dependent data for a process' virtual memoryUsed size and +// resident set size, and return the results in bytes. +// +// On failure, returns 0, 0 +// +// http://stackoverflow.com/questions/669438/how-to-get-memory-usage-at-run-time-in-c +void process_memory_usage(size_t &peak_resident_set_size, + size_t ¤t_resident_set_size) { + peak_resident_set_size = 0; + current_resident_set_size = 0; + +#ifdef _WIN32 // Windows MSVC + PROCESS_MEMORY_COUNTERS info; + GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)); + + // TODO: We could get the "PrivateUsage" on Windows as this is + // what is displayed in the Windows Task Manager? + // https://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process/ + current_resident_set_size = static_cast(info.WorkingSetSize); + peak_resident_set_size = static_cast(info.PeakWorkingSetSize); +#else // Linux and MacOS + // 'file' stat seems to give the most reliable results + std::ifstream stat_stream("/proc/self/stat", std::ios_base::in); + + // dummy vars for leading entries in stat that we don't care about + std::string pid, comm, state, ppid, pgrp, session, tty_nr; + std::string tpgid, flags, minflt, cminflt, majflt, cmajflt; + std::string utime, stime, cutime, cstime, priority, nice; + std::string O, itrealvalue, starttime; + + // the two fields we want + size_t vsize; + size_t rss; + + stat_stream >> pid >> comm >> state >> ppid >> pgrp >> session >> tty_nr >> + tpgid >> flags >> minflt >> cminflt >> majflt >> cmajflt >> utime >> + stime >> cutime >> cstime >> priority >> nice >> O >> itrealvalue >> + starttime >> vsize >> rss; // don't care about the rest + stat_stream.close(); + + // In case of x86-64, page size is configured to use 2MB pages. + const size_t page_size = static_cast(sysconf(_SC_PAGE_SIZE)); + peak_resident_set_size = vsize; + current_resident_set_size = rss * page_size; +#endif + return; +} + +// Get the all the physical RAM available to the OS, in bytes. +// +// How to get the amount of (CPU) system memory? +// https://stackoverflow.com/questions/2513505/how-to-get-available-memory-c-g +// https://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process/ +size_t system_physical_memory_total() { +#ifdef _WIN32 // Windows MSVC + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + GlobalMemoryStatusEx(&status); + + size_t total_physical_memory_bytes = status.ullTotalPhys; + return total_physical_memory_bytes; +#else // Linux and MacOS + // https://www.man7.org/linux/man-pages/man2/sysinfo.2.html + struct sysinfo memInfo; + sysinfo(&memInfo); + + size_t total_physical_memory_bytes = memInfo.totalram; + // Multiply to avoid int overflow on right hand side. + total_physical_memory_bytes *= memInfo.mem_unit; + + return total_physical_memory_bytes; +#endif +} + +// Returns memory bytes used. +// +// https://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process/ +size_t system_physical_memory_used() { +#ifdef _WIN32 // Windows MSVC + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + GlobalMemoryStatusEx(&status); + + size_t physical_memory_used_bytes = + status.ullTotalPhys - status.ullAvailPhys; + return physical_memory_used_bytes; +#else // Linux and MacOS + // https://www.man7.org/linux/man-pages/man2/sysinfo.2.html + struct sysinfo memInfo; + sysinfo(&memInfo); + + size_t physical_memory_used_bytes = memInfo.totalram - memInfo.freeram; + // Multiply to avoid int overflow on right hand side. + physical_memory_used_bytes *= memInfo.mem_unit; + + return physical_memory_used_bytes; +#endif +} + +// Returns memory bytes free. +// +// https://stackoverflow.com/questions/63166/how-to-determine-cpu-and-memory-consumption-from-inside-a-process/ +size_t system_physical_memory_free() { +#ifdef _WIN32 // Windows MSVC + MEMORYSTATUSEX status; + status.dwLength = sizeof(status); + GlobalMemoryStatusEx(&status); + + size_t physical_memory_free_bytes = status.ullAvailPhys; + return physical_memory_free_bytes; +#else // Linux and MacOS + // https://www.man7.org/linux/man-pages/man2/sysinfo.2.html + struct sysinfo memInfo; + sysinfo(&memInfo); + + size_t physical_memory_free_bytes = memInfo.freeram; + // Multiply to avoid int overflow on right hand side. + physical_memory_free_bytes *= memInfo.mem_unit; + + return physical_memory_free_bytes; +#endif +} + +} // namespace mmmemorysystem diff --git a/src/mmSolver/utilities/memory_system_utils.h b/src/mmSolver/utilities/memory_system_utils.h new file mode 100644 index 000000000..294081f79 --- /dev/null +++ b/src/mmSolver/utilities/memory_system_utils.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + * Get details about the OS system memory. + */ + +#ifndef MEMORY_SYSTEM_UTILS_H +#define MEMORY_SYSTEM_UTILS_H + +// STL +#include + +namespace mmmemorysystem { +void process_memory_usage(size_t &peak_resident_set_size, + size_t ¤t_resident_set_size); +size_t system_physical_memory_total(); +size_t system_physical_memory_free(); +size_t system_physical_memory_used(); + +} // namespace mmmemorysystem + +#endif // MEMORY_SYSTEM_UTILS_H diff --git a/src/mmSolver/utilities/memory_utils.cpp b/src/mmSolver/utilities/memory_utils.cpp new file mode 100644 index 000000000..8e6476b12 --- /dev/null +++ b/src/mmSolver/utilities/memory_utils.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#include "memory_utils.h" + +// MM Solver +#include "debug_utils.h" +#include "number_utils.h" + +namespace mmmemory { + +double bytes_as_double(const size_t size_as_bytes, + const mmmemory::MemoryUnit memory_unit) { + // NOTE: This essentially downcasts to a a 52-bit integer here, + // so in theory we could loose precision. + double result = static_cast(size_as_bytes); + if (memory_unit == mmmemory::MemoryUnit::kBytes) { + MMSOLVER_MAYA_WRN( + "mmmemory::bytes_as_double: " + "Casting bytes to a 'double' value may not maintain precision."); + } else if (memory_unit == mmmemory::MemoryUnit::kKiloBytes) { + result /= BYTES_TO_KILOBYTES; + } else if (memory_unit == mmmemory::MemoryUnit::kMegaBytes) { + result /= BYTES_TO_MEGABYTES; + } else if (memory_unit == mmmemory::MemoryUnit::kGigaBytes) { + result /= BYTES_TO_GIGABYTES; + } else { + MMSOLVER_MAYA_ERR( + "mmmemory::bytes_as_double: " + "MemoryUnit given is not supported! value=" + << static_cast(memory_unit)); + } + return result; +} + +} // namespace mmmemory diff --git a/src/mmSolver/utilities/memory_utils.h b/src/mmSolver/utilities/memory_utils.h new file mode 100644 index 000000000..313c09ef4 --- /dev/null +++ b/src/mmSolver/utilities/memory_utils.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + * Memory-related utilities. + */ + +#ifndef MEMORY_UTILS_H +#define MEMORY_UTILS_H + +// STL +#include +#include + +namespace mmmemory { + +enum class MemoryUnit : uint8_t { + kBytes = 0, + kKiloBytes, + kMegaBytes, + kGigaBytes, +}; + +double bytes_as_double(const size_t size_as_bytes, + const mmmemory::MemoryUnit memory_unit); + +} // namespace mmmemory + +#endif // MEMORY_UTILS_H diff --git a/src/mmSolver/utilities/path_utils.cpp b/src/mmSolver/utilities/path_utils.cpp new file mode 100644 index 000000000..766e303e5 --- /dev/null +++ b/src/mmSolver/utilities/path_utils.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + */ + +#include "path_utils.h" + +// Maya +#include +#include +#include + +// MM Solver +#include "debug_utils.h" + +namespace mmpath { + +// This changes the given 'file_path' directly to a resolve file path, +// or returns a non-MStatus::kSuccess status. +MStatus resolve_input_file_path(MString &file_path) { + MStatus status = MStatus::kSuccess; + const bool verbose = false; + + auto file_object = MFileObject(); + file_object.setRawFullName(file_path); + file_object.setResolveMethod(MFileObject::kInputFile); + + bool path_exists = file_object.exists(); + if (!path_exists) { + MString resolved_file_path = file_object.resolvedFullName(); + status = MStatus::kFailure; + MMSOLVER_MAYA_VRB( + "mmpath::resolve_input_file_path: Could not find file path " + << "\"" << file_path.asChar() << "\", resolved path " + << "\"" << resolved_file_path.asChar() << "\"."); + return status; + } + + MString resolved_file_path = file_object.resolvedFullName(); + status = MStatus::kFailure; + if (resolved_file_path.length() > 0) { + MMSOLVER_MAYA_VRB("mmpath::resolve_input_file_path: resolved file path " + << "\"" << resolved_file_path.asChar() << "\"."); + file_path = file_object.resolvedFullName(); + status = MStatus::kSuccess; + } + return status; +} + +} // namespace mmpath diff --git a/src/mmSolver/utilities/path_utils.h b/src/mmSolver/utilities/path_utils.h new file mode 100644 index 000000000..0caa62216 --- /dev/null +++ b/src/mmSolver/utilities/path_utils.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2024 David Cattermole. + * + * This file is part of mmSolver. + * + * mmSolver is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * mmSolver is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with mmSolver. If not, see . + * ==================================================================== + * + * Generic path helper functions. + */ + +#ifndef PATH_UTILS_H +#define PATH_UTILS_H + +// STL +#include // fabs +#include +#include // stringstream +#include + +// Maya +#include +#include + +namespace mmpath { + +MStatus resolve_input_file_path(MString &file_path); + +} // namespace mmpath + +#endif // PATH_UTILS_H diff --git a/tests/test/test_solver/test_camera_pose_from_points.py b/tests/test/test_solver/test_camera_pose_from_points.py index 8eaa1b665..027f9da6a 100644 --- a/tests/test/test_solver/test_camera_pose_from_points.py +++ b/tests/test/test_solver/test_camera_pose_from_points.py @@ -126,7 +126,6 @@ def test_six_point_pose_resection1(self): # Run solver! assert 'mmCameraPoseFromPoints' in dir(maya.cmds) s = time.time() - # for frame in frames: result = maya.cmds.mmCameraPoseFromPoints(**kwargs) print('result:', pprint.pformat(result)) e = time.time() diff --git a/tools/lensdistortion/src/CMakeLists.txt b/tools/lensdistortion/src/CMakeLists.txt index 4ab37c891..5ba3cb8c9 100644 --- a/tools/lensdistortion/src/CMakeLists.txt +++ b/tools/lensdistortion/src/CMakeLists.txt @@ -41,6 +41,11 @@ target_link_libraries(${lensdistortion_exe_name} PRIVATE ${rust_depend_on_libraries} ) +# MM Color IO dependencies +include(MMColorIOUtils) +mmcolorio_find_packages() +mmcolorio_target_link_packages(${lensdistortion_exe_name}) + target_include_directories(${lensdistortion_exe_name} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} diff --git a/tools/lensdistortion/src/main.cpp b/tools/lensdistortion/src/main.cpp index a9d0411ad..72df46e4c 100644 --- a/tools/lensdistortion/src/main.cpp +++ b/tools/lensdistortion/src/main.cpp @@ -85,7 +85,7 @@ bool run_frame(mmlens::FrameNumber frame, const std::string output_file_path_string = compute_output_file_path(output_file_path_string, frame, verbose); - const auto output_file_path = rust::Str(output_file_path_string); + const rust::Str output_file_path(output_file_path_string.c_str()); auto meta_data = mmimage::ImageMetaData(); std::chrono::duration write_duration; @@ -102,8 +102,9 @@ bool run_frame(mmlens::FrameNumber frame, bool reread_result = true; if (reread_image) { auto reread_start = std::chrono::high_resolution_clock::now(); + const bool vertical_flip = false; reread_result = mmimage::image_read_pixels_exr_f32x4( - output_file_path, meta_data, pixel_buffer); + output_file_path, vertical_flip, meta_data, pixel_buffer); auto reread_end = std::chrono::high_resolution_clock::now(); reread_duration = reread_end - reread_start; std::cout << "Re-read image file path: " << output_file_path << '\n' @@ -172,7 +173,7 @@ bool run(const Arguments& args) { const size_t image_width = 3600; const size_t image_height = 2400; const size_t num_channels = 4; // 4 channels - RGBA - const auto input_file_path = rust::Str(args.input_file_path); + const rust::Str input_file_path(args.input_file_path.c_str()); // Read input lens distortion file. // @@ -181,7 +182,7 @@ bool run(const Arguments& args) { // they are not animated. This also includes when some frames // share lens distortion, but others do not; such as frames 1 to // 10 are the same and frames 11 to 20 are animated. - const auto lens_file_path = rust::Str(args.lens_file_path); + const rust::Str lens_file_path(args.lens_file_path.c_str()); mmlens::DistortionLayers lens_layers = mmlens::read_lens_file(lens_file_path); std::cout << "read_lens_file: " << lens_layers.as_string().c_str()