├── .github └── workflows │ └── main.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── img └── simplexUiExample.png ├── meson.build ├── meson.options ├── pyproject.toml ├── quick_compile.bat ├── scripts └── simplexui │ ├── __init__.py │ ├── channelBox.py │ ├── comboCheckDialog.py │ ├── commands │ ├── __init__.py │ ├── alembicCommon.py │ ├── applyCorrectives.py │ ├── buildIceXML.py │ ├── correctiveInterface.py │ ├── expandedExport.py │ ├── hdf5Convert.py │ ├── mayaCorrectiveInterface.py │ ├── mesh.py │ ├── reorderSimplexPoints.py │ ├── rigidAlign.py │ ├── setDelta.xsicompound │ ├── smpxBlend.py │ ├── unsubdivide.py │ ├── uvTransfer.py │ └── xsiCorrectiveInterface.py │ ├── dragFilter.py │ ├── falloffDialog.py │ ├── img │ ├── ChefHead.png │ └── frozen.png │ ├── interface │ ├── __init__.py │ ├── dummyInterface.py │ ├── mayaInterface.py │ └── xsiInterface.py │ ├── interfaceModel.py │ ├── interfaceModelTrees.py │ ├── interfaceModel_Test.py │ ├── items │ ├── __init__.py │ ├── accessor.py │ ├── combo.py │ ├── falloff.py │ ├── group.py │ ├── progression.py │ ├── shape.py │ ├── simplex.py │ ├── slider.py │ ├── stack.py │ └── traversal.py │ ├── main.pyw │ ├── menu │ ├── __init__.py │ ├── dummyPlugins │ │ └── __init__.py │ ├── genericPlugins │ │ ├── __init__.py │ │ ├── _builtins.py │ │ ├── checkPossibleCombos.py │ │ ├── exportSplit.py │ │ ├── showFalloffs.py │ │ ├── showTraversals.py │ │ ├── simplexUvTransfer.py │ │ └── unsubdivide.py │ ├── mayaPlugins │ │ ├── __init__.py │ │ ├── exportOther.py │ │ ├── extractProgressives.py │ │ ├── freezeCombo.py │ │ ├── generateShapeIncrementals.py │ │ ├── importObjs.py │ │ ├── linearizeTraversal.py │ │ ├── makeShelfBtn.py │ │ ├── relaxToSelection.py │ │ ├── reloadDefinition.py │ │ ├── snapToNeutral.py │ │ ├── softSelectToCluster.py │ │ ├── tweakMix.py │ │ └── updateRestShape.py │ └── xsiPlugins │ │ └── __init__.py │ ├── simplexDialog.py │ ├── travCheckDialog.py │ ├── traversalDialog.py │ ├── ui │ ├── comboCheckDialog.ui │ ├── falloffDialog.ui │ ├── simplexDialog.ui │ ├── travCheckDialog.ui │ └── traversalDialog.ui │ └── utils.py ├── simplex_maya_installer.py ├── src ├── maya │ ├── include │ │ ├── basicBlendShape.h │ │ └── simplex_mayaNode.h │ ├── meson.build │ └── src │ │ ├── basicBlendShape.cpp │ │ ├── pluginMain.cpp │ │ ├── simplex_mayaNode.cpp │ │ └── version.h.in ├── python │ ├── meson.build │ └── pysimplex.cpp └── simplexlib │ ├── include │ ├── combo.h │ ├── enums.h │ ├── floater.h │ ├── progression.h │ ├── shape.h │ ├── shapeBase.h │ ├── shapeController.h │ ├── simplex.h │ ├── slider.h │ ├── traversal.h │ ├── trispace.h │ └── utils.h │ ├── meson.build │ └── src │ ├── combo.cpp │ ├── progression.cpp │ ├── shape.cpp │ ├── shapeController.cpp │ ├── simplex.cpp │ ├── slider.cpp │ ├── traversal.cpp │ ├── trispace.cpp │ └── utils.cpp └── subprojects ├── eigen.wrap ├── maya ├── meson.build └── meson.options └── rapidjson.wrap /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | 2 | name: build 3 | 4 | on: 5 | push: 6 | branches: [ master ] 7 | tags: 8 | - v* 9 | pull_request: 10 | branches: [ master ] 11 | 12 | # matrix: 13 | # maya: [2024] 14 | # os: [macos-latest, ubuntu-latest, windows-latest] 15 | # include: 16 | # - maya: 2024 17 | # update: 2 18 | 19 | jobs: 20 | compile_plugin: 21 | strategy: 22 | matrix: 23 | maya: [2022, 2023, 2024, 2025] 24 | os: [macos-13, macos-latest, ubuntu-latest, windows-latest] 25 | include: 26 | # Add the maya update versions here 27 | - maya: 2022 28 | update: 5 29 | - maya: 2023 30 | update: 3 31 | - maya: 2024 32 | update: 2 33 | - maya: 2025 34 | update: 1 35 | 36 | # cross-compiling is annoying so just fall back to macos-13 37 | exclude: 38 | - os: macos-latest 39 | maya: 2022 40 | - os: macos-latest 41 | maya: 2023 42 | - os: macos-13 43 | maya: 2024 44 | - os: macos-13 45 | maya: 2025 46 | 47 | fail-fast: false 48 | 49 | runs-on: ${{ matrix.os }} 50 | steps: 51 | - uses: actions/checkout@v4 52 | - run: git fetch --force --tags origin 53 | 54 | - name: Get Maya Devkit 55 | id: get-devkit 56 | uses: blurstudio/mayaModuleActions/getMayaDevkit@v1 57 | with: 58 | maya: ${{ matrix.maya }} 59 | update: ${{ matrix.update }} 60 | 61 | - name: Build Maya 62 | uses: blurstudio/mayaModuleActions/mesonBuild@v1 63 | with: 64 | setup-args: > 65 | -Dmaya:maya_version=${{ matrix.maya }} 66 | -Dmaya:maya_devkit_base=${{ steps.get-devkit.outputs.devkit-path }} 67 | -Dmaya_build=true 68 | -Dpython_build=false 69 | --buildtype release 70 | --backend ninja 71 | install-args: --skip-subprojects 72 | 73 | - name: Upload Artifacts 74 | uses: actions/upload-artifact@v4 75 | with: 76 | name: ${{ runner.os }}-${{ matrix.maya }}-plugin 77 | path: output_Maya${{ matrix.maya }}/*.${{ steps.get-devkit.outputs.plugin-ext }} 78 | if-no-files-found: error 79 | 80 | compile_python: 81 | strategy: 82 | matrix: 83 | # Sorry x86 MacOS, The switch to ARM is maiking this too complicated 84 | # Maya modules don't detect the architecture of the OS, so I've gotta pick 85 | os: [macos-latest, ubuntu-latest, windows-latest] 86 | 87 | fail-fast: false 88 | 89 | runs-on: ${{ matrix.os }} 90 | steps: 91 | - uses: actions/checkout@v4 92 | - run: git fetch --force --tags origin 93 | - name: 'Get Previous tag' 94 | id: previoustag 95 | uses: "WyriHaximus/github-action-get-previous-tag@v1" 96 | with: 97 | fallback: v0.0.1 98 | 99 | - name: Get pyver macos-latest 100 | if: ${{ matrix.os == 'macos-latest' }} 101 | shell: bash 102 | run: | 103 | echo "PY_VER=3.9" >> $GITHUB_ENV 104 | echo "PY_VER_FLAT=39" >> $GITHUB_ENV 105 | echo "PY_EXT=so" >> $GITHUB_ENV 106 | echo "PLAT_TAG=macosx_12_0_arm64" >> $GITHUB_ENV 107 | 108 | - name: Get pyver ubuntu-latest 109 | if: ${{ matrix.os == 'ubuntu-latest' }} 110 | shell: bash 111 | run: | 112 | echo "PY_VER=3.7" >> $GITHUB_ENV 113 | echo "PY_VER_FLAT=37" >> $GITHUB_ENV 114 | echo "PY_EXT=so" >> $GITHUB_ENV 115 | echo "PLAT_TAG=manylinux_2_17_x86_64" >> $GITHUB_ENV 116 | 117 | - name: Get pyver windows-latest 118 | if: ${{ matrix.os == 'windows-latest' }} 119 | shell: bash 120 | run: | 121 | echo "PY_VER=3.7" >> $GITHUB_ENV 122 | echo "PY_VER_FLAT=37" >> $GITHUB_ENV 123 | echo "PY_EXT=pyd" >> $GITHUB_ENV 124 | echo "PLAT_TAG=win_amd64" >> $GITHUB_ENV 125 | 126 | - name: Get an older python version 127 | uses: actions/setup-python@v5 128 | with: 129 | python-version: ${{ env.PY_VER }} 130 | 131 | - name: Build Python 132 | uses: blurstudio/mayaModuleActions/mesonBuild@v1 133 | with: 134 | setup-args: > 135 | -Dmaya_build=false 136 | -Dpython_build=true 137 | --buildtype release 138 | --backend ninja 139 | install-args: --skip-subprojects 140 | 141 | - name: Build Wheel 142 | shell: bash 143 | run: | 144 | python -m pip install -U pip 145 | python -m pip install -U build wheel hatch 146 | python -m hatch version ${{ steps.previoustag.outputs.tag }} 147 | python -m build --wheel 148 | for PY_WHEEL in dist/*.whl 149 | do 150 | python -m wheel tags --remove --python-tag ${{ env.PY_VER_FLAT }} --abi-tag abi3 --platform-tag ${{ env.PLAT_TAG }} ${PY_WHEEL} 151 | done 152 | 153 | - name: Upload Artifacts 154 | uses: actions/upload-artifact@v4 155 | with: 156 | name: ${{ runner.os }}-pyModule 157 | path: output_Python/*.${{ env.PY_EXT }} 158 | if-no-files-found: error 159 | 160 | - name: Upload Artifacts 161 | uses: actions/upload-artifact@v4 162 | with: 163 | name: ${{ runner.os }}-wheels 164 | path: dist/*.whl 165 | if-no-files-found: error 166 | 167 | upload_release: 168 | name: Upload release 169 | needs: [compile_plugin, compile_python] 170 | runs-on: ubuntu-latest 171 | steps: 172 | - uses: actions/checkout@v4 173 | - run: git fetch --force --tags origin 174 | - name: 'Get Previous tag' 175 | id: previoustag 176 | uses: "WyriHaximus/github-action-get-previous-tag@v1" 177 | with: 178 | fallback: v0.0.1 179 | 180 | - name: Package 181 | uses: blurstudio/mayaModuleActions/packageMayaModule@v1 182 | with: 183 | module-name: simplex 184 | folder-list: scripts icons 185 | version: ${{ steps.previoustag.outputs.tag }} 186 | py-limited-api: true 187 | 188 | - name: Upload distribution 189 | if: ${{ startsWith(github.ref, 'refs/tags/v') }} 190 | uses: softprops/action-gh-release@v1 191 | with: 192 | token: "${{ secrets.GITHUB_TOKEN }}" 193 | prerelease: false 194 | files: | 195 | *.zip 196 | 197 | - name: Publish To PyPI 198 | if: ${{ startsWith(github.ref, 'refs/tags/v') }} 199 | env: 200 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 201 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 202 | run: | 203 | shopt -s globstar 204 | cp artifacts/**/*.whl . 205 | python3 -m pip install -U pip 206 | python3 -m pip install -U twine 207 | python3 -m twine upload --verbose *.whl 208 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Release zipfile 6 | Simplex.zip 7 | 8 | # Build folders 9 | Maya20*/ 10 | **/build/ 11 | **/pybuild/ 12 | **/mayabuild/ 13 | **/mayabuild*/ 14 | **/xsibuild/ 15 | **/xsibuild*/ 16 | **/output/ 17 | **/output*/ 18 | **/Useful/ 19 | 20 | # VIM Swapfiles 21 | *.swp 22 | 23 | # Folder config file 24 | Desktop.ini 25 | 26 | # Recycle Bin used on file shares 27 | $RECYCLE.BIN/ 28 | 29 | # Windows Installer files 30 | *.cab 31 | *.msi 32 | *.msm 33 | *.msp 34 | 35 | #python bytecode 36 | *.pyc 37 | 38 | # Windows shortcuts 39 | *.lnk 40 | 41 | # ========================= 42 | # Operating System Files 43 | # ========================= 44 | 45 | # OSX 46 | # ========================= 47 | 48 | .DS_Store 49 | .AppleDouble 50 | .LSOverride 51 | 52 | # Thumbnails 53 | ._* 54 | 55 | # Files that might appear on external disk 56 | .Spotlight-V100 57 | .Trashes 58 | 59 | # Directories potentially created on remote AFP share 60 | .AppleDB 61 | .AppleDesktop 62 | Network Trash Folder 63 | Temporary Items 64 | .apdisk 65 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blurstudio/Simplex/94a738b5df8088dfbe47be79e1a07ae17dd7375d/.gitmodules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SIMPLEX UI 2 | --- 3 | 4 | ![Example Simplex UI](img/simplexUiExample.png) 5 | 6 | --- 7 | 8 | ### For Artitsts 9 | 10 | Simplex aims to provide an intuitive, cross-package UI that allows for building, editing, and controlling complex shapes, combos, and transitions for use in high-end blendshape facial rigs, or even PSD systems. 11 | 12 | This tool was built with the full **F**acial **A**ction **C**oding **S**ystem (FACS) in mind. As such, it easily handles hundreds of shapes with arbitrary combo depth. Spline interpolation for in-between shapes, positive-negative shapes, in-between combo shapes, and combo transitions are supported. Arbitrary value combinations are also fully supported (eg. ComboX activates when SliderA is at 0.25 and SliderB is at 0.33). 13 | 14 | ### For TD's 15 | 16 | Simplex aims to be fully scriptable so that it can easily be inserted into any pipeline. The UI and API are fully Python, all content creation commands are abstracted (for multi-package and external use), and all systems are built as human readable JSON strings. 17 | 18 | There is a suite of tools included that allow for manipulating .smpx files. Most of which can be run completely outside of a DCC. This includes vertex reordering, un-subdividing, splitting, and even shape-inversion. These .smpx files are nothing more than specially structured alembic caches 19 | 20 | As long as your package supports Plugins, Python, and Qt (or PySide), you can use Simplex. 21 | 22 | #### Simplex is NOT 23 | 24 | * Simplex is not a modeling toolkit 25 | * Modeling is done using whatever tools you choose on your current package 26 | * Simplex is not a deformer 27 | * It only informs a native blendshape deformer what values the current shapes should have 28 | * In the future, I *do* have ideas for building an interface to an advanced deformer for dynamically previewing arbitrary splits, but the final output will always have the ability to bake down to a basic blendshape. 29 | 30 | ### Basic Usage 31 | Follow this youtube link to a basic walkthrough of Simplex usage. This video highlights a slightly older version of Simplex, but the interaction remains basically the same. [https://www.youtube.com/watch?v=LQwzsxU8z_Q](https://www.youtube.com/watch?v=LQwzsxU8z_Q) 32 | 33 | 34 | ## Easy Installation 35 | 1. Download [this file](https://raw.githubusercontent.com/blurstudio/Simplex/master/simplex_maya_installer.py) to your computer. Make sure it's saved as a python file. 36 | 2. Drag/drop the python file into a freshly opened instance of Maya (make sure all other mayas are closed). A command prompt window may open for a couple seconds. This is normal. 37 | 3. If you have multiple Maya versions installed, repeat step 2 for those versions as well. This just ensures that numpy and Qt.py are installed for those versions. 38 | 4. Create a python shelf button with this script. 39 | ```python 40 | from simplexui import runSimplexUI 41 | runSimplexUI() 42 | ``` 43 | 44 | ## Updating 45 | 1. Download [this file](https://raw.githubusercontent.com/blurstudio/Simplex/master/simplex_maya_installer.py) to your computer. Make sure it's saved as a python file. 46 | 2. Drag/drop the python file into a freshly opened instance of Maya (make sure all other mayas are closed). A command prompt window may open for a couple seconds. This is normal. 47 | 3. If you have multiple Maya versions installed, you do NOT have to repeat step 2 for all of them. 48 | 49 | 50 | ## Manual Installation 51 | 1. Download the `simplex-v*.*.*.zip` file from the [latest release](https://github.com/blurstudio/Simplex/releases/latest) 52 | 2. Create a `modules` folder in your maya user directory. For example, on Windows, that would mean creating `C:\Users\\Documents\maya\modules` 53 | 3. Copy the `simplex.mod` file and the `simplex` folder into that directory. 54 | 4. Install numpy and Qt.py to mayapy [using pip](https://knowledge.autodesk.com/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2022/ENU/Maya-Scripting/files/GUID-72A245EC-CDB4-46AB-BEE0-4BBBF9791627-htm.html). For example, on Windows, once you're in the right place the command will be `mayapy -m pip install numpy Qt.py`. You will need admin privelages for this. 55 | 5. Run these two Python commands in Maya to start the tool. (This is probably what you should put into a shelf button) 56 | ```python 57 | from simplexui import runSimplexUI 58 | runSimplexUI() 59 | ``` 60 | 61 | ## Uninstalling 62 | 1. Delete the `simplex.mod` file and the `simplex` folder from the `modules` folder in your maya user directory. For example, on Windows, that would mean deleting `C:\Users\\Documents\maya\modules\simplex.mod` and `C:\Users\\Documents\maya\modules\simplex` 63 | 64 | 65 | ## Compiling 66 | Hopefully you don't need to do this, but if you have to, just take a look at `.github/workflows/main.yml` and you should be able to piece together how to get a compile working using CMake. You aren't required to download the devkit or set its path for CMake if you've got maya installed on your machine. Also note, I use features from CMake 3.16+ so I can target python 2 and 3 separately. 67 | -------------------------------------------------------------------------------- /img/simplexUiExample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blurstudio/Simplex/94a738b5df8088dfbe47be79e1a07ae17dd7375d/img/simplexUiExample.png -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('Simplex', 'cpp', default_options: ['cpp_std=c++20']) 2 | 3 | 4 | maya_build = get_option('maya_build') 5 | python_build = get_option('python_build') 6 | 7 | if not maya_build and not python_build 8 | error('No builds requested') 9 | endif 10 | 11 | subdir('src/simplexlib') 12 | if maya_build 13 | maya_dep = dependency('maya') 14 | maya_name_suffix = maya_dep.get_variable('name_suffix') 15 | maya_version = maya_dep.get_variable('maya_version') 16 | subdir('src/maya') 17 | endif 18 | 19 | if python_build 20 | subdir('src/python') 21 | endif 22 | -------------------------------------------------------------------------------- /meson.options: -------------------------------------------------------------------------------- 1 | option('maya_build', type : 'boolean', value : true) 2 | option('python_build', type : 'boolean', value : true) 3 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "simplex-solver" 3 | dynamic = [ 4 | "version" 5 | ] 6 | 7 | authors = [ 8 | { name="Tyler Fox", email="tyler@blur.com" }, 9 | ] 10 | description = "The Blur Studio Simplex Blendshape Combination System" 11 | readme = "README.md" 12 | requires-python = ">=3.7" 13 | classifiers = [ 14 | "Programming Language :: Python :: 3.7", 15 | "Programming Language :: Python :: 3.8", 16 | "Programming Language :: Python :: 3.9", 17 | "Programming Language :: Python :: 3.10", 18 | "Programming Language :: Python :: Implementation :: CPython", 19 | "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", 20 | "Operating System :: Microsoft :: Windows", 21 | "Operating System :: POSIX :: Linux", 22 | "Operating System :: MacOS :: MacOS X", 23 | ] 24 | 25 | dependencies = [ 26 | "six", 27 | "Qt.py", 28 | ] 29 | 30 | [project.urls] 31 | "Project Page" = "https://github.com/blurstudio/simplex" 32 | 33 | [build-system] 34 | requires = ["hatchling"] 35 | build-backend = "hatchling.build" 36 | 37 | [tool.hatch.build.targets.wheel.sources] 38 | "scripts" = "" 39 | "output_Python" = "" 40 | 41 | [tool.hatch.build.targets.wheel] 42 | only-packages = false 43 | artifacts = [ 44 | "*.so", 45 | "*.pyd", 46 | "!*.lib", 47 | ] 48 | only-include = [ 49 | "scripts/simplexui", 50 | "output_Python", 51 | ] 52 | 53 | [tool.hatch.version] 54 | path = "scripts/simplexui/__init__.py" 55 | 56 | [tool.ruff] 57 | # Exclude a variety of commonly ignored directories. 58 | exclude = [ 59 | "*.egg-info", 60 | "*.pyc", 61 | ".bzr", 62 | ".cache", 63 | ".direnv", 64 | ".eggs", 65 | ".git", 66 | ".git-rewrite", 67 | ".hg", 68 | ".ipynb_checkpoints", 69 | ".mypy_cache", 70 | ".nox", 71 | ".pants.d", 72 | ".pyenv", 73 | ".pytest_cache", 74 | ".pytype", 75 | ".ruff_cache", 76 | ".svn", 77 | ".tox", 78 | ".venv", 79 | ".vscode", 80 | "__pycache__", 81 | "__pypackages__", 82 | "_build", 83 | "buck-out", 84 | "build", 85 | "dist", 86 | "docs", 87 | "node_modules", 88 | "shared-venv", 89 | "site-packages", 90 | "venv", 91 | ] 92 | 93 | line-length = 100 94 | indent-width = 4 95 | target-version = "py37" 96 | 97 | [tool.ruff.lint] 98 | select = [ 99 | "B", 100 | "C", 101 | "E", 102 | "F", 103 | "N", 104 | "W", 105 | "B9", 106 | ] 107 | ignore = [ 108 | "B905", 109 | "C901", 110 | "E203", 111 | "E501", 112 | "E722", 113 | "N802", 114 | "N803", 115 | "N804", 116 | "N806", 117 | "N815", 118 | ] 119 | 120 | # Allow fix for all enabled rules (when `--fix`) is provided. 121 | fixable = ["ALL"] 122 | unfixable = [] 123 | 124 | # Allow unused variables when underscore-prefixed. 125 | dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" 126 | 127 | [tool.ruff.format] 128 | # Like Black, use double quotes for strings. 129 | quote-style = "double" 130 | 131 | # Like Black, indent with spaces, rather than tabs. 132 | indent-style = "space" 133 | 134 | # Like Black, respect magic trailing commas. 135 | skip-magic-trailing-comma = false 136 | 137 | # Like Black, automatically detect the appropriate line ending. 138 | line-ending = "auto" 139 | 140 | -------------------------------------------------------------------------------- /quick_compile.bat: -------------------------------------------------------------------------------- 1 | setlocal 2 | 3 | SET MAYA_VERSION=2024 4 | REM "vs" "ninja" 5 | REM use VS for the debugger, otherwise use NINJA 6 | REM Until I figure out how to debug using nvim 7 | SET BACKEND=ninja 8 | REM "debug" "debugoptimized" "release" 9 | SET BUILDTYPE=release 10 | SET BUILDDIR=mayabuild_%BUILDTYPE%_%MAYA_VERSION%_%BACKEND% 11 | 12 | if not exist %BUILDDIR%\ ( 13 | meson setup %BUILDDIR% -Dmaya:maya_version=%MAYA_VERSION% --buildtype %BUILDTYPE% --vsenv --backend %BACKEND% 14 | ) 15 | 16 | if exist %BUILDDIR%\ ( 17 | meson compile -C %BUILDDIR% 18 | meson install --skip-subprojects -C %BUILDDIR% 19 | ) 20 | 21 | pause 22 | -------------------------------------------------------------------------------- /scripts/simplexui/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from __future__ import absolute_import 19 | 20 | SIMPLEX_UI = None 21 | SIMPLEX_UI_ROOT = None 22 | __version__ = "v0.0.1-dev" 23 | 24 | 25 | def runSimplexUI(): 26 | from .interface import DISPATCH, rootWindow 27 | from .simplexDialog import SimplexDialog 28 | 29 | global SIMPLEX_UI 30 | global SIMPLEX_UI_ROOT 31 | 32 | # make and show the UI 33 | SIMPLEX_UI_ROOT = rootWindow() 34 | # Keep a global reference around, otherwise it gets GC'd 35 | SIMPLEX_UI = SimplexDialog(parent=SIMPLEX_UI_ROOT, dispatch=DISPATCH) 36 | SIMPLEX_UI.show() 37 | 38 | 39 | def tool_paths(): 40 | import os 41 | 42 | path = os.path.dirname(__file__) 43 | pathPar = os.path.dirname(path) 44 | return [path], [pathPar] 45 | 46 | 47 | if __name__ == "__main__": 48 | import os 49 | import sys 50 | 51 | folder = os.path.dirname(os.path.dirname(__file__)) 52 | if folder not in sys.path: 53 | sys.path.insert(0, folder) 54 | runSimplexUI() 55 | -------------------------------------------------------------------------------- /scripts/simplexui/commands/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | -------------------------------------------------------------------------------- /scripts/simplexui/commands/hdf5Convert.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from __future__ import absolute_import 19 | 20 | import json 21 | 22 | from .alembicCommon import buildSmpx, readSmpx 23 | 24 | 25 | def hdf5Convert(inPath, outPath, ogawa=False): 26 | """Load and parse all the data from a simplex file 27 | 28 | Parameters 29 | ---------- 30 | inPath : str 31 | The input .smpx file path 32 | outPath : str 33 | The output .smpx file path 34 | ogawa : bool 35 | Whether to write out in Ogawa format. Defaults False 36 | 37 | Returns 38 | ------- 39 | 40 | """ 41 | jsString, counts, verts, faces, uvs, uvFaces = readSmpx(inPath) 42 | 43 | js = json.loads(jsString) 44 | name = js["systemName"] 45 | 46 | buildSmpx( 47 | outPath, 48 | verts, 49 | faces, 50 | jsString, 51 | name, 52 | faceCounts=counts, 53 | uvs=uvs, 54 | uvFaces=uvFaces, 55 | ogawa=ogawa, 56 | ) 57 | 58 | 59 | if __name__ == "__main__": 60 | inPath = r"D:\Users\tyler\Desktop\Head_Morphs_Main_Head-Face_v0010.smpx" 61 | outPath = r"D:\Users\tyler\Desktop\Head_ogawa.smpx" 62 | hdf5Convert(inPath, outPath, ogawa=True) 63 | -------------------------------------------------------------------------------- /scripts/simplexui/commands/mayaCorrectiveInterface.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | """ Get the corrective deltas from a rig in Maya """ 19 | 20 | from __future__ import absolute_import 21 | 22 | from ctypes import c_float 23 | 24 | from maya import OpenMaya as om 25 | from maya import cmds 26 | from six.moves import range 27 | 28 | try: 29 | import numpy as np 30 | except ImportError: 31 | pass 32 | 33 | 34 | def setPose(pvp, multiplier): 35 | """Set a percentage of a pose 36 | 37 | Parameters 38 | ---------- 39 | pvp : [(str, float), ...] 40 | A list of property/value pairs 41 | multiplier : float 42 | The percentage multiplier of the pose 43 | """ 44 | for prop, val in pvp: 45 | cmds.setAttr(prop, val * multiplier) 46 | 47 | 48 | def resetPose(pvp): 49 | """Reset everything back to rest 50 | 51 | Parameters 52 | ---------- 53 | pvp : [(str, float), ...] 54 | A list of property/value pairs 55 | """ 56 | for prop, val in pvp: 57 | cmds.setAttr(prop, 0) 58 | 59 | 60 | def _getDagPath(mesh): 61 | sl = om.MSelectionList() 62 | sl.add(mesh) 63 | dagPath = om.MDagPath() 64 | sl.getDagPath(0, dagPath) 65 | return dagPath 66 | 67 | 68 | def _getMayaPoints(meshFn): 69 | rawPts = meshFn.getRawPoints() 70 | ptCount = meshFn.numVertices() 71 | cta = (c_float * 3 * ptCount).from_address(int(rawPts)) 72 | out = np.ctypeslib.as_array(cta) 73 | out = np.copy(out) 74 | out = out.reshape((-1, 3)) 75 | return out 76 | 77 | 78 | def getDeformerChain(chkObj): 79 | # Follow the deformer chain 80 | memo = [] 81 | while chkObj and chkObj not in memo: 82 | memo.append(chkObj) 83 | 84 | typ = cmds.nodeType(chkObj) 85 | if typ == "mesh": 86 | cnx = cmds.listConnections( 87 | chkObj + ".inMesh", destination=False, shapes=True 88 | ) or [None] 89 | chkObj = cnx[0] 90 | elif typ == "groupParts": 91 | cnx = cmds.listConnections( 92 | chkObj + ".inputGeometry", destination=False, shapes=True 93 | ) or [None] 94 | chkObj = cnx[0] 95 | elif typ == "polySoftEdge": 96 | cnx = cmds.listConnections( 97 | chkObj + ".inputPolymesh", destination=False, shapes=True 98 | ) or [None] 99 | chkObj = cnx[0] 100 | elif typ == "AlembicNode": 101 | # Alembic nodes aren't part of the deformer chain 102 | # Cut it off, and return 103 | return memo[:-1] 104 | else: 105 | cnx = cmds.ls(chkObj, type="geometryFilter") or [None] 106 | chkObj = cnx[0] 107 | if chkObj: # we have a deformer 108 | # Get the mesh index of this deformer 109 | cnx = cmds.listConnections( 110 | chkObj, connections=True, plugs=True, source=False 111 | ) 112 | prev = cmds.ls(memo[-2])[0] # Get the minimal unique name for testing 113 | defIdx = 0 114 | for i in range(0, len(cnx), 2): 115 | if cnx[i + 1].startswith(prev): 116 | defIdx = int(cnx[i].split("[")[-1][:-1]) 117 | break 118 | # Use that mesh index to get the output 119 | cnx = cmds.listConnections( 120 | chkObj + ".input[{0}].inputGeometry".format(defIdx), 121 | destination=False, 122 | shapes=True, 123 | ) or [None] 124 | chkObj = cnx[0] 125 | 126 | return memo 127 | 128 | 129 | def getShiftValues(thing): 130 | """Shift the vertices along each axis *before* the skinning 131 | op in the deformer history 132 | 133 | Parameters 134 | ---------- 135 | mesh : str 136 | The name of a mesh 137 | 138 | Returns 139 | ------- 140 | : [vert, ...] 141 | A list of un-shifted vertices 142 | : [vert, ...] 143 | A list of vertices pre-shifted by 1 along the X axis 144 | : [vert, ...] 145 | A list of vertices pre-shifted by 1 along the Y axis 146 | : [vert, ...] 147 | A list of vertices pre-shifted by 1 along the Z axis 148 | """ 149 | orig = getDeformerChain(thing)[-1] 150 | 151 | dp = _getDagPath(thing) 152 | meshFn = om.MFnMesh(dp) 153 | allVerts = "{0}.vtx[*]".format(orig) 154 | 155 | zero = _getMayaPoints(meshFn) 156 | cmds.move(1, 0, 0, allVerts, relative=1, objectSpace=1) 157 | oneX = _getMayaPoints(meshFn) 158 | cmds.move(-1, 1, 0, allVerts, relative=1, objectSpace=1) 159 | oneY = _getMayaPoints(meshFn) 160 | cmds.move(0, -1, 1, allVerts, relative=1, objectSpace=1) 161 | oneZ = _getMayaPoints(meshFn) 162 | cmds.move(0, 0, -1, allVerts, relative=1, objectSpace=1) 163 | 164 | return zero, oneX, oneY, oneZ 165 | -------------------------------------------------------------------------------- /scripts/simplexui/commands/reorderSimplexPoints.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | """ Transfer shapes between mismatched models 19 | 20 | Given a 1:1 point correspondence, transfer the shapes from one 21 | geometry to another. Figuring out the correspondence is currently 22 | outside the scope of this tool, though I may release one later. 23 | 24 | The point correspondence should look like an unordered range, and 25 | will be used as a numpy index to get the output values. It's also 26 | possible to invert the range if you think you've got it backwards 27 | """ 28 | # pylint:disable=wrong-import-position 29 | from __future__ import absolute_import, print_function 30 | 31 | import json 32 | 33 | from .alembicCommon import buildSmpx, readSmpx 34 | 35 | try: 36 | import numpy as np 37 | except ImportError: 38 | pass 39 | 40 | 41 | def reorderSimplexPoints(sourcePath, matchPath, outPath, invertMatch=False): 42 | """Transfer shape data from the sourcePath using the numpy int array 43 | at matchPath to make the final output at outPath 44 | 45 | Parameters 46 | ---------- 47 | sourcePath : str 48 | The source .smpx file path 49 | matchPath : str 50 | The new vert order in numpy, or json format. The data should be an Nx2 array 51 | of integers 52 | outPath : str 53 | The new output .smpx path 54 | invertMatch : bool 55 | Whether to directly apply the match from matchPath, or whether to invert it 56 | 57 | Returns 58 | ------- 59 | 60 | """ 61 | jsString, counts, verts, faces, uvs, uvFaces = readSmpx(sourcePath) 62 | 63 | js = json.loads(jsString) 64 | name = js["systemName"] 65 | 66 | print("Loading Correspondence") 67 | if matchPath.endswith(".json"): 68 | with open(matchPath, "r") as f: 69 | c = json.load(f) 70 | c = np.array(c) 71 | else: 72 | c = np.load(matchPath) 73 | 74 | c = c[c[:, 0].argsort()].T[1] 75 | ci = c.argsort() 76 | if invertMatch: 77 | ci, c = c, ci 78 | 79 | print("Reordering") 80 | verts = verts[:, c, :] 81 | faces = ci[faces] 82 | 83 | buildSmpx( 84 | outPath, 85 | verts, 86 | faces, 87 | jsString, 88 | name, 89 | faceCounts=counts, 90 | uvs=uvs, 91 | uvFaces=uvFaces, 92 | ) 93 | 94 | 95 | if __name__ == "__main__": 96 | import os 97 | 98 | base = r"K:\Departments\CharacterModeling\Library\Head\MaleHead_Standard\005" 99 | _sourcePath = os.path.join(base, "HeadMaleStandard_High_Split_BadOrder.smpx") 100 | _matchPath = os.path.join(base, "Reorder.np") 101 | _outPath = os.path.join(base, "HeadMaleStandard_High_Split2.smpx") 102 | 103 | reorderSimplexPoints(_sourcePath, _matchPath, _outPath) 104 | -------------------------------------------------------------------------------- /scripts/simplexui/commands/rigidAlign.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from __future__ import absolute_import 19 | 20 | from six.moves import range 21 | 22 | try: 23 | import numpy as np 24 | except ImportError: 25 | pass 26 | 27 | 28 | def rigidAlign(P, Q, iters=10): 29 | """Rigidly align meshes with matching vert order by a least-squares error. 30 | Uses a variation of an algorithm by Umeyama 31 | Relevant links: 32 | * https://gist.github.com/nh2/bc4e2981b0e213fefd4aaa33edfb3893 (this code) 33 | * http://stackoverflow.com/a/32244818/263061 (solution with scale) 34 | 35 | Parameters 36 | ---------- 37 | P : np.array 38 | Static set of points 39 | Q : np.array 40 | Points to align with non-uniform scale 41 | iters : int 42 | The number of iterations (Defaults to 10) 43 | 44 | Returns 45 | ------- 46 | : np.array 47 | The 4x4 transformation matrix that most closely aligns Q to P 48 | """ 49 | # pylint:disable=invalid-name 50 | assert P.shape == Q.shape 51 | 52 | n, dim = P.shape 53 | assert dim == 3 54 | 55 | if iters <= 1: 56 | raise ValueError("Must run at least 1 iteration") 57 | 58 | # Get the centroid of each object 59 | Qm = Q.mean(axis=0) 60 | Pm = P.mean(axis=0) 61 | 62 | # Subtract out the centroid to get the basic aligned mesh 63 | cP = P - Pm # centeredP 64 | cQRaw = Q - Qm # centeredQ 65 | 66 | cQ = cQRaw.copy() 67 | cumulation = np.eye(3) # build an accumulator for the rotation 68 | 69 | # Here, we find an approximate rotation and scaling, but only 70 | # keep track of the accumulated rotations. 71 | # Then we apply the non-uniform scale by comparing bounding boxes 72 | # This way we don't get any shear in our matrix, and we relatively 73 | # quickly walk our way towards a minimum 74 | for _ in range(iters): 75 | # Magic? 76 | C = np.dot(cP.T, cQ) / n 77 | V, S, W = np.linalg.svd(C) 78 | 79 | # Handle negative scaling 80 | d = (np.linalg.det(V) * np.linalg.det(W)) < 0.0 81 | if d: 82 | S[-1] = -S[-1] 83 | V[:, -1] = -V[:, -1] 84 | 85 | # build the rotation matrix for this iteration 86 | # and add it to the accumulation 87 | R = np.dot(V, W) 88 | cumulation = np.dot(cumulation, R.T) 89 | 90 | # Now apply the accumulated rotation to the raw point positions 91 | # Then grab the non-uniform scaling from the bounding box 92 | # And set up cQ for the next iteration 93 | cQ = np.dot(cQRaw, cumulation) 94 | sf = (cP.max(axis=0) - cP.min(axis=0)) / (cQ.max(axis=0) - cQ.min(axis=0)) 95 | cQ = cQ * sf 96 | 97 | # Build the final transformation 98 | csf = cumulation * sf 99 | tran = Pm - Qm.dot(csf) 100 | outMat = np.eye(4) 101 | outMat[:3, :3] = csf 102 | outMat[3, :3] = tran 103 | return outMat 104 | -------------------------------------------------------------------------------- /scripts/simplexui/commands/setDelta.xsicompound: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /scripts/simplexui/commands/xsiCorrectiveInterface.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | """ Get the corrective deltas from a rig in XSI """ 19 | 20 | from __future__ import absolute_import 21 | 22 | from dcc.xsi import constants, xsi 23 | from dcc.xsi.ice import ICETree # pylint:disable=import-error 24 | from six.moves import zip 25 | 26 | 27 | def setPose(pvp, multiplier): 28 | """Set a percentage of a pose 29 | 30 | Parameters 31 | ---------- 32 | pvp : [(str, float), ...] 33 | A list of property/value pairs 34 | multiplier : float 35 | The percentage multiplier of the pose 36 | """ 37 | for prop, val in pvp: 38 | xsi.setValue(prop, val * multiplier) 39 | 40 | 41 | def resetPose(pvp): 42 | """Reset everything back to rest 43 | 44 | Parameters 45 | ---------- 46 | pvp : [(str, float), ...] 47 | A list of property/value pairs 48 | """ 49 | for prop, val in pvp: 50 | xsi.setValue(prop, 0) 51 | 52 | 53 | def getMeshVerts(mesh): 54 | """Get the verts of the given mesh object 55 | 56 | Parameters 57 | ---------- 58 | mesh : XSIObject 59 | A native xsi mesh object 60 | 61 | Returns 62 | ------- 63 | : [vert, ...] 64 | A list of vertices 65 | """ 66 | vts = mesh.ActivePrimitive.Geometry.Points.PositionArray 67 | return list(zip(*vts)) 68 | 69 | 70 | def buildTree(mesh): 71 | """Build the ICE tree that allows for this procedure 72 | 73 | Parameters 74 | ---------- 75 | mesh : XSIObject 76 | A native xsi mesh object 77 | 78 | Returns 79 | ------- 80 | : XSIObject 81 | The ICE Tree object 82 | : XSIObject 83 | The vector node in the ICE Tree 84 | """ 85 | iceTree = ICETree(None, mesh, "Test", constants.siConstructionModePrimaryShape) 86 | 87 | getter = iceTree.addGetDataNode("Self.PointPosition") 88 | adder = iceTree.addNode("Add") 89 | vector = iceTree.addNode("ScalarTo3DVector") 90 | setter = iceTree.addSetDataNode("Self.PointPosition") 91 | 92 | getter.value.connect(adder.value1) 93 | vector.vector.connect(adder.value2) 94 | adder.result.connect(setter.Value) 95 | iceTree.connect(setter.Execute, 2) 96 | 97 | return iceTree, vector 98 | 99 | 100 | def getShiftValues(mesh): 101 | """Shift the vertices along each axis *before* the skinning 102 | op in the deformer history 103 | 104 | Parameters 105 | ---------- 106 | mesh : XSIObject 107 | A native xsi mesh object 108 | 109 | Returns 110 | ------- 111 | : [vert, ...] 112 | A list of un-shifted vertices 113 | : [vert, ...] 114 | A list of vertices pre-shifted by 1 along the X axis 115 | : [vert, ...] 116 | A list of vertices pre-shifted by 1 along the Y axis 117 | : [vert, ...] 118 | A list of vertices pre-shifted by 1 along the Z axis 119 | """ 120 | tree, vector = buildTree(mesh) 121 | zero = getMeshVerts(mesh) 122 | 123 | vector.x.value = 1.0 124 | oneX = getMeshVerts(mesh) 125 | vector.x.value = 0.0 126 | 127 | vector.y.value = 1.0 128 | oneY = getMeshVerts(mesh) 129 | vector.y.value = 0.0 130 | 131 | vector.z.value = 1.0 132 | oneZ = getMeshVerts(mesh) 133 | vector.z.value = 0.0 134 | 135 | tree.delete() 136 | return zero, oneX, oneY, oneZ 137 | -------------------------------------------------------------------------------- /scripts/simplexui/img/ChefHead.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blurstudio/Simplex/94a738b5df8088dfbe47be79e1a07ae17dd7375d/scripts/simplexui/img/ChefHead.png -------------------------------------------------------------------------------- /scripts/simplexui/img/frozen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/blurstudio/Simplex/94a738b5df8088dfbe47be79e1a07ae17dd7375d/scripts/simplexui/img/frozen.png -------------------------------------------------------------------------------- /scripts/simplexui/interface/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | # This file will serve as the only place where the choice of DCC will be chosen 19 | from __future__ import absolute_import 20 | 21 | import os 22 | import sys 23 | 24 | CONTEXT = os.path.basename(sys.executable) 25 | if CONTEXT in ("maya.exe", "mayabatch.exe", "maya.bin"): 26 | from .mayaInterface import DCC, DISPATCH, rootWindow, undoContext 27 | elif CONTEXT in ("XSI.exe", "xsi.bin"): 28 | from .xsiInterface import DCC, DISPATCH, rootWindow, undoContext 29 | else: 30 | from .dummyInterface import DCC, DISPATCH, rootWindow, undoContext 31 | -------------------------------------------------------------------------------- /scripts/simplexui/interfaceModel_Test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from __future__ import absolute_import, print_function 19 | 20 | import os 21 | import sys 22 | 23 | from six.moves import range 24 | 25 | # Add the parent folder to the path so I can import SimplexUI 26 | # Means I can run this test code from inside the module and 27 | # keep everything together 28 | base = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 29 | sys.path.insert(0, base) 30 | 31 | from .interfaceModel import ( 32 | ComboFilterModel, 33 | ComboModel, 34 | Simplex, 35 | SimplexModel, 36 | Slider, 37 | SliderFilterModel, 38 | SliderModel, 39 | TraversalFilterModel, 40 | TraversalModel, 41 | ) 42 | from Qt.QtCore import QModelIndex 43 | from Qt.QtWidgets import QApplication, QPushButton, QTreeView, QVBoxLayout, QWidget 44 | 45 | 46 | # HELPERS 47 | def expandRecursive(view, model, index=QModelIndex(), depth=0, debug=False): 48 | """helper to expand the whole tree""" 49 | view.setExpanded(index, True) 50 | rows = model.rowCount(index) 51 | 52 | if debug: 53 | item = model.itemFromIndex(index) 54 | print( 55 | " " * depth 56 | + "Name {0}, children {1}".format(item.name if item else None, rows) 57 | ) 58 | print( 59 | " " * depth 60 | + "Depth {0}, Row {1}, Col {2}".format(depth, index.row(), index.column()) 61 | ) 62 | 63 | for row in range(rows): 64 | child = model.index(row, 0, index) 65 | if not child.isValid(): 66 | continue 67 | expandRecursive(view, model, child, depth + 1) 68 | 69 | 70 | def showTree(model): 71 | app = QApplication(sys.argv) 72 | tv = QTreeView() 73 | tv.setModel(model) 74 | expandRecursive(tv, model) 75 | tv.resizeColumnToContents(0) 76 | tv.show() 77 | sys.exit(app.exec_()) 78 | 79 | 80 | def buildDummySystem(path, name="Face"): 81 | if path.endswith(".json"): 82 | simp = Simplex.buildSystemFromFile(path) 83 | elif path.endswith(".smpx"): 84 | simp = Simplex.buildSystemFromFile(path) 85 | else: 86 | raise IOError("Filepath is not .smpx or .json") 87 | return simp 88 | 89 | 90 | # DISPLAY TESTS 91 | def testSliderDisplay(smpxPath, applyFilter=True): 92 | simp = Simplex.buildSystemFromFile(smpxPath) 93 | model = SimplexModel(simp, None) 94 | model = SliderModel(model) 95 | if applyFilter: 96 | model = SliderFilterModel(model) 97 | showTree(model) 98 | 99 | 100 | def testComboDisplay(smpxPath, applyFilter=True): 101 | simp = Simplex.buildSystemFromFile(smpxPath) 102 | model = SimplexModel(simp, None) 103 | model = ComboModel(model) 104 | if applyFilter: 105 | model = ComboFilterModel(model) 106 | showTree(model) 107 | 108 | 109 | def testTraversalDisplay(path, applyFilter=True): 110 | simp = buildDummySystem(path) 111 | 112 | model = SimplexModel(simp, None) 113 | model = TraversalModel(model) 114 | if applyFilter: 115 | model = TraversalFilterModel(model) 116 | showTree(model) 117 | 118 | 119 | def testBaseDisplay(path): 120 | simp = buildDummySystem(path) 121 | 122 | model = SimplexModel(simp, None) 123 | showTree(model) 124 | 125 | 126 | def testEmptySimplex(): 127 | simp = Simplex.buildEmptySystem(None, "Face") 128 | model = SimplexModel(simp, None) 129 | model = SliderModel(model) 130 | showTree(model) 131 | 132 | 133 | # RowAdd Tests 134 | def testNewSlider(): 135 | simp = Simplex.buildEmptySystem(None, "Face") 136 | model = SimplexModel(simp, None) 137 | smodel = SliderModel(model) 138 | fmodel = SliderFilterModel(smodel) 139 | fmodel.doFilter = True 140 | 141 | app = QApplication(sys.argv) 142 | 143 | topWid = QWidget() 144 | lay = QVBoxLayout(topWid) 145 | 146 | tv = QTreeView(topWid) 147 | btn = QPushButton("NEW", topWid) 148 | lay.addWidget(tv) 149 | lay.addWidget(btn) 150 | 151 | tv.setModel(fmodel) 152 | expandRecursive(tv, fmodel) 153 | topWid.show() 154 | 155 | def newSlider(): 156 | return Slider.createSlider("NewSlider", simp) 157 | 158 | btn.clicked.connect(newSlider) 159 | 160 | sys.exit(app.exec_()) 161 | 162 | 163 | def testDeleteBase(path): 164 | simp = buildDummySystem(path) 165 | model = SimplexModel(simp, None) 166 | 167 | # model = SliderModel(model) 168 | # model = SliderFilterModel(model) 169 | 170 | model = ComboModel(model) 171 | # model = ComboFilterModel(model) 172 | 173 | app = QApplication(sys.argv) 174 | 175 | topWid = QWidget() 176 | lay = QVBoxLayout(topWid) 177 | 178 | tv = QTreeView(topWid) 179 | 180 | btn = QPushButton("DELETE", topWid) 181 | lay.addWidget(tv) 182 | lay.addWidget(btn) 183 | 184 | tv.setModel(model) 185 | topWid.show() 186 | 187 | expandRecursive(tv, model) 188 | tv.resizeColumnToContents(0) 189 | 190 | def delCallback(): 191 | sel = tv.selectedIndexes() 192 | sel = [i for i in sel if i.column() == 0] 193 | items = [s.model().itemFromIndex(s) for s in sel] 194 | item = items[0] 195 | print("Deleting", type(item), item.name) 196 | item.delete() 197 | tv.model().invalidateFilter() 198 | 199 | btn.clicked.connect(delCallback) 200 | 201 | sys.exit(app.exec_()) 202 | 203 | 204 | def testNewChild(path): 205 | simp = buildDummySystem(path) 206 | model = SimplexModel(simp, None) 207 | model = SliderModel(model) 208 | model = SliderFilterModel(model) 209 | 210 | app = QApplication(sys.argv) 211 | 212 | topWid = QWidget() 213 | lay = QVBoxLayout(topWid) 214 | 215 | tv = QTreeView(topWid) 216 | 217 | btn = QPushButton("NEW", topWid) 218 | lay.addWidget(tv) 219 | lay.addWidget(btn) 220 | 221 | tv.setModel(model) 222 | topWid.show() 223 | 224 | expandRecursive(tv, model) 225 | tv.resizeColumnToContents(0) 226 | 227 | def newCallback(): 228 | sel = tv.selectedIndexes() 229 | sel = [i for i in sel if i.column() == 0] 230 | items = [s.model().itemFromIndex(s) for s in sel] 231 | # item = items[0] 232 | 233 | # TODO 234 | # find the child type of item 235 | # make a new one of those 236 | 237 | # tv.model().invalidateFilter() 238 | 239 | btn.clicked.connect(newCallback) 240 | 241 | sys.exit(app.exec_()) 242 | 243 | 244 | if __name__ == "__main__": 245 | # basePath = r'D:\Users\tyler\Documents\GitHub\Simplex\scripts\SimplexUI\build' 246 | basePath = r"D:\Users\tyler\Documents\GitHub\Simplex\Useful" 247 | # path = os.path.join(basePath, 'male_Simplex_v005_Split.smpx') 248 | # path = os.path.join(basePath, 'sphere_abcd_50.smpx') 249 | # path = os.path.join(basePath, 'male_traversal3.json') 250 | path = os.path.join(basePath, "SquareTest_Floater.json") 251 | 252 | # Only works for one at a time 253 | # testEmptySimplex() 254 | # testBaseDisplay(path) 255 | # testSliderDisplay(path, applyFilter=True) 256 | # testComboDisplay(path, applyFilter=True) 257 | # testTraversalDisplay(path, applyFilter=True) 258 | # testNewSlider() 259 | testDeleteBase(path) 260 | -------------------------------------------------------------------------------- /scripts/simplexui/items/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from .combo import Combo, ComboPair 19 | from .falloff import Falloff 20 | from .group import Group 21 | from .progression import ProgPair, Progression 22 | from .shape import Shape 23 | from .simplex import Simplex 24 | from .slider import Slider 25 | from .stack import Stack, stackable 26 | from .traversal import Traversal, TravPair 27 | -------------------------------------------------------------------------------- /scripts/simplexui/items/accessor.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from __future__ import absolute_import 19 | 20 | import copy 21 | 22 | import six 23 | 24 | 25 | class SimplexAccessor(object): 26 | """The base object for all Simplex System object types 27 | This class provides access to the simplex system, draggability, 28 | name getters/setters/unifiers, proper deepcopying, and abstract tree lookup 29 | 30 | Parameters 31 | ---------- 32 | 33 | Returns 34 | ------- 35 | 36 | """ 37 | 38 | def __init__(self, simplex): 39 | self.simplex = simplex 40 | self._name = None 41 | self._splitApplied = set() 42 | 43 | self.dragStep = 0.05 44 | self.maxValue = 1.0 45 | self.minValue = 0.0 46 | 47 | def valueTick(self, ticks, mul): 48 | """Change the value of the current object by some number of ticks 49 | with some given multiplier. This is the interface for the MMB drag 50 | 51 | Parameters 52 | ---------- 53 | ticks : int 54 | The number of dragStep ticks to apply 55 | mul : float 56 | An overall multiplier 57 | """ 58 | try: 59 | val = self.value 60 | except AttributeError: 61 | return 62 | val += self.dragStep * ticks * mul 63 | if abs(val) < 1.0e-5: 64 | val = 0.0 65 | val = max(min(val, self.maxValue), self.minValue) 66 | self.value = val 67 | 68 | @property 69 | def name(self): 70 | """ """ 71 | return self._name 72 | 73 | @name.setter 74 | def name(self, val): 75 | """The name of the current object""" 76 | self._name = val 77 | 78 | @property 79 | def models(self): 80 | """ """ 81 | return self.simplex.models 82 | 83 | @property 84 | def falloffModels(self): 85 | """ """ 86 | return self.simplex.falloffModels 87 | 88 | @property 89 | def DCC(self): 90 | """ """ 91 | return self.simplex.DCC 92 | 93 | @property 94 | def stack(self): 95 | """ """ 96 | return self.simplex.stack 97 | 98 | def __deepcopy__(self, memo): 99 | cls = self.__class__ 100 | result = cls.__new__(cls) 101 | memo[id(self)] = result 102 | for k, v in six.iteritems(self.__dict__): 103 | if k == "_thing" and self.DCC.program != "dummy": 104 | # DO NOT make a copy of the DCC thing (unless its a dummy dcc) 105 | # as it may or may not be a persistent object 106 | # setattr(result, k, self._thing) 107 | setattr(result, k, None) 108 | elif k == "expanded": 109 | # Skip the expanded dict because it deals with the Qt models 110 | setattr(result, k, {}) 111 | else: 112 | setattr(result, k, copy.deepcopy(v, memo)) 113 | return result 114 | 115 | def _buildLinkedRename(self, newName, maxDepth, currentLinks): 116 | """Build the proposed set of renames specifically for this object 117 | This allows sub-classes to override the linked name behavior 118 | 119 | Parameters 120 | ---------- 121 | newName : 122 | 123 | maxDepth : 124 | 125 | currentLinks : 126 | 127 | 128 | Returns 129 | ------- 130 | 131 | """ 132 | return currentLinks 133 | 134 | def buildLinkedRename(self, newName, maxDepth=5, currentLinks=None): 135 | """For the Shape, Slider, Combo, and Traversal items, build a linked rename 136 | dictionary like {itemType: {newName: (item, maxDepth)}} recursively up to a 137 | maximum given depth 138 | 139 | The dictionary is structured like that to easily check for name clashes 140 | 141 | Parameters 142 | ---------- 143 | newName : str 144 | The new suggested name for this object 145 | maxDepth : int 146 | The maximum depth of recursion when resolving names (Default value = 5) 147 | currentLinks : dict 148 | The current set of proposed renames. (Default value = None) 149 | 150 | Returns 151 | ------- 152 | dict : 153 | A new set of propsed renames 154 | 155 | """ 156 | # Build the output dict if not done already 157 | if currentLinks is None: 158 | currentLinks = {} 159 | 160 | # return at depth 161 | if maxDepth <= 0: 162 | return currentLinks 163 | 164 | # If this doesn't need renamed, then we can prune this branch 165 | if self.name == newName: 166 | return currentLinks 167 | 168 | # Check for conflicts, or other short circuits 169 | typeLinks = currentLinks.setdefault(type(self), {}) 170 | if newName in typeLinks: 171 | tlPair = typeLinks[newName] 172 | if tlPair[0] is not self: 173 | # Error out if a name conflict is found. 174 | msg = "Linked rename produced a conflict: Trying to rename {0} {1} and {2} to {3}" 175 | msg = msg.format( 176 | type(self), typeLinks[newName].name, self.name, newName 177 | ) 178 | raise ValueError(msg) 179 | elif tlPair[1] >= maxDepth: 180 | # If we've been here before with more available depth 181 | # then we can just return because we've done this before 182 | return currentLinks 183 | 184 | # Finally add myself to the rename 185 | typeLinks[newName] = (self, maxDepth) 186 | 187 | # And now handle the type-specific stuff 188 | return self._buildLinkedRename(newName, maxDepth, currentLinks) 189 | 190 | def treeChild(self, row): 191 | """ """ 192 | return None 193 | 194 | def treeRow(self): 195 | """ """ 196 | return None 197 | 198 | def treeParent(self): 199 | """ """ 200 | return None 201 | 202 | def treeChildCount(self): 203 | """ """ 204 | return 0 205 | 206 | def treeData(self, column): 207 | """ """ 208 | if column == 0: 209 | return self.name 210 | return None 211 | 212 | def treeChecked(self): 213 | """ """ 214 | return None 215 | 216 | def icon(self): 217 | return None 218 | -------------------------------------------------------------------------------- /scripts/simplexui/items/stack.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | # pylint:disable=missing-docstring,unused-argument,no-self-use 19 | from __future__ import absolute_import 20 | 21 | import copy 22 | from collections import OrderedDict 23 | from contextlib import contextmanager 24 | from functools import wraps 25 | 26 | from ..interface import undoContext 27 | 28 | 29 | # UNDO STACK SETUP 30 | class Stack(object): 31 | """Integrate simplex into the DCC undo stack""" 32 | 33 | def __init__(self): 34 | self._stack = OrderedDict() 35 | self.depth = 0 36 | self.currentRevision = 0 37 | self.enabled = True 38 | 39 | def __setitem__(self, key, value): 40 | gt = [] 41 | # when setting a new key, remove all keys from 42 | # the previous branch 43 | for k in reversed(self._stack): # pylint: disable=bad-reversed-sequence 44 | if k > key: 45 | gt.append(k) 46 | else: 47 | # yay ordered dict 48 | break 49 | for k in gt: 50 | del self._stack[k] 51 | # traceback.print_stack() 52 | self._stack[key] = value 53 | 54 | def getRevision(self, revision): 55 | """Every time a change is made to the simplex definition, 56 | the revision counter is updated, and the revision/definition 57 | pair is put on the undo stack 58 | 59 | Parameters 60 | ---------- 61 | revision : int 62 | The revision number to get 63 | 64 | Returns 65 | ------- 66 | : Simplex or None 67 | The stored Simplex system for the given revision 68 | or None if nothing found 69 | 70 | """ 71 | # This method will ***ONLY*** be called by the undo callback 72 | # Seriously, don't call this yourself 73 | if revision != self.currentRevision: 74 | if revision in self._stack: 75 | data = self._stack[revision] 76 | self.currentRevision = revision 77 | return data 78 | return None 79 | 80 | def purge(self): 81 | """Clear the undo stack. This should be done on new-file""" 82 | self._stack = OrderedDict() 83 | self.depth = 0 84 | self.currentRevision = 0 85 | 86 | @contextmanager 87 | def store(self, wrapObj): 88 | """A context manager That will store changes to a Simplex system 89 | Nested calls to this manager will only store the first one 90 | 91 | Parameters 92 | ---------- 93 | wrapObj : object 94 | A system object that has a reference to the Simplex 95 | 96 | Returns 97 | ------- 98 | 99 | """ 100 | from .simplex import Simplex 101 | 102 | if self.enabled: 103 | with undoContext(wrapObj.DCC): 104 | self.depth += 1 105 | try: 106 | yield 107 | finally: 108 | self.depth -= 1 109 | 110 | if self.depth == 0: 111 | # Only store the top Level of the stack 112 | srevision = wrapObj.DCC.incrementRevision() 113 | if not isinstance(wrapObj, Simplex): 114 | wrapObj = wrapObj.simplex 115 | self[srevision] = copy.deepcopy(wrapObj) 116 | else: 117 | yield 118 | 119 | 120 | def stackable(method): 121 | """A Decorator to make a method auto update the stack 122 | This decorator can only be used on methods of an object 123 | that has its .simplex value set with a stack. If you need 124 | to wrap an init method, use the stack.store contextmanager 125 | 126 | Parameters 127 | ---------- 128 | method : 129 | 130 | 131 | Returns 132 | ------- 133 | 134 | """ 135 | 136 | @wraps(method) 137 | def stacked(self, *data, **kwdata): 138 | """Decorator closure that handles the stack 139 | 140 | Parameters 141 | ---------- 142 | *data : 143 | 144 | **kwdata : 145 | 146 | 147 | Returns 148 | ------- 149 | 150 | """ 151 | ret = None 152 | with self.stack.store(self): 153 | ret = method(self, *data, **kwdata) 154 | return ret 155 | 156 | return stacked 157 | -------------------------------------------------------------------------------- /scripts/simplexui/main.pyw: -------------------------------------------------------------------------------- 1 | import blurdev 2 | 3 | if __name__ == "__main__": 4 | # Ensure that my custom Qt.py import uses PyQt5 5 | import os 6 | import json 7 | pref = json.loads(os.environ.get("QT_PREFERRED_BINDING_JSON", "{}")) 8 | pref.setdefault("simplexui.Qt", ["PyQt5", "PyQt4"]) 9 | os.environ["QT_PREFERRED_BINDING_JSON"] = json.dumps(pref) 10 | os.environ["SIMPLEX_AT_BLUR"] = "true" 11 | 12 | from simplexui.simplexDialog import SimplexDialog 13 | import simplexui 14 | dlg = blurdev.launch(SimplexDialog) 15 | try: 16 | # Store the last simplex launch in these module variables 17 | simplexui.SIMPLEX_UI_ROOT = dlg.parent() 18 | simplexui.SIMPLEX_UI = dlg 19 | except RuntimeError: 20 | # If dlg.parent() raises a runtime error, then the ui has already been deleted 21 | # This happens if we're running externally instead of internally 22 | pass 23 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from __future__ import absolute_import 19 | 20 | import importlib 21 | import os 22 | import pkgutil 23 | import sys 24 | 25 | from Qt.QtWidgets import QMenu 26 | from . import genericPlugins 27 | 28 | CONTEXT = os.path.basename(sys.executable) 29 | if CONTEXT == "maya.exe": 30 | from . import mayaPlugins as plugins 31 | elif CONTEXT == "XSI.exe": 32 | from . import xsiPlugins as plugins 33 | else: 34 | from . import dummyPlugins as plugins 35 | 36 | 37 | def _iter_namespace(ns_pkg): 38 | # Specifying the second argument (prefix) to iter_modules makes the 39 | # returned name an absolute name instead of a relative one. This allows 40 | # import_module to work without having to do additional modification to 41 | # the name. 42 | return pkgutil.iter_modules(ns_pkg.__path__, ns_pkg.__name__ + ".") 43 | 44 | 45 | # Registration class 46 | def loadPlugins(): 47 | toolModules = [] 48 | contextModules = [] 49 | imod = sorted([i[1] for i in _iter_namespace(genericPlugins)]) 50 | imod += sorted([i[1] for i in _iter_namespace(plugins)]) 51 | 52 | plugs = [importlib.import_module(name) for name in imod] 53 | for module in plugs: 54 | if hasattr(module, "registerTool"): 55 | toolModules.append(module) 56 | if hasattr(module, "registerContext"): 57 | contextModules.append(module) 58 | 59 | return toolModules, contextModules 60 | 61 | 62 | def buildToolMenu(window, modules): 63 | menu = window.menuBar.addMenu("Tools") 64 | for m in modules: 65 | m.registerTool(window, menu) 66 | return menu 67 | 68 | 69 | def buildRightClickMenu(tree, indexes, modules): 70 | menu = QMenu() 71 | for m in modules: 72 | m.registerContext(tree, indexes, menu) 73 | return menu 74 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/dummyPlugins/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/genericPlugins/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/genericPlugins/checkPossibleCombos.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from __future__ import absolute_import 19 | 20 | from functools import partial 21 | 22 | from ...comboCheckDialog import ComboCheckDialog 23 | from ...items import Slider 24 | from Qt.QtWidgets import QAction 25 | 26 | 27 | def registerTool(window, menu): 28 | checkPossibleCombosACT = QAction("Check Possible Combos ...", window) 29 | menu.addAction(checkPossibleCombosACT) 30 | checkPossibleCombosACT.triggered.connect( 31 | partial(checkPossibleCombosInterface, window) 32 | ) 33 | 34 | 35 | def registerContext(tree, clickIdx, indexes, menu): 36 | window = tree.window() 37 | checkPossibleCombosACT = QAction("Check Possible Combos ...", tree) 38 | menu.addAction(checkPossibleCombosACT) 39 | checkPossibleCombosACT.triggered.connect( 40 | partial(checkPossibleCombosInterface, window) 41 | ) 42 | 43 | 44 | def checkPossibleCombosInterface(window): 45 | sliders = window.uiSliderTREE.getSelectedItems(typ=Slider) 46 | ccd = ComboCheckDialog(sliders, parent=window) 47 | ccd.show() 48 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/genericPlugins/exportSplit.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from __future__ import absolute_import 19 | 20 | from functools import partial 21 | 22 | from Qt import QtCompat 23 | from Qt.QtWidgets import QAction, QMessageBox, QProgressDialog 24 | 25 | try: 26 | import numpy as np 27 | except ImportError: 28 | np = None 29 | 30 | 31 | def registerTool(window, menu): 32 | if np is not None: 33 | exportSplitACT = QAction("Export Split", window) 34 | menu.addAction(exportSplitACT) 35 | exportSplitACT.triggered.connect(partial(exportSplitInterface, window)) 36 | 37 | 38 | def exportSplitInterface(window): 39 | if np is None: 40 | QMessageBox.warning( 41 | window, 42 | "No Numpy", 43 | "Numpy is not available here, an it is required to split a system", 44 | ) 45 | return 46 | path, _filter = QtCompat.QFileDialog.getSaveFileName( 47 | window, "Export Split", "", "Simplex (*.smpx)" 48 | ) 49 | 50 | if not path: 51 | return 52 | 53 | pBar = QProgressDialog("Exporting Split smpx File", "Cancel", 0, 100, window) 54 | pBar.show() 55 | try: 56 | split = window.simplex.split(pBar) 57 | split.exportAbc(path, pBar) 58 | except ValueError as e: 59 | QMessageBox.warning(window, "Unsplittable", str(e)) 60 | finally: 61 | pBar.close() 62 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/genericPlugins/showFalloffs.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from Qt.QtWidgets import QAction 19 | 20 | 21 | def registerTool(window, menu): 22 | editFalloffsACT = QAction("Edit Falloffs ...", window) 23 | menu.addAction(editFalloffsACT) 24 | editFalloffsACT.triggered.connect(window.showFalloffDialog) 25 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/genericPlugins/showTraversals.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from Qt.QtWidgets import QAction 19 | 20 | 21 | def registerTool(window, menu): 22 | showTraversalsACT = QAction("Show Traversals ...", window) 23 | menu.addAction(showTraversalsACT) 24 | showTraversalsACT.triggered.connect(window.showTraversalDialog) 25 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/genericPlugins/simplexUvTransfer.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from __future__ import absolute_import 19 | 20 | import json 21 | 22 | from ...commands.alembicCommon import buildSmpx, readSmpx 23 | from ...commands.mesh import Mesh 24 | from ...commands.uvTransfer import applyTransfer, getVertCorrelation 25 | 26 | try: 27 | import numpy as np 28 | except ImportError: 29 | np = None 30 | 31 | 32 | def simplexUvTransfer( 33 | srcSmpxPath, tarPath, outPath, srcUvPath=None, tol=0.0001, pBar=None 34 | ): 35 | """Transfer a simplex system onto a mesh through UV space 36 | 37 | Parameters 38 | ---------- 39 | srcSmpxPath : str 40 | The path to the source .smpx file 41 | tarPath : str 42 | The path to the mesh to recieve the blendshapes 43 | outPath : str 44 | The .smpx path that will be written 45 | srcUvPath : str 46 | If the .smpx file doesn't have UV's, then the UV's 47 | from this mesh wil be used. Defaults to None 48 | tol : float 49 | The tolerance for checking if a UV is outside of a poly 50 | pBar : QProgressDialog 51 | Optional progress bar 52 | 53 | """ 54 | if np is None: 55 | raise RuntimeError("UV Transfer requires Numpy. It is currently unavailable") 56 | 57 | if pBar is not None: 58 | pBar.setLabelText("Loading Source Mesh") 59 | from Qt.QtWidgets import QApplication 60 | 61 | QApplication.processEvents() 62 | 63 | srcUvPath = srcUvPath or srcSmpxPath 64 | if srcUvPath.endswith(".abc") or srcUvPath.endswith(".smpx"): 65 | src = Mesh.loadAbc(srcUvPath, ensureWinding=False) 66 | elif srcUvPath.endswith(".obj"): 67 | src = Mesh.loadObj(srcUvPath, ensureWinding=False) 68 | 69 | if pBar is not None: 70 | pBar.setLabelText("Loading Target Mesh") 71 | from Qt.QtWidgets import QApplication 72 | 73 | QApplication.processEvents() 74 | if tarPath.endswith(".abc"): 75 | tar = Mesh.loadAbc(tarPath, ensureWinding=False) 76 | elif tarPath.endswith(".obj"): 77 | tar = Mesh.loadObj(tarPath, ensureWinding=False) 78 | 79 | jsString, _, srcVerts, _, _, _ = readSmpx(srcSmpxPath) 80 | js = json.loads(jsString) 81 | name = js["systemName"] 82 | 83 | srcFaces = src.faceVertArray 84 | srcUvFaces = src.uvFaceMap["default"] 85 | srcUvs = np.array(src.uvMap["default"]) 86 | 87 | tarFaces = tar.faceVertArray 88 | tarUvFaces = tar.uvFaceMap["default"] 89 | tarUvs = np.array(tar.uvMap["default"]) 90 | oldTarVerts = np.array(tar.vertArray) 91 | 92 | corr = getVertCorrelation( 93 | srcUvFaces, srcUvs, tarFaces, tarUvFaces, tarUvs, tol=tol, pBar=pBar 94 | ) 95 | tarVerts = applyTransfer(srcVerts, srcFaces, corr, len(oldTarVerts)) 96 | 97 | # Apply as a delta 98 | deltas = tarVerts - tarVerts[0][None, ...] 99 | writeVerts = oldTarVerts[None, ...] + deltas 100 | 101 | buildSmpx( 102 | outPath, 103 | writeVerts, 104 | tarFaces, 105 | jsString, 106 | name, 107 | uvs=tarUvs, 108 | uvFaces=tarUvFaces, 109 | ) 110 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/genericPlugins/unsubdivide.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from __future__ import absolute_import 19 | 20 | import os 21 | from functools import partial 22 | 23 | from ...commands.unsubdivide import unsubdivideSimplex 24 | from Qt import QtCompat 25 | from Qt.QtWidgets import QAction, QMessageBox, QProgressDialog 26 | 27 | try: 28 | import imathnumpy 29 | except ImportError: 30 | imathnumpy = None 31 | 32 | 33 | def registerTool(window, menu): 34 | if imathnumpy is not None: 35 | exportUnsubACT = QAction("Un Subdivide Smpx ...", window) 36 | menu.addAction(exportUnsubACT) 37 | exportUnsubACT.triggered.connect(partial(exportUnsubInterface, window)) 38 | 39 | 40 | def exportUnsubInterface(window): 41 | if imathnumpy is None: 42 | QMessageBox.warning( 43 | window, 44 | "No ImathToNumpy", 45 | "ImathToNumpy is not available here, and it is required to unsubdivide a system", 46 | ) 47 | return 48 | 49 | path, _filter = QtCompat.QFileDialog.getOpenFileName( 50 | window, "Un Subdivide", "", "Simplex (*.smpx)" 51 | ) 52 | 53 | if not path: 54 | return 55 | 56 | outPath = path.replace(".smpx", "_UNSUB.smpx") 57 | if path == outPath: 58 | QMessageBox.warning(window, "Unable to rename smpx file: {}".format(path)) 59 | return 60 | 61 | if os.path.isfile(outPath): 62 | btns = QMessageBox.Ok | QMessageBox.Cancel 63 | msg = "Unsub file already exists.\n{0}\nOverwrite?".format(outPath) 64 | response = QMessageBox.question(window, "File already exists", msg, btns) 65 | if not response & QMessageBox.Ok: 66 | return 67 | 68 | pBar = QProgressDialog("Exporting Unsubdivided smpx File", "Cancel", 0, 100, window) 69 | pBar.show() 70 | unsubdivideSimplex(path, outPath, pBar=pBar) 71 | pBar.close() 72 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/mayaPlugins/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/mayaPlugins/exportOther.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from __future__ import absolute_import 19 | 20 | from functools import partial 21 | 22 | import maya.cmds as cmds 23 | 24 | from Qt import QtCompat 25 | from Qt.QtWidgets import QAction, QProgressDialog 26 | 27 | 28 | def registerTool(window, menu): 29 | exportOtherACT = QAction("Export Other", window) 30 | menu.addAction(exportOtherACT) 31 | exportOtherACT.triggered.connect(partial(exportOtherInterface, window)) 32 | 33 | 34 | def exportOtherInterface(window): 35 | sel = cmds.ls(sl=True) 36 | path, _filter = QtCompat.QFileDialog.getSaveFileName( 37 | window, "Export Other", "", "Simplex (*.smpx)" 38 | ) 39 | 40 | pBar = QProgressDialog( 41 | "Exporting Simplex from Other Mesh", "Cancel", 0, 100, window 42 | ) 43 | if sel and path: 44 | window.simplex.exportOther(path, sel[0], world=True, pBar=pBar) 45 | pBar.close() 46 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/mayaPlugins/extractProgressives.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from __future__ import absolute_import 19 | 20 | from functools import partial 21 | 22 | from ...interfaceModel import coerceIndexToType 23 | from ...items import Slider 24 | from Qt.QtWidgets import QAction 25 | 26 | 27 | def registerTool(window, menu): 28 | extractProgressivesACT = QAction("Extract Progressive", window) 29 | menu.addAction(extractProgressivesACT) 30 | extractProgressivesACT.triggered.connect( 31 | partial(extractProgressivesInterface, window) 32 | ) 33 | 34 | 35 | def registerContext(tree, clickIdx, indexes, menu): 36 | window = tree.window() 37 | live = window.uiLiveShapeConnectionACT.isChecked() 38 | sliders = coerceIndexToType(indexes, Slider) 39 | 40 | multis = [] 41 | for slidx in sliders: 42 | slider = slidx.model().itemFromIndex(slidx) 43 | if len(slider.prog.pairs) > 2: 44 | multis.append(slidx) 45 | 46 | if multis: 47 | extractACT = menu.addAction("Extract Progressive") 48 | extractACT.triggered.connect(partial(extractProgressivesContext, multis, live)) 49 | return True 50 | return False 51 | 52 | 53 | def extractProgressivesContext(indexes, live): 54 | sliders = [idx.model().itemFromIndex(idx) for idx in indexes] 55 | sliders = list(set(sliders)) 56 | for sli in sliders: 57 | sli.extractProgressive(live=live) 58 | 59 | 60 | def extractProgressivesInterface(window): 61 | live = window.uiLiveShapeConnectionACT.isChecked() 62 | indexes = window.uiSliderTREE.getSelectedIndexes() 63 | indexes = coerceIndexToType(indexes, Slider) 64 | sliders = [idx.model().itemFromIndex(idx) for idx in indexes] 65 | sliders = list(set(sliders)) 66 | for sli in sliders: 67 | sli.extractProgressive(live=live) 68 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/mayaPlugins/generateShapeIncrementals.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from __future__ import absolute_import 19 | 20 | from functools import partial 21 | 22 | import maya.cmds as cmds 23 | from six.moves import range 24 | 25 | from ...interfaceModel import coerceIndexToType 26 | from ...items import Combo, Slider 27 | from Qt.QtWidgets import QInputDialog, QMessageBox 28 | 29 | 30 | def registerContext(tree, clickIdx, indexes, menu): 31 | window = tree.window() 32 | sliders = coerceIndexToType(indexes, Slider) 33 | combos = coerceIndexToType(indexes, Combo) 34 | multis = [] 35 | for grp in [sliders, combos]: 36 | for idx in grp: 37 | item = idx.model().itemFromIndex(idx) 38 | if len(item.prog.pairs) == 2: 39 | multis.append(idx) 40 | 41 | if not multis: 42 | return False 43 | 44 | extractACT = menu.addAction("Generate Incrementals ...") 45 | extractACT.triggered.connect( 46 | partial(generateShapeIncrementalsContext, multis, window) 47 | ) 48 | return True 49 | 50 | 51 | def generateShapeIncrementalsContext(indexes, window): 52 | idx = indexes[0] # Only on the click index 53 | slider = idx.model().itemFromIndex(idx) 54 | if len(slider.prog.pairs) > 2: 55 | QMessageBox.warning(window, "Warning", "Slider already has incrementals") 56 | return 57 | 58 | increments, good = QInputDialog.getInt( 59 | window, "Increments", "Number of Increments", 4, 1, 100 60 | ) 61 | if not good: 62 | return 63 | 64 | rest = None 65 | target = None 66 | maxval = -1.0 67 | 68 | for pp in slider.prog.pairs: 69 | if pp.shape.isRest: 70 | rest = pp.shape 71 | elif abs(pp.value) > maxval: 72 | target = pp.shape 73 | 74 | target.name = target.name + "_100" 75 | 76 | startObj = slider.extractShape(rest, live=False) 77 | endObj = slider.extractShape(target) 78 | shapeDup = cmds.duplicate(endObj, name="shapeDup")[0] 79 | 80 | bs = cmds.blendShape(startObj, shapeDup) 81 | 82 | incs = [] 83 | for i in range(1, increments): 84 | val = float(increments - i) / increments 85 | percent = int(float(i) * 100 / increments) 86 | cmds.blendShape(bs, edit=True, weight=((0, val))) 87 | 88 | nne = endObj.replace("_100_", "_{0}_".format(percent)) 89 | nn = nne.replace("_Extract", "") 90 | inc = cmds.duplicate(shapeDup, name=nne) 91 | incs.append((percent, nn, nne)) 92 | 93 | for perc, inc, ince in incs: 94 | pp = slider.createShape(shapeName=inc, tVal=perc / 100.0) 95 | pp.shape.connectShape(mesh=ince, live=True) 96 | 97 | window.uiSliderTREE.model().invalidateFilter() 98 | 99 | cmds.delete((shapeDup, startObj)) 100 | cmds.select(endObj) 101 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/mayaPlugins/importObjs.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | 3 | import os 4 | from functools import partial 5 | 6 | import maya.cmds as cmds 7 | import six 8 | 9 | from ...items import Combo, Slider, Traversal 10 | from Qt.QtWidgets import ( 11 | QAction, 12 | QApplication, 13 | QFileDialog, 14 | QMessageBox, 15 | QProgressDialog, 16 | ) 17 | 18 | try: 19 | import numpy as np 20 | except ImportError: 21 | np = None 22 | 23 | try: 24 | from MeshCrawler.commands import setAllVerts 25 | from MeshCrawler.mesh import Mesh 26 | from MeshCrawler.meshcrawlerGen import autoCrawlMeshes 27 | except ImportError: 28 | autoCrawlMeshes = None 29 | 30 | 31 | def buildMesh(simplex, mesh): 32 | topo = simplex.DCC.getMeshTopology(mesh) 33 | faceIdxs, counts = topo[1], topo[2] 34 | faces = [] 35 | ptr = 0 36 | for c in counts: 37 | faces.append(tuple(faceIdxs[ptr : ptr + c])) 38 | ptr += c 39 | return Mesh(topo[0], tuple(faces)) 40 | 41 | 42 | def importSimpleObjs(simplex, orders, pBar): 43 | for shapeName, ctrl, shape, path in orders: 44 | pBar.setValue(pBar.value() + 1) 45 | pBar.setLabelText("Loading Obj :\n{0}".format(shapeName)) 46 | QApplication.processEvents() 47 | if pBar.wasCanceled(): 48 | return 49 | 50 | mesh = simplex.DCC.importObj(path) 51 | 52 | if isinstance(ctrl, Slider): 53 | simplex.DCC.connectShape(shape, mesh=mesh, live=False, delete=True) 54 | elif isinstance(ctrl, Combo): 55 | simplex.DCC.connectComboShape( 56 | ctrl, shape, mesh=mesh, live=False, delete=True 57 | ) 58 | elif isinstance(ctrl, Traversal): 59 | simplex.DCC.connectTraversalShape( 60 | ctrl, shape, mesh=mesh, live=False, delete=True 61 | ) 62 | 63 | 64 | def importReorderObjs(simplex, orders, pBar): 65 | reoMesh = simplex.DCC.extractShape(simplex.restShape, live=False) 66 | orderMesh = buildMesh(simplex, reoMesh) 67 | 68 | memo = {} 69 | 70 | for shapeName, ctrl, shape, path in orders: 71 | pBar.setValue(pBar.value() + 1) 72 | pBar.setLabelText("Loading Obj :\n{0}".format(shapeName)) 73 | QApplication.processEvents() 74 | if pBar.wasCanceled(): 75 | return 76 | 77 | impMesh = simplex.DCC.importObj(path) 78 | objMesh = buildMesh(simplex, impMesh) 79 | reo = memo.setdefault( 80 | objMesh.faceVertArray, autoCrawlMeshes(orderMesh, objMesh) 81 | ) 82 | verts = simplex.DCC.getNumpyShape(impMesh) 83 | 84 | setAllVerts(reoMesh, verts[reo]) 85 | 86 | if isinstance(ctrl, Slider): 87 | simplex.DCC.connectShape(shape, mesh=reoMesh, live=False, delete=False) 88 | elif isinstance(ctrl, Combo): 89 | simplex.DCC.connectComboShape( 90 | ctrl, shape, mesh=reoMesh, live=False, delete=False 91 | ) 92 | elif isinstance(ctrl, Traversal): 93 | simplex.DCC.connectTraversalShape( 94 | ctrl, shape, mesh=reoMesh, live=False, delete=False 95 | ) 96 | 97 | cmds.delete(reoMesh) 98 | 99 | 100 | def importObjList(simplex, paths, pBar, reorder=True): 101 | """Import all given .obj files 102 | 103 | Parameters 104 | ---------- 105 | paths : list 106 | The list of .obj files to import 107 | """ 108 | shapeDict = {shape.name: shape for shape in simplex.shapes} 109 | 110 | inPairs = {} 111 | for path in paths: 112 | shapeName = os.path.splitext(os.path.basename(path))[0] 113 | shape = shapeDict.get(shapeName) 114 | if shape is not None: 115 | inPairs[shapeName] = path 116 | else: 117 | sfx = "_Extract" 118 | if shapeName.endswith(sfx): 119 | shapeName = shapeName[: -len(sfx)] 120 | shape = shapeDict.get(shapeName) 121 | if shape is not None: 122 | inPairs[shapeName] = path 123 | 124 | sliderMasters, comboMasters, travMasters = {}, {}, {} 125 | for masters in [simplex.sliders, simplex.combos, simplex.traversals]: 126 | for master in masters: 127 | for pp in master.prog.pairs: 128 | shape = shapeDict.get(pp.shape.name) 129 | if shape is not None: 130 | if shape.name in inPairs: 131 | if isinstance(master, Slider): 132 | sliderMasters[shape.name] = master 133 | elif isinstance(master, Combo): 134 | comboMasters[shape.name] = master 135 | elif isinstance(master, Traversal): 136 | travMasters[shape.name] = master 137 | 138 | comboDepth = {} 139 | for k, v in six.iteritems(comboMasters): 140 | depth = len(v.pairs) 141 | comboDepth.setdefault(depth, {})[k] = v 142 | 143 | importOrder = [] 144 | for shapeName, slider in six.iteritems(sliderMasters): 145 | importOrder.append( 146 | (shapeName, slider, shapeDict[shapeName], inPairs[shapeName]) 147 | ) 148 | 149 | for depth in sorted(comboDepth.keys()): 150 | for shapeName, combo in six.iteritems(comboDepth[depth]): 151 | importOrder.append( 152 | (shapeName, combo, shapeDict[shapeName], inPairs[shapeName]) 153 | ) 154 | 155 | for shapeName, trav in six.iteritems(travMasters): 156 | importOrder.append((shapeName, trav, shapeDict[shapeName], inPairs[shapeName])) 157 | 158 | if pBar is not None: 159 | pBar.setMaximum(len(sliderMasters) + len(comboMasters) + len(travMasters)) 160 | 161 | if reorder: 162 | importReorderObjs(simplex, importOrder, pBar) 163 | else: 164 | importSimpleObjs(simplex, importOrder, pBar) 165 | 166 | pBar.close() 167 | 168 | 169 | def registerTool(window, menu): 170 | importObjsACT = QAction("Import Obj Folder", window) 171 | menu.addAction(importObjsACT) 172 | importObjsACT.triggered.connect(partial(importObjsInterface, window)) 173 | 174 | 175 | def importObjsInterface(window): 176 | reorder = True 177 | if np is None or autoCrawlMeshes is None: 178 | reorder = False 179 | 180 | folder = QFileDialog.getExistingDirectory(window, "Import Obj Folder", "") 181 | 182 | if not os.path.isdir(folder): 183 | QMessageBox.warning(window, "Warning", "Folder does not exist") 184 | return 185 | 186 | paths = os.listdir(folder) 187 | paths = [i for i in paths if i.endswith((".obj", ".OBJ"))] 188 | if not paths: 189 | QMessageBox.warning(window, "Warning", "Folder does not contain any .obj files") 190 | return 191 | paths = [os.path.join(folder, p) for p in paths] 192 | 193 | pBar = QProgressDialog("Loading from Mesh", "Cancel", 0, 100, window) 194 | pBar.show() 195 | 196 | importObjList(window.simplex, paths, pBar=pBar, reorder=reorder) 197 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/mayaPlugins/linearizeTraversal.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from __future__ import absolute_import 19 | from functools import partial 20 | import maya.cmds as cmds 21 | from Qt.QtWidgets import QAction 22 | 23 | 24 | def registerTool(window, menu): 25 | lineTravACT = QAction("Linearize Traversal", window) 26 | menu.addAction(lineTravACT) 27 | lineTravACT.triggered.connect(partial(lineTrav, window)) 28 | 29 | 30 | def lineTrav(window): 31 | simplex = window.simplex 32 | travDialog = window.travDialog 33 | sel = travDialog.uiTraversalTREE.getSelectedIndexes() 34 | travIdx = sel[0] 35 | trav = travIdx.model().itemFromIndex(travIdx) 36 | extracted = window.shapeIndexExtract([travIdx]) 37 | rest = simplex.DCC.extractTraversalShape(trav, simplex.restShape, live=False) 38 | 39 | end = extracted[-1] 40 | val = len(extracted) 41 | for i, mesh in enumerate(extracted[:-1]): 42 | startDup = cmds.duplicate(rest, name="startDup")[0] 43 | absBlend = cmds.blendShape(end, startDup) 44 | cmds.blendShape(absBlend, edit=True, weight=((0, (i + 1.0) / val))) 45 | 46 | finBlend = cmds.blendShape(startDup, mesh) 47 | cmds.blendShape(finBlend, edit=True, weight=((0, 1))) 48 | cmds.delete(startDup) 49 | cmds.delete(rest) 50 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/mayaPlugins/makeShelfBtn.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from __future__ import absolute_import 19 | 20 | import os 21 | 22 | from Qt.QtWidgets import QAction 23 | 24 | dn = os.path.dirname 25 | SHELF_DEV_BUTTON = """ 26 | import os, sys 27 | 28 | path = r'{0}' 29 | path = os.path.normcase(os.path.normpath(path)) 30 | if sys.path[0] != path: 31 | sys.path.insert(0, path) 32 | 33 | import simplexui 34 | if simplexui.SIMPLEX_UI is not None: 35 | try: 36 | simplexui.SIMPLEX_UI.close() 37 | except RuntimeError: 38 | # In case I closed it myself 39 | pass 40 | del simplexui 41 | 42 | for key, value in sys.modules.items(): 43 | try: 44 | packPath = value.__file__ 45 | except AttributeError: 46 | continue 47 | 48 | packPath = os.path.normcase(os.path.normpath(packPath)) 49 | if packPath.startswith(path): 50 | sys.modules.pop(key) 51 | 52 | import simplexui 53 | simplexui.runSimplexUI() 54 | 55 | sys.path.pop(0) 56 | """.format( 57 | dn(dn(dn(dn(__file__)))) 58 | ) 59 | 60 | 61 | def registerTool(window, menu): 62 | makeShelfBtnACT = QAction("Make Shelf Button", window) 63 | menu.addAction(makeShelfBtnACT) 64 | makeShelfBtnACT.triggered.connect(makeShelfButton) 65 | 66 | 67 | def makeShelfButton(): 68 | pass 69 | # TODO: Actually, ya know, Add the button to the shelf 70 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/mayaPlugins/relaxToSelection.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from __future__ import absolute_import 19 | 20 | import maya.cmds as cmds 21 | 22 | from Qt.QtWidgets import QAction 23 | 24 | 25 | def registerTool(window, menu): 26 | relaxToSelectionACT = QAction("Relax To Selection", window) 27 | menu.addAction(relaxToSelectionACT) 28 | relaxToSelectionACT.triggered.connect(relaxToSelectionInterface) 29 | 30 | 31 | def relaxToSelectionInterface(): 32 | sel = cmds.ls(sl=True) 33 | if len(sel) >= 2: 34 | relaxToSelection(sel[0], sel[1]) 35 | 36 | 37 | def relaxToSelection(source, target): 38 | """ 39 | Transfer high-frequency sculpts (like wrinkles) from one shape to another 40 | 41 | This sets up a delta mush on a detailed mesh (source) before blending to a 42 | less detailed shape (target). Turning up the deltaMush re-applies the 43 | high-resolution deltas to that less detailed shape so then blend it back 44 | into the target 45 | """ 46 | sourceDup = cmds.duplicate(source, name="deltaMushMesh")[0] 47 | targetDup = cmds.duplicate(target, name="targetDup")[0] 48 | deltaMushRelax = cmds.group(sourceDup, name="deltaMushRelax") 49 | cmds.hide([sourceDup, targetDup, deltaMushRelax]) 50 | 51 | cmds.addAttr( 52 | deltaMushRelax, 53 | longName="smooth_iter", 54 | attributeType="long", 55 | minValue=0, 56 | maxValue=100, 57 | defaultValue=10, 58 | ) 59 | smoothIter = "{0}.smooth_iter".format(deltaMushRelax) 60 | cmds.setAttr(smoothIter, edit=True, keyable=True) 61 | 62 | blender = cmds.blendShape(targetDup, sourceDup) 63 | deltaMush = cmds.deltaMush( 64 | sourceDup, 65 | smoothingIterations=10, 66 | smoothingStep=0.5, 67 | pinBorderVertices=1, 68 | envelope=1, 69 | )[0] 70 | cmds.connectAttr(smoothIter, deltaMush + ".smoothingIterations", force=True) 71 | 72 | cmds.delete(targetDup) 73 | finalBlend = cmds.blendShape(sourceDup, target) 74 | cmds.blendShape(finalBlend, edit=True, weight=((0, 1))) 75 | cmds.blendShape(blender, edit=True, weight=((0, 1))) 76 | 77 | cmds.select(deltaMushRelax) 78 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/mayaPlugins/reloadDefinition.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from __future__ import absolute_import 19 | from functools import partial 20 | from Qt.QtWidgets import QAction 21 | 22 | 23 | def registerTool(window, menu): 24 | reloadDefinitionACT = QAction("Reload Definition", window) 25 | menu.addAction(reloadDefinitionACT) 26 | reloadDefinitionACT.triggered.connect(partial(reloadDefinitionInterface, window)) 27 | 28 | 29 | def reloadDefinitionInterface(window): 30 | reloadDefinition(window.simplex) 31 | 32 | 33 | def reloadDefinition(simplex): 34 | simplex.DCC.setSimplexString(simplex.DCC.op, simplex.dump()) 35 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/mayaPlugins/snapToNeutral.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from __future__ import absolute_import 19 | 20 | from functools import partial 21 | 22 | import maya.cmds as cmds 23 | 24 | from Qt.QtWidgets import QAction 25 | 26 | 27 | def registerTool(window, menu): 28 | snapShapeToNeutralACT = QAction("Snap Shape To Neutral", window) 29 | menu.addAction(snapShapeToNeutralACT) 30 | snapShapeToNeutralACT.triggered.connect( 31 | partial(snapShapeToNeutralInterface, window) 32 | ) 33 | 34 | 35 | def snapShapeToNeutralInterface(window): 36 | sel = cmds.ls(sl=True) 37 | if len(sel) >= 2: 38 | snapShapeToNeutral(sel[0], sel[1]) 39 | elif len(sel) == 1: 40 | rest = window.simplex.extractRestShape() 41 | snapShapeToNeutral(sel[0], rest) 42 | cmds.delete(rest) 43 | 44 | 45 | def snapShapeToNeutral(source, target): 46 | """ 47 | Take a mesh, and find the closest location on the target head, and snap to that 48 | Then set up a blendShape so the artist can "paint" in the snapping behavior 49 | """ 50 | # Make a duplicate of the source and snap it to the target 51 | snapShape = cmds.duplicate(source, name="snp") 52 | cmds.transferAttributes( 53 | target, 54 | snapShape, 55 | transferPositions=1, 56 | sampleSpace=1, # 0=World, 1=Local, 3=UV 57 | searchMethod=0, # 0=Along Normal, 1=Closest Location 58 | ) 59 | 60 | # Then delete history 61 | cmds.delete(snapShape, constructionHistory=True) 62 | cmds.hide(snapShape) 63 | 64 | # Blend the source to the snappedShape 65 | bs = cmds.blendShape(snapShape, source)[0] 66 | cmds.blendShape(bs, edit=True, weight=((0, 1))) 67 | 68 | # But set the weights back to 0.0 for painting 69 | numVerts = cmds.polyEvaluate(source, vertex=1) 70 | setter = "{0}.inputTarget[0].inputTargetGroup[0].targetWeights[0:{1}]".format( 71 | bs, numVerts - 1 72 | ) 73 | weights = [0.0] * numVerts 74 | cmds.setAttr(setter, *weights, size=numVerts) 75 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/mayaPlugins/tweakMix.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from __future__ import absolute_import 19 | 20 | from functools import partial 21 | 22 | import maya.cmds as cmds 23 | import six 24 | from six.moves import range, zip 25 | 26 | from ...interface.mayaInterface import disconnected 27 | from ...interfaceModel import coerceIndexToType 28 | from ...items import Combo 29 | from Qt.QtWidgets import QAction 30 | 31 | 32 | def registerTool(window, menu): 33 | tweakMixACT = QAction("Tweak Mix", window) 34 | menu.addAction(tweakMixACT) 35 | tweakMixACT.triggered.connect(partial(tweakMixInterface, window)) 36 | 37 | 38 | def tweakMixInterface(window): 39 | if not window.simplex: 40 | return 41 | live = window.uiLiveShapeConnectionACT.isChecked() 42 | 43 | indexes = window.uiComboTREE.selectedIndexes() 44 | indexes = coerceIndexToType(indexes, Combo) 45 | if not indexes: 46 | return 47 | combos = [idx.model().itemFromIndex(idx) for idx in indexes] 48 | combos = list(set(combos)) 49 | tweakMix(window.simplex, combos, live) 50 | 51 | 52 | def registerContext(tree, clickIdx, indexes, menu): 53 | window = tree.window() 54 | live = window.uiLiveShapeConnectionACT.isChecked() 55 | indexes = coerceIndexToType(indexes, Combo) 56 | if indexes: 57 | indexes = list(set(indexes)) 58 | tweakMixACT = menu.addAction("Tweak Mix") 59 | tweakMixACT.triggered.connect(partial(tweakMixContext, window, indexes, live)) 60 | return True 61 | return False 62 | 63 | 64 | def tweakMixContext(window, indexes, live): 65 | combos = [idx.model().itemFromIndex(idx) for idx in indexes] 66 | combos = list(set(combos)) 67 | tweakMix(window.simplex, combos, live) 68 | 69 | 70 | def tweakMix(simplex, combos, live): 71 | # first extract the rest shape non-live 72 | restGeo = simplex.extractRestShape() 73 | 74 | floatShapes = simplex.getFloatingShapes() 75 | floatShapes = [i.thing for i in floatShapes] 76 | 77 | offset = 5 78 | for combo in combos: 79 | offset += 5 80 | 81 | shape = None 82 | for pp in combo.prog.pairs: 83 | if pp.value == 1.0: 84 | shape = pp.shape 85 | break 86 | else: 87 | continue 88 | 89 | geo = combo.extractShape(shape, live=live, offset=offset) 90 | # disconnect the controller from the operator 91 | tweakShapes = [] 92 | with disconnected(simplex.DCC.op) as sliderCnx: 93 | # disconnect any float shapes 94 | with disconnected(floatShapes): 95 | cnx = sliderCnx[simplex.DCC.op] 96 | for a in six.itervalues(cnx): 97 | cmds.setAttr(a, 0.0) 98 | 99 | # set the combo values 100 | for pair in combo.pairs: 101 | cmds.setAttr(cnx[pair.slider.thing], pair.value) 102 | 103 | for shape in simplex.shapes[1:]: # skip the restShape 104 | shapeVal = cmds.getAttr(shape.thing) 105 | if shapeVal != 0.0: # maybe handle floating point errors 106 | tweakShapes.append((shape, shapeVal)) 107 | 108 | tweakMeshes = [] 109 | with disconnected(simplex.DCC.shapeNode) as shapeCnx: 110 | cnx = shapeCnx[simplex.DCC.shapeNode] 111 | for a in six.itervalues(cnx): 112 | cmds.setAttr(a, 0.0) 113 | for tshape, shapeVal in tweakShapes: 114 | cmds.setAttr(tshape.thing, shapeVal) 115 | # print "setAttr", tshape.thing, shapeVal 116 | tweakMesh = cmds.duplicate( 117 | simplex.DCC.mesh, name="{0}_Tweak".format(tshape.name) 118 | )[0] 119 | tweakMeshes.append(tweakMesh) 120 | cmds.setAttr(tshape.thing, 0.0) 121 | 122 | # Yeah, yeah, this is REALLY ugly, but it's quick and it works 123 | tempBS = cmds.blendShape(geo, name="Temp_BS") 124 | cmds.blendShape(tempBS, edit=True, target=(geo, 0, restGeo, 1.0)) 125 | cmds.blendShape(tempBS, edit=True, weight=[(0, 1.0)]) 126 | cmds.delete(geo, constructionHistory=True) 127 | 128 | # build the actual blendshape 129 | tweakBS = cmds.blendShape(geo, name="Tweak_BS") 130 | for i, tm in enumerate(tweakMeshes): 131 | cmds.blendShape(tweakBS, edit=True, target=(geo, i, tm, 1.0)) 132 | tmLen = len(tweakMeshes) 133 | cmds.blendShape( 134 | tweakBS, edit=True, weight=list(zip(list(range(tmLen)), [1.0] * tmLen)) 135 | ) 136 | 137 | cmds.delete(tweakMeshes) 138 | cmds.delete(restGeo) 139 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/mayaPlugins/updateRestShape.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | 18 | from __future__ import absolute_import 19 | 20 | import textwrap 21 | from functools import partial 22 | 23 | import maya.cmds as cmds 24 | 25 | from Qt.QtWidgets import QAction, QMessageBox 26 | 27 | 28 | def registerTool(window, menu): 29 | updateRestShapeACT = QAction("Update Rest Shape", window) 30 | menu.addAction(updateRestShapeACT) 31 | updateRestShapeACT.triggered.connect(partial(updateRestShapeInterface, window)) 32 | 33 | 34 | def updateRestShapeInterface(window): 35 | sel = cmds.ls(sl=True) 36 | if not sel: 37 | QMessageBox.warning(window, "Nothing Selected", "Nothing Selected") 38 | return 39 | sel = sel[0] 40 | mesh = window.simplex.DCC.mesh 41 | 42 | # TODO, Check vert number and blendshape input connections 43 | selVerts = cmds.polyEvaluate(sel, vertex=1) 44 | meshVerts = cmds.polyEvaluate(mesh, vertex=1) 45 | 46 | if selVerts != meshVerts: 47 | msg = "Selected object {0} has {1} verts\nBase Object has {2} verts".format( 48 | sel, selVerts, meshVerts 49 | ) 50 | QMessageBox.warning(window, "Vert Mismatch", msg) 51 | return 52 | 53 | # TODO Check for live connections 54 | bs = window.simplex.DCC.shapeNode 55 | cnx = cmds.listConnections(bs, plugs=1, destination=0, type="mesh") 56 | if cnx: 57 | cnxs = ", ".join([i.split(".")[0] for i in cnx]) 58 | cnxs = textwrap.fill(cnxs) 59 | msg = [ 60 | "Some shapes have a live input connection:", 61 | cnxs, 62 | "", 63 | "These shapes will not get the update.", 64 | "Continue anyway?", 65 | ] 66 | msg = "\n".join(msg) 67 | btns = QMessageBox.Ok | QMessageBox.Cancel 68 | bret = QMessageBox.question(window, "Live Connections", msg, btns) 69 | if not bret & QMessageBox.Ok: 70 | return 71 | 72 | updateRestShape(mesh, sel, window=window) 73 | 74 | 75 | def updateRestShape(mesh, newRest, window=None): 76 | allShapes = cmds.listRelatives(mesh, children=1, shapes=1) or [] 77 | noInter = cmds.listRelatives(mesh, children=1, shapes=1, noIntermediate=1) or [] 78 | hist = cmds.listHistory(mesh) 79 | inter = (set(allShapes) - set(noInter)) & set(hist) 80 | if not inter: 81 | if window is not None: 82 | QMessageBox.warning( 83 | window, "No Intermediates", "No intermediate meshes found" 84 | ) 85 | return 86 | inter = list(inter) 87 | 88 | if len(inter) == 1: 89 | orig = inter[0] 90 | else: 91 | origs = [i for i in inter if "Orig" in i] 92 | if len(origs) != 1: 93 | if window is not None: 94 | QMessageBox.warning( 95 | window, 96 | "Too Many Intermediates", 97 | "Too Many intermediate meshes found: {0}".format(origs), 98 | ) 99 | return 100 | orig = origs[0] 101 | 102 | outMesh = "{0}.worldMesh[0]".format(newRest) 103 | inMesh = "{0}.inMesh".format(orig) 104 | 105 | cmds.connectAttr(outMesh, inMesh, force=1) 106 | cmds.refresh(force=1) 107 | cmds.disconnectAttr(outMesh, inMesh) 108 | -------------------------------------------------------------------------------- /scripts/simplexui/menu/xsiPlugins/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016, Blur Studio 2 | # 3 | # This file is part of Simplex. 4 | # 5 | # Simplex is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Lesser General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # Simplex is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Lesser General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Lesser General Public License 16 | # along with Simplex. If not, see . 17 | -------------------------------------------------------------------------------- /src/maya/include/basicBlendShape.h: -------------------------------------------------------------------------------- 1 | //- 2 | // ========================================================================== 3 | // Copyright 2015 Autodesk, Inc. All rights reserved. 4 | // 5 | // Use of this software is subject to the terms of the Autodesk 6 | // license agreement provided at the time of installation or download, 7 | // or which otherwise accompanies this software in either electronic 8 | // or hard copy form. 9 | // ========================================================================== 10 | //+ 11 | 12 | // 13 | // File: basicBlendShape.cpp 14 | // 15 | // Description: 16 | // Rudimentary implementation of a blendshape. 17 | // 18 | // Use this script to create a simple example. 19 | /* 20 | loadPlugin basicBlendShape; 21 | polyTorus -r 1 -sr 0.5 -tw 0 -sx 50 -sy 50 -ax 0 1 0 -cuv 1 -ch 1; 22 | polyTorus -r 1 -sr 0.5 -tw 0 -sx 50 -sy 50 -ax 0 1 0 -cuv 1 -ch 1; 23 | scale -r 0.5 1 1; 24 | makeIdentity -apply true -t 1 -r 1 -s 1 -n 0 -pn 1; 25 | select -r pTorus1; 26 | deformer -type "basicBlendShape"; 27 | blendShape -edit -t pTorus1 0 pTorus2 1.0 basicBlendShape1; 28 | */ 29 | #pragma once 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | 36 | class basicBlendShape : public MPxBlendShape { 37 | public: 38 | static void* creator(); 39 | static MStatus initialize(); 40 | 41 | // Deformation function 42 | // 43 | virtual MStatus deformData( 44 | MDataBlock& block, 45 | MDataHandle geomData, 46 | unsigned int groupId, 47 | const MMatrix& mat, 48 | unsigned int multiIndex 49 | ); 50 | 51 | static MTypeId id; 52 | }; 53 | 54 | -------------------------------------------------------------------------------- /src/maya/include/simplex_mayaNode.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Blur Studio 3 | 4 | This file is part of Simplex. 5 | 6 | Simplex is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Simplex is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Simplex. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "simplex.h" 33 | 34 | class simplex_maya : public MPxNode 35 | { 36 | public: 37 | simplex_maya(); 38 | virtual ~simplex_maya(); 39 | 40 | virtual MStatus compute( const MPlug& plug, MDataBlock& data ); 41 | virtual MStatus preEvaluation(const MDGContext& context, const MEvaluationNode& evaluationNode); 42 | virtual MStatus setDependentsDirty(const MPlug& plug, MPlugArray& plugArray); 43 | static void* creator(); 44 | static MStatus initialize(); 45 | 46 | public: 47 | static MObject aSliders; 48 | static MObject aWeights; 49 | static MObject aDefinition; 50 | static MObject aMinorUpdate; 51 | static MObject aExactSolve; 52 | 53 | static MTypeId id; 54 | 55 | 56 | private: 57 | simplex::Simplex * sPointer; 58 | std::vector cache; 59 | bool simplexIsValid = false; 60 | bool cacheIsValid = false; 61 | bool jsErrorReported = false; 62 | }; 63 | 64 | -------------------------------------------------------------------------------- /src/maya/meson.build: -------------------------------------------------------------------------------- 1 | maya_dep = dependency('maya') 2 | maya_name_suffix = maya_dep.get_variable('name_suffix') 3 | maya_version = maya_dep.get_variable('maya_version') 4 | 5 | simplex_maya_files = files([ 6 | 'src/basicBlendShape.cpp', 7 | 'src/pluginMain.cpp', 8 | 'src/simplex_mayaNode.cpp', 9 | ]) 10 | 11 | fs = import('fs') 12 | if fs.is_file('src/version.h') 13 | message('Using existing version.h') 14 | else 15 | git = find_program('git', native: true, required: true) 16 | version_h = vcs_tag( 17 | command: [git, 'describe', '--tags', '--always', '--match', 'v[0-9]*', '--dirty=+'], 18 | fallback: 'v0.0.1', 19 | input: 'src/version.h.in', 20 | output: 'version.h', 21 | ) 22 | simplex_maya_files = simplex_maya_files + version_h 23 | endif 24 | 25 | simplex_maya_inc = include_directories(['include']) 26 | rapidjson_dep = dependency('rapidjson') 27 | 28 | simplex_maya = shared_library( 29 | 'simplex_maya', 30 | simplex_maya_files, 31 | install: true, 32 | install_dir : meson.global_source_root() / 'output_Maya' + maya_version, 33 | include_directories : simplex_maya_inc, 34 | dependencies : [maya_dep, simplexlib_dep], 35 | name_prefix : '', 36 | name_suffix : maya_name_suffix, 37 | ) 38 | -------------------------------------------------------------------------------- /src/maya/src/basicBlendShape.cpp: -------------------------------------------------------------------------------- 1 | //- 2 | // ========================================================================== 3 | // Copyright 2015 Autodesk, Inc. All rights reserved. 4 | // 5 | // Use of this software is subject to the terms of the Autodesk 6 | // license agreement provided at the time of installation or download, 7 | // or which otherwise accompanies this software in either electronic 8 | // or hard copy form. 9 | // ========================================================================== 10 | //+ 11 | 12 | // 13 | // File: basicBlendShape.cpp 14 | // 15 | // Description: 16 | // Rudimentary implementation of a blendshape. 17 | // 18 | // Use this script to create a simple example. 19 | /* 20 | loadPlugin basicBlendShape; 21 | polyTorus -r 1 -sr 0.5 -tw 0 -sx 50 -sy 50 -ax 0 1 0 -cuv 1 -ch 1; 22 | polyTorus -r 1 -sr 0.5 -tw 0 -sx 50 -sy 50 -ax 0 1 0 -cuv 1 -ch 1; 23 | scale -r 0.5 1 1; 24 | makeIdentity -apply true -t 1 -r 1 -s 1 -n 0 -pn 1; 25 | select -r pTorus1; 26 | deformer -type "basicBlendShape"; 27 | blendShape -edit -t pTorus1 0 pTorus2 1.0 basicBlendShape1; 28 | */ 29 | 30 | #include "basicBlendShape.h" 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | #include 38 | #include 39 | 40 | 41 | MTypeId basicBlendShape::id(0x00122706); 42 | 43 | void* basicBlendShape::creator() { 44 | return new basicBlendShape(); 45 | } 46 | 47 | MStatus basicBlendShape::initialize() { 48 | return MStatus::kSuccess; 49 | } 50 | 51 | 52 | MStatus 53 | basicBlendShape::deformData(MDataBlock& block, 54 | MDataHandle geomData, 55 | unsigned int /*groupId*/, 56 | const MMatrix& /*m*/, 57 | unsigned int multiIndex) 58 | // 59 | // Method: deform 60 | // 61 | // Description: Deforms the point with a simple smooth skinning algorithm 62 | // 63 | // Arguments: 64 | // block : the datablock of the node 65 | // geomData : a handle to the geometry to be deformed 66 | // groupId : the group ID of the geometry to deform 67 | // m : matrix to transform the point into world space 68 | // multiIndex : the index of the geometry that we are deforming 69 | // 70 | // 71 | { 72 | MStatus returnStatus; 73 | 74 | // get the weights 75 | // 76 | MArrayDataHandle weightMH = block.inputArrayValue(weight); 77 | unsigned int numWeights = weightMH.elementCount(); 78 | MFloatArray weights; 79 | for (unsigned int w=0; w. 18 | */ 19 | 20 | #include "simplex_mayaNode.h" 21 | #include "basicBlendShape.h" 22 | #include "version.h" 23 | #include 24 | #include 25 | #include 26 | 27 | MStatus initializePlugin(MObject obj) 28 | { 29 | MStatus status; 30 | MFnPlugin plugin(obj, "Blur Studio", VERSION_STRING, "Any"); 31 | 32 | status = plugin.registerNode( 33 | "simplex_maya", 34 | simplex_maya::id, 35 | &simplex_maya::creator, 36 | &simplex_maya::initialize 37 | ); 38 | 39 | if (!status) { 40 | status.perror("registerNode simplex_maya"); 41 | return status; 42 | } 43 | 44 | status = plugin.registerNode( 45 | "basicBlendShape", 46 | basicBlendShape::id, 47 | &basicBlendShape::creator, 48 | &basicBlendShape::initialize, 49 | MPxNode::kBlendShape 50 | ); 51 | 52 | if (!status) { 53 | status.perror("registerNode basicBlendShape"); 54 | return status; 55 | } 56 | return status; 57 | } 58 | 59 | MStatus uninitializePlugin(MObject obj) 60 | { 61 | MStatus status; 62 | MFnPlugin plugin(obj); 63 | 64 | status = plugin.deregisterNode(simplex_maya::id); 65 | if (!status) { 66 | status.perror("deregisterNode simplex_maya"); 67 | return status; 68 | } 69 | 70 | status = plugin.deregisterNode(basicBlendShape::id); 71 | if (!status) { 72 | status.perror("deregisterNode basicBlendShape"); 73 | return status; 74 | } 75 | 76 | return status; 77 | } 78 | 79 | -------------------------------------------------------------------------------- /src/maya/src/simplex_mayaNode.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Blur Studio 3 | 4 | This file is part of Simplex. 5 | 6 | Simplex is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Simplex is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Simplex. If not, see . 18 | */ 19 | 20 | #include "simplex_mayaNode.h" 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #define CHECKSTAT(s) if (!(s)) { (s).perror("attributeAffects"); return (s);} 29 | 30 | // For linux and apple 31 | typedef unsigned int UINT; 32 | 33 | MTypeId simplex_maya::id(0x001226C8); 34 | 35 | // Attribute list 36 | MObject simplex_maya::aSliders; 37 | MObject simplex_maya::aWeights; 38 | MObject simplex_maya::aDefinition; 39 | MObject simplex_maya::aMinorUpdate; 40 | MObject simplex_maya::aExactSolve; 41 | 42 | 43 | simplex_maya::simplex_maya() { 44 | this->sPointer = new simplex::Simplex(); 45 | } 46 | simplex_maya::~simplex_maya() { 47 | delete this->sPointer; 48 | } 49 | 50 | MStatus simplex_maya::compute(const MPlug& plug, MDataBlock& data) { 51 | MStatus status; 52 | if( plug == aWeights ) { 53 | MArrayDataHandle inputData = data.inputArrayValue(aSliders, &status); 54 | CHECKSTAT(status); 55 | 56 | std::vector inVec; 57 | inVec.resize(inputData.elementCount()); 58 | 59 | // Read the input value from the handle. 60 | for (UINT physIdx = 0; physIdx < inputData.elementCount(); ++physIdx){ 61 | inputData.jumpToArrayElement(physIdx); 62 | auto valueHandle = inputData.inputValue(&status); 63 | CHECKSTAT(status); 64 | UINT trueIdx = inputData.elementIndex(); 65 | if (trueIdx >= inVec.size()){ 66 | inVec.resize(trueIdx+1); 67 | } 68 | inVec[trueIdx] = valueHandle.asDouble(); 69 | } 70 | 71 | if (!simplexIsValid || !this->sPointer->loaded){ 72 | MDataHandle jsonData = data.inputValue(aDefinition, &status); 73 | CHECKSTAT(status); 74 | MString ss = jsonData.asString(); 75 | std::string sss(ss.asChar(), ss.length()); 76 | this->sPointer->clear(); 77 | this->sPointer->parseJSON(sss); 78 | this->sPointer->build(); 79 | 80 | simplexIsValid = true; 81 | if (this->sPointer->hasParseError){ 82 | if (!this->jsErrorReported){ 83 | cerr << "JSON PARSE ERROR: " << this->sPointer->parseError << 84 | " \n At offset: " << std::to_string(this->sPointer->parseErrorOffset) << "\n"; 85 | this->jsErrorReported = true; 86 | } 87 | return MS::kFailure; 88 | } 89 | } 90 | 91 | if (this->sPointer->loaded){ 92 | MDataHandle exactSolve = data.inputValue(aExactSolve, &status); 93 | CHECKSTAT(status); 94 | this->sPointer->setExactSolve(exactSolve.asBool()); 95 | } 96 | 97 | inVec.resize(this->sPointer->sliderLen()); 98 | 99 | if (!cacheIsValid){ 100 | cacheIsValid = true; 101 | this->sPointer->clearValues(); 102 | cache = this->sPointer->solve(inVec); 103 | } 104 | 105 | // Set the output weights 106 | MArrayDataHandle outputArrayHandle = data.outputArrayValue(simplex_maya::aWeights, &status); 107 | CHECKSTAT(status); 108 | for (UINT physIdx = 0; physIdx < outputArrayHandle.elementCount(); ++physIdx){ 109 | outputArrayHandle.jumpToArrayElement(physIdx); 110 | UINT trueIdx = outputArrayHandle.elementIndex(); 111 | auto outHandle = outputArrayHandle.outputValue(&status); 112 | CHECKSTAT(status); 113 | if (trueIdx < cache.size()) { 114 | // because the cache size can change 115 | outHandle.setDouble(cache[trueIdx]); 116 | } 117 | } 118 | 119 | outputArrayHandle.setAllClean(); 120 | data.setClean(plug); 121 | } 122 | else { 123 | return MS::kUnknownParameter; 124 | } 125 | 126 | return MS::kSuccess; 127 | } 128 | 129 | MStatus simplex_maya::preEvaluation(const MDGContext& context, const MEvaluationNode& evaluationNode){ 130 | MStatus status; 131 | if (context.isNormal()){ 132 | if (evaluationNode.dirtyPlugExists(aDefinition, &status) && status){ 133 | this->simplexIsValid = false; 134 | this->cacheIsValid = false; 135 | } 136 | if (evaluationNode.dirtyPlugExists(aSliders, &status) && status){ 137 | this->cacheIsValid = false; 138 | } 139 | if (evaluationNode.dirtyPlugExists(aExactSolve, &status) && status){ 140 | this->cacheIsValid = false; 141 | } 142 | } 143 | else { 144 | return MS::kFailure; 145 | } 146 | return MS::kSuccess; 147 | } 148 | 149 | MStatus simplex_maya::setDependentsDirty(const MPlug& plug, MPlugArray& plugArray){ 150 | if (plug == aDefinition){ 151 | this->simplexIsValid = false; 152 | this->cacheIsValid = false; 153 | this->jsErrorReported = false; 154 | } 155 | if (plug == aSliders){ 156 | this->cacheIsValid = false; 157 | } 158 | if (plug == aExactSolve){ 159 | this->cacheIsValid = false; 160 | } 161 | return MPxNode::setDependentsDirty(plug, plugArray); 162 | } 163 | 164 | void* simplex_maya::creator(){ 165 | return new simplex_maya(); 166 | } 167 | 168 | MStatus simplex_maya::initialize(){ 169 | MFnNumericAttribute nAttr; 170 | MFnTypedAttribute tAttr; 171 | MFnStringData sData; 172 | MStatus status, status2; 173 | 174 | // Input sliders 175 | simplex_maya::aMinorUpdate = nAttr.create("minorUpdate", "mu", MFnNumericData::kBoolean, false, &status); 176 | CHECKSTAT(status); 177 | nAttr.setKeyable(false); 178 | nAttr.setReadable(true); 179 | nAttr.setWritable(true); 180 | status = simplex_maya::addAttribute(simplex_maya::aMinorUpdate); 181 | CHECKSTAT(status); 182 | 183 | simplex_maya::aExactSolve = nAttr.create("exactSolve", "es", MFnNumericData::kBoolean, true, &status); 184 | CHECKSTAT(status); 185 | nAttr.setKeyable(false); 186 | nAttr.setReadable(true); 187 | nAttr.setWritable(true); 188 | status = simplex_maya::addAttribute(simplex_maya::aExactSolve); 189 | CHECKSTAT(status); 190 | 191 | simplex_maya::aDefinition = tAttr.create("definition", "d", MFnData::kString, sData.create(&status2), &status); 192 | CHECKSTAT(status); 193 | CHECKSTAT(status2); 194 | tAttr.setStorable(true); 195 | tAttr.setKeyable(true); 196 | status = simplex_maya::addAttribute(simplex_maya::aDefinition); 197 | CHECKSTAT(status); 198 | 199 | // Input sliders 200 | simplex_maya::aSliders = nAttr.create("sliders", "s", MFnNumericData::kDouble, 0.0, &status); 201 | CHECKSTAT(status); 202 | nAttr.setKeyable(true); 203 | nAttr.setReadable(false); 204 | nAttr.setWritable(true); 205 | nAttr.setArray(true); 206 | nAttr.setUsesArrayDataBuilder(true); 207 | status = simplex_maya::addAttribute(simplex_maya::aSliders); 208 | CHECKSTAT(status); 209 | 210 | // Output weights 211 | simplex_maya::aWeights = nAttr.create("weights", "w", MFnNumericData::kDouble, 0.0, &status); 212 | CHECKSTAT(status); 213 | nAttr.setStorable(false); 214 | nAttr.setReadable(true); 215 | nAttr.setWritable(false); 216 | nAttr.setArray(true); 217 | nAttr.setUsesArrayDataBuilder(true); 218 | status = simplex_maya::addAttribute(simplex_maya::aWeights); 219 | CHECKSTAT(status); 220 | 221 | // Set data dependencies 222 | status = attributeAffects(aSliders, aWeights); 223 | CHECKSTAT(status); 224 | status = attributeAffects(aDefinition, aWeights); 225 | CHECKSTAT(status); 226 | 227 | return MS::kSuccess; 228 | } 229 | 230 | -------------------------------------------------------------------------------- /src/maya/src/version.h.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #define VERSION_STRING "@VCS_TAG@" 3 | 4 | -------------------------------------------------------------------------------- /src/python/meson.build: -------------------------------------------------------------------------------- 1 | py = import('python') 2 | py_inst = py.find_installation('python3') 3 | py_dep = py_inst.dependency() 4 | 5 | pysimplex_files = files(['pysimplex.cpp']) 6 | rapidjson_dep = dependency('rapidjson') 7 | 8 | lapi = '3.7' 9 | if get_option('buildtype') == 'debug' 10 | lapi = '' 11 | endif 12 | 13 | pysimplex = py_inst.extension_module( 14 | 'pysimplex', 15 | pysimplex_files, 16 | dependencies : simplexlib_dep, 17 | install: true, 18 | install_dir : meson.global_source_root() / 'output_Python', 19 | limited_api : lapi, 20 | ) 21 | -------------------------------------------------------------------------------- /src/python/pysimplex.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "simplex.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | typedef struct { 11 | PyObject_HEAD // No Semicolon for this Macro; 12 | PyObject *definition; 13 | simplex::Simplex *sPointer; 14 | } PySimplex; 15 | 16 | static void 17 | PySimplex_dealloc(PySimplex* self) { 18 | Py_XDECREF(self->definition); 19 | if (self->sPointer != NULL) 20 | delete self->sPointer; 21 | PyObject_Del(self); 22 | } 23 | 24 | static PyObject * 25 | PySimplex_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { 26 | 27 | PySimplex *self = PyObject_New(PySimplex, type); 28 | if (self != NULL) { 29 | self->definition = PyUnicode_FromString(""); 30 | if (self->definition == NULL) { 31 | Py_DECREF(self); 32 | return NULL; 33 | } 34 | self->sPointer = new simplex::Simplex(); 35 | } 36 | 37 | return (PyObject *)self; 38 | } 39 | 40 | static PyObject * 41 | PySimplex_getdefinition(PySimplex* self, void* closure){ 42 | Py_INCREF(self->definition); 43 | return self->definition; 44 | } 45 | 46 | static int 47 | PySimplex_setdefinition(PySimplex* self, PyObject* jsValue, void* closure){ 48 | if (jsValue == NULL || jsValue == Py_None){ 49 | jsValue = PyUnicode_FromString(""); 50 | } 51 | 52 | if (! PyUnicode_Check(jsValue)) { 53 | PyErr_SetString(PyExc_TypeError, "The simplex definition must be a string"); 54 | return -1; 55 | } 56 | 57 | PyObject *tmp = self->definition; 58 | Py_INCREF(jsValue); 59 | self->definition = jsValue; 60 | Py_DECREF(tmp); 61 | 62 | // Get the unicode as a wstring, and a wstring to string converter 63 | std::wstring simDefw = std::wstring(PyUnicode_AsWideCharString(self->definition, NULL)); 64 | std::wstring_convert> myconv; 65 | std::string simDef = myconv.to_bytes(simDefw); 66 | 67 | // set the definition in the solver 68 | self->sPointer->clear(); 69 | self->sPointer->parseJSON(simDef); 70 | self->sPointer->build(); 71 | 72 | return 0; 73 | } 74 | 75 | static PyObject * 76 | PySimplex_getexactsolve(PySimplex* self, void* closure){ 77 | if (self->sPointer->getExactSolve()){ 78 | Py_RETURN_TRUE; 79 | } 80 | Py_RETURN_FALSE; 81 | } 82 | 83 | static int 84 | PySimplex_setexactsolve(PySimplex* self, PyObject* exact, void* closure){ 85 | int truthy = PyObject_IsTrue(exact); 86 | if (truthy == -1){ 87 | PyErr_SetString(PyExc_TypeError, "The value passed cannot be cast to boolean"); 88 | return -1; 89 | } 90 | 91 | self->sPointer->setExactSolve((truthy == 1)); 92 | return 0; 93 | } 94 | 95 | static int 96 | PySimplex_init(PySimplex *self, PyObject *args, PyObject *kwds) { 97 | PyObject *jsValue=NULL, *tmp=NULL; 98 | 99 | char jsValueLiteral[] = "jsValue"; 100 | static char *kwlist[] = {jsValueLiteral, NULL}; 101 | 102 | if (! PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &jsValue)) 103 | return -1; 104 | 105 | return PySimplex_setdefinition(self, jsValue, NULL); 106 | } 107 | 108 | static PyObject * 109 | PySimplex_solve(PySimplex* self, PyObject* vec){ 110 | if (! PySequence_Check(vec)){ 111 | PyErr_SetString(PyExc_TypeError, "Input must be a list or tuple"); 112 | return NULL; 113 | } 114 | 115 | PyObject *item; 116 | std::vector stdVec, outVec; 117 | for (Py_ssize_t i=0; isPointer->clearValues(); 128 | outVec = self->sPointer->solve(stdVec); 129 | 130 | PyObject *out = PyList_New(outVec.size()); 131 | for (size_t i=0; i. 18 | */ 19 | 20 | #pragma once 21 | 22 | #include "enums.h" 23 | #include "shapeController.h" 24 | #include "rapidjson/document.h" 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | namespace simplex { 31 | 32 | class Progression; 33 | class Slider; 34 | class Simplex; 35 | 36 | typedef std::pair ComboPair; 37 | typedef std::vector ComboPairs; 38 | 39 | ComboSolve getSolveType(const rapidjson::Value &val); 40 | bool getSolvePairs(const rapidjson::Value &val, Simplex *simp, ComboPairs &state, bool &isFloater); 41 | 42 | bool solveState(const std::vector &vals, const std::vector &tars, ComboSolve solveType, bool exact, double &value); 43 | bool solveState(const ComboPairs &stateList, ComboSolve solveType, bool exact, double &value); 44 | 45 | class Combo : public ShapeController { 46 | private: 47 | bool isFloater; 48 | bool exact; 49 | ComboSolve solveType; 50 | protected: 51 | std::vector inverted; 52 | std::vector rectified; 53 | std::vector clamped; 54 | public: 55 | ComboPairs stateList; 56 | bool sliderType() const override { return false; } 57 | void setExact(bool e){exact = e;} 58 | Combo(const std::string &name, Progression* prog, size_t index, 59 | const ComboPairs &stateList, bool isFloater, ComboSolve solveType); 60 | void storeValue( 61 | const std::vector &values, 62 | const std::vector &posValues, 63 | const std::vector &clamped, 64 | const std::vector &inverses) override; 65 | static bool parseJSONv1(const rapidjson::Value &val, size_t index, Simplex *simp); 66 | static bool parseJSONv2(const rapidjson::Value &val, size_t index, Simplex *simp); 67 | static bool parseJSONv3(const rapidjson::Value &val, size_t index, Simplex *simp); 68 | }; 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/simplexlib/include/enums.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Blur Studio 3 | 4 | This file is part of Simplex. 5 | 6 | Simplex is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Simplex is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Simplex. If not, see . 18 | */ 19 | #pragma once 20 | 21 | namespace simplex { 22 | 23 | enum ProgType {linear, spline, splitSpline}; 24 | enum ComboSolve {min, allMul, extMul, mulAvgAll, mulAvgExt, None}; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/simplexlib/include/floater.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Blur Studio 3 | 4 | This file is part of Simplex. 5 | 6 | Simplex is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Simplex is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Simplex. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include "enums.h" 23 | #include "combo.h" 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | namespace simplex { 30 | 31 | class Slider; 32 | class Floater : public Combo { 33 | public: 34 | friend class TriSpace; // lets the trispace set the value for this guy 35 | Floater(const std::string &name, Progression* prog, size_t index, 36 | const std::vector> &stateList, bool isFloater) : 37 | Combo(name, prog, index, stateList, isFloater, ComboSolve::None) { 38 | } 39 | }; 40 | 41 | 42 | } 43 | 44 | -------------------------------------------------------------------------------- /src/simplexlib/include/progression.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Blur Studio 3 | 4 | This file is part of Simplex. 5 | 6 | Simplex is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Simplex is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Simplex. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include "enums.h" 23 | #include "shapeBase.h" 24 | #include "rapidjson/document.h" 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | 31 | namespace simplex { 32 | 33 | class Simplex; 34 | class Shape; 35 | 36 | typedef std::pair ProgPair; 37 | typedef std::vector ProgPairs; 38 | 39 | class Progression : public ShapeBase { 40 | private: 41 | ProgPairs pairs; 42 | ProgType interp; 43 | static size_t getInterval(double tVal, const std::vector ×, bool &outside); 44 | ProgPairs getSplineOutput(double tVal, double mul=1.0) const; 45 | ProgPairs getSplitSplineOutput(double tVal, double mul=1.0) const; 46 | ProgPairs getLinearOutput(double tVal, double mul=1.0) const; 47 | 48 | static ProgPairs getRawSplineOutput(const std::vector* > pairs, double tVal, double mul=1.0); 49 | static ProgPairs getRawLinearOutput(const std::vector* > pairs, double tVal, double mul=1.0); 50 | 51 | public: 52 | ProgPairs getOutput(double tVal, double mul=1.0) const; 53 | 54 | Progression(const std::string &name, const ProgPairs &pairs, ProgType interp); 55 | static bool parseJSONv1(const rapidjson::Value &val, size_t index, Simplex *simp); 56 | static bool parseJSONv2(const rapidjson::Value &val, size_t index, Simplex *simp); 57 | static bool parseJSONv3(const rapidjson::Value &val, size_t index, Simplex *simp); 58 | }; 59 | 60 | 61 | } 62 | 63 | -------------------------------------------------------------------------------- /src/simplexlib/include/shape.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Blur Studio 3 | 4 | This file is part of Simplex. 5 | 6 | Simplex is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Simplex is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Simplex. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include "shapeBase.h" 23 | #include "rapidjson/document.h" 24 | #include 25 | 26 | namespace simplex { 27 | 28 | class Simplex; 29 | 30 | class Shape : public ShapeBase { 31 | public: 32 | Shape(const std::string &name, size_t index): ShapeBase(name, index){} 33 | static bool parseJSONv1(const rapidjson::Value &val, size_t index, Simplex *simp); 34 | static bool parseJSONv2(const rapidjson::Value &val, size_t index, Simplex *simp); 35 | static bool parseJSONv3(const rapidjson::Value &val, size_t index, Simplex *simp); 36 | }; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/simplexlib/include/shapeBase.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Blur Studio 3 | 4 | This file is part of Simplex. 5 | 6 | Simplex is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Simplex is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Simplex. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include "enums.h" 23 | #include 24 | 25 | namespace simplex { 26 | 27 | class ShapeBase { 28 | protected: 29 | void *shapeRef; // pointer to whatever the user wants 30 | std::string name; 31 | size_t index; 32 | public: 33 | explicit ShapeBase(const std::string &name, size_t index): name(name), index(index), shapeRef(nullptr) {} 34 | explicit ShapeBase(const std::string &name): name(name), index(0u), shapeRef(nullptr) {} 35 | const std::string* getName() const {return &name;} 36 | const size_t getIndex() const { return index; } 37 | void setUserData(void *data) {shapeRef = data;} 38 | void* getUserData(){return shapeRef;} 39 | }; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/simplexlib/include/shapeController.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Blur Studio 3 | 4 | This file is part of Simplex. 5 | 6 | Simplex is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Simplex is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Simplex. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include "shapeBase.h" 23 | #include "rapidjson/document.h" 24 | 25 | #include 26 | #include 27 | 28 | namespace simplex { 29 | 30 | class Progression; 31 | 32 | class ShapeController : public ShapeBase { 33 | protected: 34 | bool enabled; 35 | double value; 36 | double multiplier; 37 | Progression* prog; 38 | public: 39 | ShapeController(const std::string &name, Progression* prog, size_t index): 40 | ShapeBase(name, index), enabled(true), value(0.0), multiplier(1.0), prog(prog) {} 41 | 42 | virtual bool sliderType() const { return true; } 43 | void clearValue(){value = 0.0; multiplier=1.0;} 44 | const double getValue() const { return value; } 45 | const double getMultiplier() const { return multiplier; } 46 | void setEnabled(bool enable){enabled = enable;} 47 | virtual void storeValue( 48 | const std::vector &values, 49 | const std::vector &posValues, 50 | const std::vector &clamped, 51 | const std::vector &inverses) = 0; 52 | void solve(std::vector &accumulator, double &maxAct) const; 53 | static bool getEnabled(const rapidjson::Value &val); 54 | }; 55 | 56 | 57 | } 58 | 59 | -------------------------------------------------------------------------------- /src/simplexlib/include/simplex.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Blur Studio 3 | 4 | This file is part of Simplex. 5 | 6 | Simplex is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Simplex is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Simplex. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include "shape.h" 23 | #include "progression.h" 24 | #include "slider.h" 25 | #include "combo.h" 26 | #include "floater.h" 27 | #include "trispace.h" 28 | #include "traversal.h" 29 | 30 | #include "rapidjson/document.h" 31 | 32 | #include 33 | #include 34 | 35 | namespace simplex { 36 | 37 | class Simplex { 38 | private: 39 | bool exactSolve; 40 | public: 41 | std::vector shapes; 42 | std::vector progs; 43 | std::vector sliders; 44 | std::vector combos; 45 | std::vector floaters; 46 | std::vector spaces; 47 | std::vector traversals; 48 | 49 | bool built; 50 | bool loaded; 51 | bool hasParseError; 52 | 53 | std::string parseError; 54 | size_t parseErrorOffset; 55 | const size_t sliderLen() const { return sliders.size(); } 56 | 57 | Simplex():exactSolve(true), built(false), loaded(false), hasParseError(false), parseErrorOffset(0) {}; 58 | explicit Simplex(const std::string &json); 59 | explicit Simplex(const char* json); 60 | 61 | void clearValues(); 62 | void clear(); 63 | bool parseJSON(const std::string &json); 64 | bool parseJSONversion(const rapidjson::Document &d, unsigned version); 65 | void build(); 66 | 67 | void setExactSolve(bool exact); 68 | bool getExactSolve() { return exactSolve; } 69 | 70 | std::vector solve(const std::vector &vec); 71 | }; 72 | 73 | } // end namespace simplex 74 | -------------------------------------------------------------------------------- /src/simplexlib/include/slider.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Blur Studio 3 | 4 | This file is part of Simplex. 5 | 6 | Simplex is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Simplex is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Simplex. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include "shapeController.h" 23 | #include "rapidjson/document.h" 24 | 25 | #include 26 | #include 27 | 28 | namespace simplex { 29 | 30 | class Simplex; 31 | class Progression; 32 | 33 | class Slider : public ShapeController { 34 | public: 35 | Slider(const std::string &name, Progression* prog, size_t index) : ShapeController(name, prog, index){} 36 | void storeValue( 37 | const std::vector &values, 38 | const std::vector &posValues, 39 | const std::vector &clamped, 40 | const std::vector &inverses){ 41 | if (!enabled) return; 42 | this->value = values[this->index]; 43 | } 44 | 45 | static bool parseJSONv1(const rapidjson::Value &val, size_t index, Simplex *simp); 46 | static bool parseJSONv2(const rapidjson::Value &val, size_t index, Simplex *simp); 47 | static bool parseJSONv3(const rapidjson::Value &val, size_t index, Simplex *simp); 48 | }; 49 | 50 | } 51 | 52 | -------------------------------------------------------------------------------- /src/simplexlib/include/traversal.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Blur Studio 3 | 4 | This file is part of Simplex. 5 | 6 | Simplex is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Simplex is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Simplex. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include "combo.h" 23 | #include "shapeController.h" 24 | #include "rapidjson/document.h" 25 | 26 | #include 27 | #include 28 | 29 | namespace simplex { 30 | 31 | class Progression; 32 | class Simplex; 33 | class Slider; 34 | 35 | 36 | class Traversal : public ShapeController { 37 | private: 38 | ComboPairs progStartState; 39 | ComboPairs progDeltaState; 40 | ComboPairs multState; 41 | ComboSolve solveType; 42 | bool exact; 43 | public: 44 | /* 45 | Traversal(const std::string &name, Progression* prog, size_t index, 46 | ShapeController* progressCtrl, ShapeController* multiplierCtrl, bool valueFlip, bool multiplierFlip): 47 | ShapeController(name, prog, index), progressCtrl(progressCtrl), multiplierCtrl(multiplierCtrl), 48 | valueFlip(valueFlip), multiplierFlip(multiplierFlip) {} 49 | */ 50 | 51 | Traversal(const std::string &name, Progression* prog, size_t index, ShapeController* progressCtrl, ShapeController* multiplierCtrl, bool valueFlip, bool multiplierFlip); 52 | Traversal(const std::string &name, Progression* prog, size_t index, const ComboPairs &startState, const ComboPairs &endState, ComboSolve solveType); 53 | 54 | void storeValue( 55 | const std::vector &values, 56 | const std::vector &posValues, 57 | const std::vector &clamped, 58 | const std::vector &inverses); 59 | static bool parseJSONv1(const rapidjson::Value &val, size_t index, Simplex *simp); 60 | static bool parseJSONv2(const rapidjson::Value &val, size_t index, Simplex *simp); 61 | static bool parseJSONv3(const rapidjson::Value &val, size_t index, Simplex *simp); 62 | }; 63 | 64 | 65 | } 66 | 67 | -------------------------------------------------------------------------------- /src/simplexlib/include/trispace.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Blur Studio 3 | 4 | This file is part of Simplex. 5 | 6 | Simplex is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Simplex is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Simplex. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include "enums.h" 23 | #include "utils.h" 24 | #include 25 | #include 26 | 27 | namespace simplex { 28 | 29 | class Floater; 30 | 31 | class TriSpace { 32 | private: 33 | // Correlates the auto-generated simplex with the user-created simplices 34 | // resulting from the splitting procedure 35 | std::unordered_map, std::vector>, vectorHash> simplexMap; 36 | std::vector> userPoints; 37 | std::vector> overrideSimplices; 38 | 39 | std::vector floaters; 40 | std::vector barycentric(const std::vector> &simplex, const std::vector &p) const; 41 | //static std::vector> simplexToCorners(const std::vector &simplex); 42 | std::vector pointToSimp(const std::vector &pt); 43 | std::vector> pointToAdjSimp(const std::vector &pt, double eps=0.01); 44 | void triangulate(); // convenience function for separating the data access from the actual math 45 | // Code to split a list of simplices by a list of points, only used in triangulate() 46 | std::vector>> splitSimps(const std::vector> &pts, const std::vector> &simps) const; 47 | std::vector > simplexToCorners(const std::vector &simplex) const; 48 | void rec(const std::vector &point, const std::vector &oVals, const std::vector &simp, std::vector > &out, double eps) const; 49 | 50 | // break down the given simplex encoding to a list of corner points for the barycentric solver and 51 | // a correlation of the point index to the floater index (or size_t_MAX if invalid) 52 | void userSimplexToCorners( 53 | const std::vector &simplex, 54 | const std::vector &original, 55 | std::vector> &out, 56 | std::vector &floaterCorners 57 | ) const; 58 | 59 | public: 60 | // Take the non-related floaters and group them by shared span and orthant 61 | static std::vector buildSpaces(std::vector &floaters); 62 | TriSpace(std::vector floaters); 63 | void storeValue( 64 | const std::vector &values, 65 | const std::vector &posValues, 66 | const std::vector &clamped, 67 | const std::vector &inverses); 68 | }; 69 | 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/simplexlib/include/utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Blur Studio 3 | 4 | This file is part of Simplex. 5 | 6 | Simplex is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Simplex is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Simplex. If not, see . 18 | */ 19 | 20 | #pragma once 21 | 22 | #include 23 | #include // for hash 24 | #include "rapidjson/document.h" 25 | #include "math.h" 26 | 27 | #define CHECK_JSON_STRING(jsIt, jsName, jsVal) \ 28 | auto (jsIt) = (jsVal).FindMember(jsName); \ 29 | if ((jsIt) == (jsVal).MemberEnd()) return false; \ 30 | if (!(jsIt)->value.IsString()) return false; 31 | 32 | #define CHECK_JSON_BOOL(jsIt, jsName, jsVal) \ 33 | auto (jsIt) = (jsVal).FindMember(jsName); \ 34 | if ((jsIt) == (jsVal).MemberEnd()) return false; \ 35 | if (!(jsIt)->value.IsBool()) return false; 36 | 37 | #define CHECK_JSON_INT(jsIt, jsName, jsVal) \ 38 | auto (jsIt) = (jsVal).FindMember(jsName); \ 39 | if ((jsIt) == (jsVal).MemberEnd()) return false; \ 40 | if (!(jsIt)->value.IsInt()) return false; 41 | 42 | #define CHECK_JSON_ARRAY(jsIt, jsName, jsVal) \ 43 | auto (jsIt) = (jsVal).FindMember(jsName); \ 44 | if ((jsIt) == (jsVal).MemberEnd()) return false; \ 45 | if (!(jsIt)->value.IsArray()) return false; 46 | 47 | 48 | namespace simplex { 49 | const double EPS = 1e-6; 50 | const int ULPS = 4; 51 | const double MAXVAL = 1.0; // max clamping value 52 | 53 | inline bool floatEQ(const double A, const double B, const double eps) { 54 | // from https://randomascii.wordpress.com/2012/01/11/tricks-with-the-floating-point-format 55 | // Check if the numbers are really close -- needed 56 | // when comparing numbers near zero. 57 | double absDiff = fabs(A - B); 58 | if (absDiff <= eps) 59 | return true; 60 | return false; 61 | } 62 | 63 | inline bool isZero(const double a) { return floatEQ(a, 0.0, EPS); } 64 | inline bool isPositive(const double a) { return a > -EPS; } 65 | inline bool isNegative(const double a) { return a < EPS; } 66 | 67 | void rectify( 68 | const std::vector &rawVec, 69 | std::vector &values, 70 | std::vector &clamped, 71 | std::vector &inverses 72 | ); 73 | 74 | double doSoftMin(double X, double Y); 75 | 76 | template 77 | struct vectorHash { 78 | size_t operator()(const std::vector & val) const { 79 | // this is the python tuple hashing algorithm 80 | size_t value = 0x345678; 81 | size_t vSize = val.size(); 82 | std::hash iHash; 83 | for (auto i = val.begin(); i != val.end(); ++i) { 84 | value = (1000003 * value) ^ iHash(*i); 85 | value = value ^ vSize; 86 | } 87 | return value; 88 | } 89 | }; 90 | 91 | } // namespace simplex 92 | -------------------------------------------------------------------------------- /src/simplexlib/meson.build: -------------------------------------------------------------------------------- 1 | simplexlib_files = files([ 2 | 'src/progression.cpp', 3 | 'src/shape.cpp', 4 | 'src/simplex.cpp', 5 | 'src/shapeController.cpp', 6 | 'src/slider.cpp', 7 | 'src/utils.cpp', 8 | 'src/trispace.cpp', 9 | 'src/combo.cpp', 10 | 'src/traversal.cpp', 11 | ]) 12 | 13 | rapidjson_dep = dependency('rapidjson') 14 | eigen_dep = dependency('eigen3') 15 | simplexlib_inc = include_directories(['include']) 16 | 17 | simplexlib_dep = declare_dependency( 18 | include_directories : simplexlib_inc, 19 | sources : simplexlib_files, 20 | dependencies : [eigen_dep, rapidjson_dep], 21 | ) 22 | -------------------------------------------------------------------------------- /src/simplexlib/src/combo.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Blur Studio 3 | 4 | This file is part of Simplex. 5 | 6 | Simplex is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Simplex is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Simplex. If not, see . 18 | */ 19 | 20 | #include "simplex.h" 21 | 22 | #include "rapidjson/rapidjson.h" 23 | #include "math.h" 24 | 25 | #include // for sort 26 | #include // for numeric_limits 27 | 28 | using namespace simplex; 29 | 30 | 31 | bool simplex::solveState(const std::vector &vals, const std::vector &tars, ComboSolve solveType, bool exact, double &value) { 32 | double mn, mx, allMul = 1.0, allSum = 0.0; 33 | mn = std::numeric_limits::infinity(); 34 | mx = -mn; 35 | 36 | for (size_t i = 0; i < vals.size(); ++i){ 37 | double val = vals[i]; 38 | double tar = tars[i]; 39 | 40 | // Specifically this instead of isNegative() 41 | // because isNegative returns true for 0.0 42 | bool valNeg = !isPositive(val); 43 | bool tarNeg = !isPositive(tar); 44 | 45 | if (valNeg != tarNeg) return false; 46 | if (valNeg) val = -val; 47 | 48 | val = (val > MAXVAL) ? MAXVAL : val; 49 | allMul *= val; 50 | allSum += val; 51 | if (val < mn) mn = val; 52 | if (val > mx) mx = val; 53 | } 54 | 55 | switch (solveType) { 56 | case ComboSolve::min: 57 | value = (exact) ? mn : doSoftMin(mx, mn); 58 | break; 59 | case ComboSolve::allMul: 60 | value = allMul; 61 | break; 62 | case ComboSolve::extMul: 63 | value = mx * mn; 64 | break; 65 | case ComboSolve::mulAvgExt: 66 | if (isZero(mx + mn)) 67 | value = 0.0; 68 | else 69 | value = 2 * (mx * mn) / (mx + mn); 70 | break; 71 | case ComboSolve::mulAvgAll: 72 | if (isZero(allSum)) 73 | value = 0.0; 74 | else 75 | value = vals.size() * allMul / allSum; 76 | break; 77 | case ComboSolve::None: 78 | value = (exact) ? mn : doSoftMin(mx, mn); 79 | break; 80 | default: 81 | value = (exact) ? mn : doSoftMin(mx, mn); 82 | } 83 | return true; 84 | } 85 | 86 | bool simplex::solveState(const ComboPairs &stateList, ComboSolve solveType, bool exact, double &value) { 87 | std::vector vals, tars; 88 | for (auto sit = stateList.begin(); sit != stateList.end(); ++sit) { 89 | //for (const auto &state: stateList){ 90 | const auto &state = *sit; 91 | vals.push_back(state.first->getValue()); 92 | tars.push_back(state.second); 93 | } 94 | return simplex::solveState(vals, tars, solveType, exact, value); 95 | } 96 | 97 | ComboSolve simplex::getSolveType(const rapidjson::Value &val) { 98 | ComboSolve solveType = ComboSolve::None; 99 | auto solveIt = val.FindMember("solveType"); 100 | if (solveIt != val.MemberEnd()) { 101 | if (!solveIt->value.IsString()) { 102 | solveType = ComboSolve::None; 103 | } 104 | else { 105 | std::string solve(solveIt->value.GetString()); 106 | if (solve == "min") 107 | solveType = ComboSolve::min; 108 | else if (solve == "allMul") 109 | solveType = ComboSolve::allMul; 110 | else if (solve == "extMul") 111 | solveType = ComboSolve::extMul; 112 | else if (solve == "mulAvgExt") 113 | solveType = ComboSolve::mulAvgExt; 114 | else if (solve == "mulAvgAll") 115 | solveType = ComboSolve::mulAvgAll; 116 | else if (solve == "None") 117 | solveType = ComboSolve::None; 118 | else 119 | solveType = ComboSolve::None; 120 | } 121 | } 122 | return solveType; 123 | } 124 | 125 | bool simplex::getSolvePairs(const rapidjson::Value &val, Simplex *simp, ComboPairs &state, bool &isFloater) { 126 | for (auto it = val.Begin(); it != val.End(); ++it) { 127 | auto &ival = *it; 128 | if (!ival.IsArray()) return false; 129 | if (!ival[0].IsInt()) return false; 130 | if (!ival[1].IsDouble()) return false; 131 | 132 | size_t slidx = (size_t)ival[0].GetInt(); 133 | double slval = (double)ival[1].GetDouble(); 134 | if (!floatEQ(fabs(slval), 1.0, EPS) && !isZero(slval)) 135 | isFloater = true; 136 | if (slidx >= simp->sliders.size()) return false; 137 | state.push_back(std::make_pair(&simp->sliders[slidx], slval)); 138 | } 139 | return true; 140 | } 141 | 142 | Combo::Combo(const std::string &name, Progression* prog, size_t index, 143 | const ComboPairs &stateList, bool isFloater, ComboSolve solveType) : 144 | ShapeController(name, prog, index), stateList(stateList), isFloater(isFloater), solveType(solveType), exact(true) { 145 | std::sort(this->stateList.begin(), this->stateList.end(), 146 | [](const ComboPair &lhs, const ComboPair &rhs) { 147 | return lhs.first->getIndex() < rhs.first->getIndex(); 148 | } 149 | ); 150 | std::vector rawVec; 151 | for (auto pit = this->stateList.begin(); pit != this->stateList.end(); ++pit) { 152 | //for (auto &p : this->stateList) { 153 | auto &p = *pit; 154 | rawVec.push_back(p.second); 155 | } 156 | rectify(rawVec, rectified, clamped, inverted); 157 | } 158 | 159 | void Combo::storeValue( 160 | const std::vector &values, 161 | const std::vector &posValues, 162 | const std::vector &clamped, 163 | const std::vector &inverses) { 164 | 165 | if (!enabled) return; 166 | if (isFloater) return; 167 | solveState(stateList, solveType, exact, value); 168 | } 169 | 170 | bool Combo::parseJSONv1(const rapidjson::Value &val, size_t index, Simplex *simp) { 171 | if (!val[0u].IsString()) return false; 172 | if (!val[1].IsInt()) return false; 173 | const rapidjson::Value &jcstate = val[2]; 174 | ComboPairs state; 175 | 176 | bool isFloater = false; 177 | rapidjson::SizeType j; 178 | for (j = 0; j < jcstate.Size(); ++j) { 179 | if (!jcstate[j][0u].IsInt()) return false; 180 | if (!jcstate[j][1].IsNumber()) return false; 181 | size_t slidx = (size_t)jcstate[j][0u].GetInt(); 182 | double slval = jcstate[j][1].GetDouble(); 183 | 184 | if (!floatEQ(fabs(slval), 1.0, EPS) && !isZero(slval)) 185 | isFloater = true; 186 | 187 | if (slidx >= simp->sliders.size()) return false; 188 | state.push_back(std::make_pair(&simp->sliders[slidx], slval)); 189 | } 190 | 191 | std::string name(val[0u].GetString()); 192 | size_t pidx = (size_t)val[1].GetInt(); 193 | if (pidx >= simp->progs.size()) return false; 194 | if (isFloater) 195 | simp->floaters.push_back(Floater(name, &simp->progs[pidx], index, state, isFloater)); 196 | simp->combos.push_back(Combo(name, &simp->progs[pidx], index, state, isFloater, ComboSolve::None)); 197 | return true; 198 | } 199 | 200 | bool Combo::parseJSONv2(const rapidjson::Value &val, size_t index, Simplex *simp) { 201 | if (!val.IsObject()) return false; 202 | CHECK_JSON_STRING(nameIt, "name", val); 203 | CHECK_JSON_INT(progIt, "prog", val); 204 | CHECK_JSON_ARRAY(pairsIt, "pairs", val); 205 | 206 | std::string name(nameIt->value.GetString()); 207 | 208 | ComboSolve solveType = getSolveType(val); 209 | ComboPairs state; 210 | bool isFloater = false; 211 | auto &pairsVal = pairsIt->value; 212 | if (!getSolvePairs(pairsVal, simp, state, isFloater)) return false; 213 | 214 | size_t pidx = (size_t)progIt->value.GetInt(); 215 | if (pidx >= simp->progs.size()) return false; 216 | 217 | bool enabled = getEnabled(val); 218 | 219 | if (isFloater) { 220 | simp->floaters.push_back(Floater(name, &simp->progs[pidx], index, state, isFloater)); 221 | simp->floaters.back().setEnabled(enabled); 222 | } 223 | // because a floater is still considered a combo 224 | // I need to add it to the list for indexing purposes 225 | 226 | simp->combos.push_back(Combo(name, &simp->progs[pidx], index, state, isFloater, solveType)); 227 | simp->combos.back().setEnabled(enabled); 228 | return true; 229 | } 230 | 231 | bool Combo::parseJSONv3(const rapidjson::Value &val, size_t index, Simplex *simp) { 232 | return parseJSONv2(val, index, simp); 233 | } 234 | 235 | -------------------------------------------------------------------------------- /src/simplexlib/src/shape.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Blur Studio 3 | 4 | This file is part of Simplex. 5 | 6 | Simplex is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Simplex is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Simplex. If not, see . 18 | */ 19 | #include "simplex.h" 20 | 21 | #include "rapidjson/document.h" 22 | 23 | #include 24 | #include 25 | 26 | using namespace simplex; 27 | 28 | bool Shape::parseJSONv1(const rapidjson::Value &val, size_t index, Simplex *simp){ 29 | if(!val.IsString()) return false; 30 | std::string name(val.GetString()); 31 | simp->shapes.push_back(Shape(name, index)); 32 | return true; 33 | } 34 | 35 | bool Shape::parseJSONv2(const rapidjson::Value &val, size_t index, Simplex *simp){ 36 | if (!val.IsObject()) return false; 37 | 38 | CHECK_JSON_STRING(nameIt, "name", val); 39 | 40 | std::string name(nameIt->value.GetString()); 41 | simp->shapes.push_back(Shape(name, index)); 42 | return true; 43 | } 44 | 45 | bool Shape::parseJSONv3(const rapidjson::Value &val, size_t index, Simplex *simp){ 46 | return parseJSONv2(val, index, simp); 47 | } 48 | 49 | -------------------------------------------------------------------------------- /src/simplexlib/src/shapeController.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Blur Studio 3 | 4 | This file is part of Simplex. 5 | 6 | Simplex is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Simplex is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Simplex. If not, see . 18 | */ 19 | #include "simplex.h" 20 | 21 | #include 22 | #include 23 | #include "math.h" 24 | 25 | using namespace simplex; 26 | 27 | void ShapeController::solve(std::vector &accumulator, double &maxAct) const { 28 | double vm = fabs(value * multiplier); 29 | if (vm > maxAct) maxAct = vm; 30 | 31 | ProgPairs shapeVals = prog->getOutput(value, multiplier); 32 | for (auto sit=shapeVals.begin(); sit!=shapeVals.end(); ++sit){ 33 | //for (const auto &svp: shapeVals){ 34 | const auto &svp = *sit; 35 | accumulator[svp.first->getIndex()] += svp.second; 36 | } 37 | } 38 | 39 | bool ShapeController::getEnabled(const rapidjson::Value &val) { 40 | auto enIt = val.FindMember("enabled"); 41 | if (enIt != val.MemberEnd()) { 42 | if (enIt->value.IsBool()) { 43 | return enIt->value.GetBool(); 44 | } 45 | } 46 | return true; 47 | } 48 | -------------------------------------------------------------------------------- /src/simplexlib/src/simplex.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Blur Studio 3 | 4 | This file is part of Simplex. 5 | 6 | Simplex is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Simplex is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Simplex. If not, see . 18 | */ 19 | #include "utils.h" 20 | #include "simplex.h" 21 | 22 | #include "rapidjson/error/en.h" 23 | #include "rapidjson/rapidjson.h" 24 | 25 | using namespace simplex; 26 | 27 | void Simplex::clearValues(){ 28 | for (auto xit = sliders.begin(); xit != sliders.end(); ++xit) { xit->clearValue(); } 29 | for (auto xit = combos.begin(); xit != combos.end(); ++xit) { xit->clearValue(); } 30 | for (auto xit = floaters.begin(); xit != floaters.end(); ++xit) { xit->clearValue(); } 31 | for (auto xit = traversals.begin(); xit != traversals.end(); ++xit) { xit->clearValue(); } 32 | 33 | //for (auto &x : sliders) x.clearValue(); 34 | //for (auto &x : combos) x.clearValue(); 35 | //for (auto &x : floaters) x.clearValue(); 36 | //for (auto &x : traversals) x.clearValue(); 37 | } 38 | 39 | void Simplex::setExactSolve(bool exact){ 40 | for (auto xit = combos.begin(); xit != combos.end(); ++xit) { xit->setExact(exact); } 41 | //for (auto &x : combos) x.setExact(exact); 42 | } 43 | 44 | std::vector Simplex::solve(const std::vector &vec){ 45 | // The solver should simply follow this pattern: 46 | // Ask each top level thing to store its value 47 | // Ask each shape controller for its contribution to the output 48 | std::vector posVec, clamped, output; 49 | std::vector inverses; 50 | rectify(vec, posVec, clamped, inverses); 51 | 52 | 53 | for (auto xit = sliders.begin(); xit != sliders.end(); ++xit){ 54 | xit->storeValue(vec, posVec, clamped, inverses); 55 | } 56 | for (auto xit = combos.begin(); xit != combos.end(); ++xit){ 57 | xit->storeValue(vec, posVec, clamped, inverses); 58 | } 59 | for (auto xit = spaces.begin(); xit != spaces.end(); ++xit){ 60 | xit->storeValue(vec, posVec, clamped, inverses); 61 | } 62 | for (auto xit = traversals.begin(); xit != traversals.end(); ++xit){ 63 | xit->storeValue(vec, posVec, clamped, inverses); 64 | } 65 | 66 | /* 67 | for (auto &x : sliders) 68 | x.storeValue(vec, posVec, clamped, inverses); 69 | for (auto &x : combos) 70 | x.storeValue(vec, posVec, clamped, inverses); 71 | for (auto &x : spaces) 72 | x.storeValue(vec, posVec, clamped, inverses); 73 | for (auto &x : traversals) 74 | x.storeValue(vec, posVec, clamped, inverses); 75 | */ 76 | 77 | output.resize(shapes.size()); 78 | double maxAct = 0.0; 79 | 80 | for (auto xit = sliders.begin(); xit != sliders.end(); ++xit) 81 | xit->solve(output, maxAct); 82 | for (auto xit = combos.begin(); xit != combos.end(); ++xit) 83 | xit->solve(output, maxAct); 84 | for (auto xit = floaters.begin(); xit != floaters.end(); ++xit) 85 | xit->solve(output, maxAct); 86 | for (auto xit = traversals.begin(); xit != traversals.end(); ++xit) 87 | xit->solve(output, maxAct); 88 | 89 | /* 90 | for (auto &x : sliders) 91 | x.solve(output, maxAct); 92 | for (auto &x : combos) 93 | x.solve(output, maxAct); 94 | for (auto &x : floaters) 95 | x.solve(output, maxAct); 96 | for (auto &x : traversals) 97 | x.solve(output, maxAct); 98 | */ 99 | 100 | // set the rest value properly 101 | if (!output.empty()) 102 | output[0] = 1.0 - maxAct; 103 | return output; 104 | } 105 | 106 | Simplex::Simplex(const std::string &json){ 107 | parseJSON(json); 108 | } 109 | 110 | Simplex::Simplex(const char *json){ 111 | parseJSON(std::string(json)); 112 | } 113 | 114 | bool Simplex::parseJSONversion(const rapidjson::Document &d, unsigned version){ 115 | // Must have these 116 | if (!d.HasMember("shapes")) return false; 117 | if (!d.HasMember("progressions")) return false; 118 | if (!d.HasMember("sliders")) return false; 119 | 120 | const rapidjson::Value &jshapes = d["shapes"]; 121 | const rapidjson::Value &jsliders = d["sliders"]; 122 | const rapidjson::Value &jprogs = d["progressions"]; 123 | 124 | if (!jshapes.IsArray()) return false; 125 | if (!jsliders.IsArray()) return false; 126 | if (!jprogs.IsArray()) return false; 127 | 128 | rapidjson::SizeType i; 129 | bool ret; 130 | for (i = 0; i(json.c_str()); 205 | 206 | hasParseError = false; 207 | if (d.HasParseError()){ 208 | hasParseError = true; 209 | parseError = std::string(rapidjson::GetParseError_En(d.GetParseError())); 210 | parseErrorOffset = d.GetErrorOffset(); 211 | return false; 212 | } 213 | 214 | unsigned encoding = 1u; 215 | if (d.HasMember("encodingVersion")){ 216 | const rapidjson::Value &ev = d["encodingVersion"]; 217 | if (!ev.IsUint()) return false; 218 | encoding = ev.GetUint(); 219 | } 220 | return parseJSONversion(d, encoding); 221 | } 222 | 223 | void Simplex::clear() { 224 | shapes.clear(); 225 | progs.clear(); 226 | sliders.clear(); 227 | combos.clear(); 228 | floaters.clear(); 229 | spaces.clear(); 230 | traversals.clear(); 231 | 232 | built = false; 233 | loaded = false; 234 | hasParseError = false; 235 | } 236 | 237 | void Simplex::build() { 238 | spaces = TriSpace::buildSpaces(floaters); 239 | built = true; 240 | } 241 | 242 | -------------------------------------------------------------------------------- /src/simplexlib/src/slider.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Blur Studio 3 | 4 | This file is part of Simplex. 5 | 6 | Simplex is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Simplex is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Simplex. If not, see . 18 | */ 19 | #include "simplex.h" 20 | 21 | #include "rapidjson/document.h" 22 | 23 | #include 24 | #include 25 | 26 | using namespace simplex; 27 | 28 | bool Slider::parseJSONv1(const rapidjson::Value &val, size_t index, Simplex *simp){ 29 | if (!val[0u].IsString()) return false; 30 | if (!val[1].IsInt()) return false; 31 | 32 | std::string name(val[0u].GetString()); // needs to be 0u 33 | size_t slidx = size_t(val[1].GetInt()); 34 | 35 | if (slidx >= simp->progs.size()) return false; 36 | simp->sliders.push_back(Slider(name, &simp->progs[slidx], index)); 37 | return true; 38 | } 39 | 40 | bool Slider::parseJSONv2(const rapidjson::Value &val, size_t index, Simplex *simp){ 41 | if (!val.IsObject()) return false; 42 | 43 | CHECK_JSON_STRING(nameIt, "name", val); 44 | CHECK_JSON_INT(progIt, "prog", val); 45 | 46 | std::string name(nameIt->value.GetString()); 47 | size_t slidx = size_t(progIt->value.GetInt()); 48 | 49 | if (slidx >= simp->progs.size()) return false; 50 | 51 | bool enabled = getEnabled(val); 52 | 53 | simp->sliders.push_back(Slider(name, &simp->progs[slidx], index)); 54 | simp->sliders.back().setEnabled(enabled); 55 | return true; 56 | } 57 | 58 | bool Slider::parseJSONv3(const rapidjson::Value &val, size_t index, Simplex *simp){ 59 | return parseJSONv2(val, index, simp); 60 | } 61 | 62 | 63 | -------------------------------------------------------------------------------- /src/simplexlib/src/traversal.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Blur Studio 3 | 4 | This file is part of Simplex. 5 | 6 | Simplex is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Simplex is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Simplex. If not, see . 18 | */ 19 | #include "simplex.h" 20 | 21 | #include "rapidjson/document.h" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | using namespace simplex; 29 | 30 | Traversal::Traversal( 31 | const std::string &name, Progression* prog, size_t index, 32 | ShapeController* progressCtrl, ShapeController* multiplierCtrl, bool valueFlip, bool multiplierFlip): 33 | ShapeController(name, prog, index), exact(true){ 34 | 35 | solveType = ComboSolve::None; 36 | if (multiplierCtrl->sliderType()) { 37 | multState.push_back(std::make_pair((Slider*)multiplierCtrl, multiplierFlip ? -1.0 : 1.0)); 38 | } 39 | else { 40 | // loop over the combos. Also, multiplier flip should *never* be negative here 41 | Combo *cmb = (Combo *) multiplierCtrl; 42 | for (auto pairIt = cmb->stateList.begin(); pairIt != cmb->stateList.end(); ++pairIt){ 43 | multState.push_back(std::make_pair(pairIt->first, pairIt->second)); 44 | } 45 | } 46 | 47 | if (progressCtrl->sliderType()) { 48 | progStartState.push_back(std::make_pair((Slider*)progressCtrl, 0.0)); 49 | progDeltaState.push_back(std::make_pair((Slider*)progressCtrl, valueFlip ? -1.0 : 1.0)); 50 | } 51 | else { 52 | // loop over the combos. Also, multiplier flip should *never* be negative here 53 | Combo *cmb = (Combo *) progressCtrl; 54 | for (auto pairIt = cmb->stateList.begin(); pairIt != cmb->stateList.end(); ++pairIt){ 55 | progStartState.push_back(std::make_pair(pairIt->first, 0.0)); 56 | progDeltaState.push_back(std::make_pair(pairIt->first, pairIt->second)); 57 | } 58 | } 59 | } 60 | 61 | Traversal::Traversal( 62 | const std::string &name, Progression* prog, size_t index, 63 | const ComboPairs &startPairs, const ComboPairs &endPairs, ComboSolve solveType): 64 | ShapeController(name, prog, index), exact(true){ 65 | 66 | std::unordered_map startSliders, endSliders; 67 | std::unordered_set allSliders; 68 | 69 | this->solveType = solveType; 70 | 71 | for (size_t i=0; isecond)); 91 | } 92 | else if (endIt == endSliders.end()){ 93 | // means slider exists in start, but not end 94 | progStartState.push_back(std::make_pair(sli, startIt->second)); 95 | progDeltaState.push_back(std::make_pair(sli, -startIt->second)); 96 | } 97 | else { 98 | if (startIt->second == endIt->second){ 99 | // if the values are the same, add it to the multiplier state 100 | multState.push_back(std::make_pair(sli, startIt->second)); 101 | } 102 | else { 103 | // if the values are different, add them to ther respective states 104 | progStartState.push_back(std::make_pair(sli, startIt->second)); 105 | progDeltaState.push_back(std::make_pair(sli, endIt->second - startIt->second)); 106 | } 107 | } 108 | } 109 | } 110 | 111 | void Traversal::storeValue( 112 | const std::vector &values, 113 | const std::vector &posValues, 114 | const std::vector &clamped, 115 | const std::vector &inverses) { 116 | 117 | if (!enabled) return; 118 | 119 | double mul = 0.0, val = 0.0; 120 | solveState(multState, solveType, exact, mul); 121 | 122 | std::vector vals, tars; 123 | 124 | for (size_t i = 0; i < progStartState.size(); ++i) { 125 | vals.push_back(progStartState[i].first->getValue() - progStartState[i].second); 126 | tars.push_back(progDeltaState[i].second); 127 | } 128 | solveState(vals, tars, solveType, exact, val); 129 | 130 | value = val; 131 | multiplier = mul; 132 | } 133 | 134 | bool Traversal::parseJSONv1(const rapidjson::Value &val, size_t index, Simplex *simp){ 135 | return parseJSONv2(val, index, simp); 136 | } 137 | 138 | bool Traversal::parseJSONv2(const rapidjson::Value &val, size_t index, Simplex *simp){ 139 | if (!val.IsObject()) return false; 140 | 141 | CHECK_JSON_STRING(nameIt, "name", val) 142 | CHECK_JSON_INT(progIt, "prog", val) 143 | CHECK_JSON_STRING(ptIt, "progressType", val) 144 | CHECK_JSON_INT(pcIt, "progressControl", val) 145 | CHECK_JSON_BOOL(pfIt, "progressFlip", val) 146 | CHECK_JSON_STRING(mtIt, "multiplierType", val) 147 | CHECK_JSON_INT(mcIt, "multiplierControl", val) 148 | CHECK_JSON_BOOL(mfIt, "multiplierFlip", val) 149 | 150 | std::string name(nameIt->value.GetString()); 151 | size_t pidx = (size_t)progIt->value.GetInt(); 152 | std::string pctype(ptIt->value.GetString()); 153 | std::string mctype(mtIt->value.GetString()); 154 | size_t pcidx = (size_t)pcIt->value.GetInt(); 155 | size_t mcidx = (size_t)mcIt->value.GetInt(); 156 | bool pcFlip = pfIt->value.GetBool(); 157 | bool mcFlip = mfIt->value.GetBool(); 158 | 159 | ShapeController *pcItem; 160 | if (!pctype.empty() && pctype[0] == 'S') { 161 | if (pcidx >= simp->sliders.size()) return false; 162 | pcItem = &simp->sliders[pcidx]; 163 | } 164 | else { 165 | if (pcidx >= simp->combos.size()) return false; 166 | pcItem = &simp->combos[pcidx]; 167 | } 168 | 169 | ShapeController *mcItem; 170 | if (!mctype.empty() && mctype[0] == 'S') { 171 | if (mcidx >= simp->sliders.size()) return false; 172 | mcItem = &simp->sliders[mcidx]; 173 | } 174 | else { 175 | if (mcidx >= simp->combos.size()) return false; 176 | mcItem = &simp->combos[mcidx]; 177 | } 178 | 179 | if (pidx >= simp->progs.size()) return false; 180 | 181 | bool enabled = getEnabled(val); 182 | 183 | simp->traversals.push_back(Traversal(name, &simp->progs[pidx], index, pcItem, mcItem, pcFlip, mcFlip)); 184 | simp->traversals.back().setEnabled(enabled); 185 | return true; 186 | } 187 | 188 | bool Traversal::parseJSONv3(const rapidjson::Value &val, size_t index, Simplex *simp){ 189 | if (!val.IsObject()) return false; 190 | 191 | CHECK_JSON_STRING(nameIt, "name", val) 192 | CHECK_JSON_INT(progIt, "prog", val) 193 | CHECK_JSON_ARRAY(startIt, "start", val) 194 | CHECK_JSON_ARRAY(endIt, "end", val) 195 | 196 | ComboSolve solveType = getSolveType(val); 197 | 198 | bool isFloater = false; 199 | ComboPairs startPairs, endPairs; 200 | if (!getSolvePairs(startIt->value, simp, startPairs, isFloater)) return false; 201 | if (!getSolvePairs(endIt->value, simp, endPairs, isFloater)) return false; 202 | 203 | std::string name(nameIt->value.GetString()); 204 | size_t pidx = (size_t)progIt->value.GetInt(); 205 | if (pidx >= simp->progs.size()) return false; 206 | 207 | bool enabled = getEnabled(val); 208 | simp->traversals.push_back(Traversal(name, &simp->progs[pidx], index, startPairs, endPairs, solveType)); 209 | simp->traversals.back().setEnabled(enabled); 210 | return true; 211 | } 212 | -------------------------------------------------------------------------------- /src/simplexlib/src/utils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016, Blur Studio 3 | 4 | This file is part of Simplex. 5 | 6 | Simplex is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Simplex is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Simplex. If not, see . 18 | */ 19 | 20 | #include "utils.h" 21 | #include "math.h" 22 | #include 23 | #include 24 | #include 25 | 26 | namespace simplex{ 27 | 28 | void rectify( 29 | const std::vector &rawVec, 30 | std::vector &values, 31 | std::vector &clamped, 32 | std::vector &inverses 33 | ){ 34 | // Rectifying just makes everything positive, keeps track of the inversion, and applies clamping 35 | values.resize(rawVec.size()); 36 | clamped.resize(rawVec.size()); 37 | inverses.resize(rawVec.size()); 38 | for (size_t i=0; i MAXVAL) ? MAXVAL : v; 46 | } 47 | } 48 | 49 | double doSoftMin(double X, double Y) { 50 | if (isZero(X) || isZero(Y)) return 0.0; 51 | if (X < Y) std::swap(X, Y); 52 | 53 | double n = 4.0; 54 | double h = 0.025; 55 | double p = 2.0; 56 | double q = 1.0 / p; 57 | 58 | double d = 2.0 * (pow(1.0 + h, q) - pow(h, q)); 59 | double s = pow(h, q); 60 | double z = pow(pow(X, p) + h, q) + pow(pow(Y, p) + h, q) - pow(pow(X - Y, p) + h, q); 61 | return (z - s) / d; 62 | } 63 | } // namespace simplex 64 | -------------------------------------------------------------------------------- /subprojects/eigen.wrap: -------------------------------------------------------------------------------- 1 | [wrap-file] 2 | directory = eigen-3.4.0 3 | source_url = https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.tar.bz2 4 | source_filename = eigen-3.4.0.tar.bz2 5 | source_hash = b4c198460eba6f28d34894e3a5710998818515104d6e74e5cc331ce31e46e626 6 | patch_filename = eigen_3.4.0-2_patch.zip 7 | patch_url = https://wrapdb.mesonbuild.com/v2/eigen_3.4.0-2/get_patch 8 | patch_hash = cb764fd9fec02d94aaa2ec673d473793c0d05da4f4154c142f76ef923ea68178 9 | source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/eigen_3.4.0-2/eigen-3.4.0.tar.bz2 10 | wrapdb_version = 3.4.0-2 11 | 12 | [provide] 13 | eigen3 = eigen_dep 14 | -------------------------------------------------------------------------------- /subprojects/maya/meson.build: -------------------------------------------------------------------------------- 1 | project('maya', 'cpp') 2 | 3 | maya_version = get_option('maya_version') 4 | maya_devkit_base = get_option('maya_devkit_base') 5 | maya_link_qt = get_option('maya_link_qt') 6 | maya_qt_extra_includes = get_option('maya_qt_extra_includes') 7 | 8 | os_name = build_machine.system() 9 | 10 | maya_inc_suffix = 'include' 11 | maya_lib_suffix = 'lib' 12 | 13 | maya_compile_args = ['-DREQUIRE_IOSTREAM', '-D_BOOL'] 14 | maya_link_args = [] 15 | 16 | if os_name == 'windows' 17 | maya_install_base = 'c:/Program Files/Autodesk' 18 | maya_plugin_ext = 'mll' 19 | maya_compile_args += ['-DNT_PLUGIN'] 20 | maya_link_args = ['/export:initializePlugin', '/export:uninitializePlugin'] 21 | elif os_name == 'darwin' 22 | maya_install_base = '/Applications/Autodesk' 23 | maya_plugin_ext = 'bundle' 24 | if maya_devkit_base == '' 25 | maya_lib_suffix = 'Maya.app/Contents/MacOS' 26 | maya_bin_suffix = 'Maya.app/Contents/bin' 27 | endif 28 | maya_compile_args += ['-DOSMac_'] 29 | if meson.get_compiler('cpp').get_id() == 'clang' 30 | maya_compile_args += ['--stdlib', 'libc++'] 31 | maya_compile_args += ['-arch', 'x86_64'] 32 | maya_link_args += ['-arch', 'x86_64'] 33 | if maya_version.version_compare('>=2024') 34 | # clang will build for both x86 and arm 35 | # if both arches are in the command line args 36 | maya_compile_args += ['-arch', 'arm64'] 37 | maya_link_args += ['-arch', 'arm64'] 38 | endif 39 | endif 40 | 41 | # ignore this warning that comes from maya's headers 42 | maya_compile_args += ['-Wno-inconsistent-missing-override'] 43 | elif os_name == 'linux' 44 | maya_install_base = '/usr/autodesk' 45 | maya_plugin_ext = 'so' 46 | maya_compile_args += ['-DLINUX', '-fPIC'] 47 | else 48 | error('Incompatible operating system') 49 | endif 50 | maya_install_path = maya_install_base / ('Maya' + maya_version) 51 | 52 | if maya_devkit_base != '' 53 | message('Using Maya Devkit:', maya_devkit_base) 54 | maya_install_path = maya_devkit_base 55 | endif 56 | 57 | maya_inc_dir = maya_install_path / maya_inc_suffix 58 | message('Searching Maya Include directory:', maya_inc_dir) 59 | maya_inc = include_directories(maya_inc_dir) 60 | 61 | maya_lib_dir = maya_install_path / maya_lib_suffix 62 | message('Searching Maya lib directory:', maya_lib_dir) 63 | 64 | # Get all the maya libraries 65 | cmplr = meson.get_compiler('cpp') 66 | maya_libs = [ 67 | cmplr.find_library('Foundation', dirs : maya_lib_dir), 68 | cmplr.find_library('OpenMaya', dirs : maya_lib_dir), 69 | cmplr.find_library('OpenMayaAnim', dirs : maya_lib_dir), 70 | cmplr.find_library('OpenMayaFX', dirs : maya_lib_dir), 71 | cmplr.find_library('OpenMayaRender', dirs : maya_lib_dir), 72 | cmplr.find_library('OpenMayaUI', dirs : maya_lib_dir), 73 | cmplr.find_library('clew', dirs : maya_lib_dir), 74 | ] 75 | 76 | # Link to maya's qt libs if required 77 | # This doesn't do MOC stuff ... yet 78 | if maya_link_qt 79 | fs = import('fs') 80 | if not fs.is_dir(maya_inc_dir / 'QtCore') 81 | error( 82 | 'Could not find Maya QT headers with `maya_link_qt` defined\n', 83 | 'You probably need to unzip `include/qt_*-include.zip`\n', 84 | 'Checking in folder: ', maya_inc_dir, 85 | ) 86 | endif 87 | 88 | maya_qt_lib_names = ['Qt5Core', 'Qt5Gui', 'Qt5Widgets'] 89 | if maya_qt_extra_includes != '' 90 | maya_qt_lib_names += maya_qt_extra_includes.split(';') 91 | endif 92 | 93 | foreach lib_name : maya_qt_lib_names 94 | maya_libs += cmplr.find_library(lib_name, dirs : maya_lib_dir) 95 | endforeach 96 | endif 97 | 98 | maya_dep = declare_dependency( 99 | dependencies : maya_libs, 100 | include_directories : maya_inc, 101 | variables : {'name_suffix' : maya_plugin_ext, 'maya_version' : maya_version}, 102 | compile_args : maya_compile_args, 103 | link_args : maya_link_args, 104 | ) 105 | 106 | meson.override_dependency('maya', maya_dep) 107 | -------------------------------------------------------------------------------- /subprojects/maya/meson.options: -------------------------------------------------------------------------------- 1 | option( 2 | 'maya_version', 3 | type : 'string', 4 | value : '2024', 5 | description : 'The version of Maya to compile for', 6 | yield : true, 7 | ) 8 | 9 | option( 10 | 'maya_devkit_base', 11 | type : 'string', 12 | description : 'Optional path to the maya devkit', 13 | yield : true, 14 | ) 15 | 16 | option( 17 | 'maya_link_qt', 18 | type : 'boolean', 19 | description : 'Whether to link to the Qt libraries that maya provides in their devkit/install', 20 | value: false, 21 | yield : true, 22 | ) 23 | 24 | option( 25 | 'maya_qt_extra_includes', 26 | type : 'string', 27 | description : 'Any qt headers other than QtCore, QtGui, or QtWidgets that you need to include, separated by semicolons', 28 | value: '', 29 | yield : true, 30 | ) 31 | -------------------------------------------------------------------------------- /subprojects/rapidjson.wrap: -------------------------------------------------------------------------------- 1 | [wrap-file] 2 | directory = rapidjson-1.1.0 3 | source_url = https://github.com/Tencent/rapidjson/archive/v1.1.0.tar.gz 4 | source_filename = rapidjson-1.1.0.tar.gz 5 | source_hash = bf7ced29704a1e696fbccf2a2b4ea068e7774fa37f6d7dd4039d0787f8bed98e 6 | patch_filename = rapidjson_1.1.0-2_patch.zip 7 | patch_url = https://wrapdb.mesonbuild.com/v2/rapidjson_1.1.0-2/get_patch 8 | patch_hash = c1480d0ecef09dbaa4b4d85d86090205386fb2c7e87f4f158b20dbbda14c9afc 9 | source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/rapidjson_1.1.0-2/rapidjson-1.1.0.tar.gz 10 | wrapdb_version = 1.1.0-2 11 | 12 | [provide] 13 | rapidjson = rapidjson_dep 14 | --------------------------------------------------------------------------------