diff --git a/GETOOLS_SOURCE/modules/GeneralWindow.py b/GETOOLS_SOURCE/modules/GeneralWindow.py index cfd98d7..ed04285 100644 --- a/GETOOLS_SOURCE/modules/GeneralWindow.py +++ b/GETOOLS_SOURCE/modules/GeneralWindow.py @@ -48,7 +48,7 @@ class GeneralWindow: - _version = "v1.3.8" + _version = "v1.4.0" _name = "GETools" _title = _name + " " + _version @@ -119,7 +119,7 @@ def ColorsPalette(*args): cmds.menuItem(dividerLabel = "Prints", divider = True) cmds.menuItem(label = "Print Selected Objects To Console", command = Print.PrintSelected, image = Icons.text) cmds.menuItem(label = "Print Animatable Attributes", command = partial(Print.PrintAttributesAnimatableOnSelected, False), image = Icons.text) - cmds.menuItem(label = "Print Animatable Attributes With Shapes", command = partial(Print.PrintAttributesAnimatableOnSelected, True), image = Icons.text) + # cmds.menuItem(label = "Print Animatable Attributes With Shapes", command = partial(Print.PrintAttributesAnimatableOnSelected, True), image = Icons.text) # FIXME cmds.menuItem(label = "Print Channel Box Selected Attributes", command = Print.PrintAttributesSelectedFromChannelBox, image = Icons.text) cmds.menuItem(dividerLabel = "Blendshapes", divider = True) cmds.menuItem(label = "Print Blendshapes Base Nodes", command = Blendshapes.GetBlendshapeNodesFromSelected, image = Icons.text) @@ -166,25 +166,25 @@ def LinkVersionHistory(self): cmds.showHelp("https://github.com/GenEugene/GETool def LinkGithub(self): cmds.showHelp("https://github.com/GenEugene/GETools", absolute = True) def LinkGumroad(self): cmds.showHelp("https://gumroad.com/l/iCNa", absolute = True) def LinkGithubWiki(self): cmds.showHelp("https://github.com/GenEugene/GETools/wiki", absolute = True) - def LinkYoutubeTutorial(self): cmds.showHelp("https://youtube.com/playlist?list=PLhwndaM4LAxhbl95yz9WVie1iYflTFy6S&si=UOoK-mdk4Rm5bVyp", absolute = True) + def LinkYoutubeVideos(self): cmds.showHelp("https://youtube.com/playlist?list=PLhwndaM4LAxhbl95yz9WVie1iYflTFy6S&si=UOoK-mdk4Rm5bVyp", absolute = True) def LinkLinkedin(self): cmds.showHelp("https://www.linkedin.com/in/geneugene", absolute = True) def LinkYoutube(self): cmds.showHelp("https://youtube.com/@EugeneGataulin", absolute = True) def LinkDiscord(self): cmds.showHelp("https://discord.gg/heMxJhTqCz", absolute = True) def LinkShareIdeas(self): cmds.showHelp("https://github.com/GenEugene/GETools/discussions/categories/ideas", absolute = True) def LinkReport(self): cmds.showHelp("https://github.com/GenEugene/GETools/discussions/categories/report-a-problem", absolute = True) - cmds.menuItem(label = "About GETools", enable = False, image = self.directory + Icons.get1[0]) # TODO add window with information + # cmds.menuItem(label = "About GETools", enable = False, image = self.directory + Icons.get1[0]) # TODO add window with information cmds.menuItem(label = "Version History", command = LinkVersionHistory) cmds.menuItem(dividerLabel = "Links", divider = True) cmds.menuItem(label = "GitHub", command = LinkGithub, image = Icons.home) cmds.menuItem(label = "Gumroad", command = LinkGumroad) cmds.menuItem(dividerLabel = "HOW TO USE", divider = True) cmds.menuItem(label = "Documentation", command = LinkGithubWiki, image = Icons.help) - cmds.menuItem(label = "Tutorial Video", command = LinkYoutubeTutorial, image = Icons.playblast) + cmds.menuItem(label = "Videos Playlist", command = LinkYoutubeVideos, image = Icons.playblast) cmds.menuItem(dividerLabel = "Contacts", divider = True) + cmds.menuItem(label = "Discord", command = LinkDiscord) cmds.menuItem(label = "Linkedin", command = LinkLinkedin) cmds.menuItem(label = "YouTube", command = LinkYoutube) - cmds.menuItem(label = "Discord", command = LinkDiscord) cmds.menuItem(dividerLabel = "Support", divider = True) cmds.menuItem(label = "Share Your Ideas", command = LinkShareIdeas, image = Icons.light) cmds.menuItem(label = "Report a Problem", command = LinkReport, image = Icons.warning) diff --git a/GETOOLS_SOURCE/modules/Rigging.py b/GETOOLS_SOURCE/modules/Rigging.py index c6eac16..c683f7d 100644 --- a/GETOOLS_SOURCE/modules/Rigging.py +++ b/GETOOLS_SOURCE/modules/Rigging.py @@ -28,11 +28,11 @@ from ..utils import Blendshapes from ..utils import Colors from ..utils import Constraints +from ..utils import Create from ..utils import Curves from ..utils import Deformers from ..utils import Other from ..utils import Skinning -from ..utils import UI class RiggingAnnotations: @@ -72,7 +72,7 @@ class RiggingAnnotations: curveCreateFromTrajectory = "***DRAFT***\nCreate a curve from objects trajectories." class Rigging: - _version = "v1.5" + _version = "v1.6" _name = "RIGGING" _title = _name + " " + _version @@ -80,8 +80,37 @@ def __init__(self): self.checkboxConstraintReverse = None self.checkboxConstraintMaintain = None # self.checkboxConstraintOffset = None + + self.intFieldPolygonWithLocatorsPoints = None + self.floatFieldPolygonWithLocatorsRadius = None + self.floatFieldPolygonWithLocatorsAngle = None + def UICreate(self, layoutMain): - ### CONSTRAINTS + ### POLYGON WITH LOCATORS + self.UILayoutPolygonWithLocators(layoutMain) + self.UILayoutConstraints(layoutMain) + self.UILayoutUtils(layoutMain) + self.UILayoutBlendshapes(layoutMain) + self.UILayoutCurves(layoutMain) + + def UILayoutPolygonWithLocators(self, layoutMain): + layoutMesh = cmds.frameLayout(parent = layoutMain, label = Settings.frames2Prefix + "POLYGON WITH LOCATORS", collapsable = True, backgroundColor = Settings.frames2Color, marginWidth = 0, marginHeight = 0) + cellWidth1 = 75 + cellWidth2 = 75 + cellWidth3 = 75 + cellWidth4 = 40 + rowLayout = cmds.rowLayout(parent = layoutMesh, numberOfColumns = 4, columnWidth4 = (cellWidth1, cellWidth2, cellWidth3, cellWidth4), columnAlign = [(1, "right"), (2, "center"), (3, "center"), (4, "center")], columnAttach = [(1, "both", 0), (2, "both", 0), (3, "both", 0), (4, "both", 0)]) + cmds.gridLayout(parent = rowLayout, numberOfColumns = 2, cellWidth = cellWidth1 / 2, cellHeight = Settings.lineHeight) + cmds.text(label = "Points") + self.intFieldPolygonWithLocatorsPoints = cmds.intField(value = 3, minValue = 3) + cmds.gridLayout(parent = rowLayout, numberOfColumns = 2, cellWidth = cellWidth2 / 2, cellHeight = Settings.lineHeight) + cmds.text(label = "Radius") + self.floatFieldPolygonWithLocatorsRadius = cmds.floatField(value = 10, minValue = 0, precision = 1) + cmds.gridLayout(parent = rowLayout, numberOfColumns = 2, cellWidth = cellWidth3 / 2, cellHeight = Settings.lineHeight) + cmds.text(label = "Angle") + self.floatFieldPolygonWithLocatorsAngle = cmds.floatField(value = 0, precision = 1) + cmds.button(parent = rowLayout, label = "Create", command = self.CreatePolygonWithLocators, backgroundColor = Colors.green10) + def UILayoutConstraints(self, layoutMain): layoutConstraints = cmds.frameLayout(parent = layoutMain, label = Settings.frames2Prefix + "CONSTRAINTS", collapsable = True, backgroundColor = Settings.frames2Color, marginWidth = 0, marginHeight = 0) layoutColumnConstraints = cmds.columnLayout(parent = layoutConstraints, adjustableColumn = True) # @@ -105,9 +134,7 @@ def UICreate(self, layoutMain): cmds.gridLayout(parent = layoutColumnConstraints, numberOfColumns = countOffsets, cellWidth = Settings.windowWidthMargin / countOffsets, cellHeight = Settings.lineHeight) cmds.button(label = "Disconnect", command = Constraints.DisconnectTargetsFromConstraintOnSelected, backgroundColor = Colors.red50, annotation = RiggingAnnotations.constraintDisconnectSelected) cmds.button(label = "Delete Constraints", command = Constraints.DeleteConstraintsOnSelected, backgroundColor = Colors.red50, annotation = RiggingAnnotations.constraintDelete) - - - ### UTILS + def UILayoutUtils(self, layoutMain): layoutUtils = cmds.frameLayout(parent = layoutMain, label = Settings.frames2Prefix + "UTILS", collapsable = True, backgroundColor = Settings.frames2Color, marginWidth = 0, marginHeight = 0) layoutColumnUtils = cmds.columnLayout(parent = layoutUtils, adjustableColumn = True) # @@ -131,8 +158,7 @@ def UICreate(self, layoutMain): countOffsets = 1 cmds.gridLayout(parent = layoutColumnUtils, numberOfColumns = countOffsets, cellWidth = Settings.windowWidthMargin / countOffsets, cellHeight = Settings.lineHeight) cmds.button(label = "Copy Skin Weights From Last Selected", command = Skinning.CopySkinWeightsFromLastMesh, backgroundColor = Colors.blue10, annotation = RiggingAnnotations.copySkinWeights) - - ### BLENDSHAPES + def UILayoutBlendshapes(self, layoutMain): layoutBlendshapes = cmds.frameLayout(parent = layoutMain, label = Settings.frames2Prefix + "BLENDSHAPES", collapsable = True, backgroundColor = Settings.frames2Color, marginWidth = 0, marginHeight = 0) layoutColumnBlendshapes = cmds.columnLayout(parent = layoutBlendshapes, adjustableColumn = True) # @@ -146,8 +172,7 @@ def UICreate(self, layoutMain): countOffsets = 1 cmds.gridLayout(parent = layoutColumnBlendshapes, numberOfColumns = countOffsets, cellWidth = Settings.windowWidthMargin / countOffsets, cellHeight = Settings.lineHeight) cmds.button(label = "Zero Weights", command = Blendshapes.ZeroBlendshapeWeightsOnSelected, backgroundColor = Colors.blackWhite100, annotation = RiggingAnnotations.blendshapeZeroWeights) - - ### CURVES + def UILayoutCurves(self, layoutMain): layoutCurves = cmds.frameLayout(parent = layoutMain, label = Settings.frames2Prefix + "CURVES", collapsable = True, backgroundColor = Settings.frames2Color, marginWidth = 0, marginHeight = 0) layoutColumnCurves = cmds.columnLayout(parent = layoutCurves, adjustableColumn = True) # @@ -156,7 +181,6 @@ def UICreate(self, layoutMain): cmds.button(label = "From Selected Objects", command = Curves.CreateCurveFromSelectedObjects, backgroundColor = Colors.blue10, annotation = RiggingAnnotations.curveCreateFromSelectedObjects) cmds.button(label = "From Trajectory", command = Curves.CreateCurveFromTrajectory, backgroundColor = Colors.orange10, annotation = RiggingAnnotations.curveCreateFromTrajectory) - ### CONSTRAINTS def GetCheckboxConstraintReverse(self): return cmds.checkBox(self.checkboxConstraintReverse, query = True, value = True) @@ -174,3 +198,10 @@ def ConstrainScale(self, *args): def ConstrainAim(self, *args): # TODO Constraints.ConstrainSelectedToLastObject(reverse = self.GetCheckboxConstraintReverse(), maintainOffset = self.GetCheckboxConstraintMaintain(), parent = False, point = False, orient = False, scale = False, aim = True) + ### MESH + def CreatePolygonWithLocators(self, *args): + points = cmds.intField(self.intFieldPolygonWithLocatorsPoints, query = True, value = True) + radius = cmds.floatField(self.floatFieldPolygonWithLocatorsRadius, query = True, value = True) + angle = cmds.floatField(self.floatFieldPolygonWithLocatorsAngle, query = True, value = True) + Create.CreatePolygonWithLocators(countPoints = points, radius = radius, rotation = angle) + diff --git a/GETOOLS_SOURCE/utils/Attributes.py b/GETOOLS_SOURCE/utils/Attributes.py index f966508..1449072 100644 --- a/GETOOLS_SOURCE/utils/Attributes.py +++ b/GETOOLS_SOURCE/utils/Attributes.py @@ -84,7 +84,7 @@ def FilterAttributesWithoutAnimation(attributes): return attributesWithoutAnimation -def GetAttributesAnimatableOnSelected(useShapes=False): +def GetAttributesAnimatableOnSelected(useShapes=False): # TODO fix shapes detection, check on curves, cameras, meshes # Check selected objects selectedList = Selector.MultipleObjects(minimalCount = 1, transformsOnly = False) if (selectedList == None): diff --git a/GETOOLS_SOURCE/utils/Create.py b/GETOOLS_SOURCE/utils/Create.py new file mode 100644 index 0000000..e519619 --- /dev/null +++ b/GETOOLS_SOURCE/utils/Create.py @@ -0,0 +1,94 @@ +# GETOOLS is under the terms of the MIT License +# Copyright (c) 2018-2024 Eugene Gataulin (GenEugene). All Rights Reserved. + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# Author: Eugene Gataulin tek942@gmail.com https://www.linkedin.com/in/geneugene +# Source code: https://github.com/GenEugene/GETools or https://app.gumroad.com/geneugene + +import maya.cmds as cmds +import math + +from ..utils import Text + + +nameGroup = "grpPolygon" +namePolygon = "customPolygon" +nameLocator = "locator_" +nameCluster = "cluster_" + + +def CreatePolygonWithLocators(countPoints=3, radius=10, rotation=0): + if countPoints < 3: + cmds.warning("Number of points must be 3 or more to create a polygon") + return None + + ### Create main group as a container for all new objects + mainGroup = cmds.group(name = Text.SetUniqueFromText(nameGroup), empty = True) + cmds.setAttr(mainGroup + ".tx", lock = True) + cmds.setAttr(mainGroup + ".ty", lock = True) + cmds.setAttr(mainGroup + ".tz", lock = True) + cmds.setAttr(mainGroup + ".rx", lock = True) + cmds.setAttr(mainGroup + ".ry", lock = True) + cmds.setAttr(mainGroup + ".rz", lock = True) + cmds.setAttr(mainGroup + ".sx", lock = True) + cmds.setAttr(mainGroup + ".sy", lock = True) + cmds.setAttr(mainGroup + ".sz", lock = True) + + ### Calculate vertex positions based on the number of points and radius + angleStep = 360 / countPoints + vertices = [] + for i in range(countPoints): + angleRadian = math.radians(i * angleStep + rotation) + x = math.cos(angleRadian) * radius + z = math.sin(angleRadian) * radius + vertices.append((x, 0, z)) + + ### Create the polygon + poly = cmds.polyCreateFacet(point = vertices, name = Text.SetUniqueFromText(namePolygon))[0] + + ### Invert normals so the polygon faces upwards + cmds.polyNormal(poly, normalMode = 0) # normalMode = 0 inverts normals + + ### Create locators for each vertex and attach them via clusters + locators = [] + handles = [] + for i, vertex in enumerate(vertices): + ### Create locator and position it at the vertex's initial position + locator = cmds.spaceLocator(name = Text.SetUniqueFromText("{0}{1}".format(nameLocator, i + 1)))[0] + locators.append(locator) + cmds.xform(locator, worldSpace = True, translation = vertex) + + ### Create a cluster for the current vertex + cluster, handle = cmds.cluster("{0}.vtx[{1}]".format(poly, i), name = Text.SetUniqueFromText("{0}{1}".format(nameCluster, i + 1))) + handles.append(handle) + cmds.setAttr(handle + ".visibility", 0) + + ### Attach the cluster to the locator + cmds.pointConstraint(locator, handle, maintainOffset = False) + + ### Parent to group + cmds.parent(poly, mainGroup) + cmds.parent(locators, mainGroup) + cmds.parent(handles, mainGroup) + + ### Select polygon + cmds.select(poly, replace = True) + + return poly, locators, handles + diff --git a/changelog.txt b/changelog.txt index 7ae0109..e6aa501 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,12 +1,10 @@ GETools changelog -***FUTURE PLAN*** -- TODO [PHYSICS] added new physics with nHair logic -- TODO [OVERLAPPY] added Chain mode with nHair -- TODO [OVERLAPPY] added collisions UI -- TODO [OVERLAPPY] added nRigid collision logic -- TODO added non-cycle origin animation with loop mode -********************************************** +v1.4.0 +- [TOOLS] added "Polygon With Locators" button to create custom polygons with locators for each vertex +- [TOOLS] extracted Rigging UI code blocks to separate functions +- [UTILS] removed "Print Animatable Attributes With Shapes" button because of redundancy +- [UI] changed some button names v1.3.8 - [CENTER OF MASS] fixed constraining to multiple selected objects diff --git a/todo.txt b/todo.txt new file mode 100644 index 0000000..f039a8d --- /dev/null +++ b/todo.txt @@ -0,0 +1,27 @@ +GETools TODO List + +[TOOLS] +- chain distribution rig +- pin scale +- rotate Order rebake +- relative Camera + +[RIGGING] +- finish Create curve from trajectory button + +[OVERLAPPY] +- fix particle offset on loop mode +- fix nucleus double nodes +- fix loop mode with rootmotion +- non-cycle origin animation with loop mode +- scale baking +- chain mode with nHair +- nRigid collision logic +- collisions UI + +[PHYSICS] +- physics with nHair logic + +[CENTER OF MASS] +- improve projection approach +