├── .gitignore ├── LICENSE ├── README.md ├── img ├── install_01.png ├── install_02.png ├── install_03.png ├── install_04.png ├── props_lock_01.png ├── record_01.png ├── record_02.png ├── record_03.png ├── record_04.png ├── retarget_01.png ├── retarget_02.png ├── retarget_03.png ├── retarget_04.png ├── retarget_05.png ├── retarget_06.png ├── stream_01.png ├── stream_02.png ├── stream_03.png ├── stream_04.png ├── stream_05.png └── stream_06.png └── neuron_mocap_live ├── __init__.py ├── core ├── __init__.py └── types.py ├── mocap_api ├── __init__.py ├── mocap_api.py └── windows │ └── MocapApi.dll ├── ops ├── __init__.py ├── armature │ ├── PN Pro.bvh │ ├── PNS - Thumb open.bvh │ └── PNS.bvh ├── armature_builder.py ├── bone_map_detector.py ├── bone_map_loadsave.py ├── mocap_connection.py └── tpose_recorder.py └── ui ├── __init__.py ├── add_armature_menu.py ├── armature_property_panel.py ├── connection_panel.py └── record_pannel.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #
NEURON MOCAP LIVE Plugin for Blender
2 | 3 | This plugin provide the ability to stream motion data from Axis Studio into Blender. 4 | 5 | ## Requirement 6 | - Blender 2.80 or higher 7 | - Window x86_64 8 | 9 | ## Features 10 | - Build **Axis Studio/Axis Studio - Thumb Open/Axis Neuron (Pro)** armature 11 | - Live motion data 12 | - Retarget motion data 13 | - Record motion data 14 | 15 | ## Installation 16 | 1. Download a release package from [release page](https://github.com/pnmocap/neuron_mocap_live-blender/releases) 17 | 18 | ![](img/install_01.png) 19 | 20 | 2. Insall plugin in Blender 21 | 22 | ![](img/install_02.png) 23 | ![](img/install_03.png) 24 | 25 | 3. Enable **Noitom:NEURON MOCAP LIVE** plugin in Blender 26 | 27 | ![](img/install_04.png) 28 | 29 | ## Live Motion Data (Direct) 30 | 1. Add a **Axis Studio/Axis Studio - Thumb Open/Axis Neuron (Pro)** armature 31 | 32 | ![](img/stream_01.png) 33 | ![](img/stream_02.png) 34 | 35 | 2. Bind the armature with a mesh (optional) 36 | 37 | ![](img/stream_03.png) 38 | 39 | 3. Enable **BVH Broadcasting** settings in **Axis Studio** 40 | 41 | ![](img/stream_04.png) 42 | 43 | 4. Configure the live settings in **NEURON MOCAP** tab in sidebar in Blender 44 | 45 | - The **Server** settings within **Connection** catagory means how to parse stream data, if you want to animate **Axis Neuron(Pro)** armature, please switch the **Server** settings to **Axis Neuron / Pro** 46 | 47 | - The **Drive Type** settings within **Armature** catagory means how to drive the active armature, under the **Direct** option, the motion data received will directly pass to armature 48 | 49 | ![](img/stream_05.png) 50 | 51 | 5. Click **Connect** to start live: 52 | 53 | ![](img/stream_06.png) 54 | 55 | ## Live Motion Data (Retarget) 56 | 1. Add a **Axis Studio/Axis Studio - Thumb Open/Axis Neuron (Pro)** armature and set the **Drive Type** settings to **Direct** 57 | 58 | ![](img/retarget_01.png) 59 | 60 | 2. Import your character 61 | 62 | ![](img/retarget_02.png) 63 | 64 | 3. Adjust your character's pose to **T-Pose**, and set the **Drive Type** to **Retarget**, check on **Live** option and click **Mark T-Pose** button 65 | 66 | ![](img/retarget_03.png) 67 | 68 | 4. Set the **Source** armature to the armature you created at first step 69 | 70 | ![](img/retarget_04.png) 71 | 72 | 5. Click **Auto Detect** try to auto match bones, if auto match failed at some bones, you can manually match these bones 73 | 74 | ![](img/retarget_05.png) 75 | 76 | 6. Click **Connect** to start live: 77 | 78 | ![](img/retarget_06.png) 79 | 80 | ## Record Motion Data 81 | 82 | 1. Click **Start Record** to start record during living, and click **Stop Record** to finish record 83 | 84 | ![](img/record_01.png) 85 | ![](img/record_02.png) 86 | 87 | 2. The data recorded is saved in an **Action** named "mocap" 88 | 89 | ![](img/record_03.png) 90 | 91 | 3. The record frame rate is same as the scene frame rate 92 | 93 | ![](img/record_04.png) 94 | 95 | 96 | ## Property Lock 97 | 98 | **Neuron Mocap Plugin** animate an armature by animating bone's **Location/Rotaion/Scale** property, but in some scenario, only **Rotation** animation is required, you can do this by lock **Location** and **Scale** property **(this feature only available on direct drived armature)** 99 | 100 | ![](img/props_lock_01.png) 101 | -------------------------------------------------------------------------------- /img/install_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/img/install_01.png -------------------------------------------------------------------------------- /img/install_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/img/install_02.png -------------------------------------------------------------------------------- /img/install_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/img/install_03.png -------------------------------------------------------------------------------- /img/install_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/img/install_04.png -------------------------------------------------------------------------------- /img/props_lock_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/img/props_lock_01.png -------------------------------------------------------------------------------- /img/record_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/img/record_01.png -------------------------------------------------------------------------------- /img/record_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/img/record_02.png -------------------------------------------------------------------------------- /img/record_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/img/record_03.png -------------------------------------------------------------------------------- /img/record_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/img/record_04.png -------------------------------------------------------------------------------- /img/retarget_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/img/retarget_01.png -------------------------------------------------------------------------------- /img/retarget_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/img/retarget_02.png -------------------------------------------------------------------------------- /img/retarget_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/img/retarget_03.png -------------------------------------------------------------------------------- /img/retarget_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/img/retarget_04.png -------------------------------------------------------------------------------- /img/retarget_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/img/retarget_05.png -------------------------------------------------------------------------------- /img/retarget_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/img/retarget_06.png -------------------------------------------------------------------------------- /img/stream_01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/img/stream_01.png -------------------------------------------------------------------------------- /img/stream_02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/img/stream_02.png -------------------------------------------------------------------------------- /img/stream_03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/img/stream_03.png -------------------------------------------------------------------------------- /img/stream_04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/img/stream_04.png -------------------------------------------------------------------------------- /img/stream_05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/img/stream_05.png -------------------------------------------------------------------------------- /img/stream_06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/img/stream_06.png -------------------------------------------------------------------------------- /neuron_mocap_live/__init__.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from . import core 3 | from . import ops 4 | from . import ui 5 | 6 | bl_info = { 7 | "name" : "NEURON MOCAP LIVE", 8 | "author" : "Noitom", 9 | "description" : "", 10 | "blender" : (2, 80, 0), 11 | "version" : (1, 0, '3 (BETA)'), 12 | "location" : "", 13 | "warning" : "", 14 | "doc_url" : "https://github.com/pnmocap/neuron_mocap_live-blender", 15 | "tracker_url" : "https://github.com/pnmocap/neuron_mocap_live-blender/issues", 16 | "category" : "Noitom" 17 | } 18 | 19 | class_list = [ 20 | ops.AddPNSArmature, 21 | ops.AddPNSThumbOpenArmature, 22 | ops.AddPNProArmature, 23 | ops.MocapConnect, 24 | ops.MocapDisconnect, 25 | ops.MocapStartRecord, 26 | ops.MocapStopRecord, 27 | ops.MarkTPose, 28 | ops.SetTPose, 29 | ops.AutoMapBone, 30 | ops.ClearBoneMap, 31 | ops.LoadBoneMap, 32 | ops.SaveBoneMap, 33 | ui.ConnectionPanel, 34 | ui.RecordPanel, 35 | ui.ArmaturePropertyPanel 36 | ] 37 | 38 | def register(): 39 | ops.init_mocap_api() 40 | 41 | core.register_types() 42 | 43 | for item in class_list: 44 | bpy.utils.register_class(item) 45 | 46 | bpy.types.VIEW3D_MT_add.append(ui.draw_add_armature_menu) 47 | 48 | def unregister(): 49 | for item in class_list: 50 | bpy.utils.unregister_class(item) 51 | 52 | bpy.types.VIEW3D_MT_add.remove(ui.draw_add_armature_menu) 53 | 54 | ops.uninit_mocap_api() 55 | -------------------------------------------------------------------------------- /neuron_mocap_live/core/__init__.py: -------------------------------------------------------------------------------- 1 | from .types import register_types -------------------------------------------------------------------------------- /neuron_mocap_live/core/types.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from mathutils import Matrix, Vector 3 | 4 | def register_types(): 5 | bpy.types.Scene.nml_server = bpy.props.EnumProperty( 6 | items = [('Axis Studio', 'Axis Studio', '', 1), ('Axis Neuron / Pro', 'Axis Neruon / Pro', '', 2)], 7 | name = 'Server', 8 | default = 'Axis Studio' 9 | ) 10 | 11 | bpy.types.Scene.nml_protocol = bpy.props.EnumProperty( 12 | items = [('TCP', 'TCP', '', 1), ('UDP', 'UDP', '', 2)], 13 | name = 'Protocol', 14 | default = 'UDP' 15 | ) 16 | 17 | bpy.types.Scene.nml_ip = bpy.props.StringProperty( 18 | name = 'IP', 19 | default = '127.0.0.1' 20 | ) 21 | 22 | bpy.types.Scene.nml_port = bpy.props.IntProperty( 23 | name = 'Port', 24 | default = 7001, 25 | max = 65535, 26 | min = 0 27 | ) 28 | 29 | bpy.types.Scene.nml_living = bpy.props.BoolProperty( 30 | name = 'Living', 31 | default = False 32 | ) 33 | 34 | bpy.types.Object.nml_active = bpy.props.BoolProperty( 35 | name = 'Live', 36 | default = False 37 | ) 38 | 39 | bpy.types.Object.nml_chr_name = bpy.props.StringProperty( 40 | name = 'Character' 41 | ) 42 | 43 | bpy.types.Scene.nml_recording = bpy.props.BoolProperty( 44 | name = 'Recording', 45 | default = False 46 | ) 47 | 48 | bpy.types.Object.nml_drive_type = bpy.props.EnumProperty( 49 | items = [('DIRECT', 'Direct', '', 1), ('RETARGET', 'Retarget', '', 2)], 50 | name = 'Drive Type', 51 | default = 'DIRECT' 52 | ) 53 | 54 | # retarget source 55 | bpy.types.Object.nml_source_armature = bpy.props.StringProperty( 56 | name = 'Armature Source' 57 | ) 58 | 59 | bpy.types.Bone.nml_source_bone = bpy.props.StringProperty( 60 | name = 'Bone Source' 61 | ) 62 | 63 | bpy.types.Object.nml_tpose_marked = bpy.props.BoolProperty( 64 | name = 'T-Pose Marked' 65 | ) 66 | 67 | bpy.types.PoseBone.nml_matrix_calculated = bpy.props.BoolProperty() 68 | 69 | def nml_encode_matrix(self, v16, m4x4): 70 | v16[0:4] = m4x4.row[0] 71 | v16[4:8] = m4x4.row[1] 72 | v16[8:12] = m4x4.row[2] 73 | v16[12:16] = m4x4.row[3] 74 | bpy.types.PoseBone.nml_encode_matrix = nml_encode_matrix 75 | 76 | def nml_decode_matrix(self, v16, m4x4): 77 | m4x4.row[0] = v16[0:4] 78 | m4x4.row[1] = v16[4:8] 79 | m4x4.row[2] = v16[8:12] 80 | m4x4.row[3] = v16[12:16] 81 | bpy.types.PoseBone.nml_decode_matrix = nml_decode_matrix 82 | 83 | bpy.types.PoseBone.nml_matrix_world_tpose = bpy.props.FloatVectorProperty( 84 | size = 16, 85 | default = ( 86 | 1, 0, 0, 0, 87 | 0, 1, 0, 0, 88 | 0, 0, 1, 0, 89 | 0, 0, 0, 1) 90 | ) 91 | 92 | def nml_set_matrix_world_tpose(self, m): 93 | self.nml_encode_matrix(self.nml_matrix_world_tpose, m) 94 | bpy.types.PoseBone.nml_set_matrix_world_tpose = nml_set_matrix_world_tpose 95 | 96 | def nml_get_matrix_world_tpose(self): 97 | m = Matrix.Identity(4) 98 | self.nml_decode_matrix(self.nml_matrix_world_tpose, m) 99 | return m 100 | bpy.types.PoseBone.nml_get_matrix_world_tpose = nml_get_matrix_world_tpose 101 | 102 | bpy.types.PoseBone.nml_matrix_basis_tpose = bpy.props.FloatVectorProperty( 103 | size = 16, 104 | default = ( 105 | 1, 0, 0, 0, 106 | 0, 1, 0, 0, 107 | 0, 0, 1, 0, 108 | 0, 0, 0, 1) 109 | ) 110 | 111 | def nml_set_matrix_basis_tpose(self, m): 112 | self.nml_encode_matrix(self.nml_matrix_basis_tpose, m) 113 | bpy.types.PoseBone.nml_set_matrix_basis_tpose = nml_set_matrix_basis_tpose 114 | 115 | def nml_get_matrix_basis_tpose(self): 116 | m = Matrix.Identity(4) 117 | self.nml_decode_matrix(self.nml_matrix_basis_tpose, m) 118 | return m 119 | bpy.types.PoseBone.nml_get_matrix_basis_tpose = nml_get_matrix_basis_tpose 120 | 121 | bpy.types.PoseBone.nml_matrix_to_world = bpy.props.FloatVectorProperty( 122 | size = 16, 123 | default = ( 124 | 1, 0, 0, 0, 125 | 0, 1, 0, 0, 126 | 0, 0, 1, 0, 127 | 0, 0, 0, 1) 128 | ) 129 | 130 | def nml_set_matrix_to_world(self, m): 131 | self.nml_encode_matrix(self.nml_matrix_to_world, m) 132 | bpy.types.PoseBone.nml_set_matrix_to_world = nml_set_matrix_to_world 133 | 134 | def nml_get_matrix_to_world(self): 135 | m = Matrix.Identity(4) 136 | self.nml_decode_matrix(self.nml_matrix_to_world, m) 137 | return m 138 | bpy.types.PoseBone.nml_get_matrix_to_world = nml_get_matrix_to_world 139 | 140 | bpy.types.PoseBone.nml_matrix_from_world = bpy.props.FloatVectorProperty( 141 | size = 16, 142 | default = ( 143 | 1, 0, 0, 0, 144 | 0, 1, 0, 0, 145 | 0, 0, 1, 0, 146 | 0, 0, 0, 1) 147 | ) 148 | 149 | def nml_set_matrix_from_world(self, m): 150 | self.nml_encode_matrix(self.nml_matrix_from_world, m) 151 | bpy.types.PoseBone.nml_set_matrix_from_world = nml_set_matrix_from_world 152 | 153 | def nml_get_matrix_from_world(self): 154 | m = Matrix.Identity(4) 155 | self.nml_decode_matrix(self.nml_matrix_from_world, m) 156 | return m 157 | bpy.types.PoseBone.nml_get_matrix_from_world = nml_get_matrix_from_world 158 | 159 | bpy.types.PoseBone.nml_scale_world = bpy.props.FloatVectorProperty( 160 | size = 3, 161 | default = ( 162 | 1, 1, 1 163 | ) 164 | ) 165 | 166 | def nml_set_scale_world(self, v3): 167 | self.nml_scale_world[0:3] = v3[0:3] 168 | bpy.types.PoseBone.nml_set_scale_world = nml_set_scale_world 169 | 170 | def nml_get_scale_world(self): 171 | v3 = Vector() 172 | v3[0:3] = self.nml_scale_world[0:3] 173 | return v3 174 | bpy.types.PoseBone.nml_get_scale_world = nml_get_scale_world 175 | 176 | bpy.types.PoseBone.nml_translation_world = bpy.props.FloatVectorProperty( 177 | size = 3, 178 | default = ( 179 | 0, 0, 0 180 | ) 181 | ) 182 | 183 | def nml_set_translation_world(self, v3): 184 | self.nml_translation_world[0:3] = v3[0:3] 185 | bpy.types.PoseBone.nml_set_translation_world = nml_set_translation_world 186 | 187 | def nml_get_translation_world(self): 188 | v3 = Vector() 189 | v3[0:3] = self.nml_translation_world[0:3] 190 | return v3 191 | bpy.types.PoseBone.nml_get_translation_world = nml_get_translation_world -------------------------------------------------------------------------------- /neuron_mocap_live/mocap_api/__init__.py: -------------------------------------------------------------------------------- 1 | from .mocap_api import MCPError 2 | from .mocap_api import MCPJointTag 3 | from .mocap_api import MCPRigidBody 4 | from .mocap_api import MCPSensorModule 5 | from .mocap_api import MCPBodyPart 6 | from .mocap_api import MCPJoint 7 | from .mocap_api import MCPAvatar 8 | from .mocap_api import MCPEventData 9 | from .mocap_api import MCPEventType 10 | from .mocap_api import MCPEvent 11 | from .mocap_api import MCPBvhRotation 12 | from .mocap_api import MCPBvhData 13 | from .mocap_api import MCPBvhDisplacement 14 | from .mocap_api import MCPSettings 15 | from .mocap_api import MCPUpVector 16 | from .mocap_api import MCPFrontVector 17 | from .mocap_api import MCPCoordSystem 18 | from .mocap_api import MCPRotatingDirection 19 | from .mocap_api import MCPPreDefinedRenderSettings 20 | from .mocap_api import MCPUnit 21 | from .mocap_api import MCPRenderSettings 22 | from .mocap_api import MCPApplication -------------------------------------------------------------------------------- /neuron_mocap_live/mocap_api/mocap_api.py: -------------------------------------------------------------------------------- 1 | from ctypes import * 2 | from collections import namedtuple 3 | from platform import * 4 | import time 5 | import os 6 | 7 | MocapApi = cdll.LoadLibrary(os.path.join(os.path.dirname(__file__), { 8 | 'DarWin' : '', 9 | 'Linux' : '', 10 | 'Windows' : 'windows/MocapApi.dll' 11 | }[system()])) 12 | 13 | MCPError = namedtuple('EMCPError', [ 14 | 'NoError', 15 | 'MoreEvent', 16 | 'InsufficientBuffer', 17 | 'InvalidObject', 18 | 'InvalidHandle', 19 | 'InvalidParameter', 20 | 'NotSupported', 21 | 'IgnoreUDPSettings', 22 | 'IgnoreTCPSettings', 23 | 'IgnoreBvhSettings', 24 | 'JointNotFound', 25 | 'WithoutTransformation', 26 | 'NoneMessage', 27 | 'NoneParent', 28 | 'NoneChild', 29 | 'AddressInUse' 30 | ])._make(range(16)) 31 | 32 | MCPJointTag = namedtuple('EMCPJointTag', [ 33 | 'Hips', 34 | 'RightUpLeg', 35 | 'RightLeg', 36 | 'RightFoot', 37 | 'LeftUpLeg', 38 | 'LeftLeg', 39 | 'LeftFoot', 40 | 'Spine', 41 | 'Spine1', 42 | 'Spine2', 43 | 'Neck', 44 | 'Neck1', 45 | 'Head', 46 | 'RightShoulder', 47 | 'RightArm', 48 | 'RightForeArm', 49 | 'RightHand', 50 | 'RightHandThumb1', 51 | 'RightHandThumb2', 52 | 'RightHandThumb3', 53 | 'RightInHandIndex', 54 | 'RightHandIndex1', 55 | 'RightHandIndex2', 56 | 'RightHandIndex3', 57 | 'RightInHandMiddle', 58 | 'RightHandMiddle1', 59 | 'RightHandMiddle2', 60 | 'RightHandMiddle3', 61 | 'RightInHandRing', 62 | 'RightHandRing1', 63 | 'RightHandRing2', 64 | 'RightHandRing3', 65 | 'RightInHandPinky', 66 | 'RightHandPinky1', 67 | 'RightHandPinky2', 68 | 'RightHandPinky3', 69 | 'LeftShoulder', 70 | 'LeftArm', 71 | 'LeftForeArm', 72 | 'LeftHand', 73 | 'LeftHandThumb1', 74 | 'LeftHandThumb2', 75 | 'LeftHandThumb3', 76 | 'LeftInHandIndex', 77 | 'LeftHandIndex1', 78 | 'LeftHandIndex2', 79 | 'LeftHandIndex3', 80 | 'LeftInHandMiddle', 81 | 'LeftHandMiddle1', 82 | 'LeftHandMiddle2', 83 | 'LeftHandMiddle3', 84 | 'LeftInHandRing', 85 | 'LeftHandRing1', 86 | 'LeftHandRing2', 87 | 'LeftHandRing3', 88 | 'LeftInHandPinky', 89 | 'LeftHandPinky1', 90 | 'LeftHandPinky2', 91 | 'LeftHandPinky3', 92 | 'Spine3', 93 | 'JointsCount', 94 | ])._make([x for x in range(61)]) 95 | 96 | MCPRigidBodyHandle = c_uint64 97 | class MCPRigidBody(object): 98 | IMCPRigidBodyApi_Version = c_char_p(b'PROC_TABLE:IMCPRigidBody_001') 99 | class MCPRigidBodyApi(Structure): 100 | _fields_ = [ 101 | ('GetRigidBodyRotation', CFUNCTYPE(c_int32, POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(c_float), MCPRigidBodyHandle)), 102 | ('GetRigidBodyPosition', CFUNCTYPE(c_int32, POINTER(c_float), POINTER(c_float), POINTER(c_float), MCPRigidBodyHandle)), 103 | ('GetRigidBodyStatus', CFUNCTYPE(c_int32, POINTER(c_int32), MCPRigidBodyHandle)), 104 | ('GetRigidBodyId', CFUNCTYPE(c_int32, POINTER(c_int32), MCPRigidBodyHandle)) 105 | ] 106 | 107 | api = POINTER(MCPRigidBodyApi)() 108 | 109 | def __init__(self, rigid_body_handle): 110 | if not self.api: 111 | err = MocapApi.MCPGetGenericInterface(self.IMCPRigidBodyApi_Version, pointer(self.api)) 112 | if err != MCPError.NoError: 113 | raise RuntimeError('Can not get MCPSensorModule interface: {0}'.format(MCPError._fields[err])) 114 | self.handle = rigid_body_handle 115 | 116 | def get_rotation(self): 117 | x = c_float() 118 | y = c_float() 119 | z = c_float() 120 | w = c_float() 121 | err = self.api.contents.GetRigidBodyRotation(pointer(x), pointer(y), pointer(z), pointer(w), self.handle) 122 | if err != MCPError.NoError: 123 | raise RuntimeError('Can not get rigid body rotation: {0}'.format(MCPError._fields[err])) 124 | return w.value, x.value, y.value, z.value 125 | 126 | def get_position(self): 127 | x = c_float() 128 | y = c_float() 129 | z = c_float() 130 | err = self.api.contents.GetRigidBodyPosition(pointer(x), pointer(y), pointer(z), self.handle) 131 | if err != MCPError.NoError: 132 | raise RuntimeError('Can not get rigid body position: {0}'.format(MCPError._fields[err])) 133 | return x.value, y.value, z.value 134 | 135 | def get_status(self): 136 | status = c_int32() 137 | err = self.api.contents.GetRigidBodyStatus(pointer(status), self.handle) 138 | if err != MCPError.NoError: 139 | raise RuntimeError('Can not get rigid body status: {0}'.format(MCPError._fields[err])) 140 | return status.value 141 | 142 | def get_id(self): 143 | rigid_id = c_int32() 144 | err = self.api.contents.GetRigidBodyId(pointer(rigid_id), self.handle) 145 | if err != MCPError.NoError: 146 | raise RuntimeError('Can not get rigid body id: {0}'.format(err)) 147 | return rigid_id.value 148 | 149 | MCPSensorModuleHandle = c_uint64 150 | class MCPSensorModule(object): 151 | IMCPSensorModuleApi_Version = c_char_p(b'PROC_TABLE:IMCPSensorModule_001') 152 | class MCPSensorModuleApi(Structure): 153 | _fields_ = [ 154 | ('GetSensorModulePosture', CFUNCTYPE(c_int32, POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(c_float), MCPSensorModuleHandle)), 155 | ('GetSensorModuleAngularVelocity', CFUNCTYPE(c_int32, POINTER(c_float), POINTER(c_float), POINTER(c_float), MCPSensorModuleHandle)), 156 | ('GetSensorModuleAcceleratedVelocity', CFUNCTYPE(c_int32, POINTER(c_float), POINTER(c_float), POINTER(c_float), MCPSensorModuleHandle)) 157 | ] 158 | 159 | api = POINTER(MCPSensorModuleApi)() 160 | 161 | def __init__(self, sensor_handle): 162 | if not self.api: 163 | err = MocapApi.MCPGetGenericInterface(self.IMCPSensorModuleApi_Version, pointer(self.api)) 164 | if err != MCPError.NoError: 165 | raise RuntimeError('Can not get MCPSensorModule interface: {0}'.format(MCPError._fields[err])) 166 | self.handle = sensor_handle 167 | 168 | def get_posture(self): 169 | x = c_float() 170 | y = c_float() 171 | z = c_float() 172 | w = c_float() 173 | err = self.api.contents.GetSensorModulePosture(pointer(x), pointer(y), pointer(z), pointer(w), self.handle) 174 | if err != MCPError.NoError: 175 | raise RuntimeError('Can not get sensor module posture: {0}'.format(MCPError._fields[err])) 176 | return w.value, x.value, y.value, z.value 177 | 178 | def get_angular_velocity(self): 179 | x = c_float() 180 | y = c_float() 181 | z = c_float() 182 | err = self.api.contents.GetSensorModuleAngularVelocity(pointer(x), pointer(y), pointer(z), self.handle) 183 | if err != MCPError.NoError: 184 | raise RuntimeError('Can not get sensor module angular velocity: {0}'.format(MCPError._fields[err])) 185 | return x.value, y.value, z.value 186 | 187 | def get_accelerated_velocity(self): 188 | x = c_float() 189 | y = c_float() 190 | z = c_float() 191 | err = self.api.contents.GetSensorModuleAcceleratedVelocity(pointer(x), pointer(y), pointer(z), self.handle) 192 | if err != MCPError.NoError: 193 | raise RuntimeError('Can not get sensor module accelerated velocity: {0}'.format(MCPError._fields[err])) 194 | return x.value, y.value, z.value 195 | 196 | MCPBodyPartHandle = c_uint64 197 | class MCPBodyPart(object): 198 | IMCPBodyPartApi_Version = c_char_p(b'PROC_TABLE:IMCPBodyPart_001') 199 | class MCPBodyPartApi(Structure): 200 | _fields_ = [ 201 | ('GetJointPosition', CFUNCTYPE(c_int32, POINTER(c_float), POINTER(c_float), POINTER(c_float), MCPBodyPartHandle)), 202 | ('GetJointDisplacementSpeed', CFUNCTYPE(c_int32, POINTER(c_float), POINTER(c_float), POINTER(c_float), MCPBodyPartHandle)), 203 | ('GetBodyPartPosture', CFUNCTYPE(c_int32, POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(c_float), MCPBodyPartHandle)) 204 | ] 205 | 206 | api = POINTER(MCPBodyPartApi)() 207 | 208 | def __init__(self, body_part_handle): 209 | if not self.api: 210 | err = MocapApi.MCPGetGenericInterface(self.IMCPBodyPartApi_Version, pointer(self.api)) 211 | if err != MCPError.NoError: 212 | raise RuntimeError('Can not get MCPBodyPartApi interface: {0}'.format(MCPError._fields[err])) 213 | self.handle = body_part_handle 214 | 215 | def get_position(self): 216 | x = c_float() 217 | y = c_float() 218 | z = c_float() 219 | err = self.api.contents.GetJointPosition(pointer(x), pointer(y), pointer(z), self.handle) 220 | if err != MCPError.NoError: 221 | raise RuntimeError('Can not get joint position:{0}'.format(MCPError._fileds[err])) 222 | return x.value, y.value, z.value 223 | 224 | def get_displacement_speed(self): 225 | x = c_float() 226 | y = c_float() 227 | z = c_float() 228 | err = self.api.contents.GetJointDisplacementSpeed(pointer(x), pointer(y), pointer(z), self.handle) 229 | if err != MCPError.NoError: 230 | raise RuntimeError('Can not get joint displacement speed: {0}'.format(MCPError._fields[err])) 231 | return x.value, y.value, z.value 232 | 233 | def get_posture(self): 234 | x = c_float() 235 | y = c_float() 236 | z = c_float() 237 | w = c_float() 238 | err = self.api.contents.GetBodyPartPosture(pointer(x), pointer(y), pointer(z), pointer(w), self.handle) 239 | if err != MCPError.NoError: 240 | raise RuntimeError('Can not get body part posture: {0}'.format(MCPError._fields[err])) 241 | return w.value, x.value, y.value, z.value 242 | 243 | MCPJointHandle = c_uint64 244 | class MCPJoint(object): 245 | IMCPJointApi_Version = c_char_p(b"PROC_TABLE:IMCPJoint_003") 246 | class MCPJointApi(Structure): 247 | _fields_ = [ 248 | ('GetJointName', CFUNCTYPE(c_int32, POINTER(c_char_p), MCPJointHandle)), 249 | ('GetJointLocalRotation', CFUNCTYPE(c_int32, POINTER(c_float), POINTER(c_float), POINTER(c_float), POINTER(c_float), MCPJointHandle)), 250 | ('GetJointLocalRotationByEuler', CFUNCTYPE(c_int32, POINTER(c_float), POINTER(c_float), POINTER(c_float), MCPJointHandle)), 251 | ('GetJointLocalTransformation', CFUNCTYPE(c_int32, POINTER(c_float), POINTER(c_float), POINTER(c_float), MCPJointHandle)), 252 | ('GetJointDefaultLocalTransformation', CFUNCTYPE(c_int32, POINTER(c_float), POINTER(c_float), POINTER(c_float), MCPJointHandle)), 253 | ('GetJointChild', CFUNCTYPE(c_int32, POINTER(MCPJointHandle), POINTER(c_uint32), MCPJointHandle)), 254 | ('GetJointBodyPart', CFUNCTYPE(c_int32, POINTER(MCPBodyPartHandle), MCPJointHandle)), 255 | ('GetJointSensorModule', CFUNCTYPE(c_int32, POINTER(MCPSensorModuleHandle), MCPJointHandle)), 256 | ('GetJointTag', CFUNCTYPE(c_int32, POINTER(c_int32), MCPJointHandle)), 257 | ('GetJointNameByTag', CFUNCTYPE(c_int32, POINTER(c_char_p), c_int32)), 258 | ('GetJointChildJointTag', CFUNCTYPE(c_int32, POINTER(c_int32), POINTER(c_uint32), c_int32)), 259 | ('GetJointParentJointTag', CFUNCTYPE(c_int32, POINTER(c_int32), c_int32)) 260 | ] 261 | 262 | api = POINTER(MCPJointApi)() 263 | 264 | def __init__(self, joint_handle): 265 | if not self.api: 266 | err = MocapApi.MCPGetGenericInterface(self.IMCPJointApi_Version, pointer(self.api)) 267 | if err != MCPError.NoError: 268 | raise RuntimeError('Can not get MCPJointApi interface: {0}'.format(MCPError._fields[err])) 269 | self.handle = joint_handle 270 | 271 | def get_name(self): 272 | joint_name = c_char_p() 273 | err = self.api.contents.GetJointName(pointer(joint_name), self.handle) 274 | if err != MCPError.NoError: 275 | raise RuntimeError('Can not get joint name: {0}'.format(MCPError._fields[err])) 276 | return str(joint_name.value, encoding='utf8') 277 | 278 | def get_local_rotation(self): 279 | x = c_float() 280 | y = c_float() 281 | z = c_float() 282 | w = c_float() 283 | err = self.api.contents.GetJointLocalRotation(pointer(x), pointer(y), pointer(z), pointer(w), self.handle) 284 | if err != MCPError.NoError: 285 | raise RuntimeError('Can not get joint local rotation: {0}'.format(MCPError._fields[err])) 286 | return w.value, x.value, y.value, z.value 287 | 288 | def get_local_rotation_by_euler(self): 289 | x = c_float() 290 | y = c_float() 291 | z = c_float() 292 | err = self.api.contents.GetJointLocalRotationByEuler(pointer(x), pointer(y), pointer(z), self.handle) 293 | if err != MCPError.NoError: 294 | raise RuntimeError('Can not get joint euler: {0}'.format(MCPError._fields[err])) 295 | return x.value, y.value, z.value 296 | 297 | def get_local_position(self): 298 | x = c_float() 299 | y = c_float() 300 | z = c_float() 301 | err = self.api.contents.GetJointLocalTransformation(pointer(x), pointer(y), pointer(z), self.handle) 302 | if err != MCPError.NoError: 303 | return None 304 | return x.value, y.value, z.value 305 | 306 | def get_default_local_position(self): 307 | x = c_float() 308 | y = c_float() 309 | z = c_float() 310 | err = self.api.contents.GetJointDefaultLocalTransformation(pointer(x), pointer(y), pointer(z), self.handle) 311 | if err != MCPError.NoError: 312 | raise RuntimeError('Can not get joint default local transformation: {0}'.format(MCPError._fields[err])) 313 | return x.value, y.value, z.value 314 | 315 | def get_children(self): 316 | joint_count = c_uint32() 317 | err = self.api.contents.GetJointChild(POINTER(MCPJointHandle)(), pointer(joint_count), self.handle) 318 | if err != MCPError.NoError: 319 | raise RuntimeError('Can not get joint child count: {0}'.format(MCPError._fields[err])) 320 | joint_handles = (MCPJointHandle * joint_count.value)() 321 | err = self.api.contents.GetJointChild(joint_handles, pointer(joint_count), self.handle) 322 | if err != MCPError.NoError: 323 | raise RuntimeError('Can not get joint child: {0}'.format(MCPError._fields[err])) 324 | return [MCPJoint(joint_handles[i]) for i in range(joint_count.value)] 325 | 326 | def get_body_part(self): 327 | body_part_handle = MCPBodyPartHandle() 328 | err = self.api.contents.GetJointBodyPart(pointer(body_part_handle), self.handle) 329 | if err != MCPError.NoError: 330 | raise RuntimeError('Can not get joint body part: {0}'.format(MCPError._fields[err])) 331 | return MCPBodyPart(body_part_handle) 332 | 333 | def get_sensor_module(self): 334 | sensor_handle = MCPSensorModuleHandle() 335 | err = self.api.contents.GetJointSensorModule(pointer(sensor_handle), self.handle) 336 | if err != MCPError.NoError: 337 | raise RuntimeError('Can not get sensor module: {0}'.format(err)) 338 | return MCPSensorModule(sensor_handle) 339 | 340 | def get_tag(self): 341 | tag = c_int32() 342 | err = self.api.contents.GetJointTag(pointer(tag), self.handle) 343 | if err != MCPError.NoError: 344 | raise RuntimeError('Can not get joint tag: {0}'.format(MCPError._fields[err])) 345 | return tag.value 346 | 347 | def get_name_by_tag(self, tag): 348 | joint_name = c_char_p() 349 | err = self.api.contents.GetJointNameByTag(pointer(joint_name), c_int32(tag)) 350 | if err != MCPError.NoError: 351 | raise RuntimeError('Can not get joint name of joint tag {0}: {1}'.format(tag, err)) 352 | return str(joint_name.value, encoding='utf8') 353 | 354 | def get_child_joint_tag(self, tag): 355 | joint_count = c_uint32() 356 | err = self.api.contents.GetJointChildJointTag(POINTER(c_int32)(), pointer(joint_count), c_int32(tag)) 357 | if err != MCPError.NoError: 358 | raise RuntimeError('Can not get joint child joint tag: {0}'.format(MCPError._fields[err])) 359 | joints_tag = (c_int32 * joint_count.value)() 360 | err = self.api.contents.GetJointChildJointTag(joints_tag, pointer(joint_count), c_int32(tag)) 361 | if err != MCPError.NoError: 362 | raise RuntimeError('Can not get joint child joint tag: {0}'.format(MCPError._fields[err])) 363 | return [joints_tag[i].value for i in range(joint_count.value)] 364 | 365 | def get_parent_joint_tag(self, tag): 366 | joint_tag = c_int32() 367 | err = self.api.contents.GetJointParentJointTag(pointer(joint_tag), c_int32(tag)) 368 | if err != MCPError.NoError: 369 | raise RuntimeError('can not get joint parent tag: {0}'.format(MCPError._fields[err])) 370 | return joint_tag.value 371 | 372 | 373 | MCPAvatarHandle = c_uint64 374 | class MCPAvatar(object): 375 | IMCPAvatarApi_Version = c_char_p(b'PROC_TABLE:IMCPAvatar_003') 376 | class MCPAvatarApi(Structure): 377 | _fields_ = [ 378 | ('GetAvatarIndex', CFUNCTYPE(c_int32, POINTER(c_uint32), MCPAvatarHandle)), 379 | ('GetAvatarRootJoint', CFUNCTYPE(c_int32, POINTER(MCPJointHandle), MCPAvatarHandle)), 380 | ('GetAvatarJoints', CFUNCTYPE(c_int32, POINTER(MCPJointHandle), POINTER(c_uint32), MCPAvatarHandle)), 381 | ('GetAvatarJointByName', CFUNCTYPE(c_int32, c_char_p, POINTER(MCPJointHandle), MCPAvatarHandle)), 382 | ('GetAvatarName', CFUNCTYPE(c_int32, POINTER(c_char_p), MCPAvatarHandle)), 383 | ('GetAvatarRigidBodies', CFUNCTYPE(c_int32, POINTER(MCPRigidBodyHandle), POINTER(c_uint32), MCPAvatarHandle)), 384 | ('GetAvatarJointHierarchy', CFUNCTYPE(c_int32, POINTER(c_char_p))), 385 | ('GetAvatarPostureIndex', CFUNCTYPE(c_int32, POINTER(c_uint32), POINTER(MCPAvatarHandle))), 386 | ('GetAvatarPostureTimeCode', CFUNCTYPE(c_int32, POINTER(c_uint32), POINTER(c_uint32), POINTER(c_uint32), POINTER(c_uint32), POINTER(MCPAvatarHandle))), 387 | ] 388 | 389 | api = POINTER(MCPAvatarApi)() 390 | 391 | def __init__(self, avatar_handle): 392 | if not self.api: 393 | err = MocapApi.MCPGetGenericInterface(self.IMCPAvatarApi_Version, pointer(self.api)) 394 | if err != MCPError.NoError: 395 | raise RuntimeError('Can not get MCPAvatar interface: {0}'.format(MCPError._fields[err])) 396 | self.handle = avatar_handle 397 | 398 | def get_index(self): 399 | index = c_uint32(0) 400 | err = self.api.contents.GetAvatarIndex(pointer(index), self.handle) 401 | if err != MCPError.NoError: 402 | raise RuntimeError('Can not get avatar index: {0}'.format(MCPError._fields[err])) 403 | return index.value 404 | 405 | def get_root_joint(self): 406 | joint_handle = MCPJointHandle() 407 | err = self.api.contents.GetAvatarRootJoint(pointer(joint_handle), self.handle) 408 | if err != MCPError.NoError: 409 | raise RuntimeError('Can not get avatar root joint: {0}'.format(err)) 410 | return MCPJoint(joint_handle) 411 | 412 | def get_joints(self): 413 | joint_count = c_uint32() 414 | err = self.api.contents.GetAvatarJoints(POINTER(MCPJointHandle)(), pointer(joint_count), self.handle) 415 | if err != MCPError.NoError: 416 | raise RuntimeError('Can not get avatar joints: {0}'.format(MCPError._fields[err])) 417 | joints_handle = (MCPJointHandle * joint_count.value)() 418 | err = self.api.contents.GetAvatarJoints(joints_handle, pointer(joint_count), self.handle) 419 | if err != MCPError.NoError: 420 | raise RuntimeError('Can not get avatar Joints: {0}'.format(MCPError._fields[err])) 421 | return [MCPJoint(joints_handle[i]) for i in range(joint_count.value)] 422 | 423 | def get_joint_by_name(self, name): 424 | joint_name = c_char_p(bytes(name, encoding='utf8')) 425 | joint_handle = MCPJointHandle() 426 | err = self.api.contents.GetAvatarJointByName(joint_name, pointer(joint_handle), self.handle) 427 | if err != MCPError.NoError: 428 | raise RuntimeError('Can not get avatar Joints: {0}'.format(MCPError._fields[err])) 429 | return MCPJoint(joint_handle) 430 | 431 | def get_name(self): 432 | avatar_name = c_char_p() 433 | err = self.api.contents.GetAvatarName(pointer(avatar_name), self.handle) 434 | if err != MCPError.NoError: 435 | raise RuntimeError('Can not get avatar name: {0}'.format(MCPError._fields[err])) 436 | return str(avatar_name.value, encoding='utf8') 437 | 438 | def get_rigid_bodies(self): 439 | rigid_body_count = c_uint32() 440 | err = self.api.contents.GetAvatarRigidBodies(POINTER(MCPRigidBodyHandle)(), pointer(rigid_body_count), self.handle) 441 | if err != MCPError.NoError: 442 | raise RuntimeError('Can not get avatar rigid bodies: {0}'.format(MCPError._fields[err])) 443 | rigid_body_handles = (MCPRigidBodyHandle * rigid_body_count.value)() 444 | err = self.api.contents.GetAvatarRigidBodies(rigid_body_handles, pointer(rigid_body_count), self.handle) 445 | if err != MCPError.NoError: 446 | raise RuntimeError('Can not get avatar rigid bodies: {0}'.format(MCPError._fields[err])) 447 | return [MCPRigidBody(rigid_body_handles[i]) for i in range(rigid_body_count.value)] 448 | 449 | def get_joint_hierarchy(self): 450 | hierarchy = c_char_p() 451 | err = self.api.contents.GetAvatarJointHierarchy(pointer(hierarchy)) 452 | if err != MCPError.NoError: 453 | raise RuntimeError('Can not get avatar joint hierarchy: {0}'.format(err)) 454 | return str(hierarchy.value, encoding='utf8') 455 | 456 | class MCPEventDataReserved(Structure): 457 | _fields_ = [ 458 | ('reserved0', c_uint64), 459 | ('reserved1', c_uint64), 460 | ('reserved2', c_uint64), 461 | ('reserved3', c_uint64), 462 | ('reserved4', c_uint64), 463 | ('reserved5', c_uint64), 464 | ] 465 | 466 | class MCPEventData(Union): 467 | _fields_ = [ 468 | ('reserved', MCPEventDataReserved), 469 | ('avatar_handle', MCPAvatarHandle), 470 | ('error', c_int32) 471 | ] 472 | 473 | MCPEventType = namedtuple('EMCPEventType', [ 474 | 'InvalidEvent', 475 | 'AvatarUpdated', 476 | 'RigidBodyUpdated', 477 | 'Error' 478 | ])(0, 256, 512, 768) 479 | 480 | class MCPEvent(Structure): 481 | _fields_ = [ 482 | ("size", c_uint32), 483 | ("event_type", c_int32), 484 | ('timestamp', c_double), 485 | ("event_data", MCPEventData) 486 | ] 487 | 488 | MCPBvhRotation = namedtuple('EMCPBvhRotation', [ 489 | 'XYZ', 490 | 'XZY', 491 | 'YXZ', 492 | 'YZX', 493 | 'ZXY', 494 | 'ZYX' 495 | ])(0, 1, 2, 3, 4, 5) 496 | 497 | MCPBvhData = namedtuple('EMCPBvhData', [ 498 | 'String', 499 | 'BinaryWithOldFrameHeader', 500 | 'Binary', 501 | 'BinaryLegacyHumanHierarchy' 502 | ])(0, 1, 2, 6) 503 | 504 | MCPBvhDisplacement = namedtuple('EMCPBvhDisplacement', [ 505 | 'Disable', 506 | 'Enable' 507 | ])(0, 1) 508 | 509 | MCPSettingsHandle = c_uint64 510 | class MCPSettings(object): 511 | IMCPSettingsApi_Version = c_char_p(b'PROC_TABLE:IMCPSettings_001') 512 | class MCPSettingsApi(Structure): 513 | _fields_ = [ 514 | ('CreateSettings', CFUNCTYPE(c_int32, POINTER(MCPSettingsHandle))), 515 | ('DestroySettings', CFUNCTYPE(c_int32, MCPSettingsHandle)), 516 | ('SetSettingsUDP', CFUNCTYPE(c_int32, c_uint16, MCPSettingsHandle)), 517 | ('SetSettingsTCP', CFUNCTYPE(c_int32, c_char_p, c_uint16, MCPSettingsHandle)), 518 | ('SetSettingsBvhRotation', CFUNCTYPE(c_int32, c_int32, MCPSettingsHandle)), 519 | ('SetSettingsBvhDisplacement', CFUNCTYPE(c_int32, c_int32, MCPSettingsHandle)), 520 | ('SetSettingsBvhData', CFUNCTYPE(c_int32, c_int32, MCPSettingsHandle)), 521 | ('SetSettingsCalcData', CFUNCTYPE(c_int32, MCPSettingsHandle)) 522 | ] 523 | 524 | api = POINTER(MCPSettingsApi)() 525 | 526 | def __init__(self): 527 | if not self.api: 528 | err = MocapApi.MCPGetGenericInterface(self.IMCPSettingsApi_Version, pointer(self.api)) 529 | if err != MCPError.NoError: 530 | raise RuntimeError('Can not get MCPSettings interface: {0}'.format(MCPError._fields[err])) 531 | self.handle = MCPSettingsHandle() 532 | err = self.api.contents.CreateSettings(pointer(self.handle)) 533 | if err != MCPError.NoError: 534 | raise RuntimeError('Can not create settings: {0}'.format(MCPError._fields[err])) 535 | 536 | def __del__(self): 537 | err = self.api.contents.DestroySettings(self.handle) 538 | if err != MCPError.NoError: 539 | raise RuntimeError('Can not destroy settings: {0}'.format(MCPError._fields[err])) 540 | 541 | def set_udp(self, local_port): 542 | err = self.api.contents.SetSettingsUDP(c_uint16(local_port), self.handle) 543 | if err != MCPError.NoError: 544 | raise RuntimeError('Can not set udp port of {0}: {1}'.format(local_port, MCPError._fields[err])) 545 | 546 | def set_tcp(self, ip, port): 547 | err = self.api.contents.SetSettingsTCP(c_char_p(bytes(ip, encoding='utf8')), c_uint16(port), self.handle) 548 | if err != MCPError.NoError: 549 | raise RuntimeError('Can not settings tcp addr of {0}:{1}: {2}'.format(ip, port, MCPError._fields[err])) 550 | 551 | def set_bvh_rotation(self, bvh_rotation): 552 | err = self.api.contents.SetSettingsBvhRotation(c_int32(bvh_rotation), self.handle) 553 | if err != MCPError.NoError: 554 | raise RuntimeError('Can not set bvh rotation: {0}'.format(MCPError._fields[err])) 555 | 556 | def set_bvh_displacement(self, bvh_transformation): 557 | err = self.api.contents.SetSettingsBvhDisplacement(c_int32(bvh_transformation), self.handle) 558 | if err != MCPError.NoError: 559 | raise RuntimeError('Can not set bvh displacement: {0}'.format(MCPError._fields[err])) 560 | 561 | def set_bvh_data(self, bvh_data): 562 | err = self.api.contents.SetSettingsBvhData(c_int32(bvh_data), self.handle) 563 | if err != MCPError.NoError: 564 | raise RuntimeError('Can not set bvh data: {0}'.format(MCPError._fields[err])) 565 | 566 | def set_calc_data(self): 567 | err = self.api.contents.SetSettingsCalcData(self.handle) 568 | if err != MCPError.NoError: 569 | raise RuntimeError('Can not set calc data: {0}'.format(MCPError._fields[err])) 570 | 571 | 572 | MCPUpVector = namedtuple('EMCPUpVector', [ 573 | 'XAxis', 574 | 'YAxis', 575 | 'ZAxis' 576 | ])(1, 2, 3) 577 | 578 | MCPFrontVector = namedtuple('EMCPFrontVector', [ 579 | 'ParityEven', 580 | 'ParityOdd' 581 | ])(1, 2) 582 | 583 | MCPCoordSystem = namedtuple('EMCPCoordSystem', [ 584 | 'RightHanded', 585 | 'LeftHanded' 586 | ])(0, 1) 587 | 588 | MCPRotatingDirection = namedtuple('EMCPRotatingDirection', [ 589 | 'Clockwise', 590 | 'CounterClockwise' 591 | ])(0, 1) 592 | 593 | MCPPreDefinedRenderSettings = namedtuple('EMCPPreDefinedRenderSettings', [ 594 | 'Default', 595 | 'UnrealEngine', 596 | 'Unity3D', 597 | 'Count' 598 | ])(0, 1, 2, 3) 599 | 600 | MCPUnit = namedtuple('EMCPUnit', [ 601 | 'Centimeter', 602 | 'Meter' 603 | ])(0, 1) 604 | 605 | MCPRenderSettingsHandle = c_uint64 606 | class MCPRenderSettings(object): 607 | IMCPRenderSettingsApi_Version = c_char_p(b'PROC_TABLE:IMCPRenderSettings_001') 608 | class MCPRenderSettingsApi(Structure): 609 | _fields_ = [ 610 | ('CreateRenderSettings', CFUNCTYPE(c_int32, POINTER(MCPRenderSettingsHandle))), 611 | ('GetPreDefRenderSettings', CFUNCTYPE(c_int32, c_int32, POINTER(MCPRenderSettingsHandle))), 612 | ('SetUpVector', CFUNCTYPE(c_int32, c_int32, c_int32, MCPRenderSettingsHandle)), 613 | ('GetUpVector', CFUNCTYPE(c_int32, POINTER(c_int32), POINTER(c_int32), MCPRenderSettingsHandle)), 614 | ('SetFrontVector', CFUNCTYPE(c_int32, c_int32, c_int32, MCPRenderSettingsHandle)), 615 | ('GetFrontVector', CFUNCTYPE(c_int32, POINTER(c_int32), POINTER(c_int32), MCPRenderSettingsHandle)), 616 | ('SetCoordSystem', CFUNCTYPE(c_int32, c_int32, MCPRenderSettingsHandle)), 617 | ('GetCoordSystem', CFUNCTYPE(c_int32, POINTER(c_int32), MCPRenderSettingsHandle)), 618 | ('SetRotatingDirection', CFUNCTYPE(c_int32, c_int32, MCPRenderSettingsHandle)), 619 | ('GetRotationDirection', CFUNCTYPE(c_int32, POINTER(c_int32), MCPRenderSettingsHandle)), 620 | ('SetUnit', CFUNCTYPE(c_int32, c_int32, MCPRenderSettingsHandle)), 621 | ('GetUnit', CFUNCTYPE(c_int32, POINTER(c_int32), MCPRenderSettingsHandle)), 622 | ('DestroyRenderSettings', CFUNCTYPE(c_int32, MCPRenderSettingsHandle)) 623 | ] 624 | 625 | api = POINTER(MCPRenderSettingsApi)() 626 | 627 | def __init__(self, pre_def = None): 628 | if not self.api: 629 | err = MocapApi.MCPGetGenericInterface(self.IMCPRenderSettingsApi_Version, pointer(self.api)) 630 | if err != MCPError.NoError: 631 | raise RuntimeError('Can not get MCPRenderSettings interface: {0}'.format(MCPError._fields[err])) 632 | self.pre_def = pre_def 633 | self.handle = MCPRenderSettingsHandle() 634 | if self.pre_def == None: 635 | err = self.api.contents.CreateRenderSettings(pointer(self.handle)) 636 | if err != MCPError.NoError: 637 | raise RuntimeError('Can not create render settings: {0}'.format(MCPError._fields[err])) 638 | else: 639 | err = self.api.contents.GetPreDefRenderSettings(c_int32(pre_def), pointer(self.handle)) 640 | if err != MCPError.NoError: 641 | raise RuntimeError('Can not get render settings: {0}'.format(MCPError._fields[err])) 642 | 643 | def __del__(self): 644 | if self.pre_def == None: 645 | err = self.api.contents.DestroyRenderSettings(self.handle) 646 | if err != MCPError.NoError: 647 | raise RuntimeError('Can not destroy render settings: {0}'.format(MCPError._fields[err])) 648 | 649 | def set_up_vector(self, up_vector, sign): 650 | err = self.api.contents.SetUpVector(c_int32(up_vector), c_int32(sign), self.handle) 651 | if err != MCPError.NoError: 652 | raise RuntimeError('Can not set up vector: {0}'.format(MCPError._fields[err])) 653 | 654 | def get_up_vector(self): 655 | up_vector = c_int32() 656 | sign = c_int32() 657 | err = self.api.contents.GetUpVector(pointer(up_vector), pointer(sign), self.handle) 658 | if err != MCPError.NoError: 659 | raise RuntimeError('Can not get up vector: {0}'.format(MCPError._fields[err])) 660 | return up_vector.value, sign.value 661 | 662 | def set_front_vector(self, front_vector, sign): 663 | err = self.api.contents.SetFrontVector(c_int32(front_vector), c_int32(sign), self.handle) 664 | if err != MCPError.NoError: 665 | raise RuntimeError('Can not set front vector: {0}'.format(MCPError._fields[err])) 666 | 667 | def get_front_vector(self): 668 | front_vector = c_int32() 669 | sign = c_int32() 670 | err = self.api.contents.GetFrontVector(pointer(front_vector), pointer(sign), self.handle) 671 | if err != MCPError.NoError: 672 | raise RuntimeError('Can not get front vector: {0}'.format(MCPError._fields[err])) 673 | return front_vector.value, sign.value 674 | 675 | def set_coord_system(self, coord_sys): 676 | err = self.api.contents.SetCoordSystem(c_int32(coord_sys), self.handle) 677 | if err != MCPError.NoError: 678 | raise RuntimeError('Can not set coord system: {0}'.format(MCPError._fields[err])) 679 | 680 | def get_coord_system(self): 681 | coord_sys = c_int32() 682 | err = self.api.contents.GetCoordSystem(pointer(coord_sys), self.handle) 683 | if err != MCPError.NoError: 684 | raise RuntimeError('Can not get coord system: {0}'.format(MCPError._fields[err])) 685 | return coord_sys.value 686 | 687 | def set_rotating_direction(self, rotating_direction): 688 | err = self.api.contents.SetRotatingDirection(c_int32(rotating_direction), self.handle) 689 | if err != MCPError.NoError: 690 | raise RuntimeError('Can not set rotating direction: {0}'.format(MCPError._fields[err])) 691 | 692 | def get_rotating_direction(self): 693 | rotating_direction = c_int32() 694 | err = self.api.contents.GetRotatingDirection(pointer(rotating_direction), self.handle) 695 | if err != MCPError.NoError: 696 | raise RuntimeError('Can not get rotating direction: {0}'.format(MCPError._fields[err])) 697 | return rotating_direction.value 698 | 699 | def set_unit(self, unit): 700 | err = self.api.contents.SetUnit(c_int32(unit), self.handle) 701 | if err != MCPError.NoError: 702 | raise RuntimeError('Can not set unit: {0}'.format(MCPError._fields[err])) 703 | 704 | def get_unit(self): 705 | unit = c_int32() 706 | err = self.api.contents.GetUnit(pointer(unit), self.handle) 707 | if err != MCPError.NoError: 708 | raise RuntimeError('Can not get unit: {0}'.format(MCPError._fields[err])) 709 | return unit.value 710 | 711 | MCPApplicationHandle = c_uint64 712 | class MCPApplication(object): 713 | IMCPApplicationApi_Version = c_char_p(b'PROC_TABLE:IMCPApplication_002') 714 | class MCPApplicationApi(Structure): 715 | _fields_ = [ 716 | ('CreateApplication', CFUNCTYPE(c_int32, POINTER(MCPApplicationHandle))), 717 | ('DestroyApplication', CFUNCTYPE(c_int32, MCPApplicationHandle)), 718 | ('SetApplicationSettings', CFUNCTYPE(c_int32, MCPSettingsHandle, MCPApplicationHandle)), 719 | ('SetApplicationRenderSettings', CFUNCTYPE(c_int32, MCPRenderSettingsHandle, MCPApplicationHandle)), 720 | ('OpenApplication', CFUNCTYPE(c_int32, MCPApplicationHandle)), 721 | ('EnableApplicationCacheEvents', CFUNCTYPE(c_int32, MCPApplicationHandle)), 722 | ('DisableApplicationCacheEvents', CFUNCTYPE(c_int32, MCPApplicationHandle)), 723 | ('ApplicationCacheEventsIsEnabled', CFUNCTYPE(c_int32, POINTER(c_bool), MCPApplicationHandle)), 724 | ('CloseApplication', CFUNCTYPE(c_int32, MCPApplicationHandle)), 725 | ('GetApplicationRigidBodies', CFUNCTYPE(c_int32, POINTER(c_uint64), POINTER(c_uint32), MCPApplicationHandle)), 726 | ('GetApplicationAvatars', CFUNCTYPE(c_int32, POINTER(c_uint64), POINTER(c_uint32), MCPApplicationHandle)), 727 | ('PollApplicationNextEvent', CFUNCTYPE(c_int32, POINTER(MCPEvent), POINTER(c_uint32), MCPApplicationHandle)) 728 | ] 729 | api = POINTER(MCPApplicationApi)() 730 | 731 | def __init__(self): 732 | if not self.api: 733 | err = MocapApi.MCPGetGenericInterface(self.IMCPApplicationApi_Version, pointer(self.api)) 734 | if err != MCPError.NoError: 735 | raise RuntimeError('Can not get IMCPApplication interface: {0}'.format(MCPError._fields[err])) 736 | self._handle = MCPApplicationHandle() 737 | err = self.api.contents.CreateApplication(pointer(self._handle)) 738 | if err != MCPError.NoError: 739 | raise RuntimeError('Can not create application: {0}'.format(MCPError._fields[err])) 740 | self._is_opened = False 741 | 742 | def __del__(self): 743 | err = self.api.contents.DestroyApplication(self._handle) 744 | if err != MCPError.NoError: 745 | raise RuntimeError('Can not destroy application: {0}'.format(MCPError._fields[err])) 746 | 747 | def set_settings(self, settings): 748 | err = self.api.contents.SetApplicationSettings(settings.handle, self._handle) 749 | if err != MCPError.NoError: 750 | raise RuntimeError('Can not set application settings: {0}'.format(MCPError._fields[err])) 751 | 752 | def set_render_settings(self, settings): 753 | err = self.api.contents.SetApplicationRenderSettings(settings.handle, self._handle) 754 | if err != MCPError.NoError: 755 | raise RuntimeError('Can not set application render settings: {0}'.format(MCPError._fields[err])) 756 | 757 | def open(self): 758 | err = self.api.contents.OpenApplication(self._handle) 759 | self._is_opened = (err == MCPError.NoError) 760 | return err == MCPError.NoError, MCPError._fields[err] 761 | 762 | def is_opened(self): 763 | return self._is_opened 764 | 765 | def enable_event_cache(self): 766 | err = self.api.contents.EnableApplicationCacheEvents(self._handle) 767 | return err == MCPError.NoError, MCPError._fields[err] 768 | 769 | def disable_event_cache(self): 770 | err = self.api.contents.DisableApplicationCacheEvents(self._handle) 771 | return err == MCPError.NoError, MCPError._fields[err] 772 | 773 | def is_event_cache_enabled(self): 774 | enable = c_bool() 775 | err = self.api.contents.ApplicationCacheEventsIsEnabled(pointer(enable), self._handle) 776 | if err != MCPError.NoError: 777 | raise RuntimeError('Can not get application event cache settings: {0}'.format(MCPError._fields[err])) 778 | return enable.value 779 | 780 | def close(self): 781 | err = self.api.contents.CloseApplication(self._handle) 782 | self._is_opened = False 783 | return err == MCPError.NoError, MCPError._fields[err] 784 | 785 | def get_rigid_bodies(self): 786 | rigid_body_size = c_uint32() 787 | err = self.api.contents.GetApplicationRigidBodies(POINTER(MCPRigidBodyHandle)(), pointer(rigid_body_size), self._handle) 788 | if err != MCPError.NoError: 789 | raise RuntimeError('Can not get application rigid bodies: {0}'.format(MCPError._fields[err])) 790 | rigid_body_handles = (MCPRigidBodyHandle * rigid_body_size.value)() 791 | err = self.api.GetApplicationRigidBodies(rigid_body_handles, pointer(rigid_body_size), self._handle) 792 | if err != MCPError.NoError: 793 | raise RuntimeError('Can not get application rigid bodies: {0}'.format(MCPError._fields[err])) 794 | return [MCPRigidBody(rigid_body_handles[i]) for i in range(rigid_body_size.value)] 795 | 796 | def get_avatars(self): 797 | avatar_count = c_uint32() 798 | err = self.api.contents.GetApplicationAvatars(POINTER(MCPAvatarHandle)(), pointer(avatar_count), self._handle) 799 | if err != MCPError.NoError: 800 | raise RuntimeError('Can not get application avatars: {0}'.format(MCPError._fields[err])) 801 | avatar_handles = (MCPAvatarHandle * avatar_count.value) () 802 | err = self.api.contents.GetApplicationAvatars(avatar_handles, pointer(avatar_count), self._handle) 803 | if err != MCPError.NoError: 804 | raise RuntimeError('Can not get application avatars: {0}'.format(MCPError._fields[err])) 805 | return [MCPAvatar(avatar_handles[i]) for i in range(avatar_count.value)] 806 | 807 | def poll_next_event(self): 808 | evt_count = c_uint32(0) 809 | err = self.api.contents.PollApplicationNextEvent(POINTER(MCPEvent)(), pointer(evt_count), self._handle) 810 | if err != MCPError.NoError: 811 | raise RuntimeError('Can not poll application next event: {0}'.format(MCPError._fields[err])) 812 | if evt_count.value == 0: 813 | return [] 814 | evt_array = (MCPEvent * evt_count.value)() 815 | for i in range(evt_count.value): 816 | evt_array[i].size = sizeof(MCPEvent) 817 | err = self.api.contents.PollApplicationNextEvent(evt_array, pointer(evt_count), self._handle) 818 | if err != MCPError.NoError: 819 | return [] 820 | if evt_count.value == 0: 821 | return [] 822 | return [evt_array[i] for i in range(evt_count.value)] 823 | 824 | if __name__ == '__main__': 825 | def print_joint(joint): 826 | print(joint.get_name()) 827 | print(joint.get_local_rotation()) 828 | print(joint.get_local_rotation_by_euler()) 829 | print(joint.get_local_position()) 830 | 831 | children = joint.get_children() 832 | for child in children: 833 | print_joint(child) 834 | 835 | app = MCPApplication() 836 | settings = MCPSettings() 837 | settings.set_udp(7001) 838 | app.set_settings(settings) 839 | app.open() 840 | while True: 841 | evts = app.poll_next_event() 842 | for evt in evts: 843 | if evt.event_type == MCPEventType.AvatarUpdated: 844 | avatar = MCPAvatar(evt.event_data.avatar_handle) 845 | print(avatar.get_index()) 846 | print(avatar.get_name()) 847 | print_joint(avatar.get_root_joint()) 848 | elif evt.event_type == MCPEventType.RigidBodyUpdated: 849 | print('rigid body updated') 850 | else: 851 | print('unknow event') 852 | 853 | time.sleep(0.001) 854 | 855 | 856 | 857 | -------------------------------------------------------------------------------- /neuron_mocap_live/mocap_api/windows/MocapApi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pnmocap/neuron_mocap_live-blender/ee01834ae2cde0f042d3aa30cc85343978664c75/neuron_mocap_live/mocap_api/windows/MocapApi.dll -------------------------------------------------------------------------------- /neuron_mocap_live/ops/__init__.py: -------------------------------------------------------------------------------- 1 | from .armature_builder import AddPNSArmature 2 | from .armature_builder import AddPNSThumbOpenArmature 3 | from .armature_builder import AddPNProArmature 4 | 5 | from .mocap_connection import MocapConnect 6 | from .mocap_connection import MocapDisconnect 7 | from .mocap_connection import MocapStartRecord 8 | from .mocap_connection import MocapStopRecord 9 | from .mocap_connection import init_mocap_api 10 | from .mocap_connection import uninit_mocap_api 11 | 12 | from .bone_map_detector import AutoMapBone 13 | from .bone_map_detector import ClearBoneMap 14 | from .tpose_recorder import MarkTPose 15 | from .tpose_recorder import SetTPose 16 | 17 | from .bone_map_loadsave import LoadBoneMap 18 | from .bone_map_loadsave import SaveBoneMap -------------------------------------------------------------------------------- /neuron_mocap_live/ops/armature/PN Pro.bvh: -------------------------------------------------------------------------------- 1 | HIERARCHY 2 | ROOT Hips 3 | { 4 | OFFSET 0.000 91.430 0.000 5 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 6 | JOINT RightUpLeg 7 | { 8 | OFFSET -9.250 -0.000 0.000 9 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 10 | JOINT RightLeg 11 | { 12 | OFFSET 0.000 -41.870 0.000 13 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 14 | JOINT RightFoot 15 | { 16 | OFFSET 0.000 -41.870 0.000 17 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 18 | End Site 19 | { 20 | OFFSET 0.000 -7.690 14.000 21 | } 22 | } 23 | } 24 | } 25 | JOINT LeftUpLeg 26 | { 27 | OFFSET 9.250 -0.000 0.000 28 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 29 | JOINT LeftLeg 30 | { 31 | OFFSET 0.000 -41.870 0.000 32 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 33 | JOINT LeftFoot 34 | { 35 | OFFSET 0.000 -41.870 0.000 36 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 37 | End Site 38 | { 39 | OFFSET 0.000 -7.690 14.000 40 | } 41 | } 42 | } 43 | } 44 | JOINT Spine 45 | { 46 | OFFSET 0.000 14.782 0.000 47 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 48 | JOINT Spine1 49 | { 50 | OFFSET 0.000 10.003 0.000 51 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 52 | JOINT Spine2 53 | { 54 | OFFSET 0.000 10.416 0.000 55 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 56 | JOINT Spine3 57 | { 58 | OFFSET 0.000 10.003 0.000 59 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 60 | JOINT Neck 61 | { 62 | OFFSET 0.000 10.692 0.000 63 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 64 | JOINT Head 65 | { 66 | OFFSET 0.000 9.470 0.000 67 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 68 | End Site 69 | { 70 | OFFSET 0.000 15.950 0.000 71 | } 72 | } 73 | } 74 | JOINT RightShoulder 75 | { 76 | OFFSET -2.792 5.108 0.000 77 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 78 | JOINT RightArm 79 | { 80 | OFFSET -13.208 0.000 0.000 81 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 82 | JOINT RightForeArm 83 | { 84 | OFFSET -26.500 0.000 0.000 85 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 86 | JOINT RightHand 87 | { 88 | OFFSET -26.000 0.000 0.000 89 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 90 | JOINT RightHandThumb1 91 | { 92 | OFFSET -2.489 0.190 3.121 93 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 94 | JOINT RightHandThumb2 95 | { 96 | OFFSET -3.683 0.000 0.000 97 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 98 | JOINT RightHandThumb3 99 | { 100 | OFFSET -2.558 0.000 0.000 101 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 102 | End Site 103 | { 104 | OFFSET -2.194 0.000 0.000 105 | } 106 | } 107 | } 108 | } 109 | JOINT RightInHandIndex 110 | { 111 | OFFSET -3.224 0.508 1.978 112 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 113 | JOINT RightHandIndex1 114 | { 115 | OFFSET -5.217 -0.091 0.999 116 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 117 | JOINT RightHandIndex2 118 | { 119 | OFFSET -3.619 0.000 0.000 120 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 121 | JOINT RightHandIndex3 122 | { 123 | OFFSET -2.052 0.000 0.000 124 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 125 | End Site 126 | { 127 | OFFSET -1.805 0.000 0.000 128 | } 129 | } 130 | } 131 | } 132 | } 133 | JOINT RightInHandMiddle 134 | { 135 | OFFSET -3.382 0.517 0.757 136 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 137 | JOINT RightHandMiddle1 138 | { 139 | OFFSET -5.174 -0.084 0.314 140 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 141 | JOINT RightHandMiddle2 142 | { 143 | OFFSET -3.949 0.000 0.000 144 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 145 | JOINT RightHandMiddle3 146 | { 147 | OFFSET -2.476 0.000 0.000 148 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 149 | End Site 150 | { 151 | OFFSET -1.975 0.000 0.000 152 | } 153 | } 154 | } 155 | } 156 | } 157 | JOINT RightInHandRing 158 | { 159 | OFFSET -3.365 0.538 -0.129 160 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 161 | JOINT RightHandRing1 162 | { 163 | OFFSET -4.635 -0.022 -0.479 164 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 165 | JOINT RightHandRing2 166 | { 167 | OFFSET -3.442 0.000 0.000 168 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 169 | JOINT RightHandRing3 170 | { 171 | OFFSET -2.388 0.000 0.000 172 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 173 | End Site 174 | { 175 | OFFSET -1.857 0.000 0.000 176 | } 177 | } 178 | } 179 | } 180 | } 181 | JOINT RightInHandPinky 182 | { 183 | OFFSET -3.161 0.469 -1.202 184 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 185 | JOINT RightHandPinky1 186 | { 187 | OFFSET -4.141 -0.022 -1.091 188 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 189 | JOINT RightHandPinky2 190 | { 191 | OFFSET -2.757 0.000 0.000 192 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 193 | JOINT RightHandPinky3 194 | { 195 | OFFSET -1.742 0.000 0.000 196 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 197 | End Site 198 | { 199 | OFFSET -1.645 0.000 0.000 200 | } 201 | } 202 | } 203 | } 204 | } 205 | } 206 | } 207 | } 208 | } 209 | JOINT LeftShoulder 210 | { 211 | OFFSET 2.792 5.108 0.000 212 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 213 | JOINT LeftArm 214 | { 215 | OFFSET 13.208 0.000 0.000 216 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 217 | JOINT LeftForeArm 218 | { 219 | OFFSET 26.500 0.000 0.000 220 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 221 | JOINT LeftHand 222 | { 223 | OFFSET 26.000 0.000 0.000 224 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 225 | JOINT LeftHandThumb1 226 | { 227 | OFFSET 2.489 0.190 3.121 228 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 229 | JOINT LeftHandThumb2 230 | { 231 | OFFSET 3.683 0.000 0.000 232 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 233 | JOINT LeftHandThumb3 234 | { 235 | OFFSET 2.558 0.000 0.000 236 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 237 | End Site 238 | { 239 | OFFSET 2.194 0.000 0.000 240 | } 241 | } 242 | } 243 | } 244 | JOINT LeftInHandIndex 245 | { 246 | OFFSET 3.224 0.508 1.978 247 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 248 | JOINT LeftHandIndex1 249 | { 250 | OFFSET 5.217 -0.091 0.999 251 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 252 | JOINT LeftHandIndex2 253 | { 254 | OFFSET 3.619 0.000 0.000 255 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 256 | JOINT LeftHandIndex3 257 | { 258 | OFFSET 2.052 0.000 0.000 259 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 260 | End Site 261 | { 262 | OFFSET 1.805 0.000 0.000 263 | } 264 | } 265 | } 266 | } 267 | } 268 | JOINT LeftInHandMiddle 269 | { 270 | OFFSET 3.382 0.517 0.757 271 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 272 | JOINT LeftHandMiddle1 273 | { 274 | OFFSET 5.174 -0.084 0.314 275 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 276 | JOINT LeftHandMiddle2 277 | { 278 | OFFSET 3.949 0.000 0.000 279 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 280 | JOINT LeftHandMiddle3 281 | { 282 | OFFSET 2.476 0.000 0.000 283 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 284 | End Site 285 | { 286 | OFFSET 1.975 0.000 0.000 287 | } 288 | } 289 | } 290 | } 291 | } 292 | JOINT LeftInHandRing 293 | { 294 | OFFSET 3.365 0.538 -0.129 295 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 296 | JOINT LeftHandRing1 297 | { 298 | OFFSET 4.635 -0.022 -0.479 299 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 300 | JOINT LeftHandRing2 301 | { 302 | OFFSET 3.442 0.000 0.000 303 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 304 | JOINT LeftHandRing3 305 | { 306 | OFFSET 2.388 0.000 0.000 307 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 308 | End Site 309 | { 310 | OFFSET 1.857 0.000 0.000 311 | } 312 | } 313 | } 314 | } 315 | } 316 | JOINT LeftInHandPinky 317 | { 318 | OFFSET 3.161 0.469 -1.202 319 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 320 | JOINT LeftHandPinky1 321 | { 322 | OFFSET 4.141 -0.022 -1.091 323 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 324 | JOINT LeftHandPinky2 325 | { 326 | OFFSET 2.757 0.000 0.000 327 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 328 | JOINT LeftHandPinky3 329 | { 330 | OFFSET 1.742 0.000 0.000 331 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 332 | End Site 333 | { 334 | OFFSET 1.645 0.000 0.000 335 | } 336 | } 337 | } 338 | } 339 | } 340 | } 341 | } 342 | } 343 | } 344 | } 345 | } 346 | } 347 | } 348 | } 349 | MOTION 350 | Frames: 0 351 | Frame Time: 0.010000 352 | -------------------------------------------------------------------------------- /neuron_mocap_live/ops/armature/PNS - Thumb open.bvh: -------------------------------------------------------------------------------- 1 | HIERARCHY 2 | ROOT Hips 3 | { 4 | OFFSET 0.000 91.430 0.000 5 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 6 | JOINT RightUpLeg 7 | { 8 | OFFSET -9.250 -0.000 0.000 9 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 10 | JOINT RightLeg 11 | { 12 | OFFSET 0.000 -41.870 0.000 13 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 14 | JOINT RightFoot 15 | { 16 | OFFSET 0.000 -41.870 0.000 17 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 18 | End Site 19 | { 20 | OFFSET 0.000 -7.690 14.000 21 | } 22 | } 23 | } 24 | } 25 | JOINT LeftUpLeg 26 | { 27 | OFFSET 9.250 -0.000 0.000 28 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 29 | JOINT LeftLeg 30 | { 31 | OFFSET 0.000 -41.870 0.000 32 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 33 | JOINT LeftFoot 34 | { 35 | OFFSET 0.000 -41.870 0.000 36 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 37 | End Site 38 | { 39 | OFFSET 0.000 -7.690 14.000 40 | } 41 | } 42 | } 43 | } 44 | JOINT Spine 45 | { 46 | OFFSET 0.000 7.818 0.000 47 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 48 | JOINT Spine1 49 | { 50 | OFFSET 0.000 17.310 0.000 51 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 52 | JOINT Spine2 53 | { 54 | OFFSET 0.000 12.285 0.000 55 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 56 | JOINT Neck 57 | { 58 | OFFSET 0.000 18.427 0.000 59 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 60 | JOINT Neck1 61 | { 62 | OFFSET 0.000 4.735 0.000 63 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 64 | JOINT Head 65 | { 66 | OFFSET 0.000 4.735 0.000 67 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 68 | End Site 69 | { 70 | OFFSET 0.000 15.950 0.000 71 | } 72 | } 73 | } 74 | } 75 | JOINT RightShoulder 76 | { 77 | OFFSET -2.792 12.843 0.000 78 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 79 | JOINT RightArm 80 | { 81 | OFFSET -13.208 0.000 0.000 82 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 83 | JOINT RightForeArm 84 | { 85 | OFFSET -26.500 0.000 0.000 86 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 87 | JOINT RightHand 88 | { 89 | OFFSET -26.000 0.000 0.000 90 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 91 | JOINT RightHandThumb1 92 | { 93 | OFFSET -2.489 0.190 3.121 94 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 95 | JOINT RightHandThumb2 96 | { 97 | OFFSET -1.841 -2.604 1.841 98 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 99 | JOINT RightHandThumb3 100 | { 101 | OFFSET -1.279 -1.809 1.279 102 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 103 | End Site 104 | { 105 | OFFSET -1.097 -1.551 1.097 106 | } 107 | } 108 | } 109 | } 110 | JOINT RightInHandIndex 111 | { 112 | OFFSET -3.224 0.508 1.978 113 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 114 | JOINT RightHandIndex1 115 | { 116 | OFFSET -5.217 -0.091 0.999 117 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 118 | JOINT RightHandIndex2 119 | { 120 | OFFSET -3.619 0.000 0.000 121 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 122 | JOINT RightHandIndex3 123 | { 124 | OFFSET -2.052 0.000 0.000 125 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 126 | End Site 127 | { 128 | OFFSET -1.805 0.000 0.000 129 | } 130 | } 131 | } 132 | } 133 | } 134 | JOINT RightInHandMiddle 135 | { 136 | OFFSET -3.382 0.517 0.757 137 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 138 | JOINT RightHandMiddle1 139 | { 140 | OFFSET -5.174 -0.084 0.314 141 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 142 | JOINT RightHandMiddle2 143 | { 144 | OFFSET -3.949 0.000 0.000 145 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 146 | JOINT RightHandMiddle3 147 | { 148 | OFFSET -2.476 0.000 0.000 149 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 150 | End Site 151 | { 152 | OFFSET -1.975 0.000 0.000 153 | } 154 | } 155 | } 156 | } 157 | } 158 | JOINT RightInHandRing 159 | { 160 | OFFSET -3.365 0.538 -0.129 161 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 162 | JOINT RightHandRing1 163 | { 164 | OFFSET -4.635 -0.022 -0.479 165 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 166 | JOINT RightHandRing2 167 | { 168 | OFFSET -3.442 0.000 0.000 169 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 170 | JOINT RightHandRing3 171 | { 172 | OFFSET -2.388 0.000 0.000 173 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 174 | End Site 175 | { 176 | OFFSET -1.857 0.000 0.000 177 | } 178 | } 179 | } 180 | } 181 | } 182 | JOINT RightInHandPinky 183 | { 184 | OFFSET -3.161 0.469 -1.202 185 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 186 | JOINT RightHandPinky1 187 | { 188 | OFFSET -4.141 -0.022 -1.091 189 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 190 | JOINT RightHandPinky2 191 | { 192 | OFFSET -2.757 0.000 0.000 193 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 194 | JOINT RightHandPinky3 195 | { 196 | OFFSET -1.742 0.000 0.000 197 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 198 | End Site 199 | { 200 | OFFSET -1.645 0.000 0.000 201 | } 202 | } 203 | } 204 | } 205 | } 206 | } 207 | } 208 | } 209 | } 210 | JOINT LeftShoulder 211 | { 212 | OFFSET 2.792 12.843 0.000 213 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 214 | JOINT LeftArm 215 | { 216 | OFFSET 13.208 0.000 0.000 217 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 218 | JOINT LeftForeArm 219 | { 220 | OFFSET 26.500 0.000 0.000 221 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 222 | JOINT LeftHand 223 | { 224 | OFFSET 26.000 0.000 0.000 225 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 226 | JOINT LeftHandThumb1 227 | { 228 | OFFSET 2.489 0.190 3.121 229 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 230 | JOINT LeftHandThumb2 231 | { 232 | OFFSET 1.841 -2.604 1.841 233 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 234 | JOINT LeftHandThumb3 235 | { 236 | OFFSET 1.279 -1.809 1.279 237 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 238 | End Site 239 | { 240 | OFFSET 1.097 -1.551 1.097 241 | } 242 | } 243 | } 244 | } 245 | JOINT LeftInHandIndex 246 | { 247 | OFFSET 3.224 0.508 1.978 248 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 249 | JOINT LeftHandIndex1 250 | { 251 | OFFSET 5.217 -0.091 0.999 252 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 253 | JOINT LeftHandIndex2 254 | { 255 | OFFSET 3.619 0.000 0.000 256 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 257 | JOINT LeftHandIndex3 258 | { 259 | OFFSET 2.052 0.000 0.000 260 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 261 | End Site 262 | { 263 | OFFSET 1.805 0.000 0.000 264 | } 265 | } 266 | } 267 | } 268 | } 269 | JOINT LeftInHandMiddle 270 | { 271 | OFFSET 3.382 0.517 0.757 272 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 273 | JOINT LeftHandMiddle1 274 | { 275 | OFFSET 5.174 -0.084 0.314 276 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 277 | JOINT LeftHandMiddle2 278 | { 279 | OFFSET 3.949 0.000 0.000 280 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 281 | JOINT LeftHandMiddle3 282 | { 283 | OFFSET 2.476 0.000 0.000 284 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 285 | End Site 286 | { 287 | OFFSET 1.975 0.000 0.000 288 | } 289 | } 290 | } 291 | } 292 | } 293 | JOINT LeftInHandRing 294 | { 295 | OFFSET 3.365 0.538 -0.129 296 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 297 | JOINT LeftHandRing1 298 | { 299 | OFFSET 4.635 -0.022 -0.479 300 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 301 | JOINT LeftHandRing2 302 | { 303 | OFFSET 3.442 0.000 0.000 304 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 305 | JOINT LeftHandRing3 306 | { 307 | OFFSET 2.388 0.000 0.000 308 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 309 | End Site 310 | { 311 | OFFSET 1.857 0.000 0.000 312 | } 313 | } 314 | } 315 | } 316 | } 317 | JOINT LeftInHandPinky 318 | { 319 | OFFSET 3.161 0.469 -1.202 320 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 321 | JOINT LeftHandPinky1 322 | { 323 | OFFSET 4.141 -0.022 -1.091 324 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 325 | JOINT LeftHandPinky2 326 | { 327 | OFFSET 2.757 0.000 0.000 328 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 329 | JOINT LeftHandPinky3 330 | { 331 | OFFSET 1.742 0.000 0.000 332 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 333 | End Site 334 | { 335 | OFFSET 1.645 0.000 0.000 336 | } 337 | } 338 | } 339 | } 340 | } 341 | } 342 | } 343 | } 344 | } 345 | } 346 | } 347 | } 348 | } 349 | MOTION 350 | Frames: 0 351 | Frame Time: 0.010000 352 | -------------------------------------------------------------------------------- /neuron_mocap_live/ops/armature/PNS.bvh: -------------------------------------------------------------------------------- 1 | HIERARCHY 2 | ROOT Hips 3 | { 4 | OFFSET 0.000 91.430 0.000 5 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 6 | JOINT RightUpLeg 7 | { 8 | OFFSET -9.250 -0.000 0.000 9 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 10 | JOINT RightLeg 11 | { 12 | OFFSET 0.000 -41.870 0.000 13 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 14 | JOINT RightFoot 15 | { 16 | OFFSET 0.000 -41.870 0.000 17 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 18 | End Site 19 | { 20 | OFFSET 0.000 -7.690 14.000 21 | } 22 | } 23 | } 24 | } 25 | JOINT LeftUpLeg 26 | { 27 | OFFSET 9.250 -0.000 0.000 28 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 29 | JOINT LeftLeg 30 | { 31 | OFFSET 0.000 -41.870 0.000 32 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 33 | JOINT LeftFoot 34 | { 35 | OFFSET 0.000 -41.870 0.000 36 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 37 | End Site 38 | { 39 | OFFSET 0.000 -7.690 14.000 40 | } 41 | } 42 | } 43 | } 44 | JOINT Spine 45 | { 46 | OFFSET 0.000 7.818 0.000 47 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 48 | JOINT Spine1 49 | { 50 | OFFSET 0.000 17.310 0.000 51 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 52 | JOINT Spine2 53 | { 54 | OFFSET 0.000 12.285 0.000 55 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 56 | JOINT Neck 57 | { 58 | OFFSET 0.000 18.427 0.000 59 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 60 | JOINT Neck1 61 | { 62 | OFFSET 0.000 4.735 0.000 63 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 64 | JOINT Head 65 | { 66 | OFFSET 0.000 4.735 0.000 67 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 68 | End Site 69 | { 70 | OFFSET 0.000 15.950 0.000 71 | } 72 | } 73 | } 74 | } 75 | JOINT RightShoulder 76 | { 77 | OFFSET -2.792 12.843 0.000 78 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 79 | JOINT RightArm 80 | { 81 | OFFSET -13.208 0.000 0.000 82 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 83 | JOINT RightForeArm 84 | { 85 | OFFSET -26.500 0.000 0.000 86 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 87 | JOINT RightHand 88 | { 89 | OFFSET -26.000 0.000 0.000 90 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 91 | JOINT RightHandThumb1 92 | { 93 | OFFSET -2.489 0.190 3.121 94 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 95 | JOINT RightHandThumb2 96 | { 97 | OFFSET -3.683 0.000 0.000 98 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 99 | JOINT RightHandThumb3 100 | { 101 | OFFSET -2.558 0.000 0.000 102 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 103 | End Site 104 | { 105 | OFFSET -2.194 0.000 0.000 106 | } 107 | } 108 | } 109 | } 110 | JOINT RightInHandIndex 111 | { 112 | OFFSET -3.224 0.508 1.978 113 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 114 | JOINT RightHandIndex1 115 | { 116 | OFFSET -5.217 -0.091 0.999 117 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 118 | JOINT RightHandIndex2 119 | { 120 | OFFSET -3.619 0.000 0.000 121 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 122 | JOINT RightHandIndex3 123 | { 124 | OFFSET -2.052 0.000 0.000 125 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 126 | End Site 127 | { 128 | OFFSET -1.805 0.000 0.000 129 | } 130 | } 131 | } 132 | } 133 | } 134 | JOINT RightInHandMiddle 135 | { 136 | OFFSET -3.382 0.517 0.757 137 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 138 | JOINT RightHandMiddle1 139 | { 140 | OFFSET -5.174 -0.084 0.314 141 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 142 | JOINT RightHandMiddle2 143 | { 144 | OFFSET -3.949 0.000 0.000 145 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 146 | JOINT RightHandMiddle3 147 | { 148 | OFFSET -2.476 0.000 0.000 149 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 150 | End Site 151 | { 152 | OFFSET -1.975 0.000 0.000 153 | } 154 | } 155 | } 156 | } 157 | } 158 | JOINT RightInHandRing 159 | { 160 | OFFSET -3.365 0.538 -0.129 161 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 162 | JOINT RightHandRing1 163 | { 164 | OFFSET -4.635 -0.022 -0.479 165 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 166 | JOINT RightHandRing2 167 | { 168 | OFFSET -3.442 0.000 0.000 169 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 170 | JOINT RightHandRing3 171 | { 172 | OFFSET -2.388 0.000 0.000 173 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 174 | End Site 175 | { 176 | OFFSET -1.857 0.000 0.000 177 | } 178 | } 179 | } 180 | } 181 | } 182 | JOINT RightInHandPinky 183 | { 184 | OFFSET -3.161 0.469 -1.202 185 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 186 | JOINT RightHandPinky1 187 | { 188 | OFFSET -4.141 -0.022 -1.091 189 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 190 | JOINT RightHandPinky2 191 | { 192 | OFFSET -2.757 0.000 0.000 193 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 194 | JOINT RightHandPinky3 195 | { 196 | OFFSET -1.742 0.000 0.000 197 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 198 | End Site 199 | { 200 | OFFSET -1.645 0.000 0.000 201 | } 202 | } 203 | } 204 | } 205 | } 206 | } 207 | } 208 | } 209 | } 210 | JOINT LeftShoulder 211 | { 212 | OFFSET 2.792 12.843 0.000 213 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 214 | JOINT LeftArm 215 | { 216 | OFFSET 13.208 0.000 0.000 217 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 218 | JOINT LeftForeArm 219 | { 220 | OFFSET 26.500 0.000 0.000 221 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 222 | JOINT LeftHand 223 | { 224 | OFFSET 26.000 0.000 0.000 225 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 226 | JOINT LeftHandThumb1 227 | { 228 | OFFSET 2.489 0.190 3.121 229 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 230 | JOINT LeftHandThumb2 231 | { 232 | OFFSET 3.683 0.000 0.000 233 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 234 | JOINT LeftHandThumb3 235 | { 236 | OFFSET 2.558 0.000 0.000 237 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 238 | End Site 239 | { 240 | OFFSET 2.194 0.000 0.000 241 | } 242 | } 243 | } 244 | } 245 | JOINT LeftInHandIndex 246 | { 247 | OFFSET 3.224 0.508 1.978 248 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 249 | JOINT LeftHandIndex1 250 | { 251 | OFFSET 5.217 -0.091 0.999 252 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 253 | JOINT LeftHandIndex2 254 | { 255 | OFFSET 3.619 0.000 0.000 256 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 257 | JOINT LeftHandIndex3 258 | { 259 | OFFSET 2.052 0.000 0.000 260 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 261 | End Site 262 | { 263 | OFFSET 1.805 0.000 0.000 264 | } 265 | } 266 | } 267 | } 268 | } 269 | JOINT LeftInHandMiddle 270 | { 271 | OFFSET 3.382 0.517 0.757 272 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 273 | JOINT LeftHandMiddle1 274 | { 275 | OFFSET 5.174 -0.084 0.314 276 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 277 | JOINT LeftHandMiddle2 278 | { 279 | OFFSET 3.949 0.000 0.000 280 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 281 | JOINT LeftHandMiddle3 282 | { 283 | OFFSET 2.476 0.000 0.000 284 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 285 | End Site 286 | { 287 | OFFSET 1.975 0.000 0.000 288 | } 289 | } 290 | } 291 | } 292 | } 293 | JOINT LeftInHandRing 294 | { 295 | OFFSET 3.365 0.538 -0.129 296 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 297 | JOINT LeftHandRing1 298 | { 299 | OFFSET 4.635 -0.022 -0.479 300 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 301 | JOINT LeftHandRing2 302 | { 303 | OFFSET 3.442 0.000 0.000 304 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 305 | JOINT LeftHandRing3 306 | { 307 | OFFSET 2.388 0.000 0.000 308 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 309 | End Site 310 | { 311 | OFFSET 1.857 0.000 0.000 312 | } 313 | } 314 | } 315 | } 316 | } 317 | JOINT LeftInHandPinky 318 | { 319 | OFFSET 3.161 0.469 -1.202 320 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 321 | JOINT LeftHandPinky1 322 | { 323 | OFFSET 4.141 -0.022 -1.091 324 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 325 | JOINT LeftHandPinky2 326 | { 327 | OFFSET 2.757 0.000 0.000 328 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 329 | JOINT LeftHandPinky3 330 | { 331 | OFFSET 1.742 0.000 0.000 332 | CHANNELS 6 Xposition Yposition Zposition Yrotation Xrotation Zrotation 333 | End Site 334 | { 335 | OFFSET 1.645 0.000 0.000 336 | } 337 | } 338 | } 339 | } 340 | } 341 | } 342 | } 343 | } 344 | } 345 | } 346 | } 347 | } 348 | } 349 | MOTION 350 | Frames: 0 351 | Frame Time: 0.010000 352 | -------------------------------------------------------------------------------- /neuron_mocap_live/ops/armature_builder.py: -------------------------------------------------------------------------------- 1 | from math import radians, ceil 2 | from mathutils import Vector, Euler, Matrix 3 | import bpy 4 | import bpy_extras 5 | import os 6 | 7 | class BVH_Node: 8 | __slots__ = ( 9 | # Bvh joint name. 10 | 'name', 11 | # BVH_Node type or None for no parent. 12 | 'parent', 13 | # A list of children of this type.. 14 | 'children', 15 | # Worldspace rest location for the head of this node. 16 | 'rest_head_world', 17 | # Localspace rest location for the head of this node. 18 | 'rest_head_local', 19 | # Worldspace rest location for the tail of this node. 20 | 'rest_tail_world', 21 | # Worldspace rest location for the tail of this node. 22 | 'rest_tail_local', 23 | # List of 6 ints, -1 for an unused channel, 24 | # otherwise an index for the BVH motion data lines, 25 | # loc triple then rot triple. 26 | 'channels', 27 | # A triple of indices as to the order rotation is applied. 28 | # [0,1,2] is x/y/z - [None, None, None] if no rotation.. 29 | 'rot_order', 30 | # Same as above but a string 'XYZ' format.. 31 | 'rot_order_str', 32 | # A list one tuple's one for each frame: (locx, locy, locz, rotx, roty, rotz), 33 | # euler rotation ALWAYS stored xyz order, even when native used. 34 | 'anim_data', 35 | # Convenience function, bool, same as: (channels[0] != -1 or channels[1] != -1 or channels[2] != -1). 36 | 'has_loc', 37 | # Convenience function, bool, same as: (channels[3] != -1 or channels[4] != -1 or channels[5] != -1). 38 | 'has_rot', 39 | # Index from the file, not strictly needed but nice to maintain order. 40 | 'index', 41 | # Use this for whatever you want. 42 | 'temp', 43 | ) 44 | 45 | _eul_order_lookup = { 46 | (None, None, None): 'XYZ', # XXX Dummy one, no rotation anyway! 47 | (0, 1, 2): 'XYZ', 48 | (0, 2, 1): 'XZY', 49 | (1, 0, 2): 'YXZ', 50 | (1, 2, 0): 'YZX', 51 | (2, 0, 1): 'ZXY', 52 | (2, 1, 0): 'ZYX', 53 | } 54 | 55 | def __init__(self, name, rest_head_world, rest_head_local, parent, channels, rot_order, index): 56 | self.name = name 57 | self.rest_head_world = rest_head_world 58 | self.rest_head_local = rest_head_local 59 | self.rest_tail_world = None 60 | self.rest_tail_local = None 61 | self.parent = parent 62 | self.channels = channels 63 | self.rot_order = tuple(rot_order) 64 | self.rot_order_str = BVH_Node._eul_order_lookup[self.rot_order] 65 | self.index = index 66 | 67 | # convenience functions 68 | self.has_loc = channels[0] != -1 or channels[1] != -1 or channels[2] != -1 69 | self.has_rot = channels[3] != -1 or channels[4] != -1 or channels[5] != -1 70 | 71 | self.children = [] 72 | 73 | # List of 6 length tuples: (lx, ly, lz, rx, ry, rz) 74 | # even if the channels aren't used they will just be zero. 75 | self.anim_data = [(0, 0, 0, 0, 0, 0)] 76 | 77 | def __repr__(self): 78 | return ( 79 | "BVH name: '%s', rest_loc:(%.3f,%.3f,%.3f), rest_tail:(%.3f,%.3f,%.3f)" % ( 80 | self.name, 81 | *self.rest_head_world, 82 | *self.rest_head_world, 83 | ) 84 | ) 85 | 86 | 87 | def sorted_nodes(bvh_nodes): 88 | bvh_nodes_list = list(bvh_nodes.values()) 89 | bvh_nodes_list.sort(key=lambda bvh_node: bvh_node.index) 90 | return bvh_nodes_list 91 | 92 | 93 | def read_bvh(context, file_path, rotate_mode='XYZ', global_scale=1.0): 94 | # File loading stuff 95 | # Open the file for importing 96 | file = open(file_path, 'rU') 97 | 98 | # Separate into a list of lists, each line a list of words. 99 | file_lines = file.readlines() 100 | # Non standard carrage returns? 101 | if len(file_lines) == 1: 102 | file_lines = file_lines[0].split('\r') 103 | 104 | # Split by whitespace. 105 | file_lines = [ll for ll in [l.split() for l in file_lines] if ll] 106 | 107 | # Create hierarchy as empties 108 | if file_lines[0][0].lower() == 'hierarchy': 109 | # print 'Importing the BVH Hierarchy for:', file_path 110 | pass 111 | else: 112 | raise Exception("This is not a BVH file") 113 | 114 | bvh_nodes = {None: None} 115 | bvh_nodes_serial = [None] 116 | bvh_frame_count = None 117 | bvh_frame_time = None 118 | 119 | channelIndex = -1 120 | 121 | lineIdx = 0 # An index for the file. 122 | while lineIdx < len(file_lines) - 1: 123 | if file_lines[lineIdx][0].lower() in {'root', 'joint'}: 124 | 125 | # Join spaces into 1 word with underscores joining it. 126 | if len(file_lines[lineIdx]) > 2: 127 | file_lines[lineIdx][1] = '_'.join(file_lines[lineIdx][1:]) 128 | file_lines[lineIdx] = file_lines[lineIdx][:2] 129 | 130 | # MAY NEED TO SUPPORT MULTIPLE ROOTS HERE! Still unsure weather multiple roots are possible? 131 | 132 | # Make sure the names are unique - Object names will match joint names exactly and both will be unique. 133 | name = file_lines[lineIdx][1] 134 | 135 | # print '%snode: %s, parent: %s' % (len(bvh_nodes_serial) * ' ', name, bvh_nodes_serial[-1]) 136 | 137 | lineIdx += 2 # Increment to the next line (Offset) 138 | rest_head_local = global_scale * Vector(( 139 | float(file_lines[lineIdx][1]), 140 | float(file_lines[lineIdx][2]), 141 | float(file_lines[lineIdx][3]), 142 | )) 143 | lineIdx += 1 # Increment to the next line (Channels) 144 | 145 | # newChannel[Xposition, Yposition, Zposition, Xrotation, Yrotation, Zrotation] 146 | # newChannel references indices to the motiondata, 147 | # if not assigned then -1 refers to the last value that will be added on loading at a value of zero, this is appended 148 | # We'll add a zero value onto the end of the MotionDATA so this always refers to a value. 149 | my_channel = [-1, -1, -1, -1, -1, -1] 150 | my_rot_order = [None, None, None] 151 | rot_count = 0 152 | for channel in file_lines[lineIdx][2:]: 153 | channel = channel.lower() 154 | channelIndex += 1 # So the index points to the right channel 155 | if channel == 'xposition': 156 | my_channel[0] = channelIndex 157 | elif channel == 'yposition': 158 | my_channel[1] = channelIndex 159 | elif channel == 'zposition': 160 | my_channel[2] = channelIndex 161 | 162 | elif channel == 'xrotation': 163 | my_channel[3] = channelIndex 164 | my_rot_order[rot_count] = 0 165 | rot_count += 1 166 | elif channel == 'yrotation': 167 | my_channel[4] = channelIndex 168 | my_rot_order[rot_count] = 1 169 | rot_count += 1 170 | elif channel == 'zrotation': 171 | my_channel[5] = channelIndex 172 | my_rot_order[rot_count] = 2 173 | rot_count += 1 174 | 175 | channels = file_lines[lineIdx][2:] 176 | 177 | my_parent = bvh_nodes_serial[-1] # account for none 178 | 179 | # Apply the parents offset accumulatively 180 | if my_parent is None: 181 | rest_head_world = Vector(rest_head_local) 182 | else: 183 | rest_head_world = my_parent.rest_head_world + rest_head_local 184 | 185 | bvh_node = bvh_nodes[name] = BVH_Node( 186 | name, 187 | rest_head_world, 188 | rest_head_local, 189 | my_parent, 190 | my_channel, 191 | my_rot_order, 192 | len(bvh_nodes) - 1, 193 | ) 194 | 195 | # If we have another child then we can call ourselves a parent, else 196 | bvh_nodes_serial.append(bvh_node) 197 | 198 | # Account for an end node. 199 | # There is sometimes a name after 'End Site' but we will ignore it. 200 | if file_lines[lineIdx][0].lower() == 'end' and file_lines[lineIdx][1].lower() == 'site': 201 | # Increment to the next line (Offset) 202 | lineIdx += 2 203 | rest_tail = global_scale * Vector(( 204 | float(file_lines[lineIdx][1]), 205 | float(file_lines[lineIdx][2]), 206 | float(file_lines[lineIdx][3]), 207 | )) 208 | 209 | bvh_nodes_serial[-1].rest_tail_world = bvh_nodes_serial[-1].rest_head_world + rest_tail 210 | bvh_nodes_serial[-1].rest_tail_local = bvh_nodes_serial[-1].rest_head_local + rest_tail 211 | 212 | # Just so we can remove the parents in a uniform way, 213 | # the end has kids so this is a placeholder. 214 | bvh_nodes_serial.append(None) 215 | 216 | if len(file_lines[lineIdx]) == 1 and file_lines[lineIdx][0] == '}': # == ['}'] 217 | bvh_nodes_serial.pop() # Remove the last item 218 | 219 | # End of the hierarchy. Begin the animation section of the file with 220 | # the following header. 221 | # MOTION 222 | # Frames: n 223 | # Frame Time: dt 224 | if len(file_lines[lineIdx]) == 1 and file_lines[lineIdx][0].lower() == 'motion': 225 | lineIdx += 1 # Read frame count. 226 | if ( 227 | len(file_lines[lineIdx]) == 2 and 228 | file_lines[lineIdx][0].lower() == 'frames:' 229 | ): 230 | bvh_frame_count = int(file_lines[lineIdx][1]) 231 | 232 | lineIdx += 1 # Read frame rate. 233 | if ( 234 | len(file_lines[lineIdx]) == 3 and 235 | file_lines[lineIdx][0].lower() == 'frame' and 236 | file_lines[lineIdx][1].lower() == 'time:' 237 | ): 238 | bvh_frame_time = float(file_lines[lineIdx][2]) 239 | 240 | lineIdx += 1 # Set the cursor to the first frame 241 | 242 | break 243 | 244 | lineIdx += 1 245 | 246 | # Remove the None value used for easy parent reference 247 | del bvh_nodes[None] 248 | # Don't use anymore 249 | del bvh_nodes_serial 250 | 251 | # importing world with any order but nicer to maintain order 252 | # second life expects it, which isn't to spec. 253 | bvh_nodes_list = sorted_nodes(bvh_nodes) 254 | 255 | while lineIdx < len(file_lines): 256 | line = file_lines[lineIdx] 257 | for bvh_node in bvh_nodes_list: 258 | # for bvh_node in bvh_nodes_serial: 259 | lx = ly = lz = rx = ry = rz = 0.0 260 | channels = bvh_node.channels 261 | anim_data = bvh_node.anim_data 262 | if channels[0] != -1: 263 | lx = global_scale * float(line[channels[0]]) 264 | 265 | if channels[1] != -1: 266 | ly = global_scale * float(line[channels[1]]) 267 | 268 | if channels[2] != -1: 269 | lz = global_scale * float(line[channels[2]]) 270 | 271 | if channels[3] != -1 or channels[4] != -1 or channels[5] != -1: 272 | 273 | rx = radians(float(line[channels[3]])) 274 | ry = radians(float(line[channels[4]])) 275 | rz = radians(float(line[channels[5]])) 276 | 277 | # Done importing motion data # 278 | anim_data.append((lx, ly, lz, rx, ry, rz)) 279 | lineIdx += 1 280 | 281 | # Assign children 282 | for bvh_node in bvh_nodes_list: 283 | bvh_node_parent = bvh_node.parent 284 | if bvh_node_parent: 285 | bvh_node_parent.children.append(bvh_node) 286 | 287 | # Now set the tip of each bvh_node 288 | for bvh_node in bvh_nodes_list: 289 | 290 | if not bvh_node.rest_tail_world: 291 | if len(bvh_node.children) == 0: 292 | # could just fail here, but rare BVH files have childless nodes 293 | bvh_node.rest_tail_world = Vector(bvh_node.rest_head_world) 294 | bvh_node.rest_tail_local = Vector(bvh_node.rest_head_local) 295 | elif len(bvh_node.children) == 1: 296 | bvh_node.rest_tail_world = Vector(bvh_node.children[0].rest_head_world) 297 | bvh_node.rest_tail_local = bvh_node.rest_head_local + bvh_node.children[0].rest_head_local 298 | else: 299 | # allow this, see above 300 | # if not bvh_node.children: 301 | # raise Exception("bvh node has no end and no children. bad file") 302 | 303 | # Removed temp for now 304 | rest_tail_world = Vector((0.0, 0.0, 0.0)) 305 | rest_tail_local = Vector((0.0, 0.0, 0.0)) 306 | for bvh_node_child in bvh_node.children: 307 | rest_tail_world += bvh_node_child.rest_head_world 308 | rest_tail_local += bvh_node_child.rest_head_local 309 | 310 | bvh_node.rest_tail_world = rest_tail_world * (1.0 / len(bvh_node.children)) 311 | bvh_node.rest_tail_local = rest_tail_local * (1.0 / len(bvh_node.children)) 312 | 313 | # Make sure tail isn't the same location as the head. 314 | if (bvh_node.rest_tail_local - bvh_node.rest_head_local).length <= 0.001 * global_scale: 315 | print("\tzero length node found:", bvh_node.name) 316 | bvh_node.rest_tail_local.y = bvh_node.rest_tail_local.y + global_scale / 10 317 | bvh_node.rest_tail_world.y = bvh_node.rest_tail_world.y + global_scale / 10 318 | 319 | return bvh_nodes, bvh_frame_time, bvh_frame_count 320 | 321 | 322 | def bvh_node_dict2armature( 323 | context, 324 | bvh_name, 325 | bvh_nodes, 326 | rotate_mode='XYZ', 327 | global_matrix=None 328 | ): 329 | # Add the new armature, 330 | scene = context.scene 331 | for obj in scene.objects: 332 | obj.select_set(False) 333 | 334 | arm_data = bpy.data.armatures.new(bvh_name) 335 | arm_ob = bpy.data.objects.new(bvh_name, arm_data) 336 | 337 | context.collection.objects.link(arm_ob) 338 | 339 | arm_ob.select_set(True) 340 | context.view_layer.objects.active = arm_ob 341 | 342 | bpy.ops.object.mode_set(mode='OBJECT', toggle=False) 343 | bpy.ops.object.mode_set(mode='EDIT', toggle=False) 344 | 345 | bvh_nodes_list = sorted_nodes(bvh_nodes) 346 | 347 | # Get the average bone length for zero length bones, we may not use this. 348 | average_bone_length = 0.0 349 | nonzero_count = 0 350 | for bvh_node in bvh_nodes_list: 351 | l = (bvh_node.rest_head_local - bvh_node.rest_tail_local).length 352 | if l: 353 | average_bone_length += l 354 | nonzero_count += 1 355 | 356 | # Very rare cases all bones could be zero length??? 357 | if not average_bone_length: 358 | average_bone_length = 0.1 359 | else: 360 | # Normal operation 361 | average_bone_length = average_bone_length / nonzero_count 362 | 363 | # XXX, annoying, remove bone. 364 | while arm_data.edit_bones: 365 | arm_ob.edit_bones.remove(arm_data.edit_bones[-1]) 366 | 367 | ZERO_AREA_BONES = [] 368 | for bvh_node in bvh_nodes_list: 369 | 370 | # New editbone 371 | bone = bvh_node.temp = arm_data.edit_bones.new(bvh_node.name) 372 | 373 | bone.head = bvh_node.rest_head_world 374 | bone.tail = bvh_node.rest_tail_world 375 | 376 | # Zero Length Bones! (an exceptional case) 377 | if (bone.head - bone.tail).length < 0.001: 378 | print("\tzero length bone found:", bone.name) 379 | if bvh_node.parent: 380 | ofs = bvh_node.parent.rest_head_local - bvh_node.parent.rest_tail_local 381 | if ofs.length: # is our parent zero length also?? unlikely 382 | bone.tail = bone.tail - ofs 383 | else: 384 | bone.tail.y = bone.tail.y + average_bone_length 385 | else: 386 | bone.tail.y = bone.tail.y + average_bone_length 387 | 388 | ZERO_AREA_BONES.append(bone.name) 389 | 390 | for bvh_node in bvh_nodes_list: 391 | if bvh_node.parent: 392 | # bvh_node.temp is the Editbone 393 | 394 | # Set the bone parent 395 | bvh_node.temp.parent = bvh_node.parent.temp 396 | 397 | # Set the connection state 398 | if( 399 | (not bvh_node.has_loc) and 400 | (bvh_node.parent.temp.name not in ZERO_AREA_BONES) and 401 | (bvh_node.parent.rest_tail_local == bvh_node.rest_head_local) 402 | ): 403 | bvh_node.temp.use_connect = True 404 | 405 | # Replace the editbone with the editbone name, 406 | # to avoid memory errors accessing the editbone outside editmode 407 | for bvh_node in bvh_nodes_list: 408 | bvh_node.temp = bvh_node.temp.name 409 | 410 | # Now Apply the animation to the armature 411 | 412 | # Get armature animation data 413 | bpy.ops.object.mode_set(mode='OBJECT', toggle=False) 414 | 415 | pose = arm_ob.pose 416 | pose_bones = pose.bones 417 | 418 | if rotate_mode == 'NATIVE': 419 | for bvh_node in bvh_nodes_list: 420 | bone_name = bvh_node.temp # may not be the same name as the bvh_node, could have been shortened. 421 | pose_bone = pose_bones[bone_name] 422 | pose_bone.rotation_mode = bvh_node.rot_order_str 423 | 424 | elif rotate_mode != 'QUATERNION': 425 | for pose_bone in pose_bones: 426 | pose_bone.rotation_mode = rotate_mode 427 | else: 428 | # Quats default 429 | pass 430 | 431 | context.view_layer.update() 432 | 433 | # finally apply matrix 434 | arm_ob.matrix_world = global_matrix 435 | bpy.ops.object.transform_apply(location=False, rotation=True, scale=False) 436 | 437 | return arm_ob 438 | 439 | def add_armature_by_bvh(ctx, name): 440 | file_path = os.path.join(os.path.dirname(__file__), 'armature', name + '.bvh') 441 | bvh_nodes, bvh_frame_time, bvh_frame_count = read_bvh( 442 | ctx, file_path, global_scale=0.01 443 | ) 444 | bvh_node_dict2armature( 445 | ctx, 446 | name, 447 | bvh_nodes, 448 | global_matrix=Matrix.Translation(ctx.scene.cursor.location).to_4x4() @ bpy_extras.io_utils.axis_conversion( 449 | from_forward='-Z', 450 | from_up='Y' 451 | ).to_4x4() 452 | ) 453 | 454 | 455 | class AddPNSArmature(bpy.types.Operator): 456 | bl_idname = 'neuron_mocap_live.add_pns_armature' 457 | bl_label = 'Noitom PNS Armature' 458 | 459 | def execute(self, ctx): 460 | add_armature_by_bvh(ctx, 'PNS') 461 | return {'FINISHED'} 462 | 463 | class AddPNSThumbOpenArmature(bpy.types.Operator): 464 | bl_idname = 'neuron_mocap_live.add_pns_thumb_open_armature' 465 | bl_label = 'Noitom PNS(Thumb open) Armature' 466 | 467 | def execute(self, ctx): 468 | print('add pns armature') 469 | add_armature_by_bvh(ctx, 'PNS - Thumb open') 470 | return {'FINISHED'} 471 | 472 | class AddPNProArmature(bpy.types.Operator): 473 | bl_idname = 'neuron_mocap_live.add_pn_pro_armature' 474 | bl_label = 'Noitom PN Pro Armature' 475 | 476 | def execute(self, ctx): 477 | add_armature_by_bvh(ctx, 'PN Pro') 478 | return {'FINISHED'} -------------------------------------------------------------------------------- /neuron_mocap_live/ops/bone_map_detector.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | class AutoMapBone(bpy.types.Operator): 4 | bl_idname = 'neuron_mocap_live.auto_map_bone' 5 | bl_label = 'Auto Map Bone' 6 | 7 | @classmethod 8 | def poll(cls, ctx): 9 | return bpy.data.objects.get(ctx.active_object.nml_source_armature) != None 10 | 11 | def execute(self, ctx): 12 | bones = ctx.active_object.pose.bones 13 | source_bones = bpy.data.objects.get(ctx.active_object.nml_source_armature).pose.bones 14 | for bone in bones: 15 | for source_bone in source_bones: 16 | if bone.name.endswith(source_bone.name): 17 | bone.bone.nml_source_bone = source_bone.name 18 | 19 | return {'FINISHED'} 20 | 21 | class ClearBoneMap(bpy.types.Operator): 22 | bl_idname = 'neuron_mocap_live.clear_bone_map' 23 | bl_label = 'Clear Bone Map' 24 | 25 | @classmethod 26 | def poll(cls, ctx): 27 | return bpy.data.objects.get(ctx.active_object.nml_source_armature) != None 28 | 29 | def execute(self, ctx): 30 | bones = ctx.active_object.pose.bones 31 | source_bones = bpy.data.objects.get(ctx.active_object.nml_source_armature).pose.bones 32 | for bone in bones: 33 | bone.bone.nml_source_bone = str() 34 | 35 | return {'FINISHED'} -------------------------------------------------------------------------------- /neuron_mocap_live/ops/bone_map_loadsave.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import json 3 | from bpy_extras.io_utils import ExportHelper, ImportHelper 4 | 5 | class LoadBoneMap(bpy.types.Operator, ImportHelper): 6 | bl_idname = 'neuron_mocap_live.load_bone_map' 7 | bl_label = 'Load Bone Map' 8 | 9 | filter_glob: bpy.props.StringProperty( 10 | default='*.nlbmap', 11 | options={'HIDDEN'}, 12 | maxlen=255 13 | ) 14 | 15 | @classmethod 16 | def poll(cls, ctx): 17 | return bpy.data.objects.get(ctx.active_object.nml_source_armature) != None 18 | 19 | def execute(self, ctx): 20 | bone_map = dict() 21 | with open(self.filepath, 'r') as f: 22 | bone_map = json.loads(f.read()) 23 | for bone in ctx.active_object.pose.bones: 24 | value = bone_map.get(bone.name) 25 | if value: 26 | bone.bone.nml_source_bone = value 27 | else: 28 | bone.bone.nml_source_bone = str() 29 | return {'FINISHED'} 30 | 31 | class SaveBoneMap(bpy.types.Operator, ExportHelper): 32 | bl_idname = 'neuron_mocap_live.save_bone_map' 33 | bl_label = 'Save Bone Map' 34 | filename_ext = '.nlbmap' 35 | 36 | filter_glob: bpy.props.StringProperty( 37 | default='*.nlbmap', 38 | options={'HIDDEN'}, 39 | maxlen=255 40 | ) 41 | 42 | @classmethod 43 | def poll(cls, ctx): 44 | return bpy.data.objects.get(ctx.active_object.nml_source_armature) != None 45 | 46 | def execute(self, ctx): 47 | bone_map = dict() 48 | for bone in ctx.active_object.pose.bones: 49 | if bone.bone.nml_source_bone and len(bone.bone.nml_source_bone) > 0: 50 | bone_map[bone.name] = bone.bone.nml_source_bone 51 | with open(self.filepath, 'w') as f: 52 | f.write(json.dumps(bone_map, indent=4)) 53 | f.close() 54 | 55 | return {'FINISHED'} 56 | -------------------------------------------------------------------------------- /neuron_mocap_live/ops/mocap_connection.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | from mathutils import Vector, Matrix, Quaternion, Euler 3 | from ..mocap_api import * 4 | import math 5 | 6 | mocap_app = None 7 | 8 | mocap_timer = None 9 | 10 | # {key:object_name, value:{key:bone_name, value:[(time_delta, location, rotation_quaternion, scale)]}} 11 | record_data = None 12 | 13 | def init_mocap_api(): 14 | global mocap_app 15 | mocap_app = MCPApplication() 16 | mocap_app.enable_event_cache() 17 | render_settings = MCPRenderSettings() 18 | render_settings.set_up_vector(MCPUpVector.ZAxis, 1) 19 | render_settings.set_coord_system(MCPCoordSystem.RightHanded) 20 | render_settings.set_front_vector(MCPFrontVector.ParityEven, -1) 21 | render_settings.set_rotating_direction(MCPRotatingDirection.CounterClockwise) 22 | render_settings.set_unit(MCPUnit.Meter) 23 | mocap_app.set_render_settings(render_settings) 24 | 25 | def uninit_mocap_api(): 26 | global mocap_app 27 | if mocap_app.is_opened(): 28 | mocap_app.close() 29 | mocap_app = None 30 | 31 | def animate_bone(ctx, obj, obj_translation, obj_quaternion, obj_scale, parent_rest_translation_world, parent_rest_matrix_from_world, parent_scalable, joint): 32 | parent_scale = Vector((1, 1, 1)) 33 | pose_bone = obj.pose.bones.get(joint.get_name()) 34 | rest_matrix_to_world = Matrix() 35 | rest_matrix_from_world = Matrix() 36 | rest_translation_world = Vector() 37 | 38 | if pose_bone != None: 39 | bone = obj.data.bones.get(joint.get_name()) 40 | bone.inherit_scale = 'NONE' 41 | 42 | rest_matrix_to_world = (bone.matrix_local.to_quaternion() @ obj_quaternion).to_matrix().to_4x4() 43 | rest_matrix_from_world = rest_matrix_to_world.inverted() 44 | rest_translation_world = obj_quaternion @ bone.matrix_local.to_translation() 45 | rest_translation_world.x = rest_translation_world.x * obj_scale.x 46 | rest_translation_world.y = rest_translation_world.y * obj_scale.y 47 | rest_translation_world.z = rest_translation_world.z * obj_scale.z 48 | 49 | location = joint.get_local_position() 50 | if location: 51 | location = Vector(location) 52 | location.x = location.x / obj_scale.x 53 | location.y = location.y / obj_scale.y 54 | location.z = location.z / obj_scale.z 55 | 56 | location_in_parent_coord = parent_rest_matrix_from_world @ location 57 | location = rest_matrix_from_world @ location 58 | 59 | ofs = rest_translation_world - parent_rest_translation_world 60 | ofs.x = ofs.x / obj_scale.x 61 | ofs.y = ofs.y / obj_scale.y 62 | ofs.z = ofs.z / obj_scale.z 63 | 64 | ofs_in_parent_coord = parent_rest_matrix_from_world @ ofs 65 | ofs = rest_matrix_to_world.inverted() @ ofs 66 | 67 | if pose_bone.parent and parent_scalable: 68 | for i in range(3): 69 | if not math.isclose(ofs_in_parent_coord[i], 0, abs_tol=1e-4): 70 | parent_scale[i] = location_in_parent_coord[i] / ofs_in_parent_coord[i] 71 | scale = 1 72 | if not math.isclose(ofs[i], 0, abs_tol=1e-4): 73 | scale = location[i] / ofs[i] 74 | if not pose_bone.parent.lock_scale[i]: 75 | location[i] = location[i] - ofs[i] * scale 76 | else: 77 | location = location - ofs 78 | 79 | for i in range(3): 80 | if not pose_bone.lock_location[i]: 81 | pose_bone.location[i] = location[i] 82 | else: 83 | for i in range(3): 84 | if not pose_bone.lock_location[i]: 85 | pose_bone.location[i] = 0 86 | 87 | 88 | if pose_bone.rotation_mode != 'QUATERNION': 89 | pose_bone.rotation_mode = 'QUATERNION' 90 | 91 | rotation_quaternion = rest_matrix_from_world.to_quaternion() @ Quaternion(joint.get_local_rotation()) @ rest_matrix_to_world.to_quaternion() 92 | for i in range(3): 93 | if not pose_bone.lock_rotation[i]: 94 | pose_bone.rotation_quaternion[i + 1] = rotation_quaternion[i + 1] 95 | 96 | if not pose_bone.lock_rotation_w: 97 | pose_bone.rotation_quaternion.w = rotation_quaternion.w 98 | 99 | scale = Vector((1, 1, 1)) 100 | children = joint.get_children() 101 | scalable = (pose_bone != None) and (len(children) == 1) 102 | for child in children: 103 | scale = animate_bone(ctx, obj, obj_translation, obj_quaternion, obj_scale, rest_translation_world, rest_matrix_from_world, scalable, child) 104 | 105 | if scalable: 106 | for i in range(3): 107 | if not pose_bone.lock_scale[i]: 108 | pose_bone.scale[i] = scale[i] 109 | else: 110 | pose_bone.scale[i] = 1 111 | 112 | return parent_scale 113 | 114 | def record_frame(ctx, obj): 115 | bones_data = record_data.get(obj.name) 116 | if bones_data == None: 117 | return 118 | for bone in obj.pose.bones: 119 | bone_data = bones_data.get(bone.name) 120 | if bone_data == None: 121 | continue 122 | bone_data.append((mocap_timer.time_delta, Vector(bone.location), Quaternion(bone.rotation_quaternion), Vector(bone.scale))) 123 | 124 | def animate_armatures_indirect(ctx, source_obj): 125 | for target_obj in bpy.data.objects: 126 | if not target_obj.nml_active: 127 | continue 128 | if target_obj.type == 'ARMATURE' and target_obj.nml_drive_type == 'RETARGET' and target_obj.nml_source_armature == source_obj.name and target_obj.nml_tpose_marked: 129 | for target_pose_bone in target_obj.pose.bones: 130 | source_pose_bone = source_obj.pose.bones.get(target_pose_bone.bone.nml_source_bone) 131 | if not source_pose_bone: 132 | continue 133 | target_pose_bone.bone.inherit_scale = 'NONE' 134 | if target_pose_bone.rotation_mode != 'QUATERNION': 135 | target_pose_bone.rotation_mode = 'QUATERNION' 136 | 137 | source_pose_bone.nml_matrix_calculated = False 138 | if not source_pose_bone.nml_matrix_calculated: 139 | source_matrix_to_world = (source_pose_bone.bone.matrix_local.to_quaternion().inverted() @ 140 | source_obj.matrix_world.to_quaternion().inverted()).to_matrix().to_4x4() 141 | 142 | source_pose_bone.nml_set_matrix_to_world(source_matrix_to_world) 143 | source_pose_bone.nml_set_matrix_from_world(source_matrix_to_world.inverted()) 144 | 145 | scale = source_obj.matrix_world.to_scale() 146 | source_pose_bone.nml_scale_world = scale 147 | 148 | translation = source_obj.matrix_world.to_quaternion() @ source_pose_bone.bone.matrix_local.to_translation() 149 | 150 | translation.x = translation.x * scale.x 151 | translation.y = translation.y * scale.y 152 | translation.z = translation.z * scale.z 153 | source_pose_bone.nml_translation_world = translation 154 | source_pose_bone.nml_matrix_calculated = True 155 | 156 | matrix_source_to_target = target_pose_bone.nml_get_matrix_to_world() @ source_pose_bone.nml_get_matrix_to_world().inverted() 157 | 158 | source_matrix_world = source_pose_bone.matrix_basis @ source_pose_bone.nml_get_matrix_to_world() 159 | target_matrix_world = matrix_source_to_target @ source_matrix_world 160 | target_pose_bone.matrix_basis = target_matrix_world @ target_pose_bone.nml_get_matrix_from_world() @ target_pose_bone.nml_get_matrix_basis_tpose() 161 | target_pose_bone.matrix_basis = target_pose_bone.matrix_basis.to_quaternion().to_matrix().to_4x4() 162 | 163 | if source_pose_bone.name == 'Hips': 164 | up_leg_pose_bone = source_obj.pose.bones.get('LeftUpLeg') 165 | leg_pose_bone = source_obj.pose.bones.get('LeftLeg') 166 | left_foot_bone = source_obj.pose.bones.get('LeftFoot') 167 | hip_height = up_leg_pose_bone.vector.length + leg_pose_bone.vector.length + leg_pose_bone.location.length + \ 168 | left_foot_bone.bone.vector.y + left_foot_bone.location.length 169 | 170 | factor = target_pose_bone.nml_get_translation_world().z / hip_height 171 | translation = source_pose_bone.nml_get_matrix_from_world() @ source_matrix_world.to_translation() 172 | for i in range(3): 173 | translation[i] = translation[i] * source_pose_bone.nml_scale_world[i] 174 | translation[0] = translation[0] * factor 175 | translation[1] = translation[1] * factor 176 | translation[2] = (translation[2] + source_pose_bone.nml_translation_world[2]) * factor - target_pose_bone.nml_translation_world[2] 177 | for i in range(3): 178 | translation[i] = translation[i] / target_pose_bone.nml_scale_world[i] 179 | translation = target_pose_bone.nml_get_matrix_to_world() @ translation 180 | for i in range(3): 181 | target_pose_bone.matrix_basis[i][3] = translation[i] 182 | 183 | if ctx.scene.nml_recording: 184 | record_frame(ctx, target_obj) 185 | 186 | def animate_armatures(ctx, mcp_avatar): 187 | for obj in bpy.data.objects: 188 | if not obj.nml_active: 189 | continue 190 | if obj.type == 'ARMATURE' and obj.nml_chr_name == mcp_avatar.get_name(): 191 | if obj.nml_drive_type == 'DIRECT': 192 | obj_translation, obj_quaternion, obj_scale = obj.matrix_world.decompose() 193 | animate_bone(ctx, obj, obj_translation, obj_quaternion, obj_scale, Vector(), Matrix(), False, mcp_avatar.get_root_joint()) 194 | if ctx.scene.nml_recording: 195 | record_frame(ctx, obj) 196 | animate_armatures_indirect(ctx, obj) 197 | 198 | def poll_data(ctx): 199 | mcp_evts = mocap_app.poll_next_event() 200 | for mcp_evt in mcp_evts: 201 | if mcp_evt.event_type == MCPEventType.AvatarUpdated: 202 | avatar = MCPAvatar(mcp_evt.event_data.avatar_handle) 203 | animate_armatures(ctx, avatar) 204 | 205 | class MocapConnect(bpy.types.Operator): 206 | bl_idname = 'neuron_mocap_live.connect' 207 | bl_label = 'Connect' 208 | 209 | def execute(self, ctx): 210 | global mocap_timer 211 | settings = MCPSettings() 212 | 213 | if ctx.scene.nml_protocol == 'TCP': 214 | settings.set_tcp(ctx.scene.nml_ip, ctx.scene.nml_port) 215 | else: 216 | settings.set_udp(ctx.scene.nml_port) 217 | 218 | if ctx.scene.nml_server == 'Axis Studio': 219 | settings.set_bvh_data(MCPBvhData.Binary) 220 | else: 221 | settings.set_bvh_data(MCPBvhData.BinaryLegacyHumanHierarchy) 222 | 223 | mocap_app.set_settings(settings) 224 | if mocap_app.is_opened() : 225 | mocap_app.close() 226 | status, msg = mocap_app.open() 227 | if status: 228 | ctx.scene.nml_living = True 229 | else: 230 | self.report({'ERROR'}, 'Connect failed: {0}'.format(msg)) 231 | ctx.window_manager.modal_handler_add(self) 232 | mocap_timer = ctx.window_manager.event_timer_add(1 / 60, window = ctx.window) 233 | return {'RUNNING_MODAL'} 234 | 235 | def modal(self, ctx, evt): 236 | if evt.type == 'TIMER': 237 | poll_data(ctx) 238 | if not ctx.scene.nml_living: 239 | return {'FINISHED'} 240 | return {'PASS_THROUGH'} 241 | 242 | class MocapDisconnect(bpy.types.Operator): 243 | bl_idname = 'neuron_mocap_live.disconnect' 244 | bl_label = 'Disconnect' 245 | 246 | def execute(self, ctx): 247 | global mocap_timer 248 | ctx.scene.nml_living = False 249 | status, msg = mocap_app.close() 250 | if not status: 251 | self.report({'ERROR'}, 'Disconnect failed: {0}'.format(msg)) 252 | ctx.window_manager.event_timer_remove(mocap_timer) 253 | return {'FINISHED'} 254 | 255 | class MocapStartRecord(bpy.types.Operator): 256 | bl_idname = 'neuron_mocap_live.start_record' 257 | bl_label = 'Start Record' 258 | 259 | def execute(self, ctx): 260 | global record_data 261 | record_data = dict() 262 | for obj in bpy.data.objects: 263 | if not obj.nml_active: 264 | continue 265 | if obj.type == 'ARMATURE': 266 | bones_data = dict() 267 | for bone in obj.data.bones: 268 | bones_data[bone.name] = list() 269 | record_data[obj.name] = bones_data 270 | 271 | ctx.scene.nml_recording = True 272 | return {'FINISHED'} 273 | 274 | def save_animation_data(ctx, obj, bones_data): 275 | obj.animation_data_create() 276 | action = bpy.data.actions.new(name='mocap') 277 | obj.animation_data.action = action 278 | dt = 1 / ctx.scene.render.fps 279 | for bone in obj.pose.bones: 280 | bone_data = bones_data.get(bone.name) 281 | if bone_data == None: 282 | continue 283 | 284 | data_path = 'pose.bones["%s"].location' % bone.name 285 | for axis_i in range(3): 286 | curve = action.fcurves.new(data_path = data_path, index = axis_i) 287 | keyframe_points = curve.keyframe_points 288 | frame_count = len(bone_data) 289 | keyframe_points.add(frame_count) 290 | frame_time = 0 291 | for frame_i in range(frame_count): 292 | keyframe_points[frame_i].co = ( 293 | frame_time / dt + 1, 294 | bone_data[frame_i][1][axis_i] 295 | ) 296 | frame_time = frame_time + bone_data[frame_i][0] 297 | 298 | data_path = 'pose.bones["%s"].rotation_quaternion' % bone.name 299 | for axis_i in range(4): 300 | curve = action.fcurves.new(data_path = data_path, index = axis_i) 301 | keyframe_points = curve.keyframe_points 302 | frame_count = len(bone_data) 303 | keyframe_points.add(frame_count) 304 | frame_time = 0 305 | for frame_i in range(frame_count): 306 | keyframe_points[frame_i].co = ( 307 | frame_time / dt + 1, 308 | bone_data[frame_i][2][axis_i] 309 | ) 310 | frame_time = frame_time + bone_data[frame_i][0] 311 | 312 | data_path = 'pose.bones["%s"].scale' % bone.name 313 | for axis_i in range(3): 314 | curve = action.fcurves.new(data_path = data_path, index = axis_i) 315 | keyframe_points = curve.keyframe_points 316 | frame_count = len(bone_data) 317 | keyframe_points.add(frame_count) 318 | frame_time = 0 319 | for frame_i in range(frame_count): 320 | keyframe_points[frame_i].co = ( 321 | frame_time / dt + 1, 322 | bone_data[frame_i][3][axis_i] 323 | ) 324 | frame_time = frame_time + bone_data[frame_i][0] 325 | 326 | for cu in action.fcurves: 327 | for bez in cu.keyframe_points: 328 | bez.interpolation = 'LINEAR' 329 | 330 | class MocapStopRecord(bpy.types.Operator): 331 | bl_idname = 'neuron_mocap_live.stop_record' 332 | bl_label = 'Stop Record' 333 | 334 | def execute(self, ctx): 335 | global record_data 336 | ctx.scene.nml_recording = False 337 | for obj in bpy.data.objects: 338 | if not obj.nml_active: 339 | continue 340 | bones_data = record_data.get(obj.name) 341 | if bones_data != None: 342 | save_animation_data(ctx, obj, bones_data) 343 | 344 | record_data = None 345 | return {'FINISHED'} 346 | -------------------------------------------------------------------------------- /neuron_mocap_live/ops/tpose_recorder.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | class MarkTPose(bpy.types.Operator): 4 | bl_idname = 'neuron_mocap_live.mark_tpose' 5 | bl_label = 'Set as T-Pose' 6 | 7 | def execute(self, ctx): 8 | for bone in ctx.active_object.pose.bones: 9 | matrix_basis_tpose = bone.matrix_basis 10 | bone.nml_set_matrix_basis_tpose(matrix_basis_tpose) 11 | 12 | matrix_world_tpose = bone.matrix @ ctx.active_object.matrix_world 13 | bone.nml_set_matrix_world_tpose(matrix_world_tpose) 14 | 15 | matrix_to_world = (bone.matrix.to_quaternion().inverted() @ ctx.active_object.matrix_world.to_quaternion().inverted()).to_matrix().to_4x4() 16 | bone.nml_set_matrix_to_world(matrix_to_world) 17 | 18 | bone.nml_set_matrix_from_world(matrix_to_world.inverted()) 19 | 20 | scale = ctx.active_object.matrix_world.to_scale() 21 | bone.nml_scale_world = scale 22 | 23 | translation = bone.bone.matrix_local.to_quaternion() @ matrix_basis_tpose.to_translation() 24 | translation = ctx.active_object.matrix_world.to_quaternion() @ (bone.bone.matrix_local.to_translation() + translation) 25 | 26 | translation.x = translation.x * scale.x 27 | translation.y = translation.y * scale.y 28 | translation.z = translation.z * scale.z 29 | bone.nml_translation_world = translation 30 | 31 | ctx.active_object.nml_tpose_marked = True 32 | return {'FINISHED'} 33 | 34 | class SetTPose(bpy.types.Operator): 35 | bl_idname = 'neuron_mocap_live.set_tpose' 36 | bl_label = 'Set T-Pose' 37 | 38 | @classmethod 39 | def poll(cls, ctx): 40 | return ctx.active_object.nml_tpose_marked 41 | 42 | def execute(self, ctx): 43 | if ctx.active_object.nml_tpose_marked: 44 | for bone in ctx.active_object.pose.bones: 45 | bone.matrix_basis = bone.nml_get_matrix_basis_tpose() 46 | else: 47 | self.report(type = {'ERROR'}, message = 'T-Pose is not marked') 48 | 49 | return {'FINISHED'} -------------------------------------------------------------------------------- /neuron_mocap_live/ui/__init__.py: -------------------------------------------------------------------------------- 1 | from .connection_panel import * 2 | from .add_armature_menu import * 3 | from .armature_property_panel import * 4 | from .record_pannel import * -------------------------------------------------------------------------------- /neuron_mocap_live/ui/add_armature_menu.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | def draw_add_armature_menu(self, ctx): 4 | layout = self.layout 5 | layout.separator() 6 | layout.operator('neuron_mocap_live.add_pns_armature', text='Axis Studio Armature', icon='OUTLINER_OB_ARMATURE') 7 | layout.operator('neuron_mocap_live.add_pns_thumb_open_armature', text='Axis Studio(Thumb open) Armature', icon='OUTLINER_OB_ARMATURE') 8 | layout.operator('neuron_mocap_live.add_pn_pro_armature', text='Axis Legacy Armature', icon='OUTLINER_OB_ARMATURE') 9 | -------------------------------------------------------------------------------- /neuron_mocap_live/ui/armature_property_panel.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | class ArmaturePropertyPanel(bpy.types.Panel): 4 | bl_idname = 'NOITOM_PT_ArmaturePropertyPanel' 5 | bl_label = 'Armature' 6 | bl_space_type = 'VIEW_3D' 7 | bl_region_type = 'UI' 8 | bl_category = "NEURON MOCAP" 9 | 10 | @classmethod 11 | def poll(cls, ctx): 12 | return ctx.active_object != None and ctx.active_object.type == 'ARMATURE' 13 | 14 | def draw(self, ctx): 15 | layout = self.layout 16 | col = layout.column() 17 | row = col.row(align=True) 18 | 19 | if ctx.scene.nml_recording: 20 | row.label(text = 'Drive Type') 21 | row.label(text = ctx.active_object.nml_drive_type) 22 | else: 23 | row.label(text = 'Drive Type') 24 | row.prop(ctx.active_object, 'nml_drive_type', text = '') 25 | 26 | if ctx.active_object.nml_drive_type == 'DIRECT': 27 | self.draw_direct_drive_settings(ctx) 28 | else: 29 | self.draw_retarget_drive_settings(ctx) 30 | 31 | 32 | def draw_direct_drive_settings(self, ctx): 33 | layout = self.layout 34 | col = layout.column() 35 | 36 | if ctx.scene.nml_recording: 37 | row = col.row(align = True) 38 | row.label(text='Character Name') 39 | row.label(text=ctx.active_object.nml_chr_name) 40 | else: 41 | row = col.row(align = True) 42 | row.label(text='Live') 43 | row.prop(ctx.active_object, 'nml_active', text = '') 44 | 45 | row = col.row(align = True) 46 | row.label(text='Character Name') 47 | row.prop(ctx.active_object, 'nml_chr_name', text = '') 48 | 49 | def draw_retarget_drive_settings(self, ctx): 50 | layout = self.layout 51 | col = layout.column() 52 | 53 | row = col.row(align = True) 54 | row.label(text='Live') 55 | row.prop(ctx.active_object, 'nml_active', text = '') 56 | 57 | row = col.row(align = True) 58 | row.operator('neuron_mocap_live.mark_tpose', text = 'Mark T-Pose', icon='OUTLINER_DATA_ARMATURE') 59 | row.operator('neuron_mocap_live.set_tpose', text = 'Set T-Pose', icon='ARMATURE_DATA') 60 | 61 | if not ctx.active_object.nml_tpose_marked: 62 | row = col.row(align = True) 63 | row.label(text="Mark T-Pose is required", icon='ERROR') 64 | 65 | row = col.row() 66 | row.label(text='Source') 67 | row.prop_search(ctx.active_object, 'nml_source_armature', ctx.scene, 'objects', text='') 68 | 69 | source = bpy.data.objects.get(ctx.active_object.nml_source_armature) 70 | if source and source.type == 'ARMATURE': 71 | col = layout.column() 72 | row = col.row() 73 | row.operator('neuron_mocap_live.auto_map_bone', text = 'Auto Detect', icon='BONE_DATA') 74 | row = col.row() 75 | row.operator('neuron_mocap_live.clear_bone_map', text = 'Clear', icon='PANEL_CLOSE') 76 | row = col.row() 77 | row.operator('neuron_mocap_live.load_bone_map', text = 'Load', icon='FILEBROWSER') 78 | row = col.row() 79 | row.operator('neuron_mocap_live.save_bone_map', text = 'Save', icon='FILE_TICK') 80 | 81 | for bone in ctx.active_object.pose.bones: 82 | if bone.bone.hide: 83 | continue 84 | row = col.row() 85 | row.label(text = bone.name) 86 | row.prop_search(bone.bone, 'nml_source_bone', source.pose, 'bones', text='') -------------------------------------------------------------------------------- /neuron_mocap_live/ui/connection_panel.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | class ConnectionPanel(bpy.types.Panel): 4 | bl_idname = 'NOITOM_PT_ConnectionPanel' 5 | bl_label = 'Connection' 6 | bl_space_type = 'VIEW_3D' 7 | bl_region_type = 'UI' 8 | bl_category = "NEURON MOCAP" 9 | 10 | def draw(self, ctx): 11 | layout = self.layout 12 | col = layout.column() 13 | 14 | if not ctx.scene.nml_living: 15 | row = col.row(align=True) 16 | row.label(text = 'Server') 17 | row.prop(ctx.scene, 'nml_server', text = '') 18 | 19 | row = col.row(align=True) 20 | row.label(text = 'Protocol') 21 | row.prop(ctx.scene, 'nml_protocol', text = '') 22 | 23 | if ctx.scene.nml_protocol == 'TCP': 24 | row = col.row(align=True) 25 | row.label(text = 'IP') 26 | row.prop(ctx.scene, 'nml_ip', text = '') 27 | 28 | row = col.row(align=True) 29 | row.label(text = 'Port') 30 | row.prop(ctx.scene, 'nml_port', text = '') 31 | 32 | row = col.row(align=True) 33 | row.operator('neuron_mocap_live.connect', icon='URL') 34 | else: 35 | row = col.row(align=True) 36 | row.label(text = 'Server') 37 | row.label(text = ctx.scene.nml_server) 38 | 39 | row = col.row(align=True) 40 | row.label(text = 'Protocol') 41 | row.label(text = ctx.scene.nml_protocol) 42 | 43 | if ctx.scene.nml_protocol == 'TCP': 44 | row = col.row(align=True) 45 | row.label(text = 'IP') 46 | row.label(text = ctx.scene.nml_ip) 47 | 48 | row = col.row(align=True) 49 | row.label(text = 'Port') 50 | row.label(text = str(ctx.scene.nml_port)) 51 | 52 | if not ctx.scene.nml_recording: 53 | row = col.row(align=True) 54 | row.operator('neuron_mocap_live.disconnect', icon='MOD_WAVE') -------------------------------------------------------------------------------- /neuron_mocap_live/ui/record_pannel.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | 3 | class RecordPanel(bpy.types.Panel): 4 | bl_idname = 'NOITOM_PT_RecordPanel' 5 | bl_label = 'Record' 6 | bl_space_type = 'VIEW_3D' 7 | bl_region_type = 'UI' 8 | bl_category = "NEURON MOCAP" 9 | 10 | @classmethod 11 | def poll(cls, ctx): 12 | return ctx.scene.nml_living 13 | 14 | def draw(self, ctx): 15 | layout = self.layout 16 | col = layout.column() 17 | 18 | if not ctx.scene.nml_recording: 19 | row = col.row(align=True) 20 | row.operator('neuron_mocap_live.start_record', icon='REC') 21 | else: 22 | row = col.row(align=True) 23 | row.operator('neuron_mocap_live.stop_record', icon='REC', depress=True) --------------------------------------------------------------------------------