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