From eee5260afb3a730f26dddea870502ad35e332472 Mon Sep 17 00:00:00 2001 From: Eugene Date: Thu, 16 Jan 2025 17:07:52 -0800 Subject: [PATCH 01/11] Update Curves.py --- GETOOLS_SOURCE/utils/Curves.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/GETOOLS_SOURCE/utils/Curves.py b/GETOOLS_SOURCE/utils/Curves.py index b1d0756..c90fd9c 100644 --- a/GETOOLS_SOURCE/utils/Curves.py +++ b/GETOOLS_SOURCE/utils/Curves.py @@ -82,4 +82,13 @@ def CreateCurveFromTrajectory(*args): # TODO rework return newCurve +def SetupSpaceDeformation(curves=None, *args): + if curves is None: + cmds.warning("No curves provided") + return None + + # 1. Get curves list + # 2. Get latest curve from list as a deformation curve + # 3. Create a "Sweep Mesh" on deformation curve with "Line" profile, interpolation mode "Start To End" and steps + # 4. Connect other curves to "Sweep Mesh" with "Wrap" deformer using "Volume" falloff mode From c386aa6a763c16b3f141cb033a60e1bec9d82a9a Mon Sep 17 00:00:00 2001 From: Eugene Date: Thu, 16 Jan 2025 17:19:40 -0800 Subject: [PATCH 02/11] update TODO --- GETOOLS_SOURCE/utils/Curves.py | 5 +---- todo.txt | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/GETOOLS_SOURCE/utils/Curves.py b/GETOOLS_SOURCE/utils/Curves.py index c90fd9c..b553fc8 100644 --- a/GETOOLS_SOURCE/utils/Curves.py +++ b/GETOOLS_SOURCE/utils/Curves.py @@ -87,8 +87,5 @@ def SetupSpaceDeformation(curves=None, *args): cmds.warning("No curves provided") return None - # 1. Get curves list - # 2. Get latest curve from list as a deformation curve - # 3. Create a "Sweep Mesh" on deformation curve with "Line" profile, interpolation mode "Start To End" and steps - # 4. Connect other curves to "Sweep Mesh" with "Wrap" deformer using "Volume" falloff mode + # TODO diff --git a/todo.txt b/todo.txt index 09957a2..5fecd31 100644 --- a/todo.txt +++ b/todo.txt @@ -22,3 +22,21 @@ GETools TODO List [CENTER OF MASS] - improve projection approach + +[CURVE SPACE DEFORMATION] +### Prepare Curves +1. Select main rootmotion control +2. Select IKs like feet and hands +3. Convert movement to curves +4. Rebind controls to curves with Motion trails and sync timings + +### Setup Deformation Space Relationship +1. Get curves list +2. Get latest curve from list as a deformation curve +3. Create a "Sweep Mesh" on deformation curve with "Line" profile, interpolation mode "Start To End" and steps +4. Connect other curves to "Sweep Mesh" with "Wrap" deformer using "Volume" falloff mode + +### Organize objects in one group per space deformation +- Parent each top level object to the group +- Use unique naming with naming converter + From 27a401549007e111cf7df322743135d4752054dd Mon Sep 17 00:00:00 2001 From: Eugene Date: Fri, 17 Jan 2025 01:44:37 -0800 Subject: [PATCH 03/11] add draft motion path converter --- GETOOLS_SOURCE/modules/Rigging.py | 1 + GETOOLS_SOURCE/utils/Curves.py | 43 ++++++++++++++++++++++++------- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/GETOOLS_SOURCE/modules/Rigging.py b/GETOOLS_SOURCE/modules/Rigging.py index 49b0f40..7772b14 100644 --- a/GETOOLS_SOURCE/modules/Rigging.py +++ b/GETOOLS_SOURCE/modules/Rigging.py @@ -190,6 +190,7 @@ def UILayoutCurves(self, layoutMain): cmds.gridLayout(parent = layoutColumn, numberOfColumns = countOffsets, cellWidth = Settings.windowWidthMargin / countOffsets, cellHeight = Settings.lineHeight) 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) + cmds.button(label = "Motion Path", command = Curves.ConvertToMotionPath) ### CONSTRAINTS def GetCheckboxConstraintReverse(self): diff --git a/GETOOLS_SOURCE/utils/Curves.py b/GETOOLS_SOURCE/utils/Curves.py index b553fc8..86803e7 100644 --- a/GETOOLS_SOURCE/utils/Curves.py +++ b/GETOOLS_SOURCE/utils/Curves.py @@ -23,6 +23,7 @@ import maya.cmds as cmds +from ..utils import Constraints from ..utils import Selector from ..values import Enums @@ -33,13 +34,13 @@ def CreateCurveFromSelectedObjects(*args): # Check selected objects - selectedList = Selector.MultipleObjects(2) - if (selectedList == None): + selected = Selector.MultipleObjects(minimalCount = 2) + if (selected == None): return None positions = [] - for item in selectedList: + for item in selected: position = cmds.xform(item, query = True, translation = True, worldSpace = True) positions.append(position) @@ -49,7 +50,7 @@ def CreateCurveFromSelectedObjects(*args): def CreateCurveFromTrajectory(*args): # TODO rework ### Variables step = 1 - degree = 3 + degree = 1 ### Names mtName = "newMotionTrail" @@ -65,8 +66,8 @@ def CreateCurveFromTrajectory(*args): # TODO rework cmds.snapshot(name = mtName, motionTrail = 1, increment = step, startTime = start, endTime = end) ### Get points from motion trail - cmds.select(mtFinalName, replace = 1) - selected = cmds.ls(selection = 1, dagObjects = 1, exactType = Enums.MotionTrail.snapshotShape) + cmds.select(mtFinalName, replace = True) + selected = cmds.ls(selection = True, dagObjects = True, exactType = Enums.MotionTrail.snapshotShape) pts = cmds.getAttr(selected[0] + "." + Enums.MotionTrail.pts) size = len(pts) for i in range(size): @@ -78,14 +79,36 @@ def CreateCurveFromTrajectory(*args): # TODO rework ### End cmds.delete(mtFinalName) - cmds.select(clear = 1) + cmds.select(clear = True) return newCurve -def SetupSpaceDeformation(curves=None, *args): +def SetupSpaceDeformation(curves=None, *args): # TODO if curves is None: cmds.warning("No curves provided") return None - - # TODO + +def ConvertToMotionPath(*args): # TODO probably move to different place + # Check selected objects + selected = Selector.MultipleObjects(minimalCount = 1) + if (selected == None): + return None + + ## Create curve and select + curve = CreateCurveFromTrajectory() + cmds.select(curve, replace = True) + + ## Create closest point node with locators + cmds.ClosestPointOn() + closestPointLocatorPos = cmds.ls(selection = True)[0] + closestPointNode = cmds.listConnections(closestPointLocatorPos, source = True, destination = False)[0] + closestPointLocatorIn = cmds.listConnections(closestPointNode, source = True, destination = False, type = "transform")[0] + cmds.select(clear = True) + + ## Constrain closestPointLocatorIn to source object + Constraints.ConstrainSecondToFirstObject(selected, closestPointLocatorIn, maintainOffset = False, parent = False, point = True, orient = False) + + ## Create motion path constraint + ## Connect closest point parameter to U parameter in motion path + From 9b3609f3998c72df2ade57e952db503052c619cf Mon Sep 17 00:00:00 2001 From: Eugene Date: Fri, 17 Jan 2025 16:39:34 -0800 Subject: [PATCH 04/11] reorganize scripts, move CurveSpace to Locators --- .../_prototypes/CurveSpaceSwitching.py | 35 ++++++++++ GETOOLS_SOURCE/modules/Rigging.py | 1 - GETOOLS_SOURCE/modules/Tools.py | 6 ++ GETOOLS_SOURCE/utils/Curves.py | 30 --------- GETOOLS_SOURCE/utils/Locators.py | 67 +++++++++++++------ 5 files changed, 89 insertions(+), 50 deletions(-) create mode 100644 GETOOLS_SOURCE/_prototypes/CurveSpaceSwitching.py diff --git a/GETOOLS_SOURCE/_prototypes/CurveSpaceSwitching.py b/GETOOLS_SOURCE/_prototypes/CurveSpaceSwitching.py new file mode 100644 index 0000000..181ed30 --- /dev/null +++ b/GETOOLS_SOURCE/_prototypes/CurveSpaceSwitching.py @@ -0,0 +1,35 @@ +# 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 https://discord.gg/heMxJhTqCz +# Source code: https://github.com/GenEugene/GETools or https://app.gumroad.com/geneugene + +import maya.cmds as cmds + +# from ..utils import Selector +# from ..values import Enums + + +def SetupSpaceDeformation(curves=None, *args): # TODO + if curves is None: + cmds.warning("No curves provided") + return None + + diff --git a/GETOOLS_SOURCE/modules/Rigging.py b/GETOOLS_SOURCE/modules/Rigging.py index 7772b14..49b0f40 100644 --- a/GETOOLS_SOURCE/modules/Rigging.py +++ b/GETOOLS_SOURCE/modules/Rigging.py @@ -190,7 +190,6 @@ def UILayoutCurves(self, layoutMain): cmds.gridLayout(parent = layoutColumn, numberOfColumns = countOffsets, cellWidth = Settings.windowWidthMargin / countOffsets, cellHeight = Settings.lineHeight) 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) - cmds.button(label = "Motion Path", command = Curves.ConvertToMotionPath) ### CONSTRAINTS def GetCheckboxConstraintReverse(self): diff --git a/GETOOLS_SOURCE/modules/Tools.py b/GETOOLS_SOURCE/modules/Tools.py index fdc754d..0918570 100644 --- a/GETOOLS_SOURCE/modules/Tools.py +++ b/GETOOLS_SOURCE/modules/Tools.py @@ -240,6 +240,12 @@ def UILayoutLocators(self, layoutMain): cmds.button(label = "Translate + Rotate", command = partial(self.LocatorsBakeAim, False), backgroundColor = Colors.orange10, annotation = ToolsAnnotations.locatorAimSpaceBakeAll) cmds.button(label = "Only Rotate", command = partial(self.LocatorsBakeAim, True), backgroundColor = Colors.orange10, annotation = ToolsAnnotations.locatorAimSpaceBakeRotate) # cmds.setParent("..") + + ### CURVE SPACE SWITCHING + layoutCurveSpace = cmds.frameLayout(parent = layoutColumn, label = "Curve Space Switching", labelIndent = 72, collapsable = False, backgroundColor = Settings.frames2Color, marginWidth = 0, marginHeight = 0) + cmds.button(label = "Create Motion Path Locator", command = Locators.CreateWithMotionPath, backgroundColor = Colors.green10) + + def UILayoutBaking(self, layoutMain): cmds.frameLayout(parent = layoutMain, label = Settings.frames2Prefix + "BAKING", collapsable = True, backgroundColor = Settings.frames2Color, highlightColor = Colors.green100, marginWidth = 0, marginHeight = 0, borderVisible = True) layoutColumn = cmds.columnLayout(adjustableColumn = True, rowSpacing = Settings.columnLayoutRowSpacing) diff --git a/GETOOLS_SOURCE/utils/Curves.py b/GETOOLS_SOURCE/utils/Curves.py index 86803e7..2aa042f 100644 --- a/GETOOLS_SOURCE/utils/Curves.py +++ b/GETOOLS_SOURCE/utils/Curves.py @@ -23,7 +23,6 @@ import maya.cmds as cmds -from ..utils import Constraints from ..utils import Selector from ..values import Enums @@ -83,32 +82,3 @@ def CreateCurveFromTrajectory(*args): # TODO rework return newCurve -def SetupSpaceDeformation(curves=None, *args): # TODO - if curves is None: - cmds.warning("No curves provided") - return None - -def ConvertToMotionPath(*args): # TODO probably move to different place - # Check selected objects - selected = Selector.MultipleObjects(minimalCount = 1) - if (selected == None): - return None - - ## Create curve and select - curve = CreateCurveFromTrajectory() - cmds.select(curve, replace = True) - - ## Create closest point node with locators - cmds.ClosestPointOn() - closestPointLocatorPos = cmds.ls(selection = True)[0] - closestPointNode = cmds.listConnections(closestPointLocatorPos, source = True, destination = False)[0] - closestPointLocatorIn = cmds.listConnections(closestPointNode, source = True, destination = False, type = "transform")[0] - cmds.select(clear = True) - - ## Constrain closestPointLocatorIn to source object - Constraints.ConstrainSecondToFirstObject(selected, closestPointLocatorIn, maintainOffset = False, parent = False, point = True, orient = False) - - ## Create motion path constraint - ## Connect closest point parameter to U parameter in motion path - - diff --git a/GETOOLS_SOURCE/utils/Locators.py b/GETOOLS_SOURCE/utils/Locators.py index 6b52e41..cdeb409 100644 --- a/GETOOLS_SOURCE/utils/Locators.py +++ b/GETOOLS_SOURCE/utils/Locators.py @@ -26,6 +26,7 @@ from ..utils import Animation from ..utils import Baker from ..utils import Constraints +from ..utils import Curves from ..utils import Parent from ..utils import Selector from ..utils import Text @@ -38,7 +39,7 @@ _minSelectedCount = 1 -# SIZE +### SIZE def GetSize(locator): shape = cmds.listRelatives(locator, shapes = True, type = Enums.Types.locator)[0] if (shape != None): @@ -86,7 +87,7 @@ def SelectedLocatorsSizeSet(value, *args): if (shape != None): SetSize(item, value, value, value) -# CREATE +### CREATE def Create(name=_nameBase, scale=_scale, hideParent=False, subLocator=False): locatorCurrent = cmds.spaceLocator(name = Text.SetUniqueFromText(name))[0] SetSize(locatorCurrent, scale, scale, scale) @@ -106,7 +107,7 @@ def Create(name=_nameBase, scale=_scale, hideParent=False, subLocator=False): else: return locatorCurrent def CreateOnSelected(name=_nameBase, scale=_scale, minSelectedCount=_minSelectedCount, hideParent=False, subLocator=False, constraint=False, bake=False, parentToLastSelected=False, constrainReverse=False, constrainTranslate=True, constrainRotate=True, euler=False): - # Check selected objects + ### Check selected objects selectedList = Selector.MultipleObjects(minSelectedCount) if (selectedList == None): return None @@ -114,7 +115,7 @@ def CreateOnSelected(name=_nameBase, scale=_scale, minSelectedCount=_minSelected locatorsList = [] sublocatorsList = [] - # Create locators on selected + ### Create locators on selected for item in selectedList: nameCurrent = Text.GetShortName(item, removeSpaces = True) + "_" + name created = Create(name = nameCurrent, scale = scale, hideParent = hideParent, subLocator = subLocator) @@ -125,12 +126,12 @@ def CreateOnSelected(name=_nameBase, scale=_scale, minSelectedCount=_minSelected locatorsList.append(created) cmds.matchTransform(locatorsList[-1], item, position = True, rotation = True, scale = True) - # Constrain locators to selected objects + ### Constrain locators to selected objects if (constraint): for i in range(len(selectedList)): Constraints.ConstrainSecondToFirstObject(selectedList[i], locatorsList[i], maintainOffset = False) - # Parent locators to last or to last sublocator + ### Parent locators to last or to last sublocator if (bake): if (parentToLastSelected): if subLocator: @@ -140,13 +141,13 @@ def CreateOnSelected(name=_nameBase, scale=_scale, minSelectedCount=_minSelected else: Parent.ListToLastObjects(locatorsList) - # Bake locators and delete constraints + ### Bake locators and delete constraints cmds.select(locatorsList) Baker.BakeSelected(euler = euler) Animation.DeleteStaticCurves() Constraints.DeleteConstraints(locatorsList) - # Reverse constrain original objects to new locators + ### Reverse constrain original objects to new locators if constrainReverse: for i in range(len(selectedList)): if subLocator: @@ -155,7 +156,7 @@ def CreateOnSelected(name=_nameBase, scale=_scale, minSelectedCount=_minSelected firstObject = locatorsList[i] Constraints.ConstrainSecondToFirstObject(firstObject, selectedList[i], maintainOffset = False, parent = constrainTranslate and constrainRotate, point = constrainTranslate, orient = constrainRotate) - # Select objects and return + ### Select objects and return if subLocator: cmds.select(sublocatorsList) return selectedList, locatorsList, sublocatorsList @@ -163,12 +164,12 @@ def CreateOnSelected(name=_nameBase, scale=_scale, minSelectedCount=_minSelected cmds.select(locatorsList) return selectedList, locatorsList def CreateAndBakeAsChildrenFromLastSelected(scale=_scale, minSelectedCount=2, hideParent=False, subLocator=False, constraintReverse=False, skipLastReverse=True, euler=False): - # Check selected objects + ### Check selected objects objects = CreateOnSelected(scale = scale, minSelectedCount = minSelectedCount, hideParent = hideParent, subLocator = subLocator, constraint = True, bake = True, parentToLastSelected = True, euler = euler) if (objects == None): return None - # Constrain objects to locators + ### Constrain objects to locators if (constraintReverse): for i in range(len(objects[0])): if (skipLastReverse and i == len(objects[0]) - 1): @@ -178,19 +179,19 @@ def CreateAndBakeAsChildrenFromLastSelected(scale=_scale, minSelectedCount=2, hi else: Constraints.ConstrainSecondToFirstObject(objects[1][i], objects[0][i], maintainOffset = False) - # Select objects and return + ### Select objects and return if subLocator: cmds.select(objects[2][-1]) else: cmds.select(objects[1][-1]) return objects def CreateOnSelectedAim(name=_nameAim, scale=_scale, minSelectedCount=_minSelectedCount, hideParent=False, subLocator=False, rotateOnly=False, vectorAim=(1,0,0), distance=100, reverse=True, euler=False): - # Check selected objects + ### Check selected objects objects = CreateOnSelected(name = name, scale = scale, minSelectedCount = minSelectedCount, hideParent = hideParent, subLocator = subLocator, euler = euler) if (objects == None): return None - # Create aim locators + ### Create aim locators groupsList = [] locatorsOffsetsList = [] locatorsTargetsList = [] @@ -231,7 +232,7 @@ def CreateOnSelectedAim(name=_nameAim, scale=_scale, minSelectedCount=_minSelect Constraints.ConstrainListToLastElement(selected = (locTarget, objects[0][i])) Constraints.ConstrainListToLastElement(selected = (locUp, objects[0][i])) - # Bake animation from original objects + ### Bake animation from original objects cmds.select(objects[1] + locatorsTargetsList, replace = True) cmds.select(objects[1] + locatorsUpList, add = True) Baker.BakeSelected(euler = euler) @@ -240,11 +241,11 @@ def CreateOnSelectedAim(name=_nameAim, scale=_scale, minSelectedCount=_minSelect Constraints.DeleteConstraints(locatorsUpList) Animation.DeleteStaticCurves() - # Create aim constraint + ### Create aim constraint for i in range(len(objects[0])): cmds.aimConstraint(locatorsTargetsList[i], locatorsOffsetsList[i], maintainOffset = True, weight = 1, aimVector = vectorAim, worldUpType = "object", worldUpObject = locatorsUpList[i]) - # Reverse constrain # TODO move constraint to temp group + ### Reverse constrain # TODO move constraint to temp group if (reverse): for i in range(len(objects[0])): parentObject = None @@ -253,17 +254,45 @@ def CreateOnSelectedAim(name=_nameAim, scale=_scale, minSelectedCount=_minSelect else: parentObject = locatorsOffsetsList[i] - # Constraints + ### Constraints if (rotateOnly): Constraints.ConstrainSecondToFirstObject(objects[0][i], objects[1][i], maintainOffset = False, parent = False, point = True, orient = False) Constraints.ConstrainSecondToFirstObject(parentObject, objects[0][i], maintainOffset = False, parent = False, point = False, orient = True) else: Constraints.ConstrainSecondToFirstObject(parentObject, objects[0][i], maintainOffset = False, parent = False, point = True, orient = True) - # Select objects and return + ### Select objects and return cmds.select(locatorsTargetsList) # if subLocator: # return objects[0], aimGroup, objects[1], locatorsOffsetsList, locatorsTargetsList, objects[2] # else: # return objects[0], aimGroup, objects[1], locatorsOffsetsList, locatorsTargetsList +def CreateWithMotionPath(*args): # TODO + ### Check selected objects + selected = Selector.MultipleObjects(minimalCount = 1) + if (selected == None): + return None + + ### TODO Create loop logic for each selected object + + ### Create curve and select + curve = Curves.CreateCurveFromTrajectory() + cmds.select(curve, replace = True) + + ### Create closest point node with locators + cmds.ClosestPointOn() + closestPointLocatorPos = cmds.ls(selection = True)[0] + closestPointNode = cmds.listConnections(closestPointLocatorPos, source = True, destination = False)[0] + closestPointLocatorIn = cmds.listConnections(closestPointNode, source = True, destination = False, type = "transform")[0] + # cmds.setAttr(closestPointLocatorPos + "." + Enums.Attributes.visibility, 0) + cmds.setAttr(closestPointLocatorIn + "." + Enums.Attributes.visibility, 0) + cmds.select(clear = True) + + ### Constrain locators to source object + Constraints.ConstrainSecondToFirstObject(selected[0], closestPointLocatorIn, maintainOffset = False, parent = False, point = True, orient = False) + Constraints.ConstrainSecondToFirstObject(selected[0], closestPointLocatorPos, maintainOffset = False, parent = False, point = False, orient = True) + + ### TODO Create motion path constraint + ### TODO Connect closest point parameter to U parameter in motion path + From f7c4f9cd1cff2a2bf0d0d126936dae13dc876d55 Mon Sep 17 00:00:00 2001 From: Eugene Date: Fri, 17 Jan 2025 17:10:34 -0800 Subject: [PATCH 05/11] update motion path logic --- GETOOLS_SOURCE/utils/Curves.py | 2 +- GETOOLS_SOURCE/utils/Locators.py | 32 ++++++++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/GETOOLS_SOURCE/utils/Curves.py b/GETOOLS_SOURCE/utils/Curves.py index 2aa042f..3cd097b 100644 --- a/GETOOLS_SOURCE/utils/Curves.py +++ b/GETOOLS_SOURCE/utils/Curves.py @@ -28,7 +28,7 @@ _curveName = "newCurve" -_curveDegree = 1 +_curveDegree = 1 # TODO add options to UI def CreateCurveFromSelectedObjects(*args): diff --git a/GETOOLS_SOURCE/utils/Locators.py b/GETOOLS_SOURCE/utils/Locators.py index cdeb409..cee9190 100644 --- a/GETOOLS_SOURCE/utils/Locators.py +++ b/GETOOLS_SOURCE/utils/Locators.py @@ -273,26 +273,50 @@ def CreateWithMotionPath(*args): # TODO selected = Selector.MultipleObjects(minimalCount = 1) if (selected == None): return None + cmds.select(clear = True) + + ### Create main group as a container for all new objects + mainGroup = cmds.group(name = Text.SetUniqueFromText("mpGroup"), empty = True) + cmds.select(clear = True) + ### TODO Create loop logic for each selected object + ### Create curve and select + cmds.select(selected[0], replace = True) curve = Curves.CreateCurveFromTrajectory() - cmds.select(curve, replace = True) + cmds.parent(curve, mainGroup) ### Create closest point node with locators + cmds.select(curve, replace = True) cmds.ClosestPointOn() closestPointLocatorPos = cmds.ls(selection = True)[0] closestPointNode = cmds.listConnections(closestPointLocatorPos, source = True, destination = False)[0] closestPointLocatorIn = cmds.listConnections(closestPointNode, source = True, destination = False, type = "transform")[0] - # cmds.setAttr(closestPointLocatorPos + "." + Enums.Attributes.visibility, 0) + cmds.setAttr(closestPointLocatorPos + "." + Enums.Attributes.visibility, 0) cmds.setAttr(closestPointLocatorIn + "." + Enums.Attributes.visibility, 0) cmds.select(clear = True) + cmds.parent(closestPointLocatorPos, mainGroup) + cmds.parent(closestPointLocatorIn, mainGroup) ### Constrain locators to source object Constraints.ConstrainSecondToFirstObject(selected[0], closestPointLocatorIn, maintainOffset = False, parent = False, point = True, orient = False) Constraints.ConstrainSecondToFirstObject(selected[0], closestPointLocatorPos, maintainOffset = False, parent = False, point = False, orient = True) - ### TODO Create motion path constraint - ### TODO Connect closest point parameter to U parameter in motion path + ### Create locator + locator = Create(name = Text.SetUniqueFromText("mpLocator")) + cmds.parent(locator, mainGroup) + cmds.select(clear = True) + + ### Create motion path constraint + timeMin = cmds.playbackOptions(query = True, min = True) + timeMax = cmds.playbackOptions(query = True, max = True) + motionPath = cmds.pathAnimation(locator, curve = curve, startTimeU = timeMin, endTimeU = timeMax, follow = False) + + ### Connect closest point parameter to U parameter in motion path + cmds.connectAttr(closestPointNode + ".parameter", motionPath + ".uValue", force = True) + + ### TODO Bake motion path animation + ### TODO Constrain original object to locator From 99dca879e29a8caad105034bfb5d64f9393863fc Mon Sep 17 00:00:00 2001 From: Eugene Date: Fri, 17 Jan 2025 18:02:11 -0800 Subject: [PATCH 06/11] cleanup code --- GETOOLS_SOURCE/utils/Baker.py | 14 +++++++------- GETOOLS_SOURCE/utils/Constraints.py | 20 ++++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/GETOOLS_SOURCE/utils/Baker.py b/GETOOLS_SOURCE/utils/Baker.py index 81de4ea..895ebec 100644 --- a/GETOOLS_SOURCE/utils/Baker.py +++ b/GETOOLS_SOURCE/utils/Baker.py @@ -32,8 +32,8 @@ def BakeSelected(classic=True, preserveOutsideKeys=True, sampleBy=1.0, selectedRange=False, channelBox=False, attributes=None, euler=False): # Check selected objects - selectedList = Selector.MultipleObjects(1) - if (selectedList == None): + selectedList = Selector.MultipleObjects(minimalCount = 1) + if selectedList is None: return # Calculate time range if range highlighted @@ -53,7 +53,7 @@ def BakeSelected(classic=True, preserveOutsideKeys=True, sampleBy=1.0, selectedR if (channelBox): bakeRegular = selectedAttributes == None if (bakeRegular): - if (attributes == None): + if attributes is None: cmds.bakeResults(time = (timeRange[0], timeRange[1]), preserveOutsideKeys = preserveOutsideKeys, simulation = True, minimizeRotation = True, sampleBy = sampleBy) else: cmds.bakeResults(time = (timeRange[0], timeRange[1]), preserveOutsideKeys = preserveOutsideKeys, simulation = True, minimizeRotation = True, sampleBy = sampleBy, attribute = attributes) @@ -76,8 +76,8 @@ def BakeSelected(classic=True, preserveOutsideKeys=True, sampleBy=1.0, selectedR def BakeSelectedByLastObject(pairOnly=False, sampleBy=1.0, selectedRange=False, channelBox=False, attributes=None, euler=False): # Check selected objects - selectedList = Selector.MultipleObjects(2) - if (selectedList == None): + selectedList = Selector.MultipleObjects(minimalCount = 2) + if selectedList is None: return # Cut list by last 2 items @@ -100,8 +100,8 @@ def BakeSelectedByLastObject(pairOnly=False, sampleBy=1.0, selectedRange=False, def BakeSelectedByWorld(sampleBy=1.0, selectedRange=False, channelBox=False, attributes=None, euler=False): # Check selected objects - selectedList = Selector.MultipleObjects(1) - if (selectedList == None): + selectedList = Selector.MultipleObjects(minimalCount = 1) + if selectedList is None: return world = cmds.group(world = True, empty = True) diff --git a/GETOOLS_SOURCE/utils/Constraints.py b/GETOOLS_SOURCE/utils/Constraints.py index b667262..ecd62f9 100644 --- a/GETOOLS_SOURCE/utils/Constraints.py +++ b/GETOOLS_SOURCE/utils/Constraints.py @@ -51,25 +51,25 @@ def ConstrainSecondToFirstObject(objectParent, objectChild, maintainOffset=True, ### Check attributes with fixed axis labels def CheckAttributes(attributeName, attributesFiltered): axisLabels = ["x", "y", "z"] - # Check which attributes are missing + ### Check which attributes are missing check = [attr in attributesFiltered for attr in attributeName] - # If all attributes are present, return "none" + ### If all attributes are present, return "none" if all(check): return "none" - # Return the axis labels for missing attributes + ### Return the axis labels for missing attributes return [axisLabels[i] for i, valid in enumerate(check) if not valid] ### General function to process attributes def ProcessAttributes(objectChild, attributeName): - # Construct attributes with object name + ### Construct attributes with object name attributes = ["{0}.{1}".format(objectChild, attr) for attr in attributeName] attributesFiltered = Attributes.FilterAttributesAnimatable(attributes=attributes, skipConstrainedKeys = False) - # If no attributes are left after filtering, return "none" + ### If no attributes are left after filtering, return "none" if not attributesFiltered: return ("x", "y", "z") - # Remove object name from attributes + ### Remove object name from attributes attributesFiltered = [attr.replace(objectChild + ".", "") for attr in attributesFiltered] - # Check attributes and return the axes to skip + ### Check attributes and return the axes to skip return CheckAttributes(attributeName, attributesFiltered) ### Use generalized logic for all attributes @@ -107,14 +107,14 @@ def ProcessAttributes(objectChild, attributeName): ConstrainAim(objectParent, objectChild, maintainOffset, weight) # TODO add customization logic def ConstrainAim(objectParent, objectChild, maintainOffset=True, weight=1, aimVector=(0, 0, 1), upVector=(0, 1, 0), worldUpVector=(0, 1, 0), worldUpObject=None): # TODO complete aim logic - # "scene" "object" "objectrotation" "vector" "none" + ### "scene" "object" "objectrotation" "vector" "none" if worldUpObject is None: cmds.aimConstraint(objectParent, objectChild, maintainOffset = maintainOffset, weight = weight, skip = "none", aimVector = aimVector, upVector = upVector, worldUpType = "vector", worldUpVector = worldUpVector) else: cmds.aimConstraint(objectParent, objectChild, maintainOffset = maintainOffset, weight = weight, skip = "none", aimVector = aimVector, upVector = upVector, worldUpType = "objectrotation", worldUpVector = worldUpVector, worldUpObject = worldUpObject) def DeleteConstraints(selected): - # First pass + ### First pass connections = Selector.GetConnectionsOfType(selected, type = Enums.Types.constraint, source = True, destination = False) for item in connections: if item is None: @@ -126,7 +126,7 @@ def DeleteConstraints(selected): if constraint in connection: cmds.delete(connection) - # Second pass with checking child objects (if constraint exists but not connected) + ### Second pass with checking child objects (if constraint exists but not connected) children = Selector.GetChildrenOfType(selected, type = Enums.Types.constraint) for i in range(len(selected)): if children[i] is not None: From 1ac67756be510367b89183e1e506bf1ae102ec38 Mon Sep 17 00:00:00 2001 From: Eugene Date: Fri, 17 Jan 2025 18:02:26 -0800 Subject: [PATCH 07/11] update curve space --- GETOOLS_SOURCE/utils/Curves.py | 5 ++--- GETOOLS_SOURCE/utils/Locators.py | 30 ++++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/GETOOLS_SOURCE/utils/Curves.py b/GETOOLS_SOURCE/utils/Curves.py index 3cd097b..142d7f8 100644 --- a/GETOOLS_SOURCE/utils/Curves.py +++ b/GETOOLS_SOURCE/utils/Curves.py @@ -46,7 +46,7 @@ def CreateCurveFromSelectedObjects(*args): curve = cmds.curve(name = _curveName, degree = _curveDegree, point = positions) return curve -def CreateCurveFromTrajectory(*args): # TODO rework +def CreateCurveFromTrajectory(name="curve", *args): # TODO rework ### Variables step = 1 degree = 1 @@ -54,7 +54,6 @@ def CreateCurveFromTrajectory(*args): # TODO rework ### Names mtName = "newMotionTrail" mtFinalName = mtName + Enums.MotionTrail.handle - curveName = "testCurve" ### Get time start/end @@ -74,7 +73,7 @@ def CreateCurveFromTrajectory(*args): # TODO rework #print "{0}: {1}".format(i, pts[i]) ### Create curve - newCurve = cmds.curve(name = curveName, degree = degree, point = pts) + newCurve = cmds.curve(name = name, degree = degree, point = pts) ### End cmds.delete(mtFinalName) diff --git a/GETOOLS_SOURCE/utils/Locators.py b/GETOOLS_SOURCE/utils/Locators.py index cee9190..3288de5 100644 --- a/GETOOLS_SOURCE/utils/Locators.py +++ b/GETOOLS_SOURCE/utils/Locators.py @@ -281,11 +281,12 @@ def CreateWithMotionPath(*args): # TODO ### TODO Create loop logic for each selected object + firstObject = selected[0] ### Create curve and select - cmds.select(selected[0], replace = True) - curve = Curves.CreateCurveFromTrajectory() + cmds.select(firstObject, replace = True) + curve = Curves.CreateCurveFromTrajectory(name = Text.SetUniqueFromText("mpCurve")) cmds.parent(curve, mainGroup) ### Create closest point node with locators @@ -297,12 +298,9 @@ def CreateWithMotionPath(*args): # TODO cmds.setAttr(closestPointLocatorPos + "." + Enums.Attributes.visibility, 0) cmds.setAttr(closestPointLocatorIn + "." + Enums.Attributes.visibility, 0) cmds.select(clear = True) - cmds.parent(closestPointLocatorPos, mainGroup) - cmds.parent(closestPointLocatorIn, mainGroup) ### Constrain locators to source object - Constraints.ConstrainSecondToFirstObject(selected[0], closestPointLocatorIn, maintainOffset = False, parent = False, point = True, orient = False) - Constraints.ConstrainSecondToFirstObject(selected[0], closestPointLocatorPos, maintainOffset = False, parent = False, point = False, orient = True) + Constraints.ConstrainSecondToFirstObject(firstObject, closestPointLocatorIn, maintainOffset = False, parent = False, point = True, orient = False) ### Create locator locator = Create(name = Text.SetUniqueFromText("mpLocator")) @@ -313,10 +311,26 @@ def CreateWithMotionPath(*args): # TODO timeMin = cmds.playbackOptions(query = True, min = True) timeMax = cmds.playbackOptions(query = True, max = True) motionPath = cmds.pathAnimation(locator, curve = curve, startTimeU = timeMin, endTimeU = timeMax, follow = False) + Constraints.ConstrainSecondToFirstObject(firstObject, locator, maintainOffset = False, parent = False, point = False, orient = True) ### Connect closest point parameter to U parameter in motion path cmds.connectAttr(closestPointNode + ".parameter", motionPath + ".uValue", force = True) - ### TODO Bake motion path animation - ### TODO Constrain original object to locator + ### Bake motion path animation + cmds.select(motionPath, replace = True) + cmds.bakeResults(time = (timeMin, timeMax), preserveOutsideKeys = True, simulation = True, minimizeRotation = True, sampleBy = 1, disableImplicitControl = True) + cmds.select(clear = True) + + ### Bake locator rotation # HACK need to figure out how to keep rotation through motion path node + cmds.select(locator, replace = True) + cmds.bakeResults(time = (timeMin, timeMax), preserveOutsideKeys = True, simulation = True, minimizeRotation = True, sampleBy = 1, disableImplicitControl = True, attribute = Enums.Attributes.rotateShort) + cmds.select(clear = True) + + ### Delete temp locators + cmds.delete(closestPointNode) + cmds.delete(closestPointLocatorPos) + cmds.delete(closestPointLocatorIn) + + ### Constrain original object to locator + Constraints.ConstrainSecondToFirstObject(locator, firstObject, maintainOffset = False, parent = False, point = True, orient = True) From 517225f3bd109040ec74d10fda0bdfa30638fc57 Mon Sep 17 00:00:00 2001 From: Eugene Date: Fri, 17 Jan 2025 18:11:56 -0800 Subject: [PATCH 08/11] update comments --- GETOOLS_SOURCE/utils/Locators.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GETOOLS_SOURCE/utils/Locators.py b/GETOOLS_SOURCE/utils/Locators.py index 3288de5..527e62f 100644 --- a/GETOOLS_SOURCE/utils/Locators.py +++ b/GETOOLS_SOURCE/utils/Locators.py @@ -311,7 +311,7 @@ def CreateWithMotionPath(*args): # TODO timeMin = cmds.playbackOptions(query = True, min = True) timeMax = cmds.playbackOptions(query = True, max = True) motionPath = cmds.pathAnimation(locator, curve = curve, startTimeU = timeMin, endTimeU = timeMax, follow = False) - Constraints.ConstrainSecondToFirstObject(firstObject, locator, maintainOffset = False, parent = False, point = False, orient = True) + Constraints.ConstrainSecondToFirstObject(firstObject, locator, maintainOffset = False, parent = False, point = False, orient = True) # HACK find another way to transfer rotation ### Connect closest point parameter to U parameter in motion path cmds.connectAttr(closestPointNode + ".parameter", motionPath + ".uValue", force = True) @@ -321,7 +321,7 @@ def CreateWithMotionPath(*args): # TODO cmds.bakeResults(time = (timeMin, timeMax), preserveOutsideKeys = True, simulation = True, minimizeRotation = True, sampleBy = 1, disableImplicitControl = True) cmds.select(clear = True) - ### Bake locator rotation # HACK need to figure out how to keep rotation through motion path node + ### TODO Bake locator rotation # FIXME need to figure out how to keep rotation through motion path node cmds.select(locator, replace = True) cmds.bakeResults(time = (timeMin, timeMax), preserveOutsideKeys = True, simulation = True, minimizeRotation = True, sampleBy = 1, disableImplicitControl = True, attribute = Enums.Attributes.rotateShort) cmds.select(clear = True) From dc4fea3d330e4b3cece1420a7dcef9f23fe6fb14 Mon Sep 17 00:00:00 2001 From: Eugene Date: Sat, 18 Jan 2025 03:30:17 -0800 Subject: [PATCH 09/11] add inner locator to better rotation preservation --- GETOOLS_SOURCE/utils/Curves.py | 8 +++----- GETOOLS_SOURCE/utils/Locators.py | 27 +++++++++++++++------------ 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/GETOOLS_SOURCE/utils/Curves.py b/GETOOLS_SOURCE/utils/Curves.py index 142d7f8..98f8592 100644 --- a/GETOOLS_SOURCE/utils/Curves.py +++ b/GETOOLS_SOURCE/utils/Curves.py @@ -27,11 +27,9 @@ from ..values import Enums -_curveName = "newCurve" -_curveDegree = 1 # TODO add options to UI - - def CreateCurveFromSelectedObjects(*args): + degree = 1 # TODO add options to UI + # Check selected objects selected = Selector.MultipleObjects(minimalCount = 2) if (selected == None): @@ -43,7 +41,7 @@ def CreateCurveFromSelectedObjects(*args): position = cmds.xform(item, query = True, translation = True, worldSpace = True) positions.append(position) - curve = cmds.curve(name = _curveName, degree = _curveDegree, point = positions) + curve = cmds.curve(name = "newCurve", degree = degree, point = positions) return curve def CreateCurveFromTrajectory(name="curve", *args): # TODO rework diff --git a/GETOOLS_SOURCE/utils/Locators.py b/GETOOLS_SOURCE/utils/Locators.py index 527e62f..3c829ca 100644 --- a/GETOOLS_SOURCE/utils/Locators.py +++ b/GETOOLS_SOURCE/utils/Locators.py @@ -302,35 +302,38 @@ def CreateWithMotionPath(*args): # TODO ### Constrain locators to source object Constraints.ConstrainSecondToFirstObject(firstObject, closestPointLocatorIn, maintainOffset = False, parent = False, point = True, orient = False) - ### Create locator - locator = Create(name = Text.SetUniqueFromText("mpLocator")) - cmds.parent(locator, mainGroup) + ### Create locator outer + locatorOuter = Create(name = Text.SetUniqueFromText("mpLocatorOuter"), scale = 0.1) + cmds.parent(locatorOuter, mainGroup) cmds.select(clear = True) - ### Create motion path constraint + ### Create locator inner + locatorInner = Create(name = Text.SetUniqueFromText("mpLocatorInner"), scale = 1) + cmds.parent(locatorInner, locatorOuter) + cmds.select(clear = True) + + ### Create motion path constraint with outer locator timeMin = cmds.playbackOptions(query = True, min = True) timeMax = cmds.playbackOptions(query = True, max = True) - motionPath = cmds.pathAnimation(locator, curve = curve, startTimeU = timeMin, endTimeU = timeMax, follow = False) - Constraints.ConstrainSecondToFirstObject(firstObject, locator, maintainOffset = False, parent = False, point = False, orient = True) # HACK find another way to transfer rotation + motionPath = cmds.pathAnimation(locatorOuter, curve = curve, startTimeU = timeMin, endTimeU = timeMax, follow = True, worldUpType = "scene", bank = False) + + ### Constrain inner locator to outer locator + Constraints.ConstrainSecondToFirstObject(firstObject, locatorInner, maintainOffset = False, parent = False, point = False, orient = True) ### Connect closest point parameter to U parameter in motion path cmds.connectAttr(closestPointNode + ".parameter", motionPath + ".uValue", force = True) ### Bake motion path animation cmds.select(motionPath, replace = True) + cmds.select(locatorInner, add = True) cmds.bakeResults(time = (timeMin, timeMax), preserveOutsideKeys = True, simulation = True, minimizeRotation = True, sampleBy = 1, disableImplicitControl = True) cmds.select(clear = True) - ### TODO Bake locator rotation # FIXME need to figure out how to keep rotation through motion path node - cmds.select(locator, replace = True) - cmds.bakeResults(time = (timeMin, timeMax), preserveOutsideKeys = True, simulation = True, minimizeRotation = True, sampleBy = 1, disableImplicitControl = True, attribute = Enums.Attributes.rotateShort) - cmds.select(clear = True) - ### Delete temp locators cmds.delete(closestPointNode) cmds.delete(closestPointLocatorPos) cmds.delete(closestPointLocatorIn) ### Constrain original object to locator - Constraints.ConstrainSecondToFirstObject(locator, firstObject, maintainOffset = False, parent = False, point = True, orient = True) + Constraints.ConstrainSecondToFirstObject(locatorInner, firstObject, maintainOffset = True, parent = False, point = True, orient = True) From 36eda31470ff40880b1fd17cafb20e2873758e70 Mon Sep 17 00:00:00 2001 From: Eugene Date: Sat, 18 Jan 2025 14:10:04 -0800 Subject: [PATCH 10/11] Update Locators.py --- GETOOLS_SOURCE/utils/Locators.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/GETOOLS_SOURCE/utils/Locators.py b/GETOOLS_SOURCE/utils/Locators.py index 3c829ca..40621a6 100644 --- a/GETOOLS_SOURCE/utils/Locators.py +++ b/GETOOLS_SOURCE/utils/Locators.py @@ -315,7 +315,7 @@ def CreateWithMotionPath(*args): # TODO ### Create motion path constraint with outer locator timeMin = cmds.playbackOptions(query = True, min = True) timeMax = cmds.playbackOptions(query = True, max = True) - motionPath = cmds.pathAnimation(locatorOuter, curve = curve, startTimeU = timeMin, endTimeU = timeMax, follow = True, worldUpType = "scene", bank = False) + motionPath = cmds.pathAnimation(locatorOuter, curve = curve, startTimeU = timeMin, endTimeU = timeMax, follow = True, worldUpType = "scene") # , fractionMode = False, bank = False ### Constrain inner locator to outer locator Constraints.ConstrainSecondToFirstObject(firstObject, locatorInner, maintainOffset = False, parent = False, point = False, orient = True) @@ -323,10 +323,10 @@ def CreateWithMotionPath(*args): # TODO ### Connect closest point parameter to U parameter in motion path cmds.connectAttr(closestPointNode + ".parameter", motionPath + ".uValue", force = True) - ### Bake motion path animation + ### Bake motion path uValue cmds.select(motionPath, replace = True) cmds.select(locatorInner, add = True) - cmds.bakeResults(time = (timeMin, timeMax), preserveOutsideKeys = True, simulation = True, minimizeRotation = True, sampleBy = 1, disableImplicitControl = True) + cmds.bakeResults(time = (timeMin, timeMax), preserveOutsideKeys = True, simulation = True, minimizeRotation = True, sampleBy = 1, disableImplicitControl = True, attribute = ("uValue",) + Enums.Attributes.rotateShort) cmds.select(clear = True) ### Delete temp locators From fc97feb824e7f225ed1dd0f84b87db334f73ba66 Mon Sep 17 00:00:00 2001 From: Eugene Date: Sun, 19 Jan 2025 22:22:14 -0800 Subject: [PATCH 11/11] Update Locators.py --- GETOOLS_SOURCE/utils/Locators.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/GETOOLS_SOURCE/utils/Locators.py b/GETOOLS_SOURCE/utils/Locators.py index 40621a6..2816cdc 100644 --- a/GETOOLS_SOURCE/utils/Locators.py +++ b/GETOOLS_SOURCE/utils/Locators.py @@ -303,29 +303,29 @@ def CreateWithMotionPath(*args): # TODO Constraints.ConstrainSecondToFirstObject(firstObject, closestPointLocatorIn, maintainOffset = False, parent = False, point = True, orient = False) ### Create locator outer - locatorOuter = Create(name = Text.SetUniqueFromText("mpLocatorOuter"), scale = 0.1) + locatorOuter = Create(name = Text.SetUniqueFromText("mpLocatorOuter"), scale = 1) cmds.parent(locatorOuter, mainGroup) cmds.select(clear = True) ### Create locator inner - locatorInner = Create(name = Text.SetUniqueFromText("mpLocatorInner"), scale = 1) - cmds.parent(locatorInner, locatorOuter) - cmds.select(clear = True) + # locatorInner = Create(name = Text.SetUniqueFromText("mpLocatorInner"), scale = 1) + # cmds.parent(locatorInner, locatorOuter) + # cmds.select(clear = True) ### Create motion path constraint with outer locator timeMin = cmds.playbackOptions(query = True, min = True) timeMax = cmds.playbackOptions(query = True, max = True) - motionPath = cmds.pathAnimation(locatorOuter, curve = curve, startTimeU = timeMin, endTimeU = timeMax, follow = True, worldUpType = "scene") # , fractionMode = False, bank = False + motionPath = cmds.pathAnimation(locatorOuter, curve = curve, startTimeU = timeMin, endTimeU = timeMax, follow = False) ### Constrain inner locator to outer locator - Constraints.ConstrainSecondToFirstObject(firstObject, locatorInner, maintainOffset = False, parent = False, point = False, orient = True) + # Constraints.ConstrainSecondToFirstObject(firstObject, locatorInner, maintainOffset = False, parent = False, point = False, orient = True) ### Connect closest point parameter to U parameter in motion path cmds.connectAttr(closestPointNode + ".parameter", motionPath + ".uValue", force = True) ### Bake motion path uValue cmds.select(motionPath, replace = True) - cmds.select(locatorInner, add = True) + # cmds.select(locatorInner, add = True) cmds.bakeResults(time = (timeMin, timeMax), preserveOutsideKeys = True, simulation = True, minimizeRotation = True, sampleBy = 1, disableImplicitControl = True, attribute = ("uValue",) + Enums.Attributes.rotateShort) cmds.select(clear = True) @@ -335,5 +335,5 @@ def CreateWithMotionPath(*args): # TODO cmds.delete(closestPointLocatorIn) ### Constrain original object to locator - Constraints.ConstrainSecondToFirstObject(locatorInner, firstObject, maintainOffset = True, parent = False, point = True, orient = True) + Constraints.ConstrainSecondToFirstObject(locatorOuter, firstObject, maintainOffset = True, parent = False, point = True, orient = False)