Update
5
.gitignore
vendored
@@ -45,3 +45,8 @@ Thumbs.db
|
|||||||
|
|
||||||
build/
|
build/
|
||||||
dist/
|
dist/
|
||||||
|
|
||||||
|
plug-ins/ARTv1/
|
||||||
|
plug-ins/ARTv1_Origin/
|
||||||
|
plug-ins/ARTv2/
|
||||||
|
plug-ins/ARTv2_Origin/
|
||||||
|
|||||||
@@ -1,210 +0,0 @@
|
|||||||
"""
|
|
||||||
GS CurveTools License:
|
|
||||||
This collection of code named GS CurveTools is a property of George Sladkovsky (Yehor Sladkovskyi)
|
|
||||||
and can not be copied or distributed without his written permission.
|
|
||||||
|
|
||||||
GS CurveTools v1.3.8 Personal
|
|
||||||
Copyright 2024, George Sladkovsky (Yehor Sladkovskyi)
|
|
||||||
All Rights Reserved
|
|
||||||
|
|
||||||
UI font is Roboto that is licensed under the Apache 2.0 License:
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Autodesk Maya is a property of Autodesk, Inc:
|
|
||||||
https://www.autodesk.com/
|
|
||||||
|
|
||||||
Social Media and Contact Links:
|
|
||||||
|
|
||||||
Discord Server: https://discord.gg/f4DH6HQ
|
|
||||||
Online Store: https://sladkovsky3d.artstation.com/store
|
|
||||||
Online Documentation: https://gs-curvetools.readthedocs.io/
|
|
||||||
Twitch Channel: https://www.twitch.tv/videonomad
|
|
||||||
YouTube Channel: https://www.youtube.com/c/GeorgeSladkovsky
|
|
||||||
ArtStation Portfolio: https://www.artstation.com/sladkovsky3d
|
|
||||||
Contact Email: george.sladkovsky@gmail.com
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
https://www.artstation.com/marketplace-product-eula
|
|
||||||
|
|
||||||
Marketplace Product & Services Agreement
|
|
||||||
|
|
||||||
End User Agreement
|
|
||||||
|
|
||||||
This Marketplace End User Agreement applies to all downloadable products and professional services (e.g. mentorships, personal training, portfolio reviews) sold via the ArtStation Marketplace, unless a custom agreement or license is provided by the seller.
|
|
||||||
|
|
||||||
The EUA is an agreement between the buyer and the seller providing the goods or services.
|
|
||||||
|
|
||||||
PLEASE READ THIS DOCUMENT CAREFULLY. IT SIGNIFICANTLY ALTERS YOUR LEGAL RIGHTS AND REMEDIES.
|
|
||||||
|
|
||||||
BY CLICKING “I AGREE” OR DOWNLOADING OR USING THE DIGITAL PRODUCT OR RECEIVING THE PROFESSIONAL SERVICES TO WHICH THIS AGREEMENT RELATES YOU ACCEPT ALL OF THIS AGREEMENT’S TERMS, INCLUDING THE DISCLAIMERS OF WARRANTIES AND LIMITATIONS ON DAMAGES, USE AND TRANSFERABILITY. IF YOU DO NOT ACCEPT THIS AGREEMENT’S TERMS, DO NOT DOWNLOAD, INSTALL OR USE THE DIGITAL PRODUCT OR RECEIVE OR USE THE PROFESSIONAL SERVICES.
|
|
||||||
|
|
||||||
This end-user agreement (“Agreement”) is a legally binding agreement between you, the licensee and customer (“you” or “your”), and the provider (“we” or “us” or “our”) of the digital products (“Products”) or instructional, training, mentorship or other professional service packages (“Professional Services”) that you purchase through the ArtStation Marketplace, regarding your rights and obligations regarding those Products and Professional Services.
|
|
||||||
|
|
||||||
1. Your Status
|
|
||||||
In this Agreement, “you” means the person or entity acquiring rights in the Products or purchasing Professional Services. That may be a natural person, or a corporate or business entity or organization.
|
|
||||||
|
|
||||||
(a) If you are a natural person then you must be, and you confirm that you are, at least 13 years old. If you are between 13 years and the age of majority in your jurisdiction of residence, you confirm that your parent or legal guardian has reviewed and agrees to this Agreement and is happy for you to access and use the Product or receive the Professional Services.
|
|
||||||
|
|
||||||
(b) If you are a corporate entity then: (i) the rights granted under this Agreement are granted to that entity; (ii) you represent and warrant that the individual completing and accepting this Agreement is an authorized your representative and has the authority to legally bind that you to the Agreement; and (iii) to the extent that one or more of your employees are granted any rights in the Product or to receive Professional Services under this Agreement, you will ensure that your employees comply with this Agreement and you will be responsible and liable for any breach of this Agreement by any employee.
|
|
||||||
|
|
||||||
2. ArtStation
|
|
||||||
ArtStation is a division of Epic Games, Inc., You acknowledge and agree that Epic is a third-party beneficiary of this Agreement and therefore will be entitled to directly enforce and rely upon any provision in this Agreement that confers a benefit on, or rights in favour of, Epic. In addition, you authorize Epic to act as your authorized representative to file a lawsuit or other formal action against a licensor in a court or with any other governmental authority if Epic knows or suspects that a licensor breached any representations or warranties under this Agreement. The foregoing authorization is nonexclusive, and Epic shall be under no obligation to pursue any claim. Epic will not initiate any such action on your behalf without first consulting with and obtaining your approval.
|
|
||||||
|
|
||||||
Products
|
|
||||||
The following sections 3 through 9 apply to any Products you acquire from us through the ArtStation Marketplace:
|
|
||||||
|
|
||||||
3. Product Licence
|
|
||||||
Subject to this Agreement’s terms and conditions, we hereby grant you a limited, non-exclusive, worldwide, non-transferable right and licence to (which will be perpetual unless the licence terminates as set out in this Agreement): (a) download the Product; and (b) copy and use the Product. We reserve all rights not expressly granted to you under this Agreement.
|
|
||||||
|
|
||||||
4. Licence Scope and Restrictions
|
|
||||||
(a) Tutorials
|
|
||||||
You are purchasing ONE licence to create ONE copy of the Product for use by you only (or, if you are a corporate entity, for use by a single authorized employee).
|
|
||||||
|
|
||||||
If this Product is bundled with a stock digital asset then you receive a limited personal use licence regarding that stock digital asset, and you may use that stock digital asset for your personal use only. You will not use that stock digital asset in any commercial manner unless you purchase a separate commercial licence.
|
|
||||||
|
|
||||||
(b) Installable Tools
|
|
||||||
You may purchase one or more licences for the Product. A single licence allows you to install the Product on a single computer at a time for use by a single authorized user. If you are a corporate entity and the authorized employee completing the transaction on your behalf purchases multiple licences, you may choose to store the Product on a single server or shared hard drive for use by a single authorized employee at a time for each licence purchased.
|
|
||||||
|
|
||||||
Provided that you comply with the restrictions on users set out above, you may use the Product on an unlimited number of projects.
|
|
||||||
|
|
||||||
(c) Stock Assets
|
|
||||||
Subject to the restrictions set out in this Agreement, you may copy, use, modify, adapt, translate, distribute, publicly display, transmit, broadcast, and create derivative works from the Product in works you create (“Works”), which may include things like films, videos, multi-media projects, computer games, models, images, publications, broadcasts, documents, and presentations.
|
|
||||||
|
|
||||||
If you are a corporate entity, you may make the Product available for use by your employees in accordance with this Agreement (for example, by storing the Product on a network server).
|
|
||||||
|
|
||||||
You may only share the Product with external people or entities where:
|
|
||||||
- You are collaborating with the external parties in the creation of your Work and you need to share the Product for that purpose, provided that any external party that receives the Product may only use it in your Work and must secure and limit access to the Product for that purpose;
|
|
||||||
- You are working as a contractor for a client in the creation of a Work and need to share the Product with your client, or any external parties working with your client, provided that your client and any such external parties may use the Product only for your client’s Work, and all parties secure and limit access to the Product for that purpose.
|
|
||||||
|
|
||||||
For any other use of the Product by any other party, that party must purchase a licence to the Product.
|
|
||||||
|
|
||||||
In addition to any other restrictions in this Agreement, you will not:
|
|
||||||
- publish, sell, license, offer or make available for sale or licensing, or otherwise distribute the Product except as part of a Work or through a form of sharing that is authorized in this Agreement; or
|
|
||||||
- publish, distribute or make available the Product through any online clearinghouse platform.
|
|
||||||
|
|
||||||
FURTHER SPECIFIC TERMS
|
|
||||||
In addition to the restrictions set out above, the following terms and conditions apply to the following forms of commercial licences for the Product:
|
|
||||||
|
|
||||||
Standard Commercial Licence
|
|
||||||
If you have purchased a Standard Commercial licence then you may exercise your rights under that licence:
|
|
||||||
- for personal use on an unlimited number of personal projects that are not used or distributed in any commercial manner; and
|
|
||||||
- respect to one commercial Work, with up to a maximum of, as applicable, 2,000 sales of the Work or 20,000 monthly views of the Work.
|
|
||||||
|
|
||||||
Extended Commercial Licence
|
|
||||||
If you have purchased an Extended Commercial licence then you may exercise your rights under that licence:
|
|
||||||
- for personal use on an unlimited number of personal projects that are not used or distributed in any commercial manner; and
|
|
||||||
- with respect to any number of commercial Works, with no limit on sales or views.
|
|
||||||
|
|
||||||
5. Additional Restrictions
|
|
||||||
Except as expressly permitted under this Agreement, you will not:
|
|
||||||
|
|
||||||
(a) make any copy of the Product except for archival or backup purposes;
|
|
||||||
|
|
||||||
(b) circumvent or disable any access control technology, security device, procedure, protocol, or technological protection mechanism that may be included or established in or as part of the Product;
|
|
||||||
|
|
||||||
(c) hack, reverse engineer, decompile, disassemble, modify or create derivative works of the Product or any part of the Product;
|
|
||||||
|
|
||||||
(d) publish, sell distribute or otherwise make the Product available to others to use, download or copy;
|
|
||||||
|
|
||||||
(e) transfer or sub-license the Product or any rights under this Agreement to any third party, whether voluntarily or by operation of law;
|
|
||||||
|
|
||||||
(f) use the Product for any purpose that may be defamatory, threatening, abusive, harmful or invasive of anyone’s privacy, or that may otherwise violate any law or give rise to civil or other liability;
|
|
||||||
|
|
||||||
(g) misrepresent yourself as the creator or owner of the Property;
|
|
||||||
|
|
||||||
(h) remove or modify any proprietary notice, symbol or label in or on the Product;
|
|
||||||
|
|
||||||
(i) directly or indirectly assist, facilitate or encourage any third party to carry on any activity prohibited by this Agreement.
|
|
||||||
|
|
||||||
6. Proprietary Rights
|
|
||||||
The Product is protected by copyright laws and international copyright treaties, as well as other intellectual property laws and treaties. You are licensing the Product and the right to access, install and use the Product in accordance with this Agreement, not buying the Product. As between you and us, we own all right, title and interest in and to the Product, and you are not acquiring any ownership of or rights in the Product except the limited rights granted under this Agreement.
|
|
||||||
|
|
||||||
7. No Epic Support
|
|
||||||
You acknowledge and agree that you are licensing the Product from us (the Provider), not from Epic, and that Epic has no obligation to support the Product.
|
|
||||||
|
|
||||||
8. Interruptions and Errors
|
|
||||||
Your use of the Product might be interrupted and might not be free of errors.
|
|
||||||
|
|
||||||
9. Updates
|
|
||||||
We have no obligation to update the Product.
|
|
||||||
|
|
||||||
Professional Services
|
|
||||||
The following sections 10 and 11 apply to any Professional Services you purchase from us through the ArtStation Marketplace:
|
|
||||||
|
|
||||||
10. Provision of Professional Services
|
|
||||||
We will provide the Professional Services directly to you and, subject to this Agreement, will assume all responsibility for all aspects of the Professional Services. We represent and warrant that we have the right to offer and provide the Professional Services and that we have appropriate qualifications and experience to provide the Professional Services.
|
|
||||||
|
|
||||||
11. Epic is not Involved
|
|
||||||
You acknowledge and agree that:
|
|
||||||
|
|
||||||
(a) Epic is only a provider of the online ArtStation Marketplace where you purchased the Professional Services, and does not provide or exercise any control or oversight over us or the Professional Services, and is not responsible for us or the Professional Services or any shortcomings in them, including any damages, losses or legal issues caused by us or the Professional Services;
|
|
||||||
|
|
||||||
(b) this Agreement (and any dispute under it) is an agreement between us and you only, and not with Epic, and Epic is not a party to this Agreement;
|
|
||||||
|
|
||||||
(c) we are not Epic’s employee, agent or subcontractor;
|
|
||||||
|
|
||||||
(d) Epic does not have any obligation to attempt to resolve any dispute between us and you; and
|
|
||||||
|
|
||||||
(e) we will provide the Professional Services directly to you, and we (and not Epic) are solely responsible for the Professional Services, and Epic has no obligation or liability to you with respect to the Professional Services.
|
|
||||||
|
|
||||||
Both Products and Services
|
|
||||||
The following sections 12 through 25 apply to all Products or Services you purchase from us through the ArtStation Marketplace:
|
|
||||||
|
|
||||||
12. Disclaimer
|
|
||||||
ANY PRODUCTS OR PROFESSIONAL SERVICES ARE PROVIDED ON AN “AS IS” AND “AS AVAILABLE” BASIS, WITHOUT ANY REPRESENTATIONS, WARRANTIES OR CONDITIONS OF ANY KIND.
|
|
||||||
|
|
||||||
TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW WE DISCLAIM, AND YOU WAIVE (WITH REGARD TO US AND ALSO TO EPIC, ITS AFFILIATES, AND ITS AND THEIR LICENSORS AND SERVICE PROVIDERS (COLLECTIVELY, THE “EPIC PARTIES”), ALL TERMS, CONDITIONS, GUARANTEES, REPRESENTATIONS AND WARRANTIES (EXPRESS, IMPLIED, STATUTORY AND OTHERWISE), IN RESPECT OF THE PRODUCTS AND PROFESSIONAL SERVICES, INCLUDING THOSE OF MERCHANTABILITY, NON-INFRINGEMENT, TITLE, QUALITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
|
|
||||||
NEITHER WE NOR ANY OF THE EPIC PARTIES REPRESENT OR WARRANT THAT: (A) ANY PRODUCT OR PROFESSIONAL SERVICE IS ACCURATE, COMPLETE, RELIABLE, CURRENT OR ERROR-FREE; (B) ANY PRODUCT OR PROFESSIONAL SERVICE WILL MEET YOUR REQUIREMENTS OR EXPECTATIONS; (C) ANY PRODUCT OR PROFESSIONAL SERVICES IS FREE OF VIRUSES OR OTHER HARMFUL COMPONENTS; OR (D) ANY DEFECTS IN ANY PRODUCT OR PROFESSIONAL SERVICE WILL BE CORRECTED.
|
|
||||||
|
|
||||||
13. Exclusion and Limitation of Liability
|
|
||||||
(a) YOU DOWNLOAD, INSTALL AND OTHERWISE USE ALL PRODUCTS, AND RECEIVE AND USE ALL PROFESSIONAL SERVICES, AT YOUR OWN RISK. YOU AGREE TO, AND HEREBY DO:
|
|
||||||
|
|
||||||
(i) WAIVE ANY CLAIMS THAT YOU MAY HAVE AGAINST US OR THE EPIC PARTIES OR OUR RESPECTIVE DIRECTORS, OFFICERS, EMPLOYEES, AGENTS, REPRESENTATIVES, LICENSORS, SUCCESSORS AND ASSIGNS (COLLECTIVELY THE “RELEASEES”) ARISING FROM OR RELATING TO ANY PRODUCTS OR PROFESSIONAL SERVICES, AND
|
|
||||||
|
|
||||||
(ii) RELEASE THE RELEASEES FROM ANY LIABILITY FOR ANY LOSS, DAMAGE, EXPENSE OR INJURY ARISING FROM OR RELATING TO YOUR USE OF ANY PRODUCT OR PROFESSIONAL SERVICE, WHETHER ARISING IN TORT (INCLUDING NEGLIGENCE), CONTRACT OR OTHERWISE, EVEN IF THE RELEASEES ARE EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH LOSS, INJURY OR DAMAGE AND EVEN IF THAT LOSS, INJURY OR DAMAGE IS FORESEEABLE.
|
|
||||||
|
|
||||||
(b) NEITHER WE NOR THE EPIC PARTIES WILL BE LIABLE FOR ANY LOSSES, DAMAGES, CLAIMS OR EXPENSES THAT CONSTITUTE: (I) LOSS OF INTEREST, PROFIT, BUSINESS, CUSTOMERS OR REVENUE; (II) BUSINESS INTERRUPTIONS; (III) COST OF REPLACEMENT PRODUCTS OR SERVICES; OR (IV) LOSS OF OR DAMAGE TO REPUTATION OR GOODWILL.
|
|
||||||
|
|
||||||
(c) NEITHER WE NOR THE EPIC PARTIES WILL BE LIABLE FOR ANY LOSSES, DAMAGES, CLAIMS OR EXPENSES THAT CONSTITUTE INCIDENTAL, CONSEQUENTIAL, SPECIAL, PUNITIVE, EXEMPLARY, MULTIPLE OR INDIRECT DAMAGES, EVEN IF WE HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, DAMAGES, CLAIMS OR EXPENSES.
|
|
||||||
|
|
||||||
(d) MAXIMUM LIABILITY: IF, DESPITE THE LIMITATIONS SET OUT ABOVE, WE OR ANY EPIC PARTY BECOME LIABLE TO YOU IN RESPECT OF ANY PRODUCT OR PROFESSIONAL SERVICE OR OTHERWISE UNDER THIS AGREEMENT, THE ENTIRE CUMULATIVE LIABILITY OF US AND THE EPIC PARTIES, AND YOUR EXCLUSIVE AND CUMULATIVE REMEDY, FOR ANY DAMAGES (REGARDLESS OF THE CAUSE OR FORM OR ACTION), WILL BE LIMITED TO CAD$10.
|
|
||||||
|
|
||||||
14. Indemnity
|
|
||||||
As a condition of your use of any Product or any Professional Services, you agree to hold harmless and indemnify the Releasees from any liability for any loss or damage to any third party resulting from your access to, installation or use of the Product or your receipt and use of the Professional Services.
|
|
||||||
|
|
||||||
15. Term and Termination
|
|
||||||
This Agreement is effective until terminated. Your rights under this Agreement will terminate automatically without notice if: (a) you breach any terms of this Agreement; or (b) you do not complete payment for the Product or Professional Services, or any payment you make is refunded, reversed or cancelled for any reason. Upon this Agreement’s termination, you will cease all use of the Product and destroy all copies, full or partial, of the Product in your possession. Sections 11 through 25 will survive the termination of this Agreement.
|
|
||||||
|
|
||||||
16. Compliance with Laws
|
|
||||||
You will comply with all applicable laws when using any Product or Professional Services (including intellectual property and export control laws).
|
|
||||||
|
|
||||||
17. Entire Agreement
|
|
||||||
This Agreement supersedes all prior agreements of the parties regarding the Product or Professional Services, and constitutes the whole agreement with respect to the Product or Professional Services.
|
|
||||||
|
|
||||||
18. Disputes
|
|
||||||
If you have any concerns about the Product or Professional Services, please contact us through our ArtStation Marketplace account and we will work with you to try to resolve the issue. You acknowledge and agree that any such dispute is between you and us, and that Epic will not be involved in the dispute and has no obligation to try to resolve the dispute.
|
|
||||||
|
|
||||||
19. Persons Bound
|
|
||||||
This Agreement will enure to the benefit of and be binding upon the parties and their heirs, executors, administrators, legal representatives, lawful successors and permitted assigns.
|
|
||||||
|
|
||||||
20. Assignment
|
|
||||||
We may assign this Agreement without notice to you. You may not assign this Agreement or any of your rights under it without our prior written consent, which we will not withhold unreasonably.
|
|
||||||
|
|
||||||
21. Waiver
|
|
||||||
No waiver, delay, or failure to act by us regarding any particular default or omission will prejudice or impair any of our rights or remedies regarding that or any subsequent default or omission that are not expressly waived in writing.
|
|
||||||
|
|
||||||
22. Applicable Law and Jurisdiction
|
|
||||||
You agree that this Agreement will be deemed to have been made and executed in the State of North Carolina, U.S.A., and any dispute will be resolved in accordance with the laws of North Carolina, excluding that body of law related to choice of laws, and of the United States of America. Any action or proceeding brought to enforce the terms of this Agreement or to adjudicate any dispute must be brought in the Superior Court of Wake County, State of North Carolina or the United States District Court for the Eastern District of North Carolina. You agree to the exclusive jurisdiction and venue of these courts. You waive any claim of inconvenient forum and any right to a jury trial. The Convention on Contracts for the International Sale of Goods will not apply. Any law or regulation which provides that the language of a contract shall be construed against the drafter will not apply to this Agreement.
|
|
||||||
|
|
||||||
23. Legal Effect
|
|
||||||
This Agreement describes certain legal rights. You may have other rights under the laws of your country. This Agreement does not change your rights under the laws of your country if the laws of your country do not permit it to do so.
|
|
||||||
|
|
||||||
24. Interpretation
|
|
||||||
In this Agreement, "we", "us", and "our" refer to the licensor of the Product alone and never refer to the combination of you and that licensor (that combination is referred to as "the parties"), or the combination of you or the licensor with Epic.
|
|
||||||
|
|
||||||
25. Artificial Intelligence
|
|
||||||
For purposes of this Agreement, “Generative AI Programs” means artificial intelligence, machine learning, deep learning, neural networks, or similar technologies designed to automate the generation of or aid in the creation of new content, including but not limited to audio, visual, or text-based content.
|
|
||||||
|
|
||||||
We (the licensor of the Product) represent and warrant that where the Product was created using Generative AI Programs, we have applied the “CreatedWithAI” tag. Under this Agreement, a Product is considered to be created using Generative AI Programs where a material portion of a Product is generated with Generative AI Programs, whether characters, backgrounds, or other material elements. A Product is not considered to be created using Generative AI Programs merely for use of features that solely operate on a Product (e.g., AI-based upscaling or content-aware fill).
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
GS CurveTools installation
|
|
||||||
|
|
||||||
1. Copy gs_curvetools folder to {Path to Documents}\Documents\Maya\{Maya Version}\scripts\
|
|
||||||
|
|
||||||
Example of the final folder structure:
|
|
||||||
|
|
||||||
Documents\Maya\2022\scripts\gs_curvetools\fonts
|
|
||||||
Documents\Maya\2022\scripts\gs_curvetools\icons
|
|
||||||
Documents\Maya\2022\scripts\gs_curvetools\utils
|
|
||||||
Documents\Maya\2022\scripts\gs_curvetools\__init__.py
|
|
||||||
Documents\Maya\2022\scripts\gs_curvetools\core.py
|
|
||||||
Documents\Maya\2022\scripts\gs_curvetools\init.py
|
|
||||||
Documents\Maya\2022\scripts\gs_curvetools\LICENSE.txt
|
|
||||||
Documents\Maya\2022\scripts\gs_curvetools\main.py
|
|
||||||
Documents\Maya\2022\scripts\gs_curvetools\README.txt
|
|
||||||
|
|
||||||
2. Run Maya
|
|
||||||
|
|
||||||
3. Copy and paste this line to "Python" command box and press "Enter":
|
|
||||||
|
|
||||||
import gs_curvetools.init as ct_init;from imp import reload;reload(ct_init);ct_init.Init();
|
|
||||||
|
|
||||||
IMPORTANT: There should be no spaces or tabs before this command!
|
|
||||||
|
|
||||||
4. Look for GS tab on your Shelf
|
|
||||||
|
|
||||||
5. Click CT UI button to run the menu. Click again to hide the menu.
|
|
||||||
|
|
||||||
NOTES:
|
|
||||||
>> To reset to factory defaults click CT with "refresh" arrow button.
|
|
||||||
>> To stop all scripts and close the menu press CT DEL button.
|
|
||||||
>> You can use middle-mouse button drag to move the buttons to any tab.
|
|
||||||
>> All the hotkeys are available in Hotkey Editor > Custom Scripts > GS > GS_CurveTools.
|
|
||||||
>> Always repeat initialization steps when updating the plug-in to a new version.
|
|
||||||
>> You can always repeat initialization steps if you lost control buttons or shelf.
|
|
||||||
@@ -1,951 +0,0 @@
|
|||||||
<!--
|
|
||||||
Tooltips are added in format:
|
|
||||||
# Widget
|
|
||||||
Tooltip
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- main.py -->
|
|
||||||
|
|
||||||
<!-- Options Menu -->
|
|
||||||
# importCurves
|
|
||||||
Imports Curves that were Exported using the Export Button.
|
|
||||||
NOTE: Only import files that were exported using Export Curves button.
|
|
||||||
WARNING: This operation is NOT undoable!
|
|
||||||
|
|
||||||
# exportCurves
|
|
||||||
Exports selected curves into a .curves (or .ma) file. Can then be Imported using Import Curves.
|
|
||||||
|
|
||||||
# changeScaleFactor
|
|
||||||
Opens a window that controls the Scale Factor and Precision Scale.
|
|
||||||
***
|
|
||||||
Scale factor determines the initial size of the created curve and adjusts other parameters.
|
|
||||||
Scale factor is stored in a global preset, in the scene and on each curve.
|
|
||||||
Priority of scale factors Curve>Scene>Global.
|
|
||||||
Setting the appropriate scale factor before starting a project can help to have a good initial Width and size of the cards.
|
|
||||||
***
|
|
||||||
Precision Scale controls the world scale of the curve without changing the curve appearance, CV positions and other parameters.
|
|
||||||
Lowering the Precision scale (from 1.0 to 0.05 for example) helps to fix the geometry deformation on very small curves.
|
|
||||||
The default value of 0.05 should be sufficient for most cases. Values lower than 0.01 can affect performance.
|
|
||||||
***
|
|
||||||
Normalize Selected and Normalize Selected to Default will change the Scale Factor and Precision Scale of the selected curves, without changing their appearance or CV positions.
|
|
||||||
Normalize Selected to Default will reset the slider values to default (0.5 and 0.05) and Normalize Selected will use the current slider values.
|
|
||||||
|
|
||||||
# normalizeSelectedButton
|
|
||||||
Normalize selected curves to the chosen slider values.
|
|
||||||
Normalization will not change the CV positions or geometry appearance, but the selected objects will have new Scale Factor and Precision Scale applied.
|
|
||||||
|
|
||||||
# normalizeSelectedToDefaultButton
|
|
||||||
Normalize selected curves to the default slider values (0.5, 0.05).
|
|
||||||
Resets the slider values to the default.
|
|
||||||
Normalization will not change the CV positions or geometry appearance, but the selected objects will have new Scale Factor and Precision Scale applied.
|
|
||||||
|
|
||||||
# saveScaleFactorAndPrecisionScaleButton
|
|
||||||
Saves the selected slider values to the global storage and to the current scene.
|
|
||||||
New scenes will automatically have these Scale Factor and Precision Scale values.
|
|
||||||
NOTE: This will not apply the new values to already existing objects. See Normalize buttons.
|
|
||||||
|
|
||||||
# saveScaleFactorAndPrecisionScaleButtonAndClose
|
|
||||||
Saves the selected slider values to the global storage and to the current scene.
|
|
||||||
New scenes will automatically have these Scale Factor and Precision Scale values.
|
|
||||||
Closes the window after save.
|
|
||||||
NOTE: This will not apply the new values to already existing objects. See Normalize buttons.
|
|
||||||
|
|
||||||
# closePrecisionScaleWindow
|
|
||||||
Close this window without save
|
|
||||||
|
|
||||||
# globalCurveThickness
|
|
||||||
Opens a window that controls the thickness of the curves in the scene as well as the global curve thickness preset across the scenes.
|
|
||||||
|
|
||||||
# setAOSettings
|
|
||||||
Manually sets the recommended AO settings for older Maya versions.
|
|
||||||
These AO settings are needed to use the "See Through" or "Toggle Always on Top" functions.
|
|
||||||
Are applied automatically by those functions.
|
|
||||||
|
|
||||||
# setTransparencySettings
|
|
||||||
Sets recommended transparency settings for Maya viewport.
|
|
||||||
***
|
|
||||||
Simple Transparency is fast but very inaccurate render mode. Only suitable for simple, one layer hair.
|
|
||||||
Object Sorting Transparency has average performance impact and quality. Can have issues on complex multi-layered grooms.
|
|
||||||
Depth Transparency - these are the optimal settings for the highest quality of the hair cards preview. Can have performance impact on slower systems.
|
|
||||||
|
|
||||||
# convertToWarpCard
|
|
||||||
Converts selection to Warp Cards.
|
|
||||||
Compatible attributes are retained.
|
|
||||||
|
|
||||||
# convertToWarpTube
|
|
||||||
Converts selection to Warp Tubes.
|
|
||||||
Compatible attributes are retained.
|
|
||||||
|
|
||||||
# convertToExtrudeCard
|
|
||||||
Converts selection to Extrude Cards.
|
|
||||||
Compatible attributes are retained.
|
|
||||||
|
|
||||||
# convertToExtrudeTube
|
|
||||||
Converts selection to Extrude Tubes.
|
|
||||||
Compatible attributes are retained.
|
|
||||||
|
|
||||||
# duplicateUnparentCurves
|
|
||||||
Duplicates selected NURBS curves and unparents them (parents them to the world).
|
|
||||||
Original curves are not deleted.
|
|
||||||
Can be used to easily extract and export curves from GS CurveTools objects.
|
|
||||||
|
|
||||||
# syncCurveColor
|
|
||||||
Toggles the syncing of the curve color to the layer color.
|
|
||||||
|
|
||||||
# colorizedRegroup
|
|
||||||
Toggles the colorization of the regrouped layers when Regroup by Layer function is used.
|
|
||||||
|
|
||||||
# colorOnlyDiffuse
|
|
||||||
Colorize only the diffuse component of the card material.
|
|
||||||
Alpha will stay the same.
|
|
||||||
|
|
||||||
# checkerPattern
|
|
||||||
Toggles the use of the checker pattern when the Color mode is enabled.
|
|
||||||
|
|
||||||
# ignoreLastLayer
|
|
||||||
Toggles the filtering (All, Curve, Geo) and Extraction (Extract All) of the last layer. If enabled, the last layer is ignored.
|
|
||||||
NOTE: Last layer is typically used for templates, so it is ignored by default.
|
|
||||||
|
|
||||||
# ignoreTemplateCollections
|
|
||||||
Ignores the filtering (All, Curve, Geo) and Extraction (Extract All) of all the collections that have "template" in their name. Case insensitive.
|
|
||||||
|
|
||||||
# groupTemplateCollections
|
|
||||||
Collections that have "template" in their name (case insensitive) will be grouped together into "CT_Templates" group by Regroup By Layer function.
|
|
||||||
|
|
||||||
# syncOutlinerLayerVis
|
|
||||||
Syncs the outliner and layer visibility. If enabled, hiding the layer will also hide the curve groups in the outliner.
|
|
||||||
|
|
||||||
# keepCurveAttributes
|
|
||||||
If enabled, the attributes that are stored in the curve will be restored if the curve that was duplicated (on its own, without the geo) is used to create other cards or tubes.
|
|
||||||
If disabled, the attributes will be ignored and reset.
|
|
||||||
Example:
|
|
||||||
1. Create a card and change its twist value.
|
|
||||||
2. Ctrl+D and Shift+P the curve (not the curve group).
|
|
||||||
3. Click Curve Card and the twist value will be restored on a newly created card.
|
|
||||||
|
|
||||||
# boundCurvesFollowParent
|
|
||||||
Will ensure that moving a parent curve in a Bound Object (Bound Group) will also move all the child curves along with it to a new layer. Recommended to keep this enabled.
|
|
||||||
|
|
||||||
# massBindOption
|
|
||||||
Will bind selected hair clump (or geometry) to all selected "empty" curves.
|
|
||||||
|
|
||||||
# bindDuplicatesCurves
|
|
||||||
Will automatically duplicate the curves before binding them to the curve, leaving old curves behind with no edits.
|
|
||||||
|
|
||||||
# bindFlipUVs
|
|
||||||
Enabling this option will flip the UVs of the original cards before binding them to the curve.
|
|
||||||
It will also automatically flip the bound geo and rotate it 90 deg.
|
|
||||||
This option will also flip the UVs back when using Unbind for better workflow.
|
|
||||||
Disabling this option will result in an old Bind/Unbind behaviour.
|
|
||||||
|
|
||||||
# unpackDeleteOriginalObject
|
|
||||||
Unpack (Shift + Click on Unbind) will delete the original Bind object.
|
|
||||||
|
|
||||||
# unpackCVMatch
|
|
||||||
Unpack (Shift + Click on Unbind) will match the CVs of the original Bind object.
|
|
||||||
|
|
||||||
# addBetweenBlendAttributes
|
|
||||||
Enables blending of the attributes when using Add Cards/Tubes or Fill functions.
|
|
||||||
|
|
||||||
# fillCreateCurvesOnly
|
|
||||||
When enabled Fill function will only create curves (not the geo).
|
|
||||||
|
|
||||||
# convertInstances
|
|
||||||
Will automatically convert instanced curves to normal curves before any other function is applied.
|
|
||||||
|
|
||||||
# replacingCurveLayerSelection
|
|
||||||
Will disable additive selection for the layers. When holding Ctrl and clicking on a new layer, old layer will be deselected automatically.
|
|
||||||
|
|
||||||
# useAutoRefineOnNewCurves
|
|
||||||
Automatically enables auto-refine on the new curves.
|
|
||||||
|
|
||||||
# flipUVsAfterMirror
|
|
||||||
Enabling this option will flip the UVs horizontally after mirroring the cards to achieve exact mirroring.
|
|
||||||
|
|
||||||
# enableTooltips
|
|
||||||
Will toggle the visibility of these tooltips you are reading right now.
|
|
||||||
|
|
||||||
# showLayerCollectionsMenu
|
|
||||||
Shows layer collections menu widget.
|
|
||||||
|
|
||||||
# importIntoANewCollection
|
|
||||||
If enabled, all the imported curves will be placed into a new "Imported Curves" layer collection.
|
|
||||||
If disabled, all the imported curves will be placed into the "Main" layer collection
|
|
||||||
|
|
||||||
# layerNumbersOnly
|
|
||||||
Layers will use only numbers if enabled.
|
|
||||||
|
|
||||||
# convertToNewLayerSystem
|
|
||||||
Converts all the layers in the scene to a new layer system that is hidden from the Channel Box Display Layers window.
|
|
||||||
Layers can still be accessed from Window->Relationship Editors->Display Layer window.
|
|
||||||
|
|
||||||
# updateLayers
|
|
||||||
Utility function that will manually update all layers. Used for if layers are correct for some reason.
|
|
||||||
|
|
||||||
# resetToDefaults
|
|
||||||
Resets every option and the GS CurveTools to the default "factory" state.
|
|
||||||
|
|
||||||
# maya2020UVFix
|
|
||||||
This function will fix any broken UVs when trying to open old scenes in Maya 2020 or 2022 or when opening scenes in 2020 and 2022 when using Maya Binary file type. This will have no effect on older versions of Maya (<2020). This bug is native to Maya and thus can’t be fixed in GS CurveTools plug-in.
|
|
||||||
|
|
||||||
# mayaFixBrokenGraphs
|
|
||||||
This function will attempt to fix all the broken graphs in the scene.
|
|
||||||
Use if one of the graphs (Width, Twist or Profile) is in a broken state.
|
|
||||||
|
|
||||||
# convertBezierToNurbs
|
|
||||||
Converts the selected Bezier curves to NURBS curves.
|
|
||||||
Bezier curves are not supported by the GS CurveTools.
|
|
||||||
|
|
||||||
# maya2020TwistAttribute
|
|
||||||
This function will fix any broken cards created in Maya 2020.4 before v1.2.2 update.
|
|
||||||
|
|
||||||
# maya2020UnbindFix
|
|
||||||
This function will fix any cards that are not unbinding properly and were created before v1.2.3 update in Maya 2020.4.
|
|
||||||
|
|
||||||
# deleteAllAnimationKeys
|
|
||||||
This function will delete all the animation keys on all the curves present in the scene.
|
|
||||||
This can fix deformation issues when using duplicate or other GS CurveTools functions.
|
|
||||||
Some functions (like duplicate) will delete the keys automatically, however the keys can still cause issues.
|
|
||||||
|
|
||||||
<!-- Main Buttons -->
|
|
||||||
|
|
||||||
# warpSwitch
|
|
||||||
Advanced cards and tubes suitable for longer hair.
|
|
||||||
Additional options and controls.
|
|
||||||
Slower than Extrude (viewport performance).
|
|
||||||
Can have issues on very small scales.
|
|
||||||
|
|
||||||
# extrudeSwitch
|
|
||||||
Simple cards and tubes suitable for shorter hair and brows.
|
|
||||||
Has limited controls.
|
|
||||||
Much faster than Warp (viewport performance).
|
|
||||||
Better for small scales.
|
|
||||||
|
|
||||||
# newCard
|
|
||||||
Creates a new Card in the middle of the world. Used at the beginning of the project to create templates.
|
|
||||||
|
|
||||||
# newTube
|
|
||||||
Creates a new Tube in the middle of the world. Used at the beginning of the project to create templates.
|
|
||||||
|
|
||||||
# curveCard
|
|
||||||
Converts selected Maya curve to CurveTools Card.
|
|
||||||
|
|
||||||
# curveTube
|
|
||||||
Converts selected Maya curve to CurveTools Tube.
|
|
||||||
|
|
||||||
# gsBind
|
|
||||||
Binds selection to a single curve. Creates a Bind Group. Selection options:
|
|
||||||
1. Single "empty" curve (default Maya curve) and single combined geometry.
|
|
||||||
2. Single "empty" curve (default Maya curve) and any number of Curve Cards and Curve Tubes.
|
|
||||||
***
|
|
||||||
Shift + Click will duplicate the original curves/geo before binding it to the empty curve.
|
|
||||||
Same option is available in the Options menu (Duplicate Curves Before Bind).
|
|
||||||
|
|
||||||
# gsUnbind
|
|
||||||
Normal Click:
|
|
||||||
UnBinds geometry or Cards/Tubes from selected Bound object.
|
|
||||||
Geometry and Cards/Tubes will be placed at the origin.
|
|
||||||
***
|
|
||||||
Shift + Click:
|
|
||||||
Will UNPACK the selected Bind object in-place.
|
|
||||||
Unpack will attempt to create an in-place approximation of the cards and tubes that Bind object consists of.
|
|
||||||
Basically it will extract the geometry and create cards (or tubes) based on that geometry.
|
|
||||||
The original Bind object will be deleted in the process. Optionally, you can keep it (toggle in the options menu).
|
|
||||||
This operation is not procedural, so you will not be able to return back to the Bind object after (unlike regular UnBind).
|
|
||||||
WARNING: Unpack is not a 1 to 1 conversion. It will try its best to approximate the shape, but complex twists and bends will not be captured.
|
|
||||||
|
|
||||||
# addCards
|
|
||||||
Creates Cards in-between selected Cards based on the Add slider value.
|
|
||||||
Bound objects are NOT supported.
|
|
||||||
NOTE: Selection order defines the direction of added Cards if more than 2 initial Cards are selected.
|
|
||||||
|
|
||||||
# addTubes
|
|
||||||
Creates Tubes in-between selected Tubes based on the Add slider value.
|
|
||||||
Bound objects are NOT supported.
|
|
||||||
NOTE: Selection order defines the direction of added Tubes if more than 2 initial Tubes are selected.
|
|
||||||
|
|
||||||
# gsFill
|
|
||||||
Creates Cards/Tubes or Bound Groups in between selected Cards/Tubes or Bound Groups based on the Add slider value.
|
|
||||||
NOTE 1: Selection order defines the direction of added curves if more than 2 initial curves are selected.
|
|
||||||
NOTE 2: The type of Card or Tube or Bound Group is defined by the previous curve in the selection chain.
|
|
||||||
NOTE 3: Options -> Fill Creates Only Curves option will make the Fill function create only NURBS curves, but not the geo.
|
|
||||||
|
|
||||||
# gsSubdivide
|
|
||||||
Subdivides selected curve into multiple curves based on the Add slider value
|
|
||||||
Shift + Click subdivides selected curve but does not delete the original curve
|
|
||||||
|
|
||||||
# gsEdgeToCurve
|
|
||||||
Converts selected geometry edges to curves.
|
|
||||||
Multiple unconnected edge groups can be selected at the same time.
|
|
||||||
***
|
|
||||||
Key Combinations:
|
|
||||||
Shift + Click will create a curve without the curvature (first degree curve or simply a line)
|
|
||||||
|
|
||||||
# gsGeoToCurve
|
|
||||||
Opens the Geo-to-Curve UI
|
|
||||||
Geo to Curve algorithm will attempt to generate GS CurveTools cards and tubes from selected geometry.
|
|
||||||
Selected geometry should be either one-sided cards or regular tubes without caps (or both).
|
|
||||||
Multi-selection compatible, but selected geometries should be separate objects and not one combined object.
|
|
||||||
|
|
||||||
# layerCollectionsComboBox
|
|
||||||
Layer collections drop-down menu.
|
|
||||||
Allows to separate the project into different layer collections, up to 80 layers in each collection.
|
|
||||||
Has additional functionality in marking menu (Hold RMB):
|
|
||||||
***
|
|
||||||
Marking menu:
|
|
||||||
1. Clear - will delete all the cards from the current layer. Undoable operation.
|
|
||||||
2. Merge Up, Merge Down - merge all the cards from the current layer to the layer above or below it.
|
|
||||||
3. Copy, Paste - will copy all the cards from the current layer and paste them to the layer that user selects.
|
|
||||||
4. Move Up, Move Down - will rearrange the current layer collections by moving the currently selected collection up or down in the list.
|
|
||||||
5. Rename - will rename the current layer collection
|
|
||||||
|
|
||||||
# layerCollectionsPlus
|
|
||||||
Add additional layer collection after the current one.
|
|
||||||
|
|
||||||
# layerCollectionsMinus
|
|
||||||
Remove current layer collection. All the cards from the removed collection will be merged one layer UP.
|
|
||||||
|
|
||||||
# gsAllFilter
|
|
||||||
Layer filter. Controls the visibility of all objects in all layers:
|
|
||||||
Normal click will show all curves and geometry in all layers.
|
|
||||||
Shift + Click will hide all curves and geometry in all layers
|
|
||||||
Ctrl + Click will show all the curves and geometry in all layers and all collections.
|
|
||||||
Ctrl + Shift + Click will hide all curves and geometry in all layers and all collections.
|
|
||||||
|
|
||||||
# gsCurveFilter
|
|
||||||
Layer filter. Hides all geometry and shows all the curves in all layers.
|
|
||||||
Ctrl + Click will do the same thing, but for all layers and all collections.
|
|
||||||
NOTE: Holding RMB will open a marking menu with Toggle Always on Top function as well as "Auto-Hide Inactive Curves" function.
|
|
||||||
Toggle Always on Top function will toggle the Always on Top feature that will show the curve component always on top. The effect is different in different Maya versions
|
|
||||||
Auto-Hide Inactive Curves will hide all the curve components on all inactive layer collections when switching between collections.
|
|
||||||
|
|
||||||
# gsGeoFilter
|
|
||||||
Layer filter. Hides all curves and shows only geometry.
|
|
||||||
Ctrl + Click will do the same thing, but for all layers and all collections.
|
|
||||||
|
|
||||||
# colorMode
|
|
||||||
Color mode toggle. Enables colors for each layer and (optionally) UV checker material.
|
|
||||||
NOTE: Checker pattern can be disabled in the Options menu
|
|
||||||
|
|
||||||
# curveGrp0
|
|
||||||
Curve Layers that are used for organization of the cards and tubes in the scene.
|
|
||||||
Selected layer (white outline) will be used to store newly created cards.
|
|
||||||
Holding RMB will open a marking menu with all the functions of current layer.
|
|
||||||
***
|
|
||||||
Key Combinations:
|
|
||||||
Shift + Click: additively select the contents of the layers.
|
|
||||||
Ctrl + Click: exclusively select the contents of the layer.
|
|
||||||
Alt + Click: show/hide selected layer.
|
|
||||||
Ctrl + Shift: show/hide curve component on selected layer.
|
|
||||||
Ctrl + Alt: show/hide geo component for the selected layer.
|
|
||||||
Shift + Alt + Click: isolate select the layer.
|
|
||||||
Shift + Ctrl + Alt + Click: enable Always on Top for each layer (only for Maya 2022+).
|
|
||||||
***
|
|
||||||
Layer MMB Dragging:
|
|
||||||
MMB + Drag: move the contents of one layer to another layer. Combine if target layer has contents.
|
|
||||||
MMB + Shift + Drag: copy the contents of one layer to another layer. Copy and Add if target layer has contents.
|
|
||||||
|
|
||||||
# gsExtractSelected
|
|
||||||
Extracts (Duplicates) the geometry component from the selected curves:
|
|
||||||
***
|
|
||||||
Key Combinations:
|
|
||||||
Normal click will extract geometry and combine it.
|
|
||||||
Shift + Click will extract geometry as individual cards.
|
|
||||||
Ctrl + Click will extract geometry, combine it, open export menu and delete the extracted geo after export.
|
|
||||||
Shift + Ctrl click will extract geometry, open export menu and delete the extracted geo after export.
|
|
||||||
|
|
||||||
# gsExtractAll
|
|
||||||
Extracts (Duplicates) the geometry component from all layers and collections. Original layers are HIDDEN, NOT deleted:
|
|
||||||
Last Layer in the current Collection is ignored by default. Can be changed in the options.
|
|
||||||
Collections with "template" in their name (case insensitive) will be ignored by default. Can be changed in the options.
|
|
||||||
***
|
|
||||||
Key Combinations:
|
|
||||||
Normal click will extract geometry and combine it.
|
|
||||||
Shift + Click will extract geometry as individual cards grouped by layers.
|
|
||||||
Ctrl + Click will extract geometry, combine it, open export menu and delete the extracted geo after export.
|
|
||||||
Shift + Ctrl click will extract geometry, open export menu and delete the extracted geo after export.
|
|
||||||
|
|
||||||
# gsSelectCurve
|
|
||||||
Selects the curve components of the selected Curve Cards/Tubes.
|
|
||||||
NOTE: Useful during the selection in the outliner.
|
|
||||||
|
|
||||||
# gsSelectGeo
|
|
||||||
Selects the geometry component of the selected Curve Cards/Tubes.
|
|
||||||
NOTE: Useful for quick assignment of the materials.
|
|
||||||
|
|
||||||
# gsSelectGroup
|
|
||||||
Selects the group component of the selected Curve Cards/Tubes.
|
|
||||||
NOTE: Useful when you are deleting curves from viewport selection.
|
|
||||||
|
|
||||||
# gsGroupCurves
|
|
||||||
Groups the selected curves and assigns the name from Group Name input field (or default name if empty).
|
|
||||||
|
|
||||||
# gsRegroupByLayer
|
|
||||||
Regroups all the curves based on their layer number, group names and collection names.
|
|
||||||
Group names can be changed in the Layer Names & Colors menu.
|
|
||||||
Groups can be colorized if the "Colorize Regrouped Layers" is enabled in the Options menu.
|
|
||||||
Collections with "template" in their name will be grouped under "CT_Templates". Can be changed in the options.
|
|
||||||
|
|
||||||
# gsGroupNameTextField
|
|
||||||
The name used by the Group Curves function.
|
|
||||||
If empty, uses the default name.
|
|
||||||
|
|
||||||
# gsCustomLayerNamesAndColors
|
|
||||||
Opens a menu where group names and colors can be changed and stored in a global preset.
|
|
||||||
|
|
||||||
# gsTransferAttributes
|
|
||||||
Transfers attributes from the FIRST selected curve to ALL the other curves in the selection.
|
|
||||||
NOTE: Shift + Click transfers the attributes from the LAST selected curve to ALL others.
|
|
||||||
NOTE2: Holding RMB on this button opens a marking menu with Copy-Paste and Filter functionality
|
|
||||||
|
|
||||||
# gsTransferUVs
|
|
||||||
Transfers UVs from the FIRST selected curve to ALL the other curves in the selection.
|
|
||||||
NOTE: Shift + Click transfers the UVs from the LAST selected curve to ALL others.
|
|
||||||
NOTE2: Holding RMB on this button opens a marking menu with Copy-Paste and Filter functionality
|
|
||||||
|
|
||||||
# gsResetPivot
|
|
||||||
Resets the pivot on all selected curves to the default position (root CV).
|
|
||||||
|
|
||||||
# gsRebuildWithCurrentValue
|
|
||||||
Rebuild selected curves using current rebuild slider value
|
|
||||||
|
|
||||||
# gsResetRebuildSliderRange
|
|
||||||
Reset rebuild slider range (1 to 50)
|
|
||||||
|
|
||||||
# gsDuplicateCurve
|
|
||||||
Duplicates all the selected curves and selects them.
|
|
||||||
NOTE: You can select either NURBS curve component, geometry component or group to duplicate.
|
|
||||||
|
|
||||||
# gsRandomizeCurve
|
|
||||||
Opens a window where different attributes of the selected curves can be randomized:
|
|
||||||
1. Enable the sections of interest and change the parameters.
|
|
||||||
2. Dragging the sliders in each section enables a PREVIEW of the randomization. Releasing the slider will reset the curves.
|
|
||||||
3. Click Randomize if you wish to apply the current randomization.
|
|
||||||
|
|
||||||
# gsExtendCurve
|
|
||||||
Lengthens a selected curves based on the Factor slider.
|
|
||||||
|
|
||||||
# gsReduceCurve
|
|
||||||
Shortens the selected curves based on the Factor slider.
|
|
||||||
|
|
||||||
# gsSmooth
|
|
||||||
Smoothes selected curves or curve CVs based on the Factor slider.
|
|
||||||
NOTE 1: At least 3 CVs should be selected for component smoothing.
|
|
||||||
NOTE 2: Holding RMB will open a marking menu where you can select a stronger smoothing algorithm.
|
|
||||||
|
|
||||||
# mirrorX
|
|
||||||
Mirrors or Flips all the selected curves on the World X axis.
|
|
||||||
|
|
||||||
# mirrorY
|
|
||||||
Mirrors or Flips all the selected curves on the World Y axis.
|
|
||||||
|
|
||||||
# mirrorZ
|
|
||||||
Mirrors or Flips all the selected curves on the World Z axis.
|
|
||||||
|
|
||||||
# gsControlCurve
|
|
||||||
Adds a Control Curve Deformer to the selected curves. Can be used to adjust groups of curves.
|
|
||||||
NOTE 1: Should NOT be used to permanently control clumps of cards. Use Bind instead.
|
|
||||||
|
|
||||||
# gsApplyControlCurve
|
|
||||||
Applies the Control Curve Deformer.
|
|
||||||
Either the Control Curve or any controlled Curves can be selected for this to work.
|
|
||||||
|
|
||||||
# gsCurveControlWindow
|
|
||||||
Opens a Curve Control Window. Contains all the available controls for curves.
|
|
||||||
|
|
||||||
# gsUVEditorMain
|
|
||||||
Opens a UV editor that can be used to setup and adjust UVs on multiple cards.
|
|
||||||
NOTE 1: Lambert material with PNG, JPG/JPEG or TIF/TIFF (LZW or No Compression) texture file is recommended. TGA (24bit and no RLE) is also supported.
|
|
||||||
NOTE 2: Make sure to select the curves or the group, not the geo, to adjust the UVs.
|
|
||||||
NOTE 3: Using default Maya UV editor will break GS CurveTools Cards, Tubes and Bound Groups.
|
|
||||||
NOTE 4: Default UV editor can be used when custom geometry is used in a Bound Groups.
|
|
||||||
|
|
||||||
<!-- ui.py --->
|
|
||||||
<!-- Curve Control Window Buttons and Frames --->
|
|
||||||
|
|
||||||
# gsLayerSelector
|
|
||||||
Shows the Layer of the selected curve.
|
|
||||||
Selecting different layer will change the layer of all the selected curves.
|
|
||||||
|
|
||||||
# gsColorPicker
|
|
||||||
Layer/Card Color Picker.
|
|
||||||
|
|
||||||
# gsCurveColorPicker
|
|
||||||
Curve Color Picker.
|
|
||||||
|
|
||||||
# selectedObjectName
|
|
||||||
Selected object name. Editing this field will rename all the selected objects.
|
|
||||||
|
|
||||||
# lineWidth
|
|
||||||
Controls the thickness of the selected curves.
|
|
||||||
|
|
||||||
# gsBindAxisAuto
|
|
||||||
Automatic selection of the bind Axis (recommended).
|
|
||||||
NOTE: Change to manual X, Y, Z axis if bind operation result is not acceptable.
|
|
||||||
|
|
||||||
# AxisFlip
|
|
||||||
Flips the direction of the bound geometry.
|
|
||||||
|
|
||||||
# editOrigObj
|
|
||||||
Temporarily disables curve bind and shows the original objects.
|
|
||||||
Used to adjust the objects after the bind.
|
|
||||||
To add or remove from the Bound group use Unbind.
|
|
||||||
|
|
||||||
# selectOriginalCurves
|
|
||||||
Selects the original curves that were attached to a bind curve.
|
|
||||||
Allows to edit their attributes without using Unbind or Edit Original Objects
|
|
||||||
|
|
||||||
# twistCurveFrame
|
|
||||||
Advanced twist control graph. Allows for precise twisting of the geometry along the curve. Click to expand.
|
|
||||||
|
|
||||||
# Magnitude
|
|
||||||
Twist multiplier. The larger the values, the more the twist. Default is 0.5.
|
|
||||||
|
|
||||||
# gsTwistGraphResetButton
|
|
||||||
Resets the graph to the default state.
|
|
||||||
|
|
||||||
# gsTwistGraphPopOut
|
|
||||||
Opens a larger graph in a separate window that is synced to the main graph.
|
|
||||||
|
|
||||||
# widthLockSwitch
|
|
||||||
Links/Unlinks the X and Z width sliders.
|
|
||||||
If linked, the sliders will move as one.
|
|
||||||
|
|
||||||
# LengthLock
|
|
||||||
Locks/Unlocks the length slider.
|
|
||||||
When Locked the geometry is stretched to the length of the curve and the slider is ignored.
|
|
||||||
|
|
||||||
# widthCurveFrame
|
|
||||||
Advanced width control graph. Allows for precise scaling of the geometry along the curve. Click to expand.
|
|
||||||
|
|
||||||
# gsWidthGraphResetButton
|
|
||||||
Resets the graph to the default state.
|
|
||||||
|
|
||||||
# gsWidthGraphPopOut
|
|
||||||
Opens a larger graph in a separate window that is synced to the main graph.
|
|
||||||
|
|
||||||
# profileCurveGraph
|
|
||||||
Advanced control over the profile of the card. Modifies the profile applied by the Profile slider. Click to expand.
|
|
||||||
Add or remove points and change them to increase or decrease the Profile value along the curve.
|
|
||||||
|
|
||||||
# autoEqualizeSwitchOn
|
|
||||||
Locks the points horizontally to equal intervals to avoid geometry deformation.
|
|
||||||
|
|
||||||
# autoEqualizeSwitchOff
|
|
||||||
Unlocks the points and allows for the full control.
|
|
||||||
|
|
||||||
# equalizeCurveButton
|
|
||||||
Snaps the points to the equal horizontal intervals.
|
|
||||||
|
|
||||||
# gsResetProfileGraphButton
|
|
||||||
Resets the curve to the default state.
|
|
||||||
|
|
||||||
# gsProfileGraphPopOut
|
|
||||||
Opens a larger graph in a separate window that is synced to the main graph.
|
|
||||||
|
|
||||||
# reverseNormals
|
|
||||||
Reverses the normals on the selected cards/tubes.
|
|
||||||
|
|
||||||
# orientToNormalsFrame
|
|
||||||
Orient selected cards/tubes to the normals of the selected geo.
|
|
||||||
|
|
||||||
# gsOrientToNormalsSelectTarget
|
|
||||||
Set selected mesh as a target for the algorithm.
|
|
||||||
|
|
||||||
# orientRefreshViewport
|
|
||||||
Toggles the viewport update during the alignment process.
|
|
||||||
Disabling can speed up the process.
|
|
||||||
|
|
||||||
# gsOrientToNormals
|
|
||||||
Starts the alignment process.
|
|
||||||
Will align the selected cards to the normals of the selected geometry.
|
|
||||||
|
|
||||||
# flipUV
|
|
||||||
Flips the UVs of the card/tube horizontally.
|
|
||||||
|
|
||||||
# resetControlSliders
|
|
||||||
Resets the range of the sliders to the default state.
|
|
||||||
|
|
||||||
# UVFrame
|
|
||||||
Legacy controls for the UVs. Just use the new UV Editor.
|
|
||||||
|
|
||||||
# solidifyFrame
|
|
||||||
Expands controls that control the thickness of the cards/tubes.
|
|
||||||
|
|
||||||
# solidify
|
|
||||||
Toggles the thickness of the geometry.
|
|
||||||
|
|
||||||
<!-- Curve Control Window Sliders --->
|
|
||||||
|
|
||||||
# lengthDivisions
|
|
||||||
Change the length divisions of the selected cards/tubes.
|
|
||||||
|
|
||||||
# dynamicDivisions
|
|
||||||
Toggles the dynamic divisions mode.
|
|
||||||
Dynamic divisions will change the divisions of the cards/tubes based on the length of the curves.
|
|
||||||
In dynamic divisions mode, L-Div slider will control the density of the divisions, not the fixed divisions count.
|
|
||||||
|
|
||||||
# widthDivisions
|
|
||||||
Change the width divisions of the selected cards/tubes.
|
|
||||||
|
|
||||||
# Orientation
|
|
||||||
Change the orientation of the card/tube around the curve.
|
|
||||||
|
|
||||||
# Twist
|
|
||||||
Smoothly twist the entire geometry card/tube. Twists the tip of the card.
|
|
||||||
|
|
||||||
# invTwist
|
|
||||||
Smoothly twist the entire geometry card. Twists the root of the card.
|
|
||||||
|
|
||||||
# Width
|
|
||||||
Change the width of the selected card.
|
|
||||||
|
|
||||||
# Taper
|
|
||||||
Linearly changes the width of the card/tube along the length of the curve.
|
|
||||||
|
|
||||||
# WidthX
|
|
||||||
Change the width of the tube along the X axis.
|
|
||||||
|
|
||||||
# WidthZ
|
|
||||||
Change the width of the tube along the Z axis.
|
|
||||||
|
|
||||||
# Length
|
|
||||||
Change the length of the attached geometry. Works only when Length Unlock button is checked.
|
|
||||||
|
|
||||||
# Offset
|
|
||||||
Offset the geometry along the curve.
|
|
||||||
|
|
||||||
# Profile
|
|
||||||
Change the profile of the card along the length of the curve uniformly.
|
|
||||||
|
|
||||||
# profileSmoothing
|
|
||||||
Smoothing will smooth the profile transition.
|
|
||||||
|
|
||||||
# otherFrame
|
|
||||||
Other less used options
|
|
||||||
|
|
||||||
# curveRefine
|
|
||||||
Controls the number of "virtual" vertices on the curve. These are the vertices that are used to calculate the geometry deformation.
|
|
||||||
Zero (0) value will disable the refinement and the geometry will be attached directly to the curve. The fastest option.
|
|
||||||
Larger refine values means smoother geometry that is a closer fit to the curve.
|
|
||||||
Only increase past 20 if you need additional precision or if there are any visual glitches with the geometry.
|
|
||||||
Large refine values can cause significant performance drop, lag and other issues on smaller curve sizes.
|
|
||||||
Recommended values are:
|
|
||||||
20 for curves with less than 20 CVs.
|
|
||||||
0 (disabled) or same as the number of CVs for curves with more than 20 CVs.
|
|
||||||
|
|
||||||
# autoRefine
|
|
||||||
Enables auto-refine for selected curves. Recommended to keep this on.
|
|
||||||
Manual refinement can be helpful if the geometry deformation is wrong or not precise enough.
|
|
||||||
|
|
||||||
# samplingAccuracy
|
|
||||||
Increases the sampling accuracy of the deformer that attaches the geometry to a curve.
|
|
||||||
Larger values = more precise geometry fit to a curve and more lag.
|
|
||||||
|
|
||||||
# surfaceNormals
|
|
||||||
Changes the smoothing angle of the normals of the geometry.
|
|
||||||
|
|
||||||
# gsIterationsSlider
|
|
||||||
Controls the number of iterations per card.
|
|
||||||
|
|
||||||
# gsMinimumAngle
|
|
||||||
Controls the target angle difference between the normal of the mesh and the card.
|
|
||||||
|
|
||||||
# solidifyThickness
|
|
||||||
Controls the amount of thickness on the geometry.
|
|
||||||
|
|
||||||
# solidifyDivisions
|
|
||||||
Controls the number of divisions on the solidify extrusion.
|
|
||||||
|
|
||||||
# solidifyScaleX
|
|
||||||
Changes the scale on the X axis.
|
|
||||||
|
|
||||||
# solidifyScaleY
|
|
||||||
Changes the scale on the Y axis.
|
|
||||||
|
|
||||||
# solidifyOffset
|
|
||||||
Controls the offset of the solidify extrusion.
|
|
||||||
|
|
||||||
# solidifyNormals
|
|
||||||
Controls the smoothing angle for normals of the solidify extrusion.
|
|
||||||
|
|
||||||
# geometryHighlight
|
|
||||||
If enabled, selecting the curve will also highlight the geometry component that is attached to that curve.
|
|
||||||
Works only on GS CurveTools curves and geo.
|
|
||||||
|
|
||||||
# curveHighlight
|
|
||||||
If enabled, selected curves and their components will be additionally highlighted for better visibility.
|
|
||||||
The curves and components will be in X-Ray mode by default.
|
|
||||||
Colors and transparency values can be changes in the menu below.
|
|
||||||
|
|
||||||
# gsSelectedCVColor
|
|
||||||
Selected CV highlight color
|
|
||||||
|
|
||||||
# gsSelectedCVAlpha
|
|
||||||
Selected CV highlight transparency (alpha)
|
|
||||||
|
|
||||||
# gsDeselectedCVColor
|
|
||||||
Deselected CV highlight color
|
|
||||||
|
|
||||||
# gsDeselectedCVAlpha
|
|
||||||
Deselected CV highlight transparency (alpha)
|
|
||||||
|
|
||||||
# curveVisibility
|
|
||||||
Toggle selected curves highlight
|
|
||||||
|
|
||||||
# gsCurveHighlightColor
|
|
||||||
Selected curve highlight color
|
|
||||||
|
|
||||||
# gsCurveHighlightAlpha
|
|
||||||
Selected curve highlight transparency (alpha)
|
|
||||||
|
|
||||||
# hullVisibility
|
|
||||||
Toggle hull visibility.
|
|
||||||
Hull is a line that connects all the CVs on the curve.
|
|
||||||
|
|
||||||
# gsHullHighlightColor
|
|
||||||
Hull highlight color
|
|
||||||
|
|
||||||
# gsHullHighlightAlpha
|
|
||||||
Hull highlight transparency (alpha)
|
|
||||||
|
|
||||||
# advancedVisibilityFrame
|
|
||||||
Better highlights for selected curves and components.
|
|
||||||
|
|
||||||
# lazyUpdate
|
|
||||||
Enables lazy update for selected curves.
|
|
||||||
Lazy update can slightly increase the performance of the highlight,
|
|
||||||
however it has some visual drawbacks (curve highlight can fail to update when switching curves in component selection mode)
|
|
||||||
|
|
||||||
# alwaysOnTop
|
|
||||||
Toggles X-Ray (always on top) drawing for highlighted components.
|
|
||||||
Disabling this defeats the purpose of the advanced visibility, but hey, it's your choice.
|
|
||||||
|
|
||||||
# curveDistanceColor
|
|
||||||
Toggles the distance color effect on the curve highlight.
|
|
||||||
Distance color darkens the curve color the further it is from the camera.
|
|
||||||
|
|
||||||
# cvDistanceColor
|
|
||||||
Toggles the distance color effect on the CV highlight.
|
|
||||||
Distance color darkens the CVs color the further it is from the camera.
|
|
||||||
|
|
||||||
# hullDistanceColor
|
|
||||||
Toggles the distance color effect on the hull highlight.
|
|
||||||
Distance color darkens the hull color the further it is from the camera.
|
|
||||||
|
|
||||||
# gsDistanceColorMinValue
|
|
||||||
Distance color minimum.
|
|
||||||
This value is the minimum allowed color multiplier for the Distance Color effect.
|
|
||||||
The lower this value, the darker further parts of the curve will be.
|
|
||||||
Black at 0.0
|
|
||||||
Original color at 1.0
|
|
||||||
|
|
||||||
# gsDistanceColorMaxValue
|
|
||||||
Distance color maximum.
|
|
||||||
This value is the maximum allowed color multiplier for the Distance Color effect.
|
|
||||||
The higher this value, the brighter closest parts of the curve will be.
|
|
||||||
Black at 0.0
|
|
||||||
Original color at 1.0
|
|
||||||
|
|
||||||
# CVocclusion
|
|
||||||
Toggles the experimental CV occlusion mode (hull is affected as well)
|
|
||||||
When the appropriate mesh name is added to Occluder Mesh input field,
|
|
||||||
this function will automatically hide CVs and hull lines that are behind this mesh (even in X-Ray mode).
|
|
||||||
Warning: enabling this mode can negatively impart viewport performance.
|
|
||||||
|
|
||||||
# gsSelectOccluderButton
|
|
||||||
This button adds the selected mesh name to the Occluder Mesh input field.
|
|
||||||
|
|
||||||
# gsOccluderMeshName
|
|
||||||
Type the full path for the occluder mesh here, or use the "Select Occluder" button on the left <-
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Layer Customization --->
|
|
||||||
|
|
||||||
# gsGenerateLayerColorGradient
|
|
||||||
Generate a color gradient for the layer colors.
|
|
||||||
Rows control the number of Rows to generate.
|
|
||||||
Left color picker sets the initial color.
|
|
||||||
Right color picker sets the final color.
|
|
||||||
|
|
||||||
# gsRandomizeLayerColors
|
|
||||||
Generate random colors for the layers.
|
|
||||||
SatMin controls the minimum allowed saturation.
|
|
||||||
SatMax controls the maximum allowed saturation.
|
|
||||||
|
|
||||||
# gsResetAllLayerColors
|
|
||||||
Resets all the color swatches to the default color.
|
|
||||||
|
|
||||||
# gsGetCurrentSceneLayers
|
|
||||||
Populates the menu with the names and colors stored in the scene.
|
|
||||||
|
|
||||||
# gsSetAsCurrentSceneLayers
|
|
||||||
Applies the names and colors from the menu to the scene.
|
|
||||||
|
|
||||||
# gsLoadGlobalLayerPreset
|
|
||||||
Load the global names and colors preset to the menu.
|
|
||||||
NOTE: Don't forget to Set to Scene before closing the menu.
|
|
||||||
|
|
||||||
# gsSaveGlobalLayerPreset
|
|
||||||
Saves the current names and colors from the menu to the global preset.
|
|
||||||
|
|
||||||
<!-- UV Editor Window --->
|
|
||||||
|
|
||||||
# gsUVSelect
|
|
||||||
Enables the selection of the UVs.
|
|
||||||
Drag to draw a box selection.
|
|
||||||
|
|
||||||
# gsUVMove
|
|
||||||
Enables the Move tool.
|
|
||||||
Move the selected UVs or move individual UVs if nothing is selected.
|
|
||||||
|
|
||||||
# gsUVRotate
|
|
||||||
Enables the Rotation of the selected UVs.
|
|
||||||
Hold LMB and drag anywhere in the viewport to rotate the selected UVs.
|
|
||||||
Rotation pivot is the center of the individual unscaled UV.
|
|
||||||
|
|
||||||
# gsUVScale
|
|
||||||
Enables the Scaling of the selected UVs.
|
|
||||||
Hold LMB and drag in the viewport to scale the card Horizontally of Vertically.
|
|
||||||
Repeated hotkey click will toggle between Horizontal and Vertical scaling.
|
|
||||||
|
|
||||||
# gsUVHorizontalScale
|
|
||||||
Horizontal scaling mode selector.
|
|
||||||
|
|
||||||
# gsUVVerticalScale
|
|
||||||
Vertical scaling mode selector.
|
|
||||||
|
|
||||||
# gsDrawUVs
|
|
||||||
Enables the UVs Drawing Tool:
|
|
||||||
1. Select UVs using Selection Mode.
|
|
||||||
2. Enable the UV Drawing Tool.
|
|
||||||
3. Draw a UV Rectangle anywhere in the viewport to create/move the UVs there.
|
|
||||||
|
|
||||||
# gsHorizontalFlipUV
|
|
||||||
Flips the selected UVs horizontally.
|
|
||||||
Flipped UVs have the blue circle indicator inside the root rectangle.
|
|
||||||
|
|
||||||
# gsVerticalFlipUV
|
|
||||||
Flips the selected UVs vertically.
|
|
||||||
|
|
||||||
# gsResetUVs
|
|
||||||
Resets the selected UVs to the default 0,1 rectangle.
|
|
||||||
|
|
||||||
# gsSyncSelectionUVs
|
|
||||||
Syncs selection between UV editor and Maya Viewport.
|
|
||||||
|
|
||||||
# gsRandomizeUVs
|
|
||||||
Randomize selected UV positions between already existing UV positions.
|
|
||||||
***
|
|
||||||
Normal click will keep the overall density distribution of the UVs.
|
|
||||||
This means that if there is one card in one position and twenty cards in the other,
|
|
||||||
it will keep this distribution of 1 to 20.
|
|
||||||
***
|
|
||||||
Shift+Click will ignore the original density distribution
|
|
||||||
and simply randomize the UVs between the original positions.
|
|
||||||
|
|
||||||
# gsFocusUVs
|
|
||||||
Focuses on the selected UVs or on all the UVs if nothing is selected.
|
|
||||||
|
|
||||||
# gsUVIsolateSelect
|
|
||||||
Hides all the unselected UVs and shows only the selected ones.
|
|
||||||
|
|
||||||
# gsUVShowAll
|
|
||||||
Shows all the hidden UVs.
|
|
||||||
|
|
||||||
# UVEditorUseTransforms
|
|
||||||
Use "Coverage" and "Translate Frame" parameters from place2dTexture node for texture.
|
|
||||||
Offset is not supported.
|
|
||||||
Diffuse and Alpha channel MUST have the same coverage and translate frame values.
|
|
||||||
|
|
||||||
# UVEditorTransparencyToggle
|
|
||||||
Enable texture map transparency using Alpha map from Transparency plug in the material node
|
|
||||||
|
|
||||||
# UVEditorBGColorPicker
|
|
||||||
Background Color
|
|
||||||
|
|
||||||
# UVEditorGridColorPicker
|
|
||||||
Grid Color
|
|
||||||
|
|
||||||
# UVEditorFrameColorPicker
|
|
||||||
Frame Color
|
|
||||||
|
|
||||||
# UVEditorUVFrameSelectedColorPicker
|
|
||||||
Selected UV frame color
|
|
||||||
|
|
||||||
# UVEditorUVFrameDeselectedColorPicker
|
|
||||||
Deselected UV frame color
|
|
||||||
|
|
||||||
# UVEditorUVCardFillColorPicker
|
|
||||||
UV frame background color
|
|
||||||
|
|
||||||
<!-- Card to Curve Window --->
|
|
||||||
|
|
||||||
# gsGeoToCurve_outputTypeSwitch
|
|
||||||
Controls the output of Geo-to-Curve algorithm
|
|
||||||
|
|
||||||
# gsGeoToCurve_generateAuto
|
|
||||||
Automatically determine the final object type (card or tube) based on the selected geometry.
|
|
||||||
|
|
||||||
# gsGeoToCurve_generateCards
|
|
||||||
Generate cards from selected geometry (one-sided cards or tubes)
|
|
||||||
|
|
||||||
# gsGeoToCurve_generateTubes
|
|
||||||
Generate tubes from selected geometry (one-sided cards or tubes)
|
|
||||||
|
|
||||||
# gsGeoToCurve_generateCurves
|
|
||||||
Generate curves from selected geometry (one-sided cards or tubes)
|
|
||||||
|
|
||||||
# gsGeoToCurve_cardType
|
|
||||||
Controls the type of generated objects (Warp or Extrude)
|
|
||||||
|
|
||||||
# gsGeoToCurve_warp
|
|
||||||
Generate Warp cards or tubes
|
|
||||||
|
|
||||||
# gsGeoToCurve_extrude
|
|
||||||
Generate Extrude cards or tubes
|
|
||||||
|
|
||||||
# gsGeoToCurve_matchAttributes
|
|
||||||
Controls which attributes on the new cards/tubes should be approximated from the original geometry.
|
|
||||||
NOTE: This process is not perfect and final result can be inaccurate.
|
|
||||||
|
|
||||||
# gsGeoToCurve_orientation
|
|
||||||
Match orientation attribute during the generation process
|
|
||||||
|
|
||||||
# gsGeoToCurve_width
|
|
||||||
Match orientation attribute during the generation process
|
|
||||||
|
|
||||||
# gsGeoToCurve_taper
|
|
||||||
Match taper attribute during the generation process
|
|
||||||
|
|
||||||
# gsGeoToCurve_twist
|
|
||||||
Match twist attribute during the generation process
|
|
||||||
|
|
||||||
# gsGeoToCurve_profile
|
|
||||||
Match profile attribute during the generation process
|
|
||||||
|
|
||||||
# gsGeoToCurve_material
|
|
||||||
Copy material (shader) from the original geometry
|
|
||||||
|
|
||||||
# gsGeoToCurve_UVs
|
|
||||||
Tries to approximate the UVs from the original geometry
|
|
||||||
NOTE: Matches the bounding box of the UVs. Rotated and deformed UVs are not matched precisely.
|
|
||||||
|
|
||||||
# gsGeoToCurve_UVMatchOptions
|
|
||||||
Controls UV matching behaviour
|
|
||||||
|
|
||||||
# gsGeoToCurve_verticalFlip
|
|
||||||
Vertically flip matched UVs
|
|
||||||
|
|
||||||
# gsGeoToCurve_horizontalFlip
|
|
||||||
Horizontally flip matched UVs
|
|
||||||
|
|
||||||
# gsGeoToCurve_reverseCurve
|
|
||||||
Reverse generated curve direction
|
|
||||||
Root CV should be generated near the scalp of the model.
|
|
||||||
Enable or disable if resulting card direction and taper are reversed.
|
|
||||||
|
|
||||||
# gsGeoToCurve_convertSelected
|
|
||||||
Convert selected geometry to cards, tubes or curves based on the selected options.
|
|
||||||
Newly created procedural objects will be placed in the currently selected layer.
|
|
||||||
NOTE: if the currently selected layer is hidden (grayed out), the newly created objects will be hidden as well.
|
|
||||||
|
Before Width: | Height: | Size: 234 B |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 118 B |
|
Before Width: | Height: | Size: 178 B |
|
Before Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
@@ -1,93 +0,0 @@
|
|||||||
"""
|
|
||||||
CV Manipulator (highlighting) plug-in entry point for Mac
|
|
||||||
|
|
||||||
GS CurveTools License:
|
|
||||||
This collection of code named GS CurveTools is a property of George Sladkovsky (Yehor Sladkovskyi)
|
|
||||||
and can not be copied or distributed without his written permission.
|
|
||||||
|
|
||||||
GS CurveTools v1.3.8 Personal
|
|
||||||
Copyright 2024, George Sladkovsky (Yehor Sladkovskyi)
|
|
||||||
All Rights Reserved
|
|
||||||
|
|
||||||
UI font is Roboto that is licensed under the Apache 2.0 License:
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Autodesk Maya is a property of Autodesk, Inc:
|
|
||||||
https://www.autodesk.com/
|
|
||||||
|
|
||||||
Social Media and Contact Links:
|
|
||||||
|
|
||||||
Discord Server: https://discord.gg/f4DH6HQ
|
|
||||||
Online Store: https://sladkovsky3d.artstation.com/store
|
|
||||||
Online Documentation: https://gs-curvetools.readthedocs.io/
|
|
||||||
Twitch Channel: https://www.twitch.tv/videonomad
|
|
||||||
YouTube Channel: https://www.youtube.com/c/GeorgeSladkovsky
|
|
||||||
ArtStation Portfolio: https://www.artstation.com/sladkovsky3d
|
|
||||||
Contact Email: george.sladkovsky@gmail.com
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# pylint: disable-all
|
|
||||||
|
|
||||||
import sys
|
|
||||||
|
|
||||||
try:
|
|
||||||
from importlib import reload
|
|
||||||
except ImportError:
|
|
||||||
from imp import reload
|
|
||||||
|
|
||||||
import maya.api.OpenMaya as om
|
|
||||||
import maya.api.OpenMayaRender as omr
|
|
||||||
|
|
||||||
from gs_curvetools.plugins import cv_manip_src # type: ignore
|
|
||||||
|
|
||||||
reload(cv_manip_src)
|
|
||||||
|
|
||||||
# API parameters
|
|
||||||
maya_useNewAPI = True
|
|
||||||
|
|
||||||
|
|
||||||
# ------------ Init & UnInit Plugin ------------
|
|
||||||
def initializePlugin(obj):
|
|
||||||
plugin = om.MFnPlugin(obj, "GeorgeSladkovsky", "1.3", "Any")
|
|
||||||
try:
|
|
||||||
plugin.registerNode(
|
|
||||||
"GSCT_CurveTools_DrawManagerNode",
|
|
||||||
cv_manip_src.DrawManagerNode.id,
|
|
||||||
cv_manip_src.DrawManagerNode.creator,
|
|
||||||
cv_manip_src.DrawManagerNode.initialize,
|
|
||||||
om.MPxNode.kLocatorNode,
|
|
||||||
cv_manip_src.DrawManagerNode.drawDbClassification,
|
|
||||||
)
|
|
||||||
except BaseException:
|
|
||||||
sys.stderr.write("Failed to register node\n")
|
|
||||||
raise
|
|
||||||
|
|
||||||
try:
|
|
||||||
omr.MDrawRegistry.registerDrawOverrideCreator(
|
|
||||||
cv_manip_src.DrawManagerNode.drawDbClassification,
|
|
||||||
cv_manip_src.DrawManagerNode.drawRegistrantId,
|
|
||||||
cv_manip_src.DrawOverride.creator,
|
|
||||||
)
|
|
||||||
except BaseException:
|
|
||||||
sys.stderr.write("Failed to register override\n")
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
def uninitializePlugin(obj):
|
|
||||||
om.MMessage.removeCallbacks(cv_manip_src.CALLBACK_IDS)
|
|
||||||
cv_manip_src.CALLBACK_IDS = []
|
|
||||||
plugin = om.MFnPlugin(obj)
|
|
||||||
try:
|
|
||||||
plugin.deregisterNode(cv_manip_src.DrawManagerNode.id)
|
|
||||||
except BaseException:
|
|
||||||
sys.stderr.write("Failed to deregister node\n")
|
|
||||||
raise
|
|
||||||
|
|
||||||
try:
|
|
||||||
omr.MDrawRegistry.deregisterGeometryOverrideCreator(
|
|
||||||
cv_manip_src.DrawManagerNode.drawDbClassification, cv_manip_src.DrawManagerNode.drawRegistrantId
|
|
||||||
)
|
|
||||||
except BaseException:
|
|
||||||
sys.stderr.write("Failed to deregister override\n")
|
|
||||||
raise
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
# ngSkinTools2 模块
|
|
||||||
|
|
||||||
ngSkinTools2 是一个强大的 Maya 蒙皮权重编辑工具。
|
|
||||||
|
|
||||||
## 📁 文件夹结构
|
|
||||||
|
|
||||||
```
|
|
||||||
ngskintools2/ # ngSkinTools2 核心模块
|
|
||||||
├── __init__.py # 模块初始化(原始)
|
|
||||||
├── launcher.py # 启动器脚本(新增)
|
|
||||||
├── api/ # API 接口
|
|
||||||
├── ui/ # 用户界面
|
|
||||||
├── operations/ # 操作功能
|
|
||||||
└── README.md # 本文档
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🚀 使用方法
|
|
||||||
|
|
||||||
### 从工具架启动
|
|
||||||
|
|
||||||
1. 打开 Maya
|
|
||||||
2. 切换到 **Nexus_Rigging** 工具架
|
|
||||||
3. 点击 **ngSkin** 按钮
|
|
||||||
|
|
||||||
### 从 Python 启动
|
|
||||||
|
|
||||||
```python
|
|
||||||
from rigging_tools.ngskintools2 import launcher
|
|
||||||
launcher.LaunchNgSkinTools()
|
|
||||||
```
|
|
||||||
|
|
||||||
### 直接使用 ngSkinTools2 API
|
|
||||||
|
|
||||||
```python
|
|
||||||
from rigging_tools.ngskintools2 import open_ui
|
|
||||||
open_ui()
|
|
||||||
```
|
|
||||||
|
|
||||||
## ✨ 主要功能
|
|
||||||
|
|
||||||
- 高级权重绘制和编辑
|
|
||||||
- 权重镜像和传递
|
|
||||||
- 多层权重管理
|
|
||||||
- 权重导入/导出
|
|
||||||
- 影响对象管理
|
|
||||||
|
|
||||||
## 📝 注意事项
|
|
||||||
|
|
||||||
- 自动检测 Maya 版本并加载对应插件
|
|
||||||
- 支持 Maya 2018-2026 版本
|
|
||||||
- 如果当前版本没有完全匹配的插件,会自动使用向下兼容的版本
|
|
||||||
- 建议在绑定工作流程中使用
|
|
||||||
|
|
||||||
## 🔧 版本兼容性
|
|
||||||
|
|
||||||
启动器会自动检测当前 Maya 版本并加载对应的插件:
|
|
||||||
- **完全匹配**:优先使用与 Maya 版本完全匹配的插件
|
|
||||||
- **向下兼容**:如果没有完全匹配,使用小于等于当前版本的最高版本插件
|
|
||||||
- **兜底策略**:如果当前版本比所有可用插件都旧,使用最旧的可用插件
|
|
||||||
|
|
||||||
支持的 Maya 版本:2018, 2019, 2020, 2022, 2023, 2024, 2025, 2026
|
|
||||||
@@ -432,7 +432,7 @@ class InfluenceMappingConfig(Object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if axis is not None and not isinstance(axis, int):
|
if axis is not None and not isinstance(axis, int):
|
||||||
raise Exception("invalid axis type, need int")
|
raise Exception("无效的轴类型,需要整数")
|
||||||
|
|
||||||
self.__mirror_axis = axis
|
self.__mirror_axis = axis
|
||||||
|
|
||||||
@@ -487,10 +487,10 @@ class InfluenceMapping(Object):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.config = InfluenceMappingConfig() # type:InfluenceMappingConfig
|
self.config = InfluenceMappingConfig() # type:InfluenceMappingConfig
|
||||||
"assigned config"
|
"分配的配置"
|
||||||
|
|
||||||
self.influences = [] # type: list[InfluenceInfo]
|
self.influences = [] # type: list[InfluenceInfo]
|
||||||
"Source influences list. Can be assigned to result of :py:meth:`Layers.list_influences`"
|
"源影响列表. 可以分配给结果 :py:meth:`Layers.list_influences`"
|
||||||
|
|
||||||
self.destinationInfluences = None
|
self.destinationInfluences = None
|
||||||
self.calculatedMapping = None
|
self.calculatedMapping = None
|
||||||
@@ -498,7 +498,7 @@ class InfluenceMapping(Object):
|
|||||||
|
|
||||||
def calculate(self):
|
def calculate(self):
|
||||||
mirror_mode = self.config.mirror_axis is not None
|
mirror_mode = self.config.mirror_axis is not None
|
||||||
log.info("calculate influence mapping, mirror mode: %s", mirror_mode)
|
log.info("计算影响映射,镜像模式: %s", mirror_mode)
|
||||||
if self.destinationInfluences is None:
|
if self.destinationInfluences is None:
|
||||||
self.destinationInfluences = self.influences
|
self.destinationInfluences = self.influences
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class LayerEffects(Object):
|
|||||||
mirror_mask = mirror_dq = mirror_weights = everything
|
mirror_mask = mirror_dq = mirror_weights = everything
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
"configure mirror: layer %s mask %r weights %r dq %r direction %r",
|
"配置镜像: 图层 %s 遮罩 %r 权重 %r dq %r 方向 %r",
|
||||||
self.__layer.name,
|
self.__layer.name,
|
||||||
mirror_mask,
|
mirror_mask,
|
||||||
mirror_weights,
|
mirror_weights,
|
||||||
@@ -103,7 +103,7 @@ class Layer(Object):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def load(cls, mesh, layer_id):
|
def load(cls, mesh, layer_id):
|
||||||
if layer_id < 0:
|
if layer_id < 0:
|
||||||
raise Exception("invalid layer ID: %s" % layer_id)
|
raise Exception("无效图层 ID: %s" % layer_id)
|
||||||
result = Layer(mesh, layer_id)
|
result = Layer(mesh, layer_id)
|
||||||
result.reload()
|
result.reload()
|
||||||
return result
|
return result
|
||||||
@@ -112,7 +112,7 @@ class Layer(Object):
|
|||||||
self.mesh = mesh
|
self.mesh = mesh
|
||||||
self.id = id
|
self.id = id
|
||||||
self.effects = LayerEffects(self) # type: LayerEffects
|
self.effects = LayerEffects(self) # type: LayerEffects
|
||||||
"configure effects for this layer"
|
"配置此图层的效果"
|
||||||
|
|
||||||
self.__state = None
|
self.__state = None
|
||||||
if state is not None:
|
if state is not None:
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class Mirror(Object):
|
|||||||
"""
|
"""
|
||||||
:type mapping: map[int] -> int
|
:type mapping: map[int] -> int
|
||||||
"""
|
"""
|
||||||
log.info("mapping updated: %r", mapping)
|
log.info("映射已更新: %r", mapping)
|
||||||
|
|
||||||
mapping_as_string = ','.join(str(k) + "," + str(v) for (k, v) in list(mapping.items()))
|
mapping_as_string = ','.join(str(k) + "," + str(v) for (k, v) in list(mapping.items()))
|
||||||
plugin.ngst2Layers(self.target, configureMirrorMapping=True, influencesMapping=mapping_as_string)
|
plugin.ngst2Layers(self.target, configureMirrorMapping=True, influencesMapping=mapping_as_string)
|
||||||
@@ -119,7 +119,7 @@ class Mirror(Object):
|
|||||||
existing_ref_mesh = self.get_reference_mesh()
|
existing_ref_mesh = self.get_reference_mesh()
|
||||||
if existing_ref_mesh:
|
if existing_ref_mesh:
|
||||||
cmds.select(existing_ref_mesh)
|
cmds.select(existing_ref_mesh)
|
||||||
raise Exception("symmetry mesh already configured for %s: %s" % (str(sc), existing_ref_mesh))
|
raise Exception("对称网格已配置为 %s: %s" % (str(sc), existing_ref_mesh))
|
||||||
|
|
||||||
def get_shape(node):
|
def get_shape(node):
|
||||||
return cmds.listRelatives(node, shapes=True)[0]
|
return cmds.listRelatives(node, shapes=True)[0]
|
||||||
@@ -177,7 +177,7 @@ def set_reference_mesh_from_selection():
|
|||||||
selection = cmds.ls(sl=True, long=True)
|
selection = cmds.ls(sl=True, long=True)
|
||||||
|
|
||||||
if len(selection) != 2:
|
if len(selection) != 2:
|
||||||
log.debug("wrong selection size")
|
log.debug("错误的选择尺寸")
|
||||||
return
|
return
|
||||||
|
|
||||||
m = Mirror(selection[1])
|
m = Mirror(selection[1])
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ class Session(Object):
|
|||||||
|
|
||||||
@signal.on(self.events.targetChanged)
|
@signal.on(self.events.targetChanged)
|
||||||
def on_target_change():
|
def on_target_change():
|
||||||
log.info("target changed: clearing target context")
|
log.info("目标已更改:清除目标上下文")
|
||||||
self.context.selected_layers.set([])
|
self.context.selected_layers.set([])
|
||||||
|
|
||||||
self.events.nodeSelectionChanged.emit()
|
self.events.nodeSelectionChanged.emit()
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ def add_influences(influences, target):
|
|||||||
def long_names(names):
|
def long_names(names):
|
||||||
result = set(cmds.ls(names, long=True))
|
result = set(cmds.ls(names, long=True))
|
||||||
if len(result) != len(names):
|
if len(result) != len(names):
|
||||||
raise Exception("could not convert to a list of influences names: " + str(names))
|
raise Exception("无法转换为影响名称列表: " + str(names))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
existing = long_names([i.name if not i.path else i.path for i in list_influences(skin_cluster)])
|
existing = long_names([i.name if not i.path else i.path for i in list_influences(skin_cluster)])
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ def merge_layers(layers):
|
|||||||
# verify that all layers are from the same parent
|
# verify that all layers are from the same parent
|
||||||
for i, j in zip(layers[:-1], layers[1:]):
|
for i, j in zip(layers[:-1], layers[1:]):
|
||||||
if i.mesh != j.mesh:
|
if i.mesh != j.mesh:
|
||||||
raise Exception("layers are not from the same mesh")
|
raise Exception("层不是来自同一个网格")
|
||||||
|
|
||||||
result = plugin.ngst2tools(
|
result = plugin.ngst2tools(
|
||||||
tool="mergeLayers",
|
tool="mergeLayers",
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ class LayersTransfer(Object):
|
|||||||
def calc_influences_mapping_as_flat_list(self):
|
def calc_influences_mapping_as_flat_list(self):
|
||||||
mapping_pairs = list(self.influences_mapping.asIntIntMapping(self.influences_mapping.calculate()).items())
|
mapping_pairs = list(self.influences_mapping.asIntIntMapping(self.influences_mapping.calculate()).items())
|
||||||
if len(mapping_pairs) == 0:
|
if len(mapping_pairs) == 0:
|
||||||
raise Exception("no mapping between source and destination influences")
|
raise Exception("源和目标影响之间没有映射")
|
||||||
# convert dict to flat array
|
# convert dict to flat array
|
||||||
return list(itertools.chain.from_iterable(mapping_pairs))
|
return list(itertools.chain.from_iterable(mapping_pairs))
|
||||||
|
|
||||||
|
|||||||
@@ -54,12 +54,12 @@ class Undo(Object):
|
|||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
log.debug("UNDO chunk %r: start", self.name)
|
log.debug("UNDO chunk %r: 开始", self.name)
|
||||||
cmds.undoInfo(openChunk=True, chunkName=self.name)
|
cmds.undoInfo(openChunk=True, chunkName=self.name)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def __exit__(self, _type, value, traceback):
|
def __exit__(self, _type, value, traceback):
|
||||||
log.debug("UNDO chunk %r: end", self.name)
|
log.debug("UNDO chunk %r: 结束", self.name)
|
||||||
cmds.undoInfo(closeChunk=True)
|
cmds.undoInfo(closeChunk=True)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 340 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 9.4 KiB |
@@ -1,353 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<!-- saved from url=(0106)https://apps.autodesk.com/RVT/en/Detail/HelpDoc?appId=2231912794620250494&appLang=en&os=Win64&mode=preview -->
|
|
||||||
<html lang="en" class=" js canvas canvastext no-touch hashchange history draganddrop rgba multiplebgs borderimage boxshadow textshadow cssgradients csstransitions sessionstorage" data-lt-installed="true"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
||||||
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
|
||||||
|
|
||||||
<title>Autodesk App Store - help file</title><link rel="stylesheet" href="./Resources/appstore-combined.min.css"></head>
|
|
||||||
<body>
|
|
||||||
<div id="main" class="detail-page helpdoc-page clearfix" style="min-height: 619px;">
|
|
||||||
<div id="content">
|
|
||||||
<div id="helpdoc-head">
|
|
||||||
<img id="helpdoc-head-icon" src="./Resources/resized_5ca803cc-8cd1-4cbe-8825-98efaeef156e_.png" alt="ngSkinTools 2">
|
|
||||||
<div id="helpdoc-head-description">
|
|
||||||
<h1 id="helpdoc-product-title">ngSkinTools 2</h1>
|
|
||||||
|
|
||||||
<div class="clear"></div>
|
|
||||||
<hr>
|
|
||||||
<div class="seller">Viktoras Makauskas</div>
|
|
||||||
<div class="description">ngSkinTools is a skinning plugin for Autodesk® Maya®, introducing new concepts to character skinning such as layers, any-pose-mirroring, enhanced paint brushes, true smoothing, and more.</div>
|
|
||||||
</div>
|
|
||||||
<div class="clear"></div>
|
|
||||||
</div>
|
|
||||||
<div class="clear"></div>
|
|
||||||
<div id="helpdoc-tag"><a href="#description" class="helpdoc-breadcrumb">Description</a>
|
|
||||||
|
|
||||||
<a href="#generalinfo" class="helpdoc-breadcrumb">General Usage Instructions</a>
|
|
||||||
<a href="#screensinfo" class="helpdoc-breadcrumb">Screenshots</a>
|
|
||||||
<a href="#inunininfo" class="helpdoc-breadcrumb">Installation/Uninstallation</a>
|
|
||||||
<a href="#knownissueinfo" class="helpdoc-breadcrumb">Known Issues</a>
|
|
||||||
<a href="#contactinfo" class="helpdoc-breadcrumb">Contact</a>
|
|
||||||
<a href="#versionhistoryinfo" class="">Version History</a>
|
|
||||||
<div class="clear"></div>
|
|
||||||
</div><div class="helpdoc-element description">
|
|
||||||
<div class="clear"></div>
|
|
||||||
<h1 id="description">Description</h1>
|
|
||||||
<div class="helpdoc-text">
|
|
||||||
|
|
||||||
<h3 id="layers">Layers</h3>
|
|
||||||
<p>Skinning
|
|
||||||
layers are a central feature of ngSkinTools. With them, you break your rig down
|
|
||||||
into easier manageable parts and edit them separately, then blend everything
|
|
||||||
together through layer transparency.</p>
|
|
||||||
<p>They’re
|
|
||||||
not just a simple way to make your work more organized - they also physically
|
|
||||||
isolate groups of influences from the rest of the rig, so paint and edit
|
|
||||||
operations won’t mix-in influences you were not expecting. This also allows you
|
|
||||||
to do things that were impossible before: per-layer mirroring, adjusting
|
|
||||||
influence weight up/down through layer transparency, blend transferred weights
|
|
||||||
with previous weights, to name a few.</p>
|
|
||||||
|
|
||||||
<h3 id="viewport-tools">Viewport
|
|
||||||
tools</h3>
|
|
||||||
<p>Just
|
|
||||||
like in the previous version, ngSkinTools brings its own weight painting tools.
|
|
||||||
Improving viewport experience is the main focus of V2, and it's complete revamp
|
|
||||||
over the previous implementation.</p>
|
|
||||||
<ul>
|
|
||||||
<li>Selecting
|
|
||||||
influences on screen, a #1 requested feature from users, is nowhere. Just hold
|
|
||||||
“S” and drag over the surface to select dominant influence from that part of
|
|
||||||
the mesh, or hover over a joint pivot to select precisely the joints you
|
|
||||||
want;</li>
|
|
||||||
<li>In
|
|
||||||
addition to the usual surface projection mode for the brush, the new “screen”
|
|
||||||
brush projection mode is useful when you want to quickly set weights for both
|
|
||||||
sides of the mesh;</li>
|
|
||||||
<li>Custom
|
|
||||||
shortcuts while in paint mode allow for quick access to intensity
|
|
||||||
presets;</li>
|
|
||||||
<li>Color
|
|
||||||
feedback is now provided through VP2 APIs, greatly improving the performance
|
|
||||||
of displayed meshes.</li></ul><br>
|
|
||||||
|
|
||||||
<h3 id="smoothing">Smoothing</h3>
|
|
||||||
<p>Keeping
|
|
||||||
weights in harmony with each other is not easy. ngSkinTools help you smooth
|
|
||||||
weights with the control you need, allowing you to control the intensity, number
|
|
||||||
of iterations and effective radius. For very dense meshes, added “iterations”
|
|
||||||
argument now allows for the quicker spread of smoothness over larger areas of
|
|
||||||
the mesh.</p>
|
|
||||||
<p>The
|
|
||||||
“relax” tool from V1 is gone. With major performance rework, you’ll notice that
|
|
||||||
simple flood-smoothing is now much faster and should be a near-instant operation
|
|
||||||
even with large meshes.</p>
|
|
||||||
<p>The
|
|
||||||
opposite “brother” of smooth brush, “sharpen”, is also there - for cases where
|
|
||||||
you want to just bring out the dominant influences</p>
|
|
||||||
|
|
||||||
<h3 id="mirroring">Mirroring</h3>
|
|
||||||
<p>Mirroring
|
|
||||||
is one of the most frequent automated tasks you might want from your skinning
|
|
||||||
tool. With ngSkinTools, you’ll be able to:</p>
|
|
||||||
<ul>
|
|
||||||
<li>Mirror
|
|
||||||
rigs in any pose; no need to switch to T-pose;</li>
|
|
||||||
<li>Have
|
|
||||||
granular control over left/right/center influences mapping, matching
|
|
||||||
left/right joints by naming convention, joint labels, etc;</li>
|
|
||||||
<li>Easily
|
|
||||||
mirror parts of your rig by leveraging layers;</li>
|
|
||||||
<li>Automatic
|
|
||||||
mirroring of weights to the opposite side as you paint so that you don’t need
|
|
||||||
to get distracted from painting while working on symmetrical layers.</li></ul><br>
|
|
||||||
|
|
||||||
<h3 id="layer-effects">Layer
|
|
||||||
effects</h3>
|
|
||||||
<p>With
|
|
||||||
the “mirror as a layer effect” feature, ngSkinTools introduce a new concept to
|
|
||||||
ngSkinTools - layer effects. This differs from automatic mirroring of weights as
|
|
||||||
it’s not directly modifying your layer weights; instead, it’s a post-effect that
|
|
||||||
happens in the background buffer. This has multiple benefits, like a much
|
|
||||||
cleaner seamline of left/right sides, the ability to tweak mirroring settings
|
|
||||||
AFTER weights are painted, etc.</p>
|
|
||||||
|
|
||||||
<h3 id="compatibility">Compatibility</h3>
|
|
||||||
<p>As
|
|
||||||
it's predecessor, ngSkinTools2 operates on standard Maya skinCluster (also known
|
|
||||||
as “smooth skin”), so no custom nodes will be required to use your rig. The
|
|
||||||
plugin has a couple of custom nodes, but they’re only required while you work on
|
|
||||||
setting up your skin weights and can be deleted after, so your work should stay
|
|
||||||
compatible with most pipelines out there.</p>
|
|
||||||
|
|
||||||
<h3 id="performance">Performance</h3>
|
|
||||||
<p>A
|
|
||||||
lot of speed improvements have been made since V2, like improving the
|
|
||||||
utilization of modern multi-core processors, or eliminating bottlenecks through
|
|
||||||
much heavier use of performance profiling. Having a responsive, snappy tool is
|
|
||||||
always a pleasure to work with.</p></div></div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="helpdoc-element ">
|
|
||||||
<div class="clear"></div>
|
|
||||||
<h1 id="generalinfo">General Usage Instructions</h1>
|
|
||||||
<div class="helpdoc-text"><p>The installer from Autodesk App Store loads the application under the Custom shelf.</p>
|
|
||||||
<p><a href="http://www.ngskintools.com/docs"target="_blank">http://www.ngskintools.com/docs/</a></p></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div id="helpdoc-element-screenshot" class="helpdoc-element ">
|
|
||||||
<div class="clear"></div>
|
|
||||||
<h1 id="screensinfo">Screenshots</h1>
|
|
||||||
<div>
|
|
||||||
<div class="helpdoc-screenshot">
|
|
||||||
<a href="./Resources/original_89910fa8-2c30-4376-a905-12c8a003d16b_.png" title="" data-mime="Image" rel="gallery">
|
|
||||||
<div class="helpdoc-img-container">
|
|
||||||
<span class="helper"></span>
|
|
||||||
<img class="helpdoc-screenshot-img" src="./Resources/original_89910fa8-2c30-4376-a905-12c8a003d16b_.png">
|
|
||||||
<span class="helper"></span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="clear"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="helpdoc-command helpdoc-element helpdoc-element-hidden">
|
|
||||||
<div class="clear"></div>
|
|
||||||
<h1 id="commandinfo">Commands</h1>
|
|
||||||
<div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="helpdoc-element ">
|
|
||||||
<div class="clear"></div>
|
|
||||||
<h1 id="inunininfo">Installation/Uninstallation</h1>
|
|
||||||
<div class="helpdoc-text"><div id="pills-tabContent">
|
|
||||||
<div id="pills-windows">
|
|
||||||
<p>The installer that ran when you downloaded this plug-in from Autodesk App Store has already installed the plug-in. Windows only: To uninstall this plug-in, simply rerun the installer downloaded, and select the 'Uninstall' button, or you can uninstall it from 'Control Panel\Programs\Programs and Features', just as you would uninstall any other application from your system. The panel on the Plug-ins tab will not be removed until Maya is restarted.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p>Linux and OSX: To uninstall this plug-in, simply delete the module directory from your system. The panel on the Plug-ins tab will not be removed until Maya is restarted.</p>
|
|
||||||
<div id="pills-tabContent">
|
|
||||||
<div id="pills-windows">
|
|
||||||
<p>Download .msi and run it on your computer. The installation will place files in <code>C:\ProgramData\Autodesk\ApplicationPlugins\ngskintools2</code> (unless your <code>%ProgramData%</code> the environment variable is different).</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p>Using an autoloader system, nothing needs to be configured additionally. Maya scans autoloader locations for plugins at startup and configures each discovered plugin automatically. The autoloader will create a “ngSkinTools2” shelf with a button to open UI.</p>
|
|
||||||
<p>Now, restart Maya and a new tab ngSkinTools2 should appear on your shelf.</p></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3>Available on</h3>
|
|
||||||
<table>
|
|
||||||
<tr><td><img height=48 src="./Resources/win64.png" /></td><td><img height=48 src="./Resources/macos64.png" /></td><td><img height=48 src="./Resources/linux64.png" /></td></tr>
|
|
||||||
<tr><td align="center">Windows</td><td align="center">Mac OSX</td><td align="center">Linux</td></tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<div class="helpdoc-element helpdoc-element-hidden">
|
|
||||||
<div class="clear"></div>
|
|
||||||
<h1 id="addinfo">Additional Information</h1>
|
|
||||||
<div class="helpdoc-text"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="helpdoc-element ">
|
|
||||||
<div class="clear"></div>
|
|
||||||
<h1 id="knownissueinfo">Known Issues</h1>
|
|
||||||
<div class="helpdoc-text"><p>For upcoming features and bugfixes, visit <a href="http://ngskintools.com/v2/roadmap"target="_blank">ngSkinTools v2 public roadmap</a>.</p></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="helpdoc-element ">
|
|
||||||
<div class="clear"></div>
|
|
||||||
<h1 id="contactinfo">Contact</h1>
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div class="">Company Name: Viktoras Makauskas</div>
|
|
||||||
<div class="">Company URL: <a href="http://www.ngskintools.com/" target="_blank">http://www.ngskintools.com</a></div>
|
|
||||||
<div class="">Support Contact: <a href="mailto:support@ngskintools.com">support@ngskintools.com</a></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="helpdoc-block ">
|
|
||||||
<div class="clear"></div>
|
|
||||||
<h1 id="authorinfo">Author/Company Information</h1>
|
|
||||||
<div class="helpdoc-text">Viktoras Makauskas</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="helpdoc-block ">
|
|
||||||
<div class="clear"></div>
|
|
||||||
<h1 id="supportinfo">Support Information</h1>
|
|
||||||
<div class="helpdoc-text"><p>If you have a problem, a question, a feature request, let me know. Your feedback is what drives the project forward!<strong> </strong></p>
|
|
||||||
<p><strong>Documentation</strong></p>
|
|
||||||
<p>Features, tutorials & FAQ available at <a href="http://ngskintools.com/v2/" target="_blank">ngSkinTools official website</a>. </p>
|
|
||||||
<p><strong>Contact in private</strong></p>
|
|
||||||
<p>Use the <a href="http://ngskintools.com/contact/" target="_blank">online contact form</a> or email to <a href="mailto:support@ngskintools.com">support@ngskintools.com</a>.</p></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="helpdoc-version helpdoc-element">
|
|
||||||
<div class="clear"></div>
|
|
||||||
<h1 id="versionhistoryinfo">Version History</h1>
|
|
||||||
<div>
|
|
||||||
<table class="helpdoc-table" id="helpdoc-table-version">
|
|
||||||
<colgroup>
|
|
||||||
<col style="width:150px">
|
|
||||||
<col style="width:680px">
|
|
||||||
</colgroup>
|
|
||||||
<tbody><tr>
|
|
||||||
<th>Version Number</th>
|
|
||||||
<th>Version Description</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<p>
|
|
||||||
2.0.24
|
|
||||||
</p>
|
|
||||||
</td>
|
|
||||||
<td style="white-space: pre-wrap;">2.0.23 (2021-Mar-12)
|
|
||||||
* Fixed: stylus pressure is not updated during the stroke;
|
|
||||||
* Fixed: Maya crashes when smoothing an empty layer with "adjust existing influences only";
|
|
||||||
* Fixed: (regression) UI is not opening on macOS;
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<p>
|
|
||||||
2.0.23
|
|
||||||
</p>
|
|
||||||
</td>
|
|
||||||
<td style="white-space: pre-wrap;">2.0.23 (2021-Mar-10)
|
|
||||||
* Added: adjustable brush size: don't reset to zero when changing brush size in the viewport.
|
|
||||||
* Added: randomize influence colors in paint/display settings;
|
|
||||||
* Added: layers on/off "eye" button in layers tree UI;
|
|
||||||
* Fixed: ngSkinTools will not modify skinCluster's `normalizeWeights` value anymore; for performance boost, you still
|
|
||||||
can disable skinCluster's normalization by setting `normalizeWeights=None`. In "normalizeWeights:interactive"
|
|
||||||
skinCluster mode, Maya will no longer complain that "The weight total would have exceeded 1.0". ngSkinTools will try
|
|
||||||
extra hard to normalize each vertex to a perfect 1.0;
|
|
||||||
* Fixed: UI is not displayed correctly on high DPI displays when UI scaling is enabled in Maya;
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<p>
|
|
||||||
2.0.22
|
|
||||||
</p>
|
|
||||||
</td>
|
|
||||||
<td style="white-space: pre-wrap;">2.0.22 (2021-Feb-06)
|
|
||||||
Added: convenience tool for adding influences to existing skin clusters. Select influences, target mesh and select “Tools | Add Influence”;
|
|
||||||
Fixed: weights will now display properly when viewport option “use default material” is turned on;
|
|
||||||
Added: (v1 feature) Limit max influences per vertex before writing to skin cluster;
|
|
||||||
Added: (v1 feature) Prune small weights before writing to skin cluster.
|
|
||||||
|
|
||||||
2.0.21 (2021-Jan-11)
|
|
||||||
Added: copy/paste vertex weights between different selections (tab “tools” - “copy component weights/paste average component weights”);
|
|
||||||
Added: tool “fill transparency” - for all empty vertices in a layer, assign weights from closest non-empty vertex;
|
|
||||||
Added: “duplicate layer” operation;
|
|
||||||
Added: “Merge layers” operation: combine selected layers into one.
|
|
||||||
|
|
||||||
2.0.20 (2020-Dec-02)
|
|
||||||
Fixed: Linux: crashing on startup
|
|
||||||
|
|
||||||
2.0.19 (2020-Nov-28)
|
|
||||||
Added: influences mapping in mirror screen will now allow matching joints by DG connections; if symmetrical joints are linked between themselves with message connections, ngSkinTools will be able to leverage that information when mirroring weights;
|
|
||||||
Added: symmetry mesh: an option to provide an alternative mesh for calculating vertex mapping for mirroring;
|
|
||||||
Fixed: deleting visibility node can crash Maya sometimes, e.g. switching to component mode (F8) while paint tool is active
|
|
||||||
|
|
||||||
2.0.18 (2020-Nov-20)
|
|
||||||
Added: “use all joints” option for “weights from closest joint” tool; few internal optimizations to speedup operation;
|
|
||||||
Added: “weights from closest joint” option: create a new layer
|
|
||||||
Fixed: “weights from closest joint”: the tool is only using joints as spots, but not as segments;
|
|
||||||
Fixed: after “weights from closest joint” operation influences list is not refreshed;
|
|
||||||
Fixed: “weights from closest joint”: “assign” button sometimes disabled;
|
|
||||||
|
|
||||||
2.0.17 (2020-Nov-19)
|
|
||||||
Fixed: “resume in workspace” error while opening UI
|
|
||||||
|
|
||||||
2.0.16 (2020-Nov-15)
|
|
||||||
Added: skin data will be compressed for ngSkinTools data nodes, which should substantially reduce file size for scenes with lots of skinning layers;
|
|
||||||
Added: paint mode intensity sliders are now exponential: “smooth”, “add” and “sharpen” sliders will now be more precise for lower values, and “scale” mode will allow for more precision when setting high values.
|
|
||||||
|
|
||||||
2.0.15 (2020-Nov-10)
|
|
||||||
Added: new “Set Weights” tab contains tools to apply weights to vertex/edge/polygon selection instead of painting.
|
|
||||||
Added: a new option for smooth tool - “only adjust existing vertex influences”; when this is turned on, the smooth tool will prevent influences weights spreading across the surface
|
|
||||||
Fixed: layer mirror effects correctly saved/loaded in files;
|
|
||||||
Fixed: mask mirror effect was not correctly used by layer blending engine
|
|
||||||
|
|
||||||
2.0.14 (2020-Oct-04)
|
|
||||||
Fixed: occasional crashes when using mirror effect on layers;
|
|
||||||
Additional stability fixes.
|
|
||||||
|
|
||||||
2.0.13 (2020-Oct-04)
|
|
||||||
Fixed: minor bug in 2.0.12 blocks UI from opening;
|
|
||||||
|
|
||||||
2.0.12 (2020-Oct-03)
|
|
||||||
Added: option to view used influences in influences list;
|
|
||||||
Added: hide “DQ weights” channel in influences list if skin cluster skinning method is not set to “Weight Blended”;
|
|
||||||
|
|
||||||
2.0.11 (2020-Oct-01)
|
|
||||||
Fixed: broken Linux builds
|
|
||||||
|
|
||||||
2.0.10 (2020-Sep-26)
|
|
||||||
Added: pressing “f” while painting focuses viewport camera to current paint target; for joints and other influences, the current joint pivot is used as camera interest point; when current paint target is a mask, viewport centers around painted values;
|
|
||||||
Fixed: influence mapping UI error if some influences are no joints;
|
|
||||||
|
|
||||||
2.0.9 (2020-Sep-11)
|
|
||||||
Fixed: undo paint crashing Maya;
|
|
||||||
Fixed: incorrect brush behavior with multiple viewports open;
|
|
||||||
Fixed: incorrect mesh display / VP2 transparency setting sensitive;
|
|
||||||
Fixed: clearing selection while painting does not update the display of current mesh;</td>
|
|
||||||
</tr>
|
|
||||||
</tbody></table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div></body></html>
|
|
||||||
@@ -1,158 +1,43 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
"""
|
"""
|
||||||
ngSkinTools2 启动器
|
ngSkinTools2 Launcher
|
||||||
用于从工具架快速启动 ngSkinTools2
|
Provides a simple interface to launch ngSkinTools2 from Maya shelf buttons
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
|
||||||
|
|
||||||
def get_maya_version():
|
|
||||||
"""
|
|
||||||
获取当前 Maya 版本号
|
|
||||||
返回格式: '2023', '2024' 等
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
from maya import cmds
|
|
||||||
maya_version = cmds.about(version=True)
|
|
||||||
# Maya 版本格式可能是 "2023" 或 "2023.1" 等,取主版本号
|
|
||||||
return maya_version.split('.')[0]
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Failed to get Maya version: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def find_compatible_plugin_dir(base_dir, maya_version):
|
|
||||||
"""
|
|
||||||
查找兼容的插件目录
|
|
||||||
优先使用完全匹配的版本,如果没有则向下查找最接近的版本
|
|
||||||
|
|
||||||
Args:
|
|
||||||
base_dir: 插件基础目录 (plug-ins/)
|
|
||||||
maya_version: Maya 版本号字符串,如 '2023'
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
插件目录路径,如果找不到则返回 None
|
|
||||||
"""
|
|
||||||
if not maya_version:
|
|
||||||
return None
|
|
||||||
|
|
||||||
try:
|
|
||||||
maya_ver_int = int(maya_version)
|
|
||||||
except ValueError:
|
|
||||||
print(f"Invalid Maya version format: {maya_version}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
# 列出所有可用的插件版本目录
|
|
||||||
available_versions = []
|
|
||||||
if os.path.exists(base_dir):
|
|
||||||
for item in os.listdir(base_dir):
|
|
||||||
item_path = os.path.join(base_dir, item)
|
|
||||||
if os.path.isdir(item_path):
|
|
||||||
try:
|
|
||||||
ver_int = int(item)
|
|
||||||
available_versions.append((ver_int, item_path))
|
|
||||||
except ValueError:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if not available_versions:
|
|
||||||
return None
|
|
||||||
|
|
||||||
# 按版本号排序
|
|
||||||
available_versions.sort(reverse=True)
|
|
||||||
|
|
||||||
# 首先尝试完全匹配
|
|
||||||
for ver, path in available_versions:
|
|
||||||
if ver == maya_ver_int:
|
|
||||||
print(f"Found exact match plugin for Maya {maya_version}: {path}")
|
|
||||||
return path
|
|
||||||
|
|
||||||
# 如果没有完全匹配,使用小于等于当前版本的最高版本
|
|
||||||
for ver, path in available_versions:
|
|
||||||
if ver <= maya_ver_int:
|
|
||||||
print(f"Using compatible plugin for Maya {maya_version}: {path} (version {ver})")
|
|
||||||
return path
|
|
||||||
|
|
||||||
# 如果当前版本比所有可用版本都旧,使用最旧的版本
|
|
||||||
oldest_ver, oldest_path = available_versions[-1]
|
|
||||||
print(f"Warning: Maya {maya_version} is older than available plugins. Using oldest: {oldest_path} (version {oldest_ver})")
|
|
||||||
return oldest_path
|
|
||||||
|
|
||||||
|
|
||||||
def LaunchNgSkinTools():
|
def LaunchNgSkinTools():
|
||||||
"""
|
"""
|
||||||
启动 ngSkinTools2 主界面
|
Launch ngSkinTools2 main UI window
|
||||||
自动检测 Maya 版本并加载对应的插件
|
|
||||||
|
This function handles two scenarios:
|
||||||
|
1. If module alias exists (ngSkinTools2 -> rigging_tools.ngskintools2), use it
|
||||||
|
2. If not, create the alias on-the-fly and then launch
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from maya import cmds, mel
|
# Check if module alias already exists
|
||||||
|
if 'ngSkinTools2' not in sys.modules:
|
||||||
# 获取当前 Maya 版本
|
# Create the alias on-the-fly
|
||||||
maya_version = get_maya_version()
|
|
||||||
if not maya_version:
|
|
||||||
print("Warning: Could not determine Maya version, will try to continue anyway")
|
|
||||||
else:
|
|
||||||
print(f"Detected Maya version: {maya_version}")
|
|
||||||
|
|
||||||
# 将当前目录添加到 Python 路径,并使用别名
|
|
||||||
# 这样 ngSkinTools2 内部的 "from ngSkinTools2.ui" 就能找到模块
|
|
||||||
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
||||||
parent_dir = os.path.dirname(current_dir)
|
|
||||||
|
|
||||||
# 添加父目录到路径
|
|
||||||
if parent_dir not in sys.path:
|
|
||||||
sys.path.insert(0, parent_dir)
|
|
||||||
|
|
||||||
# 添加当前目录到路径,作为 ngSkinTools2 模块
|
|
||||||
if current_dir not in sys.path:
|
|
||||||
sys.path.insert(0, current_dir)
|
|
||||||
|
|
||||||
# 在 sys.modules 中创建别名,让 ngSkinTools2 指向 ngskintools2
|
|
||||||
import rigging_tools.ngskintools2 as ngskintools2_module
|
|
||||||
sys.modules['ngSkinTools2'] = ngskintools2_module
|
|
||||||
|
|
||||||
# 查找并添加插件路径
|
|
||||||
# 根据 Maya 版本自动选择对应的插件目录
|
|
||||||
plugin_base_dir = os.path.join(current_dir, 'plug-ins')
|
|
||||||
plugin_dir = find_compatible_plugin_dir(plugin_base_dir, maya_version)
|
|
||||||
|
|
||||||
if plugin_dir and os.path.exists(plugin_dir):
|
|
||||||
# 添加插件路径
|
|
||||||
current_plugin_path = os.environ.get('MAYA_PLUG_IN_PATH', '')
|
|
||||||
if plugin_dir not in current_plugin_path:
|
|
||||||
os.environ['MAYA_PLUG_IN_PATH'] = plugin_dir + os.pathsep + current_plugin_path
|
|
||||||
|
|
||||||
# 加载插件
|
|
||||||
try:
|
try:
|
||||||
# 检查插件是否已加载
|
import rigging_tools.ngskintools2
|
||||||
if not cmds.pluginInfo('ngSkinTools2', query=True, loaded=True):
|
sys.modules['ngSkinTools2'] = rigging_tools.ngskintools2
|
||||||
cmds.loadPlugin(os.path.join(plugin_dir, 'ngSkinTools2.mll'))
|
print("Created ngSkinTools2 module alias")
|
||||||
print(f"ngSkinTools2 plugin loaded successfully from {plugin_dir}")
|
except ImportError as import_err:
|
||||||
else:
|
print(f"Error: Failed to import rigging_tools.ngskintools2 - {import_err}")
|
||||||
print(f"ngSkinTools2 plugin already loaded")
|
print("Please ensure the ngskintools2 directory is in the correct location")
|
||||||
except RuntimeError:
|
return
|
||||||
# 插件不存在,尝试加载
|
|
||||||
try:
|
|
||||||
cmds.loadPlugin(os.path.join(plugin_dir, 'ngSkinTools2.mll'))
|
|
||||||
print(f"ngSkinTools2 plugin loaded successfully from {plugin_dir}")
|
|
||||||
except Exception as plugin_error:
|
|
||||||
print(f"Warning: Could not load ngSkinTools2 plugin: {plugin_error}")
|
|
||||||
except Exception as plugin_error:
|
|
||||||
print(f"Warning: Could not load ngSkinTools2 plugin: {plugin_error}")
|
|
||||||
else:
|
|
||||||
print(f"Warning: Plugin directory not found: {plugin_dir}")
|
|
||||||
|
|
||||||
# 现在可以导入并打开 UI
|
# Now import and launch
|
||||||
from rigging_tools.ngskintools2 import open_ui
|
from ngSkinTools2 import open_ui
|
||||||
open_ui()
|
open_ui()
|
||||||
print("ngSkinTools2 UI opened successfully")
|
print("ngSkinTools2 launched successfully")
|
||||||
|
|
||||||
|
except ImportError as e:
|
||||||
|
print(f"Error: Failed to import ngSkinTools2 - {e}")
|
||||||
|
print("Please ensure ngSkinTools2 is properly installed")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Failed to open ngSkinTools2: {e}")
|
print(f"Error: Failed to launch ngSkinTools2 - {e}")
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
LaunchNgSkinTools()
|
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
ngSkinTools Software License Agreement.
|
|
||||||
|
|
||||||
|
|
||||||
This is a legal agreement between you and ngSkinTools author (Viktoras Makauskas) covering your use of
|
|
||||||
ngSkinTools (the "Software").
|
|
||||||
|
|
||||||
1) ngSkinTools is provided as freeware.
|
|
||||||
|
|
||||||
2) ngSkinTools Software is owned by Viktoras Makauskas and is protected by copyright laws and international
|
|
||||||
treaty provisions. Therefore, you must treat the Software like any other copyrighted material.
|
|
||||||
|
|
||||||
3) You may not distribute, rent, sub-license or otherwise make available to others the Software or
|
|
||||||
documentation or copies thereof, except as expressly permitted in this License without prior written consent
|
|
||||||
from ngSkinTools author (Viktoras Makauskas). In the case of an authorized transfer, the transferee must agree
|
|
||||||
to be bound by the terms and conditions of this License Agreement.
|
|
||||||
|
|
||||||
4) You may not remove any proprietary notices, labels, trademarks on the Software or documentation. You may not
|
|
||||||
modify, de-compile, disassemble or reverse engineer the Software.
|
|
||||||
|
|
||||||
5) Limited warranty: ngSkinTools software and documentation are "as is" without any warranty as to their
|
|
||||||
performance, merchantability or fitness for any particular purpose. The licensee assumes the entire risk as to
|
|
||||||
the quality and performance of the software. In no event shall ngSkinTools author or anyone else who has been
|
|
||||||
involved in the creation, development, production, or delivery of this software be liable for any direct,
|
|
||||||
incidental or consequential damages, such as, but not limited to, loss of anticipated profits, benefits, use,
|
|
||||||
or data resulting from the use of this software, or arising out of any breach of warranty.
|
|
||||||
|
|
||||||
Copyright (C) 2009-2020 Viktoras Makauskas
|
|
||||||
|
|
||||||
http://www.ngskintools.com
|
|
||||||
support@ngskintools.com
|
|
||||||
|
|
||||||
All rights reserved.
|
|
||||||
@@ -22,4 +22,4 @@ class ObservableValue(Object):
|
|||||||
if default != Undefined:
|
if default != Undefined:
|
||||||
return default
|
return default
|
||||||
|
|
||||||
raise Exception("using observable value before setting it")
|
raise Exception("在设置之前使用可观察值")
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ def action_copy_cut(session, parent, cut):
|
|||||||
operation(session.state.currentLayer.layer, influences)
|
operation(session.state.currentLayer.layer, influences)
|
||||||
|
|
||||||
operation_name = "Cut" if cut else "Copy"
|
operation_name = "Cut" if cut else "Copy"
|
||||||
result = actions.define_action(parent, operation_name + " weights to clipboard", callback=cut_copy_callback)
|
result = actions.define_action(parent, operation_name + " 将权重复制到剪贴板", callback=cut_copy_callback) # 将权重复制到剪贴板 weights to clipboard
|
||||||
|
|
||||||
@signal.on(session.events.currentLayerChanged, session.events.currentInfluenceChanged, qtParent=parent)
|
@signal.on(session.events.currentLayerChanged, session.events.currentInfluenceChanged, qtParent=parent)
|
||||||
def on_selection_changed():
|
def on_selection_changed():
|
||||||
@@ -49,13 +49,13 @@ def action_paste(session, parent, operation):
|
|||||||
api.paste_weights(session.state.currentLayer.layer, operation, influences=influences)
|
api.paste_weights(session.state.currentLayer.layer, operation, influences=influences)
|
||||||
|
|
||||||
labels = {
|
labels = {
|
||||||
PasteOperation.add: 'Paste weights (add to existing)',
|
PasteOperation.add: '粘贴权重(添加到现有)',
|
||||||
PasteOperation.subtract: 'Paste weight (subtract from existing)',
|
PasteOperation.subtract: '粘贴权重(从现有值中减去)',
|
||||||
PasteOperation.replace: 'Paste weights (replace existing)',
|
PasteOperation.replace: '粘贴权重(替换现有)',
|
||||||
}
|
}
|
||||||
|
|
||||||
result = actions.define_action(parent, labels[operation], callback=paste_callback)
|
result = actions.define_action(parent, labels[operation], callback=paste_callback)
|
||||||
result.setToolTip("Paste previously copied weights from clipboard")
|
result.setToolTip("从剪贴板粘贴先前复制的权重")
|
||||||
|
|
||||||
@signal.on(session.events.currentLayerChanged)
|
@signal.on(session.events.currentLayerChanged)
|
||||||
def on_selection_changed():
|
def on_selection_changed():
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ def buildAction_export(session, parent):
|
|||||||
|
|
||||||
result = actions.define_action(
|
result = actions.define_action(
|
||||||
parent,
|
parent,
|
||||||
"Export Layers to Json...",
|
"导出图层...",
|
||||||
callback=export_callback,
|
callback=export_callback,
|
||||||
tooltip="Save layer info to external file, suitable for importing weights to different scene/mesh",
|
tooltip="Save layer info to external file, suitable for importing weights to different scene/mesh",
|
||||||
)
|
)
|
||||||
@@ -79,7 +79,7 @@ def buildAction_import(session, parent, file_dialog_func=None):
|
|||||||
t.customize_callback = transfer_dialog
|
t.customize_callback = transfer_dialog
|
||||||
t.execute()
|
t.execute()
|
||||||
|
|
||||||
result = actions.define_action(parent, "Import Layers from Json...", callback=import_callback, tooltip="Load previously exported weights")
|
result = actions.define_action(parent, "导入图层...", callback=import_callback, tooltip="Load previously exported weights")
|
||||||
|
|
||||||
@signal.on(session.events.targetChanged, qtParent=parent)
|
@signal.on(session.events.targetChanged, qtParent=parent)
|
||||||
def update():
|
def update():
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ def build_action_import_v1(session, parent):
|
|||||||
update_state()
|
update_state()
|
||||||
session.events.targetChanged.emitIfChanged()
|
session.events.targetChanged.emitIfChanged()
|
||||||
|
|
||||||
result = actions.define_action(parent, "Convert From v1.0 Layers", callback=do_convert)
|
result = actions.define_action(parent, "从v1.0图层转换", callback=do_convert)
|
||||||
result.setToolTip("Convert skinning layers from previous version of ngSkinTools; after completing this action, v1 nodes will be deleted.")
|
result.setToolTip("“转化旧版ngSkinTools的图层;完成此操作后,v1节点将被删除。")
|
||||||
|
|
||||||
@signal.on(session.events.targetChanged)
|
@signal.on(session.events.targetChanged)
|
||||||
def update_state():
|
def update_state():
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ def initializeLayers(createFirstLayer=True):
|
|||||||
layers = ngSkinTools2.api.init_layers(target)
|
layers = ngSkinTools2.api.init_layers(target)
|
||||||
with ngSkinTools2.api.suspend_updates(target):
|
with ngSkinTools2.api.suspend_updates(target):
|
||||||
if createFirstLayer:
|
if createFirstLayer:
|
||||||
layer = layers.add("Base weights")
|
layer = layers.add("基础权重") #Base weights
|
||||||
layer.set_current()
|
layer.set_current()
|
||||||
Mirror(target).set_mirror_config(config.mirrorInfluencesDefaults)
|
Mirror(target).set_mirror_config(config.mirrorInfluencesDefaults)
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ def initializeLayers(createFirstLayer=True):
|
|||||||
|
|
||||||
if ngSkinTools2.api.is_slow_mode_skin_cluster(target):
|
if ngSkinTools2.api.is_slow_mode_skin_cluster(target):
|
||||||
dialogs.info(
|
dialogs.info(
|
||||||
"ngSkinTools switched to slow maya API for setting skin cluster weights for this skinCluster, to workaround a Maya bug when skinCluster uses dg nodes as inputs"
|
"切换为设置皮肤集群权重,以解决 Maya皮肤集群使用装点作为输入时的错误。"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -73,15 +73,15 @@ def build_action_initialize_layers(session, parent):
|
|||||||
def do_initialize():
|
def do_initialize():
|
||||||
if import_v1_actions.can_import(session):
|
if import_v1_actions.can_import(session):
|
||||||
q = (
|
q = (
|
||||||
"Skinning layers from previous version of ngSkinTools are present on this mesh. This operation will initialize "
|
"来自旧版 ngSkinTools的皮肤层存在于此网格上。此操作将初始化"
|
||||||
"skinning layers from scratch, discarding previous layers information. Do you want to continue?"
|
"从头开始剥离图层,丢弃之前的图层信息。您要继续吗?"
|
||||||
)
|
)
|
||||||
if not dialogs.yesNo(q):
|
if not dialogs.yesNo(q):
|
||||||
return
|
return
|
||||||
|
|
||||||
initializeLayers()
|
initializeLayers()
|
||||||
|
|
||||||
result = actions.define_action(parent, "Initialize Skinning Layers", callback=do_initialize)
|
result = actions.define_action(parent, "初始化蒙皮层", callback=do_initialize)
|
||||||
|
|
||||||
@signal.on(session.events.nodeSelectionChanged)
|
@signal.on(session.events.nodeSelectionChanged)
|
||||||
def update():
|
def update():
|
||||||
@@ -95,7 +95,7 @@ def build_action_initialize_layers(session, parent):
|
|||||||
def buildAction_createLayer(session, parent):
|
def buildAction_createLayer(session, parent):
|
||||||
from ngSkinTools2.ui import actions
|
from ngSkinTools2.ui import actions
|
||||||
|
|
||||||
result = actions.define_action(parent, "Create Layer", callback=addLayer, icon=":/newLayerEmpty.png", shortcut=QtCore.Qt.Key_Insert)
|
result = actions.define_action(parent, "创建图层", callback=addLayer, icon=":/newLayerEmpty.png", shortcut=QtCore.Qt.Key_Insert)
|
||||||
|
|
||||||
@signal.on(session.events.targetChanged)
|
@signal.on(session.events.targetChanged)
|
||||||
def update_to_target():
|
def update_to_target():
|
||||||
@@ -109,7 +109,7 @@ def buildAction_createLayer(session, parent):
|
|||||||
def buildAction_deleteLayer(session, parent):
|
def buildAction_deleteLayer(session, parent):
|
||||||
from ngSkinTools2.ui import actions
|
from ngSkinTools2.ui import actions
|
||||||
|
|
||||||
result = actions.define_action(parent, "Delete Layer", callback=deleteSelectedLayers, shortcut=QtCore.Qt.Key_Delete)
|
result = actions.define_action(parent, "删除图层", callback=deleteSelectedLayers, shortcut=QtCore.Qt.Key_Delete)
|
||||||
|
|
||||||
@signal.on(session.context.selected_layers.changed, session.events.targetChanged, qtParent=parent)
|
@signal.on(session.context.selected_layers.changed, session.events.targetChanged, qtParent=parent)
|
||||||
def update_to_target():
|
def update_to_target():
|
||||||
@@ -126,12 +126,12 @@ def setCurrentLayer(layer):
|
|||||||
:type layer: ngSkinTools2.api.layers.Layer
|
:type layer: ngSkinTools2.api.layers.Layer
|
||||||
"""
|
"""
|
||||||
if not session.active():
|
if not session.active():
|
||||||
logger.info("didn't set current layer: no session")
|
logger.info("未设置当前图层:没有会话")
|
||||||
|
|
||||||
if not session.state.layersAvailable:
|
if not session.state.layersAvailable:
|
||||||
logger.info("didn't set current layer: layers not enabled")
|
logger.info("未设置当前图层:图层未启用")
|
||||||
|
|
||||||
logger.info("setting current layer to %r on %r", layer, session.state.selectedSkinCluster)
|
logger.info("将当前图层设置为 %r on %r", layer, session.state.selectedSkinCluster)
|
||||||
layer.set_current()
|
layer.set_current()
|
||||||
session.events.currentLayerChanged.emitIfChanged()
|
session.events.currentLayerChanged.emitIfChanged()
|
||||||
|
|
||||||
@@ -158,7 +158,7 @@ def deleteSelectedLayers():
|
|||||||
|
|
||||||
|
|
||||||
class ToggleEnabledAction(Action):
|
class ToggleEnabledAction(Action):
|
||||||
name = "Enabled"
|
name = "启用图层" #Enabled
|
||||||
checkable = True
|
checkable = True
|
||||||
|
|
||||||
def __init__(self, session):
|
def __init__(self, session):
|
||||||
@@ -192,7 +192,7 @@ class ToggleEnabledAction(Action):
|
|||||||
for i in selected_layers:
|
for i in selected_layers:
|
||||||
i.enabled = enabled
|
i.enabled = enabled
|
||||||
|
|
||||||
logger.info("layers toggled: %r", selected_layers)
|
logger.info("图层已切换: %r", selected_layers)
|
||||||
|
|
||||||
session.events.layerListChanged.emitIfChanged()
|
session.events.layerListChanged.emitIfChanged()
|
||||||
|
|
||||||
@@ -209,8 +209,8 @@ def build_action_randomize_influences_colors(session, parent):
|
|||||||
:type session: ngSkinTools2.api.session.Session
|
:type session: ngSkinTools2.api.session.Session
|
||||||
"""
|
"""
|
||||||
|
|
||||||
result = QAction("Randomize colors", parent)
|
result = QAction("随机颜色", parent)
|
||||||
result.setToolTip("Choose random colors for each influence, selecting from Maya's pallete of indexed colors")
|
result.setToolTip("为每个影响选择随机颜色,从Maya的索引色板中选择。")
|
||||||
|
|
||||||
def color_filter(c):
|
def color_filter(c):
|
||||||
brightness = c[0] * c[0] + c[1] * c[1] + c[2] * c[2]
|
brightness = c[0] * c[0] + c[1] * c[1] + c[2] * c[2]
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ log = getLogger("operations/paint")
|
|||||||
|
|
||||||
|
|
||||||
class FloodAction(Action):
|
class FloodAction(Action):
|
||||||
name = "Flood"
|
name = "填充" # Flood
|
||||||
tooltip = "Apply current brush to whole selection"
|
tooltip = "将当前画笔应用于整个选择区域"
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
session.paint_tool.flood(self.session.state.currentLayer.layer, influences=self.session.state.currentLayer.layer.paint_targets)
|
session.paint_tool.flood(self.session.state.currentLayer.layer, influences=self.session.state.currentLayer.layer.paint_targets)
|
||||||
@@ -28,8 +28,8 @@ class FloodAction(Action):
|
|||||||
|
|
||||||
|
|
||||||
class PaintAction(Action):
|
class PaintAction(Action):
|
||||||
name = "Paint"
|
name = "绘制" # Paint
|
||||||
tooltip = "Toggle paint tool"
|
tooltip = "切换绘制工具"
|
||||||
checkable = True
|
checkable = True
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|||||||
@@ -43,14 +43,14 @@ def list_custom_nodes_for_meshes(meshes):
|
|||||||
return list(itertools.chain.from_iterable([list_custom_nodes_for_mesh(i) for i in meshes]))
|
return list(itertools.chain.from_iterable([list_custom_nodes_for_mesh(i) for i in meshes]))
|
||||||
|
|
||||||
|
|
||||||
message_scene_noCustomNodes = 'Scene does not contain any custom ngSkinTools nodes.'
|
message_scene_noCustomNodes = '场景中不包含任何自定义的ngSkinTools节点。'
|
||||||
message_selection_noCustomNodes = 'Selection does not contain any custom ngSkinTools nodes.'
|
message_selection_noCustomNodes = '选择不包含任何自定义ngskinTools节点。'
|
||||||
message_scene_warning = (
|
message_scene_warning = (
|
||||||
'This command deletes all custom ngSkinTools nodes. Skin weights ' 'will be preserved, but all layer data will be lost. Do you want to continue?'
|
'此命令删除所有自定义ngSkinTools节点。蒙皮权重“”将被保留,但所有图层数据都将丢失。您想继续吗?'
|
||||||
)
|
)
|
||||||
message_selection_warning = (
|
message_selection_warning = (
|
||||||
'This command deletes custom ngSkinTools nodes for selection. Skin weights '
|
'此命令删除要选择的自定义ngSkinTools节点。皮肤重量'
|
||||||
'will be preserved, but all layer data will be lost. Do you want to continue?'
|
'将被保留,但所有图层数据都将丢失。您想继续吗?'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ def remove_custom_nodes(interactive=False, session=None, meshes=None):
|
|||||||
|
|
||||||
if PaintTool.is_painting():
|
if PaintTool.is_painting():
|
||||||
# make sure that painting is canceled to restore mesh display etc
|
# make sure that painting is canceled to restore mesh display etc
|
||||||
cmds.setToolTo("Move")
|
cmds.setToolTo("移除") # Move
|
||||||
|
|
||||||
if session is not None:
|
if session is not None:
|
||||||
session.events.targetChanged.emitIfChanged()
|
session.events.targetChanged.emitIfChanged()
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ def create_action__from_closest_joint(parent, session):
|
|||||||
if not options.all_influences():
|
if not options.all_influences():
|
||||||
influences = layer.paint_targets
|
influences = layer.paint_targets
|
||||||
if not influences:
|
if not influences:
|
||||||
dialogs.info("Select one or more influences in Influences list")
|
dialogs.info("在影响列表中选择一个或多个影响")
|
||||||
return
|
return
|
||||||
|
|
||||||
if options.create_new_layer():
|
if options.create_new_layer():
|
||||||
@@ -77,8 +77,8 @@ def create_action__from_closest_joint(parent, session):
|
|||||||
__create_tool_action__(
|
__create_tool_action__(
|
||||||
parent,
|
parent,
|
||||||
session,
|
session,
|
||||||
action_name=u"Assign From Closest Joint",
|
action_name=u"从最近的关节分配",
|
||||||
action_tooltip="Assign 1.0 weight for closest influence per each vertex in selected layer",
|
action_tooltip="为选定层中每个顶点的最近影响分配权重1.0",
|
||||||
exec_handler=exec_handler,
|
exec_handler=exec_handler,
|
||||||
),
|
),
|
||||||
options,
|
options,
|
||||||
@@ -105,8 +105,8 @@ def create_action__unify_weights(parent, session):
|
|||||||
__create_tool_action__(
|
__create_tool_action__(
|
||||||
parent,
|
parent,
|
||||||
session,
|
session,
|
||||||
action_name=u"Unify Weights",
|
action_name=u"统一权重", # Unify Weights
|
||||||
action_tooltip="For selected vertices, make verts the same for all verts",
|
action_tooltip="对于选定的顶点,使所有顶点相同。", #对于选定的顶点,使所有顶点相同。
|
||||||
exec_handler=exec_handler,
|
exec_handler=exec_handler,
|
||||||
),
|
),
|
||||||
options,
|
options,
|
||||||
@@ -130,8 +130,8 @@ def create_action__merge_layers(parent, session):
|
|||||||
return __create_tool_action__(
|
return __create_tool_action__(
|
||||||
parent,
|
parent,
|
||||||
session,
|
session,
|
||||||
action_name=u"Merge",
|
action_name=u"合并", # Merge
|
||||||
action_tooltip="Merge contents of this layer into underlying layer. Pre-effects weights will be used for this",
|
action_tooltip="将本层的元素合并到底层。预效果权重将用于此。",
|
||||||
exec_handler=exec_handler,
|
exec_handler=exec_handler,
|
||||||
enabled_handler=enabled_handler,
|
enabled_handler=enabled_handler,
|
||||||
)
|
)
|
||||||
@@ -155,8 +155,8 @@ def create_action__duplicate_layer(parent, session):
|
|||||||
return __create_tool_action__(
|
return __create_tool_action__(
|
||||||
parent,
|
parent,
|
||||||
session,
|
session,
|
||||||
action_name=u"Duplicate",
|
action_name=u"复制",
|
||||||
action_tooltip="Duplicate selected layer(s)",
|
action_tooltip="复制选择的图层(多选)",
|
||||||
exec_handler=exec_handler,
|
exec_handler=exec_handler,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -176,8 +176,8 @@ def create_action__fill_transparency(parent, session):
|
|||||||
return __create_tool_action__(
|
return __create_tool_action__(
|
||||||
parent,
|
parent,
|
||||||
session,
|
session,
|
||||||
action_name=u"Fill Transparency",
|
action_name=u"填充透明度",
|
||||||
action_tooltip="All transparent vertices in the selected layer(s) receive weights from their closest non-empty neighbour vertex",
|
action_tooltip="所选图层中的所有透明顶点接收其最近非空邻接顶点的权重,",
|
||||||
exec_handler=exec_handler,
|
exec_handler=exec_handler,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -195,8 +195,8 @@ def create_action__copy_component_weights(parent, session):
|
|||||||
return __create_tool_action__(
|
return __create_tool_action__(
|
||||||
parent,
|
parent,
|
||||||
session,
|
session,
|
||||||
action_name=u"Copy Component Weights",
|
action_name=u"复制组件权重",
|
||||||
action_tooltip="Store components weights in memory for further component-based paste actions",
|
action_tooltip="将组件权重存储在内存中,以便进行进一步的基于组件的粘贴操作",
|
||||||
exec_handler=exec_handler,
|
exec_handler=exec_handler,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -214,8 +214,8 @@ def create_action__paste_average_component_weight(parent, session):
|
|||||||
return __create_tool_action__(
|
return __create_tool_action__(
|
||||||
parent,
|
parent,
|
||||||
session,
|
session,
|
||||||
action_name=u"Paste Average Component Weight",
|
action_name=u"粘贴平均组件权重",
|
||||||
action_tooltip="Compute average of copied component weights and set that value to currently selected components",
|
action_tooltip="计算复制的组件重量的平均值,并将该值设置为当前选定的组件",
|
||||||
exec_handler=exec_handler,
|
exec_handler=exec_handler,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -229,7 +229,7 @@ def create_action__add_influences(parent, session):
|
|||||||
def exec_handler():
|
def exec_handler():
|
||||||
selection = cmds.ls(sl=True, l=True)
|
selection = cmds.ls(sl=True, l=True)
|
||||||
if len(selection) < 2:
|
if len(selection) < 2:
|
||||||
logger.info("invalid selection: %s", selection)
|
logger.info("无效选择: %s", selection)
|
||||||
return
|
return
|
||||||
api.add_influences(selection[:-1], selection[-1])
|
api.add_influences(selection[:-1], selection[-1])
|
||||||
cmds.select(selection[-1])
|
cmds.select(selection[-1])
|
||||||
@@ -238,8 +238,8 @@ def create_action__add_influences(parent, session):
|
|||||||
return __create_tool_action__(
|
return __create_tool_action__(
|
||||||
parent,
|
parent,
|
||||||
session,
|
session,
|
||||||
action_name=u"Add Influences",
|
action_name=u"增加影响",
|
||||||
action_tooltip="Add selected influences to current skin cluster.",
|
action_tooltip="将选定的影响添加到当前皮肤集群。",
|
||||||
exec_handler=exec_handler,
|
exec_handler=exec_handler,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -289,7 +289,7 @@ def create_action__select_affected_vertices(parent, session):
|
|||||||
return __create_tool_action__(
|
return __create_tool_action__(
|
||||||
parent,
|
parent,
|
||||||
session,
|
session,
|
||||||
action_name=u"Select Affected Vertices",
|
action_name=u"选择受影响的顶点",
|
||||||
action_tooltip="Select vertices that have non-zero weight for current influence.",
|
action_tooltip="选择当前影响中权重不为要的顶点。",
|
||||||
exec_handler=exec_handler,
|
exec_handler=exec_handler,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -12,10 +12,10 @@ def website_base_url():
|
|||||||
|
|
||||||
class WebsiteLinksActions(Object):
|
class WebsiteLinksActions(Object):
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
self.api_root = make_documentation_action(parent, "API Documentation", "/v2/api")
|
self.api_root = make_documentation_action(parent, "API 文档", "/v2/api")
|
||||||
self.user_guide = make_documentation_action(parent, "User Guide", "/v2/")
|
self.user_guide = make_documentation_action(parent, "用户指南", "/v2/")
|
||||||
self.changelog = make_documentation_action(parent, "Change Log", "/v2/changelog", icon=None)
|
self.changelog = make_documentation_action(parent, "更新日志", "/v2/changelog", icon=None)
|
||||||
self.contact = make_documentation_action(parent, "Contact", "/contact/", icon=None)
|
self.contact = make_documentation_action(parent, "联系", "/contact/", icon=None)
|
||||||
|
|
||||||
|
|
||||||
def make_documentation_action(parent, title, url, icon=":/help.png"):
|
def make_documentation_action(parent, title, url, icon=":/help.png"):
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ class SignalQueue(Object):
|
|||||||
|
|
||||||
def emit(self, handler):
|
def emit(self, handler):
|
||||||
if len(self.queue) > self.max_length:
|
if len(self.queue) > self.max_length:
|
||||||
log.error("queue max length reached: emitting too many events?")
|
log.error("队列最大长度已达到:发出的事件过多?")
|
||||||
raise Exception("queue max length reached: emitting too many events?")
|
raise Exception("队列最大长度已达到:正在发出太多事件?")
|
||||||
|
|
||||||
should_start = len(self.queue) == 0
|
should_start = len(self.queue) == 0
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ class SignalQueue(Object):
|
|||||||
current_handler += 1
|
current_handler += 1
|
||||||
|
|
||||||
if len(self.queue) > 50:
|
if len(self.queue) > 50:
|
||||||
log.info("handler queue finished with %d items", len(self.queue))
|
log.info("处理器队列完成,共 %d 项", len(self.queue))
|
||||||
self.queue = []
|
self.queue = []
|
||||||
|
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ class Signal(Object):
|
|||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
if name is None:
|
if name is None:
|
||||||
raise Exception("need name for debug purposes later")
|
raise Exception("需要稍后用于调试目的的名称")
|
||||||
self.name = name
|
self.name = name
|
||||||
self.handlers = []
|
self.handlers = []
|
||||||
self.executing = False
|
self.executing = False
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ def show(parent):
|
|||||||
layout = QtWidgets.QVBoxLayout()
|
layout = QtWidgets.QVBoxLayout()
|
||||||
layout.addStretch()
|
layout.addStretch()
|
||||||
layout.addWidget(QtWidgets.QLabel("<h1>ngSkinTools</h1>"))
|
layout.addWidget(QtWidgets.QLabel("<h1>ngSkinTools</h1>"))
|
||||||
layout.addWidget(QtWidgets.QLabel("Version {0}".format(version.pluginVersion())))
|
layout.addWidget(QtWidgets.QLabel("当前版本 {0}".format(version.pluginVersion())))
|
||||||
layout.addWidget(QtWidgets.QLabel(version.COPYRIGHT))
|
layout.addWidget(QtWidgets.QLabel(version.COPYRIGHT))
|
||||||
|
|
||||||
url = QtWidgets.QLabel('<a href="{0}" style="color: #007bff;">{0}</a>'.format(version.PRODUCT_URL))
|
url = QtWidgets.QLabel('<a href="{0}" style="color: #007bff;">{0}</a>'.format(version.PRODUCT_URL))
|
||||||
@@ -65,7 +65,7 @@ def show(parent):
|
|||||||
def buttonsRow(window):
|
def buttonsRow(window):
|
||||||
layout = QtWidgets.QHBoxLayout()
|
layout = QtWidgets.QHBoxLayout()
|
||||||
layout.addStretch()
|
layout.addStretch()
|
||||||
btnClose = QtWidgets.QPushButton("Close")
|
btnClose = QtWidgets.QPushButton("退出")
|
||||||
btnClose.setMinimumWidth(100 * scale_multiplier)
|
btnClose.setMinimumWidth(100 * scale_multiplier)
|
||||||
layout.addWidget(btnClose)
|
layout.addWidget(btnClose)
|
||||||
layout.setContentsMargins(20 * scale_multiplier, 15 * scale_multiplier, 20 * scale_multiplier, 15 * scale_multiplier)
|
layout.setContentsMargins(20 * scale_multiplier, 15 * scale_multiplier, 20 * scale_multiplier, 15 * scale_multiplier)
|
||||||
@@ -76,7 +76,7 @@ def show(parent):
|
|||||||
window = QtWidgets.QWidget(parent, Qt.Window | Qt.WindowTitleHint | Qt.CustomizeWindowHint)
|
window = QtWidgets.QWidget(parent, Qt.Window | Qt.WindowTitleHint | Qt.CustomizeWindowHint)
|
||||||
window.resize(600 * scale_multiplier, 500 * scale_multiplier)
|
window.resize(600 * scale_multiplier, 500 * scale_multiplier)
|
||||||
window.setAttribute(Qt.WA_DeleteOnClose)
|
window.setAttribute(Qt.WA_DeleteOnClose)
|
||||||
window.setWindowTitle("About ngSkinTools")
|
window.setWindowTitle("关于 ngSkinTools")
|
||||||
layout = QtWidgets.QVBoxLayout()
|
layout = QtWidgets.QVBoxLayout()
|
||||||
window.setLayout(layout)
|
window.setLayout(layout)
|
||||||
layout.setContentsMargins(0, 0, 0, 0)
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ def build_action_delete_custom_nodes_for_selection(parent, session):
|
|||||||
|
|
||||||
result = define_action(
|
result = define_action(
|
||||||
parent,
|
parent,
|
||||||
"Delete Custom Nodes For Selection",
|
"删除选中网格的Ng节点",
|
||||||
callback=lambda: removeLayerData.remove_custom_nodes_from_selection(interactive=True, session=session),
|
callback=lambda: removeLayerData.remove_custom_nodes_from_selection(interactive=True, session=session),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -86,7 +86,7 @@ class Actions(Object):
|
|||||||
self.toolsUnifyWeights, self.toolsUnifyWeightsOptions = tools.create_action__unify_weights(parent, session)
|
self.toolsUnifyWeights, self.toolsUnifyWeightsOptions = tools.create_action__unify_weights(parent, session)
|
||||||
|
|
||||||
self.toolsDeleteCustomNodes = define_action(
|
self.toolsDeleteCustomNodes = define_action(
|
||||||
parent, "Delete All Custom Nodes", callback=lambda: removeLayerData.remove_custom_nodes(interactive=True, session=session)
|
parent, "删除所有网格的Ng节点", callback=lambda: removeLayerData.remove_custom_nodes(interactive=True, session=session)
|
||||||
)
|
)
|
||||||
|
|
||||||
self.toolsDeleteCustomNodesOnSelection = build_action_delete_custom_nodes_for_selection(parent, session)
|
self.toolsDeleteCustomNodesOnSelection = build_action_delete_custom_nodes_for_selection(parent, session)
|
||||||
@@ -135,10 +135,10 @@ class Actions(Object):
|
|||||||
context.addAction(self.toggle_layer_enabled)
|
context.addAction(self.toggle_layer_enabled)
|
||||||
|
|
||||||
def addInfluencesActions(self, context):
|
def addInfluencesActions(self, context):
|
||||||
context.addAction(self.separator(context, "Actions"))
|
context.addAction(self.separator(context, "动作"))
|
||||||
context.addAction(self.toolsAssignFromClosestJointSelectedInfluences)
|
context.addAction(self.toolsAssignFromClosestJointSelectedInfluences)
|
||||||
context.addAction(self.select_affected_vertices)
|
context.addAction(self.select_affected_vertices)
|
||||||
context.addAction(self.separator(context, "Clipboard"))
|
context.addAction(self.separator(context, "剪贴板"))
|
||||||
context.addAction(self.cut_influences)
|
context.addAction(self.cut_influences)
|
||||||
context.addAction(self.copy_influences)
|
context.addAction(self.copy_influences)
|
||||||
context.addAction(self.paste_weights)
|
context.addAction(self.paste_weights)
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ def open_as_dialog(parent, matcher, result_callback):
|
|||||||
window.close()
|
window.close()
|
||||||
|
|
||||||
def save_defaults():
|
def save_defaults():
|
||||||
if not yesNo("Save current settings as default?"):
|
if not yesNo("将当前设置保存为默认设置?"):
|
||||||
return
|
return
|
||||||
config.mirrorInfluencesDefaults = matcher.config.as_json()
|
config.mirrorInfluencesDefaults = matcher.config.as_json()
|
||||||
|
|
||||||
@@ -47,18 +47,18 @@ def open_as_dialog(parent, matcher, result_callback):
|
|||||||
|
|
||||||
return widgets.button_row(
|
return widgets.button_row(
|
||||||
[
|
[
|
||||||
("Apply", apply),
|
("应用", apply),
|
||||||
("Cancel", window.close),
|
("取消", window.close),
|
||||||
],
|
],
|
||||||
side_menu=[
|
side_menu=[
|
||||||
("Save As Default", save_defaults),
|
("储存为默认值", save_defaults),
|
||||||
("Load Defaults", load_defaults),
|
("加载默认值", load_defaults),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
window = QtWidgets.QDialog(parent)
|
window = QtWidgets.QDialog(parent)
|
||||||
cleanup.registerCleanupHandler(window.close)
|
cleanup.registerCleanupHandler(window.close)
|
||||||
window.setWindowTitle("Influence Mirror Mapping")
|
window.setWindowTitle("影响镜像映射")
|
||||||
window.setAttribute(QtCore.Qt.WA_DeleteOnClose)
|
window.setAttribute(QtCore.Qt.WA_DeleteOnClose)
|
||||||
window.resize(720 * scale_multiplier, 500 * scale_multiplier)
|
window.resize(720 * scale_multiplier, 500 * scale_multiplier)
|
||||||
window.setLayout(QtWidgets.QVBoxLayout())
|
window.setLayout(QtWidgets.QVBoxLayout())
|
||||||
@@ -102,10 +102,10 @@ def build_ui(parent, matcher):
|
|||||||
split_path = path.rsplit("|", 1)
|
split_path = path.rsplit("|", 1)
|
||||||
parent_path, name = split_path if len(split_path) == 2 else ["", split_path[0]]
|
parent_path, name = split_path if len(split_path) == 2 else ["", split_path[0]]
|
||||||
|
|
||||||
item = QtWidgets.QTreeWidgetItem([name, '-', '(not in skin cluster)' if is_intermediate else '?'])
|
item = QtWidgets.QTreeWidgetItem([name, '-', '(不在皮肤簇中)' if is_intermediate else '?'])
|
||||||
tree_items[path] = item
|
tree_items[path] = item
|
||||||
|
|
||||||
parent_item = None if parent_path == "" else find_item(parent_path, True)
|
parent_item = None if parent_path is "" else find_item(parent_path, True)
|
||||||
|
|
||||||
if parent_item is not None:
|
if parent_item is not None:
|
||||||
parent_item.addChild(item)
|
parent_item.addChild(item)
|
||||||
@@ -142,7 +142,7 @@ def build_ui(parent, matcher):
|
|||||||
def pattern():
|
def pattern():
|
||||||
result = QtWidgets.QTableWidget()
|
result = QtWidgets.QTableWidget()
|
||||||
result.setColumnCount(2)
|
result.setColumnCount(2)
|
||||||
result.setHorizontalHeaderLabels(["Pattern", "Opposite"] if mirror_mode else ["Source", "Destination"])
|
result.setHorizontalHeaderLabels(["左", "右"] if mirror_mode else ["来源", "目标"])
|
||||||
result.setEditTriggers(QtWidgets.QTableWidget.AllEditTriggers)
|
result.setEditTriggers(QtWidgets.QTableWidget.AllEditTriggers)
|
||||||
|
|
||||||
result.verticalHeader().setVisible(False)
|
result.verticalHeader().setVisible(False)
|
||||||
@@ -209,12 +209,12 @@ def build_ui(parent, matcher):
|
|||||||
|
|
||||||
def automaticRules():
|
def automaticRules():
|
||||||
form = QtWidgets.QFormLayout()
|
form = QtWidgets.QFormLayout()
|
||||||
use_joint_names = QtWidgets.QCheckBox("Match by joint name")
|
use_joint_names = QtWidgets.QCheckBox("匹配骨骼名称")
|
||||||
naming_patterns = pattern()
|
naming_patterns = pattern()
|
||||||
use_position = QtWidgets.QCheckBox("Match by position")
|
use_position = QtWidgets.QCheckBox("匹配位置")
|
||||||
tolerance_scroll = tolerance()
|
tolerance_scroll = tolerance()
|
||||||
use_joint_labels = QtWidgets.QCheckBox("Match by joint label")
|
use_joint_labels = QtWidgets.QCheckBox("匹配骨骼标签")
|
||||||
use_dg_links = QtWidgets.QCheckBox("Match by dependency graph links")
|
use_dg_links = QtWidgets.QCheckBox("匹配依存关系图连接")
|
||||||
|
|
||||||
def update_enabled_disabled():
|
def update_enabled_disabled():
|
||||||
def enable_form_row(form_item, e):
|
def enable_form_row(form_item, e):
|
||||||
@@ -260,27 +260,27 @@ def build_ui(parent, matcher):
|
|||||||
use_dg_links.setChecked(matcher.config.use_dg_link_matching)
|
use_dg_links.setChecked(matcher.config.use_dg_link_matching)
|
||||||
update_enabled_disabled()
|
update_enabled_disabled()
|
||||||
|
|
||||||
g = QtWidgets.QGroupBox("Rules")
|
g = QtWidgets.QGroupBox("规则")
|
||||||
g.setLayout(form)
|
g.setLayout(form)
|
||||||
form.addRow(use_dg_links)
|
form.addRow(use_dg_links)
|
||||||
form.addRow("Attribute name:", dg_attribute)
|
form.addRow("属性名称:", dg_attribute)
|
||||||
form.addRow(use_joint_labels)
|
form.addRow(use_joint_labels)
|
||||||
form.addRow(use_joint_names)
|
form.addRow(use_joint_names)
|
||||||
form.addRow("Naming scheme:", naming_patterns)
|
form.addRow("命名方案:", naming_patterns)
|
||||||
form.addRow(use_position)
|
form.addRow(use_position)
|
||||||
form.addRow("Position tolerance:", tolerance_scroll.layout())
|
form.addRow("位置容差:", tolerance_scroll.layout())
|
||||||
|
|
||||||
update_values()
|
update_values()
|
||||||
return g
|
return g
|
||||||
|
|
||||||
def scriptedRules():
|
def scriptedRules():
|
||||||
g = QtWidgets.QGroupBox("Scripted rules")
|
g = QtWidgets.QGroupBox("脚本规则")
|
||||||
g.setLayout(QtWidgets.QVBoxLayout())
|
g.setLayout(QtWidgets.QVBoxLayout())
|
||||||
g.layout().addWidget(QtWidgets.QLabel("TODO"))
|
g.layout().addWidget(QtWidgets.QLabel("TODO"))
|
||||||
return g
|
return g
|
||||||
|
|
||||||
def manualRules():
|
def manualRules():
|
||||||
g = QtWidgets.QGroupBox("Manual overrides")
|
g = QtWidgets.QGroupBox("手动覆盖")
|
||||||
g.setLayout(QtWidgets.QVBoxLayout())
|
g.setLayout(QtWidgets.QVBoxLayout())
|
||||||
g.layout().addWidget(QtWidgets.QLabel("TODO"))
|
g.layout().addWidget(QtWidgets.QLabel("TODO"))
|
||||||
return g
|
return g
|
||||||
@@ -302,7 +302,7 @@ def build_ui(parent, matcher):
|
|||||||
def createMappingView():
|
def createMappingView():
|
||||||
view = QtWidgets.QTreeWidget()
|
view = QtWidgets.QTreeWidget()
|
||||||
view.setColumnCount(3)
|
view.setColumnCount(3)
|
||||||
view.setHeaderLabels(["Source", "Destination", "Matched by rule"])
|
view.setHeaderLabels(["来源", "目标", "按规则匹配"])
|
||||||
view.setIndentation(7)
|
view.setIndentation(7)
|
||||||
view.setExpandsOnDoubleClick(False)
|
view.setExpandsOnDoubleClick(False)
|
||||||
|
|
||||||
@@ -316,14 +316,14 @@ def build_ui(parent, matcher):
|
|||||||
:type mapping: dict[InfluenceInfo, InfluenceInfo]
|
:type mapping: dict[InfluenceInfo, InfluenceInfo]
|
||||||
"""
|
"""
|
||||||
for treeItem in list(usedItems.values()):
|
for treeItem in list(usedItems.values()):
|
||||||
treeItem.setText(1, "(not matched)")
|
treeItem.setText(1, "(不匹配)")
|
||||||
treeItem.setText(2, "")
|
treeItem.setText(2, "")
|
||||||
|
|
||||||
for k, v in list(mapping.items()):
|
for k, v in list(mapping.items()):
|
||||||
treeItem = usedItems.get(k.path_name(), None)
|
treeItem = usedItems.get(k.path_name(), None)
|
||||||
if treeItem is None:
|
if treeItem is None:
|
||||||
continue
|
continue
|
||||||
treeItem.setText(1, "(self)" if k == v['infl'] else v["infl"].shortestPath)
|
treeItem.setText(1, "(自己)" if k == v['infl'] else v["infl"].shortestPath)
|
||||||
treeItem.setText(2, v["matchedRule"])
|
treeItem.setText(2, v["matchedRule"])
|
||||||
treeItem.setData(1, linkedItemRole, v["infl"].path)
|
treeItem.setData(1, linkedItemRole, v["infl"].path)
|
||||||
|
|
||||||
@@ -343,7 +343,7 @@ def build_ui(parent, matcher):
|
|||||||
matches = matcher.calculate()
|
matches = matcher.calculate()
|
||||||
mappingView_updateMatches(matches)
|
mappingView_updateMatches(matches)
|
||||||
|
|
||||||
g = QtWidgets.QGroupBox("Calculated mapping")
|
g = QtWidgets.QGroupBox("计算映射")
|
||||||
g.setLayout(QtWidgets.QVBoxLayout())
|
g.setLayout(QtWidgets.QVBoxLayout())
|
||||||
mappingView, mappingView_updateMatches = createMappingView()
|
mappingView, mappingView_updateMatches = createMappingView()
|
||||||
g.layout().addWidget(mappingView)
|
g.layout().addWidget(mappingView)
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ def build_used_influences_action(parent):
|
|||||||
|
|
||||||
result = actions.define_action(
|
result = actions.define_action(
|
||||||
parent,
|
parent,
|
||||||
"Used Influences Only",
|
"只显示有权重的影响物",
|
||||||
callback=toggle,
|
callback=toggle,
|
||||||
tooltip="If enabled, influences view will only show influences that have weights on current layer",
|
tooltip="如果启用,影响视图将仅显示当前层上有权重的影响。",
|
||||||
)
|
)
|
||||||
|
|
||||||
@signal.on(config.influences_show_used_influences_only.changed, qtParent=parent)
|
@signal.on(config.influences_show_used_influences_only.changed, qtParent=parent)
|
||||||
@@ -46,7 +46,7 @@ def build_set_influences_sorted_action(parent):
|
|||||||
parent,
|
parent,
|
||||||
"Show influences sorted",
|
"Show influences sorted",
|
||||||
callback=toggle,
|
callback=toggle,
|
||||||
tooltip="Sort influences by name",
|
tooltip="按名称排序影响",
|
||||||
)
|
)
|
||||||
|
|
||||||
@signal.on(config.influences_show_used_influences_only.changed, qtParent=parent)
|
@signal.on(config.influences_show_used_influences_only.changed, qtParent=parent)
|
||||||
@@ -108,7 +108,7 @@ def build_view(parent, actions, session, filter):
|
|||||||
targets = (item_id,)
|
targets = (item_id,)
|
||||||
|
|
||||||
layer.locked_influences = add_or_remove(layer.locked_influences, targets, lock)
|
layer.locked_influences = add_or_remove(layer.locked_influences, targets, lock)
|
||||||
log.info("updated locked influences to %r", layer.locked_influences)
|
log.info("更新锁定的影响为 %r", layer.locked_influences)
|
||||||
session.events.influencesListUpdated.emit()
|
session.events.influencesListUpdated.emit()
|
||||||
|
|
||||||
return handler
|
return handler
|
||||||
@@ -170,14 +170,14 @@ def build_view(parent, actions, session, filter):
|
|||||||
view.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
view.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
|
||||||
view.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
|
view.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
|
||||||
actions.addInfluencesActions(view)
|
actions.addInfluencesActions(view)
|
||||||
view.addAction(actions.separator(parent, "View Options"))
|
view.addAction(actions.separator(parent, "显示设置"))
|
||||||
view.addAction(actions.show_used_influences_only)
|
view.addAction(actions.show_used_influences_only)
|
||||||
view.addAction(actions.set_influences_sorted)
|
view.addAction(actions.set_influences_sorted)
|
||||||
view.setIndentation(10 * scale_multiplier)
|
view.setIndentation(10 * scale_multiplier)
|
||||||
view.header().setStretchLastSection(False)
|
view.header().setStretchLastSection(False)
|
||||||
view.header().setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
|
view.header().setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
|
||||||
|
|
||||||
view.setHeaderLabels(["Influences", ""])
|
view.setHeaderLabels(["影响物", ""])
|
||||||
view.header().setSectionResizeMode(1, QtWidgets.QHeaderView.Fixed)
|
view.header().setSectionResizeMode(1, QtWidgets.QHeaderView.Fixed)
|
||||||
view.setColumnWidth(1, 25 * scale_multiplier)
|
view.setColumnWidth(1, 25 * scale_multiplier)
|
||||||
|
|
||||||
@@ -208,7 +208,7 @@ def build_view(parent, actions, session, filter):
|
|||||||
if not session.state.currentLayer.layer:
|
if not session.state.currentLayer.layer:
|
||||||
build_items(view, [], None)
|
build_items(view, [], None)
|
||||||
else:
|
else:
|
||||||
log.info("current layer changed to %s", session.state.currentLayer.layer)
|
log.info("当前图层更改为 %s", session.state.currentLayer.layer)
|
||||||
refresh_items()
|
refresh_items()
|
||||||
current_influence_changed()
|
current_influence_changed()
|
||||||
|
|
||||||
@@ -217,7 +217,7 @@ def build_view(parent, actions, session, filter):
|
|||||||
if session.state.currentLayer.layer is None:
|
if session.state.currentLayer.layer is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
log.info("current influence changed - updating item selection")
|
log.info("当前影响已更改-更新项目选择")
|
||||||
with qt.signals_blocked(view):
|
with qt.signals_blocked(view):
|
||||||
targets = session.state.currentLayer.layer.paint_targets
|
targets = session.state.currentLayer.layer.paint_targets
|
||||||
first = True
|
first = True
|
||||||
@@ -239,12 +239,12 @@ def build_view(parent, actions, session, filter):
|
|||||||
if not session.state.currentLayer.layer:
|
if not session.state.currentLayer.layer:
|
||||||
return
|
return
|
||||||
|
|
||||||
log.info("focused item changed: %r", get_item_id(curr))
|
log.info("焦点项已更改: %r", get_item_id(curr))
|
||||||
sync_paint_targets_to_selection()
|
sync_paint_targets_to_selection()
|
||||||
|
|
||||||
@qt.on(view.itemSelectionChanged)
|
@qt.on(view.itemSelectionChanged)
|
||||||
def sync_paint_targets_to_selection():
|
def sync_paint_targets_to_selection():
|
||||||
log.info("syncing paint targets")
|
log.info("同步绘画目标")
|
||||||
selected_ids = [get_item_id(item) for item in view.selectedItems()]
|
selected_ids = [get_item_id(item) for item in view.selectedItems()]
|
||||||
selected_ids = [i for i in selected_ids if i is not None]
|
selected_ids = [i for i in selected_ids if i is not None]
|
||||||
|
|
||||||
|
|||||||
@@ -48,12 +48,12 @@ def build_view(parent, actions):
|
|||||||
child_layer = item_to_layer(child)
|
child_layer = item_to_layer(child)
|
||||||
|
|
||||||
if child_layer.parent_id != parent_layer_id:
|
if child_layer.parent_id != parent_layer_id:
|
||||||
log.info("changing layer parent: %r->%r (was %r)", parent_layer_id, child_layer, child_layer.parent_id)
|
log.info("更改图层父级: %r->%r (was %r)", parent_layer_id, child_layer, child_layer.parent_id)
|
||||||
child_layer.parent = parent_layer_id
|
child_layer.parent = parent_layer_id
|
||||||
|
|
||||||
new_index = tree_item.childCount() - i - 1
|
new_index = tree_item.childCount() - i - 1
|
||||||
if child_layer.index != new_index:
|
if child_layer.index != new_index:
|
||||||
log.info("changing layer index: %r->%r (was %r)", child_layer, new_index, child_layer.index)
|
log.info("更改图层索引: %r->%r (was %r)", child_layer, new_index, child_layer.index)
|
||||||
child_layer.index = new_index
|
child_layer.index = new_index
|
||||||
|
|
||||||
sync_item(child, child_layer.id)
|
sync_item(child, child_layer.id)
|
||||||
@@ -82,7 +82,7 @@ def build_view(parent, actions):
|
|||||||
view.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
|
view.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
|
||||||
actions.addLayersActions(view)
|
actions.addLayersActions(view)
|
||||||
|
|
||||||
view.setHeaderLabels(["Layers", ""])
|
view.setHeaderLabels(["图层", ""])
|
||||||
# view.setHeaderHidden(True)
|
# view.setHeaderHidden(True)
|
||||||
view.header().setMinimumSectionSize(1)
|
view.header().setMinimumSectionSize(1)
|
||||||
view.header().setStretchLastSection(False)
|
view.header().setStretchLastSection(False)
|
||||||
@@ -100,7 +100,7 @@ def build_view(parent, actions):
|
|||||||
bar = QtWidgets.QToolBar(parent=parent)
|
bar = QtWidgets.QToolBar(parent=parent)
|
||||||
bar.setMovable(False)
|
bar.setMovable(False)
|
||||||
bar.setIconSize(QtCore.QSize(visibility_icon_size * scale_multiplier, visibility_icon_size * scale_multiplier))
|
bar.setIconSize(QtCore.QSize(visibility_icon_size * scale_multiplier, visibility_icon_size * scale_multiplier))
|
||||||
a = bar.addAction(icon_visible if layer is None or layer.enabled else icon_hidden, "Toggle enabled/disabled")
|
a = bar.addAction(icon_visible if layer is None or layer.enabled else icon_hidden, "切换 启用/禁用")
|
||||||
|
|
||||||
@qt.on(a.triggered)
|
@qt.on(a.triggered)
|
||||||
def handler():
|
def handler():
|
||||||
@@ -117,11 +117,11 @@ def build_view(parent, actions):
|
|||||||
|
|
||||||
# build map "parent id->list of children "
|
# build map "parent id->list of children "
|
||||||
|
|
||||||
log.info("syncing items...")
|
log.info("同步项目q...")
|
||||||
|
|
||||||
# save selected layers IDs to restore item selection later
|
# save selected layers IDs to restore item selection later
|
||||||
selected_layer_ids = {item_to_layer(item).id for item in view.selectedItems()}
|
selected_layer_ids = {item_to_layer(item).id for item in view.selectedItems()}
|
||||||
log.info("selected layer IDs: %r", selected_layer_ids)
|
log.info("选择层 IDs: %r", selected_layer_ids)
|
||||||
current_item_id = None if view.currentItem() is None else item_to_layer(view.currentItem()).id
|
current_item_id = None if view.currentItem() is None else item_to_layer(view.currentItem()).id
|
||||||
|
|
||||||
hierarchy = {}
|
hierarchy = {}
|
||||||
@@ -167,7 +167,7 @@ def build_view(parent, actions):
|
|||||||
|
|
||||||
@signal.on(session.events.layerListChanged, qtParent=view)
|
@signal.on(session.events.layerListChanged, qtParent=view)
|
||||||
def refresh_layer_list():
|
def refresh_layer_list():
|
||||||
log.info("event handler for layer list changed")
|
log.info("图层列表更改的事件处理程序")
|
||||||
if not session.state.layersAvailable:
|
if not session.state.layersAvailable:
|
||||||
build_items([])
|
build_items([])
|
||||||
else:
|
else:
|
||||||
@@ -177,7 +177,7 @@ def build_view(parent, actions):
|
|||||||
|
|
||||||
@signal.on(session.events.currentLayerChanged, qtParent=view)
|
@signal.on(session.events.currentLayerChanged, qtParent=view)
|
||||||
def current_layer_changed():
|
def current_layer_changed():
|
||||||
log.info("event handler for currentLayerChanged")
|
log.info("当前图层更改的事件处理程序")
|
||||||
layer = session.state.currentLayer.layer
|
layer = session.state.currentLayer.layer
|
||||||
current_item = view.currentItem()
|
current_item = view.currentItem()
|
||||||
if layer is None:
|
if layer is None:
|
||||||
@@ -189,14 +189,14 @@ def build_view(parent, actions):
|
|||||||
if prev_layer is None or prev_layer.id != layer.id:
|
if prev_layer is None or prev_layer.id != layer.id:
|
||||||
item = tree_items.get(layer.id, None)
|
item = tree_items.get(layer.id, None)
|
||||||
if item is not None:
|
if item is not None:
|
||||||
log.info("setting current item to " + item.text(0))
|
log.info("将当前项目设置为 " + item.text(0))
|
||||||
view.setCurrentItem(item, 0, QtCore.QItemSelectionModel.SelectCurrent | QtCore.QItemSelectionModel.ClearAndSelect)
|
view.setCurrentItem(item, 0, QtCore.QItemSelectionModel.SelectCurrent | QtCore.QItemSelectionModel.ClearAndSelect)
|
||||||
|
|
||||||
item.setSelected(True)
|
item.setSelected(True)
|
||||||
|
|
||||||
@qt.on(view.currentItemChanged)
|
@qt.on(view.currentItemChanged)
|
||||||
def current_item_changed(curr, _):
|
def current_item_changed(curr, _):
|
||||||
log.info("current item changed")
|
log.info("当前项目已更改")
|
||||||
if curr is None:
|
if curr is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -209,7 +209,7 @@ def build_view(parent, actions):
|
|||||||
|
|
||||||
@qt.on(view.itemChanged)
|
@qt.on(view.itemChanged)
|
||||||
def item_changed(item, column):
|
def item_changed(item, column):
|
||||||
log.info("item changed")
|
log.info("项目已更改")
|
||||||
layers.renameLayer(item_to_layer(item), item.text(column))
|
layers.renameLayer(item_to_layer(item), item.text(column))
|
||||||
|
|
||||||
@qt.on(view.itemSelectionChanged)
|
@qt.on(view.itemSelectionChanged)
|
||||||
@@ -217,7 +217,7 @@ def build_view(parent, actions):
|
|||||||
selection = [item_to_layer(item) for item in view.selectedItems()]
|
selection = [item_to_layer(item) for item in view.selectedItems()]
|
||||||
|
|
||||||
if selection != session.context.selected_layers(default=[]):
|
if selection != session.context.selected_layers(default=[]):
|
||||||
log.info("new selected layers: %r", selection)
|
log.info("新选择图层: %r", selection)
|
||||||
session.context.selected_layers.set(selection)
|
session.context.selected_layers.set(selection)
|
||||||
|
|
||||||
refresh_layer_list()
|
refresh_layer_list()
|
||||||
|
|||||||
@@ -46,20 +46,20 @@ def build_menu(parent, actions):
|
|||||||
sub_item.setTearOffEnabled(True)
|
sub_item.setTearOffEnabled(True)
|
||||||
return sub_item
|
return sub_item
|
||||||
|
|
||||||
sub = top_level_menu("File")
|
sub = top_level_menu("文件")
|
||||||
sub.addSeparator().setText("Import/Export")
|
sub.addSeparator().setText("导入/导出")
|
||||||
sub.addAction(actions.importFile)
|
sub.addAction(actions.importFile)
|
||||||
sub.addAction(actions.exportFile)
|
sub.addAction(actions.exportFile)
|
||||||
|
|
||||||
sub = top_level_menu("Layers")
|
sub = top_level_menu("图层")
|
||||||
sub.addSeparator().setText("Layer actions")
|
sub.addSeparator().setText("图层操作")
|
||||||
sub.addAction(actions.initialize)
|
sub.addAction(actions.initialize)
|
||||||
sub.addAction(actions.import_v1)
|
sub.addAction(actions.import_v1)
|
||||||
actions.addLayersActions(sub)
|
actions.addLayersActions(sub)
|
||||||
sub.addSeparator().setText("Copy")
|
sub.addSeparator().setText("复制")
|
||||||
sub.addAction(actions.transfer)
|
sub.addAction(actions.transfer)
|
||||||
|
|
||||||
sub = top_level_menu("Tools")
|
sub = top_level_menu("工具")
|
||||||
sub.addAction(actions.add_influences)
|
sub.addAction(actions.add_influences)
|
||||||
sub.addAction(actions.toolsAssignFromClosestJoint)
|
sub.addAction(actions.toolsAssignFromClosestJoint)
|
||||||
sub.addSeparator()
|
sub.addSeparator()
|
||||||
@@ -68,17 +68,17 @@ def build_menu(parent, actions):
|
|||||||
sub.addAction(actions.toolsDeleteCustomNodesOnSelection)
|
sub.addAction(actions.toolsDeleteCustomNodesOnSelection)
|
||||||
sub.addAction(actions.toolsDeleteCustomNodes)
|
sub.addAction(actions.toolsDeleteCustomNodes)
|
||||||
|
|
||||||
sub = top_level_menu("View")
|
sub = top_level_menu("查看")
|
||||||
sub.addAction(actions.show_used_influences_only)
|
sub.addAction(actions.show_used_influences_only)
|
||||||
|
|
||||||
sub = top_level_menu("Help")
|
sub = top_level_menu("帮助")
|
||||||
sub.addAction(actions.documentation.user_guide)
|
sub.addAction(actions.documentation.user_guide)
|
||||||
sub.addAction(actions.documentation.api_root)
|
sub.addAction(actions.documentation.api_root)
|
||||||
sub.addAction(actions.documentation.changelog)
|
sub.addAction(actions.documentation.changelog)
|
||||||
sub.addAction(actions.documentation.contact)
|
sub.addAction(actions.documentation.contact)
|
||||||
sub.addSeparator()
|
sub.addSeparator()
|
||||||
sub.addAction(actions.check_for_updates)
|
sub.addAction(actions.check_for_updates)
|
||||||
sub.addAction("About...").triggered.connect(lambda: aboutwindow.show(parent))
|
sub.addAction("关于...").triggered.connect(lambda: aboutwindow.show(parent))
|
||||||
|
|
||||||
return menu
|
return menu
|
||||||
|
|
||||||
@@ -107,11 +107,11 @@ def build_ui(parent):
|
|||||||
|
|
||||||
tabs = QtWidgets.QTabWidget(window)
|
tabs = QtWidgets.QTabWidget(window)
|
||||||
|
|
||||||
tabs.addTab(tabPaint.build_ui(tabs, actions), "Paint")
|
tabs.addTab(tabPaint.build_ui(tabs, actions), "绘制")
|
||||||
tabs.addTab(tabSetWeights.build_ui(tabs), "Set Weights")
|
tabs.addTab(tabSetWeights.build_ui(tabs), "设置权重")
|
||||||
tabs.addTab(tabMirror.build_ui(tabs), "Mirror")
|
tabs.addTab(tabMirror.build_ui(tabs), "镜像")
|
||||||
tabs.addTab(tabLayerEffects.build_ui(), "Effects")
|
tabs.addTab(tabLayerEffects.build_ui(), "样式")
|
||||||
tabs.addTab(tabTools.build_ui(actions, session), "Tools")
|
tabs.addTab(tabTools.build_ui(actions, session), "工具")
|
||||||
|
|
||||||
@signal.on(options.current_tab.changed)
|
@signal.on(options.current_tab.changed)
|
||||||
def set_current_tab():
|
def set_current_tab():
|
||||||
|
|||||||
@@ -19,4 +19,4 @@ def bind(ui, model):
|
|||||||
model.set(ui.value())
|
model.set(ui.value())
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise Exception("could not bind control to model")
|
raise Exception("无法将控件绑定到模型")
|
||||||
|
|||||||
@@ -99,10 +99,10 @@ def save_option(varName, value):
|
|||||||
elif is_string(value):
|
elif is_string(value):
|
||||||
key = 'sv'
|
key = 'sv'
|
||||||
else:
|
else:
|
||||||
raise ValueError("could not save option %s: invalid value %r" % (varName, value))
|
raise ValueError("无法保存选项 %s: 无效值 %r" % (varName, value))
|
||||||
|
|
||||||
kvargs = {key: (varName, value)}
|
kvargs = {key: (varName, value)}
|
||||||
log.info("saving optionvar: %r", kvargs)
|
log.info("保存选项变量: %r", kvargs)
|
||||||
cmds.optionVar(**kvargs)
|
cmds.optionVar(**kvargs)
|
||||||
|
|
||||||
|
|
||||||
@@ -153,11 +153,11 @@ class Config(Object):
|
|||||||
|
|
||||||
def __get_value__(self, name, default_value):
|
def __get_value__(self, name, default_value):
|
||||||
result = self.__state__.get(name, default_value)
|
result = self.__state__.get(name, default_value)
|
||||||
log.info("config: return %s=%r", name, result)
|
log.info("配置: return %s=%r", name, result)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def __set_value__(self, name, value):
|
def __set_value__(self, name, value):
|
||||||
log.info("config: save %s=%r", name, value)
|
log.info("配置: save %s=%r", name, value)
|
||||||
self.__state__[name] = value
|
self.__state__[name] = value
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
|||||||