This commit is contained in:
Jeffreytsai1004 2025-01-14 02:59:09 +08:00
parent f11c74e09e
commit e4fd7e9a1a
81 changed files with 22225 additions and 0 deletions

View File

@ -0,0 +1,479 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
_____ _____ ______ _____ ______
/ ____| __ \| ____| /\ / ____| ____|_
| | | |__) | |__ / \ | (___ | |__ _| |_
| | | _ /| __| / /\ \ \___ \| __|_ _|
| |____| | \ \| |____ / ____ \ ____) | |____|_|
\_____|_| \_\______/_/ \_\_____/|______|
"""
import maya.cmds as cmds
import maya.api.OpenMaya as om
# uses python maya 2
maya_useNewAPI = True
class MayaVerObj():
def __init__(self):
self.num = None
self.extnum = 0
def cPwhatmayaversion():
verstring = cmds.about(v=True)
splited = verstring.split()
num = None
extnum = 0
for r in splited:
if "20" in r:
num = int(r)
break
i = -1
for r in splited:
i += 1
if r.lower() == "extension" or r.lower() == "ext":
j = i+1
for j in range(len(splited)):
if splited[j].isdigit():
extnum = int(splited[j])
break
break
if not num:
raise Exception("can't get maya version")
mayavobj = MayaVerObj()
mayavobj.num = num
mayavobj.extnum = extnum
return mayavobj
global_creasePlus_mayaver = cPwhatmayaversion()
def getmayaver():
global global_creasePlus_mayaver
return global_creasePlus_mayaver
class CpMsg:
kNoSel = 'nothing is selected'
kNoSelCurve = 'no curve(s) selected'
kNoSelMesh = 'no mesh(es) selected'
kNoSelEdge = 'no edge(s) selected'
kNoSelVert = 'no vertice(s) selected'
kNoSelFace = 'no face(s) selected'
kSelOneMesh = 'select one mesh'
kSelMesh = 'mesh(es) must be selected'
kSelEdge = 'edge(s) must be selected'
kSelFace = 'face(s) must be selected'
kSelVert = 'vertice(s) must be selected'
kSelEdgeOrOneMesh = 'select edge(s) or a mesh'
kSelCurveCv = 'curve cv(s) must be selected'
kWorksForOne = 'works just for one entity'
kNoHardEdges = 'no hard edge(s) were found'
kSelLeastTwoMesh = 'select at least two meshes'
kSelLeastTwoCurve = 'select at least two curves'
kInvalidFuncArgs = 'function called with invalid arguments'
kRequModelView = 'you are required to be in a modeling view/pan'
kWrongSel = 'wrong selection'
kcPnodePluginNotLoaded = \
'creasePlus nodes plugin must be loaded to use this command'
# obj assumed to be a parent
def cPgotoChild(obj, mfntyp):
dagn = om.MFnDagNode(obj)
for i in range(dagn.childCount()):
cur = dagn.child(i)
if cur.hasFn(mfntyp):
return cur
return om.MObject.kNullObj
def cPshapeDagPath(obj):
dagn = om.MFnDagNode(obj)
return dagn.getPath()
def cPhardEdgesStrings(shape):
sel = om.MSelectionList()
sel.add(shape)
dagp = sel.getDagPath(0)
edgeIter = om.MItMeshEdge(dagp)
selStrings = []
shapeString = dagp.partialPathName()
while not edgeIter.isDone():
if edgeIter.isSmooth == False:
selStrings.append(shapeString + '.e['
+ str(edgeIter.index()) + ']')
edgeIter.next()
return selStrings
def cPgetShapeStringsFromSel(mfntyp):
sel = om.MGlobal.getActiveSelectionList()
selIt = om.MItSelectionList(sel)
selStrings = []
dagFn = om.MFnDagNode()
while not selIt.isDone():
if selIt.itemType() != selIt.kDagSelectionItem:
selIt.next()
continue
obj = selIt.getDependNode()
if not obj.hasFn(mfntyp):
obj = cPgotoChild(obj, mfntyp)
if not obj.hasFn(mfntyp):
selIt.next()
continue
dagFn.setObject(obj)
selStrings.append(dagFn.partialPathName())
selIt.next()
return selStrings
def cPcameraDominantPlane():
activePan = cmds.getPanel(wf=True)
if cmds.getPanel(to=activePan) != 'modelPanel':
cmds.error(CpMsg.kRequModelView)
camPos = cmds.camera(cmds.modelEditor(activePan, q=True, cam=True),
q=True, p=True)
camTarget = cmds.camera(cmds.modelEditor(activePan, q=True, cam=True),
q=True, wci=True)
camDir = om.MVector(abs(camTarget[0] - camPos[0]), abs(camTarget[1]
- camPos[1]), abs(camTarget[2] - camPos[2]))
maxv = 0
ddir = 'x'
if maxv < camDir.x:
maxv = camDir.x
ddir = 'x'
if maxv < camDir.y:
maxv = camDir.y
ddir = 'y'
if maxv < camDir.z:
maxv = camDir.z
ddir = 'z'
return ddir
# cmds.ls(sl=True)
# get shape + v, e, f comps in a tuple
def cPgetShapeAndCoStrings(mselit):
shape = None
v = None
e = None
f = None
if mselit.itemType() != mselit.kDagSelectionItem:
return (shape, v, e, f)
hasComp = mselit.hasComponents()
if hasComp == True:
comp = mselit.getComponent()
if comp[0].node().hasFn(om.MFn.kMesh):
shape = comp[0]
if comp[1].hasFn(om.MFn.kMeshPolygonComponent):
f = comp[1]
elif comp[1].hasFn(om.MFn.kMeshEdgeComponent):
e = comp[1]
elif comp[1].hasFn(om.MFn.kMeshVertComponent):
v = comp[1]
else:
obj = mselit.getDependNode()
if not obj.hasFn(om.MFn.kMesh):
obj = cPgotoChild(obj, om.MFn.kMesh)
if obj.hasFn(om.MFn.kMesh):
shape = cPshapeDagPath(obj)
return (shape, v, e, f)
def cPfaceToHardEdgeStrings(dagp, faceComps):
edgeIds = set()
faceIt = om.MItMeshPolygon(dagp, faceComps)
meshFn = om.MFnMesh(dagp.node())
while not faceIt.isDone():
fEdges = faceIt.getEdges()
for idx in fEdges:
if not meshFn.isEdgeSmooth(idx):
edgeIds.add(idx)
faceIt.next(None) # maya api bug
shapeString = dagp.partialPathName()
return [shapeString + '.e[' + str(idx) + ']' for idx in edgeIds]
def cPedgeToStrings(dagp, edgeComps):
edgeStrings = []
edgeIt = om.MItMeshEdge(dagp, edgeComps)
shapeString = dagp.partialPathName()
while not edgeIt.isDone():
edgeStrings.append(shapeString + '.e[' + str(edgeIt.index())
+ ']')
edgeIt.next()
return edgeStrings
################################################################ CONTEXTS #################################################################
# draw curve ctx
try:
global_cPcurveCtxStr
except:
global_cPcurveCtxStr = cmds.curveCVCtx('cPcurveCtx', degree=1)
#
# booleanop context
try:
global_cPboolOpCtxStr
except:
global_cPboolOpCtxStr = cmds.dragAttrContext('cPboolOpCtx')
global_cPboolOpAttrCnt = 3
global_cPboolOpAttrIter = 0
def cPboolOpIterSetVal(val):
global global_cPboolOpAttrIter
global_cPboolOpAttrIter = val
return global_cPboolOpAttrIter % global_cPboolOpAttrCnt
def cPboolOpIterVal():
global global_cPboolOpAttrIter
return global_cPboolOpAttrIter % global_cPboolOpAttrCnt
def cPboolOpIterIncVal():
global global_cPboolOpAttrIter
global_cPboolOpAttrIter += 1
return global_cPboolOpAttrIter % global_cPboolOpAttrCnt
#
# mirror context
try:
global_cPmirrorCtxStr
except:
global_cPmirrorCtxStr = cmds.dragAttrContext('cPmirrorCtx')
global_cPmirrorAttrCnt = None
if getmayaver().num > 2016:
global_cPmirrorAttrCnt = 1
else:
global_cPmirrorAttrCnt = 3
global_cPmirrorAttrIter = 0
def cPmirrorIterSetVal(val):
global global_cPmirrorAttrIter
global_cPmirrorAttrIter = val
return global_cPmirrorAttrIter % global_cPmirrorAttrCnt
def cPmirrorIterVal():
global global_cPmirrorAttrIter
return global_cPmirrorAttrIter % global_cPmirrorAttrCnt
def cPmirrorIterIncVal():
global global_cPmirrorAttrIter
global_cPmirrorAttrIter += 1
return global_cPmirrorAttrIter % global_cPmirrorAttrCnt
#
# hbevel context
try:
global_hBevelCtxStr
except:
global_hBevelCtxStr = cmds.dragAttrContext('hBevelCtx')
global_hBevelAttrCnt = 2
global_hBevelAttrIter = 0
def cPhBevelIterSetVal(val):
global global_hBevelAttrIter
global_hBevelAttrIter = val
return global_hBevelAttrIter % global_hBevelAttrCnt
def cPhBevelIterVal():
global global_hBevelAttrIter
return global_hBevelAttrIter % global_hBevelAttrCnt
def cPhBevelIterIncVal():
global global_hBevelAttrIter
global_hBevelAttrIter += 1
return global_hBevelAttrIter % global_hBevelAttrCnt
#
# curvebevel context
try:
global_cPcurveBevelCtxStr
except:
global_cPcurveBevelCtxStr = cmds.dragAttrContext('cPcurveBvlCtx')
global_cPcurveBevelAttrCnt = 2
global_cPcurveBevelAttrIter = 0
def cPcurveBevelIterSetVal(val):
global global_cPcurveBevelAttrIter
global_cPcurveBevelAttrIter = val
return global_cPcurveBevelAttrIter % global_cPcurveBevelAttrCnt
def cPcurveBevelIterVal():
global global_cPcurveBevelAttrIter
return global_cPcurveBevelAttrIter % global_cPcurveBevelAttrCnt
def cPcurveBevelIterIncVal():
global global_cPcurveBevelAttrIter
global_cPcurveBevelAttrIter += 1
return global_cPcurveBevelAttrIter % global_cPcurveBevelAttrCnt
#
# crease tool context
try:
global_cPcreaseCtxStr
except:
global_cPcreaseCtxStr = cmds.polyCreaseCtx('cPcreaseCtx', es=True, r=True)
#
# physical crease context
try:
global_pCreaseCtxStr
except:
global_pCreaseCtxStr = cmds.dragAttrContext('pCreaseCtx')
global_pCreaseAttrCnt = 2
global_pCreaseAttrIter = 0
def cPpCreaseIterSetVal(val):
global global_pCreaseAttrIter
global_pCreaseAttrIter = val
return global_pCreaseAttrIter % global_pCreaseAttrCnt
def cPpCreaseIterVal():
global global_pCreaseAttrIter
return global_pCreaseAttrIter % global_pCreaseAttrCnt
def cPpCreaseIterIncVal():
global global_pCreaseAttrIter
global_pCreaseAttrIter += 1
return global_pCreaseAttrIter % global_pCreaseAttrCnt
#
def cPcontextUndo():
if cmds.currentCtx() == global_cPboolOpCtxStr:
# cmds.setToolTo('moveSuperContext')
cmds.dragAttrContext(global_cPboolOpCtxStr, e=True, reset=True)
cmds.setToolTo(global_cPboolOpCtxStr)
elif cmds.currentCtx() == global_hBevelCtxStr:
# cmds.setToolTo('moveSuperContext')
cmds.dragAttrContext(global_hBevelCtxStr, e=True, reset=True)
cmds.setToolTo(global_hBevelCtxStr)
elif cmds.currentCtx() == global_cPmirrorCtxStr:
# cmds.setToolTo('moveSuperContext')
cmds.dragAttrContext(global_cPmirrorCtxStr, e=True, reset=True)
cmds.setToolTo(global_cPmirrorCtxStr)
elif cmds.currentCtx() == global_cPcurveBevelCtxStr:
# cmds.setToolTo('moveSuperContext')
cmds.dragAttrContext(global_cPcurveBevelCtxStr, e=True, reset=True)
cmds.setToolTo(global_cPcurveBevelCtxStr)
elif cmds.currentCtx() == global_pCreaseCtxStr:
# cmds.setToolTo('moveSuperContext')
cmds.dragAttrContext(global_pCreaseCtxStr, e=True, reset=True)
cmds.setToolTo(global_pCreaseCtxStr)
try:
global_creasePlusCtxUndoJob
except:
global_creasePlusCtxUndoJob = cmds.scriptJob(event=['Undo', cPcontextUndo])
############################################################################################################
def main():
return None
if __name__ == '__main__':
main()

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,6 @@
import maya.api.OpenMaya as om
def cPexcept(excepstr=""):
om.MGlobal.displayError(excepstr)
return Exception(excepstr)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,507 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
_____ _____ ______ _____ ______
/ ____| __ \| ____| /\ / ____| ____|_
| | | |__) | |__ / \ | (___ | |__ _| |_
| | | _ /| __| / /\ \ \___ \| __|_ _|
| |____| | \ \| |____ / ____ \ ____) | |____|_|
\_____|_| \_\______/_/ \_\_____/|______|
"""
# import maya.cmds as mc
import maya.api.OpenMaya as om
# import copy
# uses python maya 2
maya_useNewAPI = True
#################
def cPhardEdgeIds(mesh):
edgeIter = om.MItMeshEdge(mesh)
ids = []
while not edgeIter.isDone():
if not edgeIter.isSmooth:
ids.append(edgeIter.index())
edgeIter.next()
return ids
def cPcompToIds(compdata, cptyp):
compDataFn = om.MFnComponentListData(compdata)
# compDataFn
ids = []
for i in range(compDataFn.length()):
curcomp = compDataFn.get(i)
if curcomp.hasFn(cptyp):
sic = om.MFnSingleIndexedComponent(curcomp)
for j in range(sic.elementCount):
curIdx = sic.element(j)
ids.append(curIdx)
return ids
def cPidsToComp(ids, cptyp):
sic = om.MFnSingleIndexedComponent()
sic.create(cptyp) # om.MFn.kMeshEdgeComponent
sic.addElements(ids)
compData = om.MFnComponentListData()
compObj = compData.create()
compData.add(sic.object())
return (sic.object(), compObj)
def cPtransformMeshPoints(mesh, mat):
meshFn = om.MFnMesh(mesh)
pts = meshFn.getPoints()
for pt in pts:
pt *= mat
meshFn.setPoints(pts)
"""
input: mesh
output: componentlist
"""
MayaNodeT = om.MPxNode
class CpCurveBevel(MayaNodeT):
kNodeName = "creasePlusCurveBevel"
kNodeId = om.MTypeId(0x1154)
aCvs = None
aOffset = None
aSeg = None
aOffsetFrac = None
aInputCurve = None
aOutput = None
def __init__(self):
super(CpCurveBevel, self).__init__()
@classmethod
def creator(cls):
return cls()
@classmethod
def initialize(cls):
nAttr = om.MFnNumericAttribute()
tAttr = om.MFnTypedAttribute()
cls.aCvs = tAttr.create("cvComponentList", "cvs",
om.MFnComponentListData.kComponentList)
cls.aOffset = nAttr.create("offset", "off", om.MFnNumericData.kFloat)
nAttr.setMin(0)
nAttr.default = 0.5
nAttr.keyable = True
cls.aSeg = nAttr.create("segments", "seg", om.MFnNumericData.kInt)
nAttr.setMin(1)
nAttr.default = 1
nAttr.keyable = True
cls.aOffsetFrac = nAttr.create("offsetAsFraction", "oaf",
om.MFnNumericData.kBoolean)
nAttr.default = False
nAttr.keyable = True
cls.aInputCurve = tAttr.create("inCurve", "inc",
om.MFnData.kNurbsCurve)
cls.aOutput = tAttr.create("outCurve", "out", om.MFnData.kNurbsCurve)
tAttr.storable = False
tAttr.writable = False
MayaNodeT.addAttribute(cls.aCvs)
MayaNodeT.addAttribute(cls.aOffset)
MayaNodeT.addAttribute(cls.aSeg)
MayaNodeT.addAttribute(cls.aOffsetFrac)
MayaNodeT.addAttribute(cls.aInputCurve)
MayaNodeT.addAttribute(cls.aOutput)
MayaNodeT.attributeAffects(cls.aCvs, cls.aOutput)
MayaNodeT.attributeAffects(cls.aOffset, cls.aOutput)
MayaNodeT.attributeAffects(cls.aSeg, cls.aOutput)
MayaNodeT.attributeAffects(cls.aOffsetFrac, cls.aOutput)
MayaNodeT.attributeAffects(cls.aInputCurve, cls.aOutput)
def setOutputToCopy(self, data):
inCurveHandle = data.inputValue(CpCurveBevel.aInputCurve)
outHandle = data.outputValue(CpCurveBevel.aOutput)
outHandle.copy(inCurveHandle)
outHandle.setClean()
def compute(self, plug, data):
if plug != CpCurveBevel.aOutput:
return None
inCurveHandle = data.inputValue(CpCurveBevel.aInputCurve)
inCurve = inCurveHandle.asNurbsCurveTransformed()
inCvsHandle = data.inputValue(CpCurveBevel.aCvs)
compData = inCvsHandle.data()
ids = cPcompToIds(compData, om.MFn.kCurveCVComponent)
#
inOffset = data.inputValue(CpCurveBevel.aOffset).asFloat()
inSeg = data.inputValue(CpCurveBevel.aSeg).asInt()
inFrac = data.inputValue(CpCurveBevel.aOffsetFrac).asBool()
if len(ids) == 0 or inOffset == 0:
self.setOutputToCopy(data)
return
curveFn = om.MFnNurbsCurve(inCurve)
curveDegree = curveFn.degree
curveForm = curveFn.form
# curveKnots = curveFn.knots()
curvePts = curveFn.cvPositions() # to be modified
numcv = len(curvePts) # to be modified
bevelParam = 1 / float(inSeg)
bevelPts = []
ids.sort()
for cvIdx in ids:
mpos = om.MVector(curvePts[cvIdx])
if curveForm == curveFn.kPeriodic:
lpos = om.MVector(curvePts[(cvIdx + numcv -
(1 + curveDegree)) %
(numcv - curveDegree)])
rpos = om.MVector(curvePts[(cvIdx + 1) %
(numcv - curveDegree)])
else:
lpos = om.MVector(curvePts[(cvIdx + numcv - 1) % numcv])
rpos = om.MVector(curvePts[(cvIdx + 1) % numcv])
lvec = lpos - mpos
rvec = rpos - mpos
lanchor = None
ranchor = None
if inFrac:
lanchor = mpos + (inOffset * lvec)
ranchor = mpos + (inOffset * rvec)
else:
lanchor = mpos + (inOffset * lvec.normal())
ranchor = mpos + (inOffset * rvec.normal())
bevelPts.append(lanchor)
t = 1 * bevelParam
for i in range(inSeg - 1):
# (1-t)^2A+2t(1-t)B+t^2C
res = (1 - t)**2 * lanchor + (2 * t * (1 - t) * mpos) + (
t**2 * ranchor)
bevelPts.append(res)
t += bevelParam
bevelPts.append(ranchor)
#
i = len(bevelPts) - (inSeg + 1)
ids.sort(reverse=True)
for cvIdx in ids:
curvePts[cvIdx] = bevelPts[i]
curvePts[cvIdx + 1:cvIdx + 1] = bevelPts[i + 1:i + inSeg + 1]
i -= (inSeg + 1)
numcv = len(curvePts)
if curveForm == curveFn.kPeriodic:
curvePts[-curveDegree:] = curvePts[:curveDegree]
if curveDegree == 1:
knots = [float(i) for i in range(numcv)]
else:
knots = [None] * (numcv - curveDegree + (2 * curveDegree) - 1)
if curveForm == curveFn.kPeriodic:
knots[:curveDegree] = [
float(i) for i in reversed(range(0, -curveDegree, -1))
]
knots[curveDegree:] = [
float(i) for i in range(1,
len(knots) - curveDegree + 1)
]
else:
knots[:curveDegree] = [float(0)] * curveDegree
knots[-curveDegree:] = [float(numcv - curveDegree)
] * curveDegree
knots[curveDegree:-curveDegree] = [
float(i)
for i in range(1,
len(knots) - (2 * curveDegree) + 1)
]
curveDataFn = om.MFnNurbsCurveData()
curveDataFn.create()
knots = om.MDoubleArray(knots)
curveFn.create(
curvePts,
knots,
curveDegree,
curveForm,
False,
False,
parent=curveDataFn.object())
out = data.outputValue(CpCurveBevel.aOutput)
out.setMObject(curveDataFn.object())
out.setClean()
class CpCurveToPoly(MayaNodeT):
kNodeName = "creasePlusCurveToPoly"
kNodeId = om.MTypeId(0x12547)
aCount = None
aRevNorm = None
aControlPts = None
aInputCurve = None
aOutput = None
def __init__(self):
super(CpCurveToPoly, self).__init__()
@classmethod
def creator(cls):
return cls()
@classmethod
def initialize(cls):
nAttr = om.MFnNumericAttribute()
tAttr = om.MFnTypedAttribute()
cls.aRevNorm = nAttr.create("reverse", "rev",
om.MFnNumericData.kBoolean)
nAttr.default = False
nAttr.keyable = True
cls.aCount = nAttr.create("count", "cnt", om.MFnNumericData.kInt)
nAttr.setMin(3)
nAttr.default = 12
nAttr.keyable = True
cls.aControlPts = nAttr.create("controlPts", "cv",
om.MFnNumericData.kBoolean)
nAttr.default = True
nAttr.keyable = True
cls.aInputCurve = tAttr.create("inCurve", "inc",
om.MFnData.kNurbsCurve)
cls.aOutput = tAttr.create("outPoly", "out", om.MFnData.kMesh)
tAttr.storable = False
tAttr.writable = False
MayaNodeT.addAttribute(cls.aRevNorm)
MayaNodeT.addAttribute(cls.aCount)
MayaNodeT.addAttribute(cls.aControlPts)
MayaNodeT.addAttribute(cls.aInputCurve)
MayaNodeT.addAttribute(cls.aOutput)
MayaNodeT.attributeAffects(cls.aRevNorm, cls.aOutput)
MayaNodeT.attributeAffects(cls.aCount, cls.aOutput)
MayaNodeT.attributeAffects(cls.aControlPts, cls.aOutput)
MayaNodeT.attributeAffects(cls.aInputCurve, cls.aOutput)
def setOutputToNull(self, data):
out = data.outputValue(CpCurveToPoly.aOutput)
out.setMObject(om.MObject.kNullObj)
out.setClean()
def compute(self, plug, data):
if plug != CpCurveToPoly.aOutput:
return None
reverseNormal = data.inputValue(CpCurveToPoly.aRevNorm).asBool()
inCurveHandle = data.inputValue(CpCurveToPoly.aInputCurve)
inCurve = inCurveHandle.asNurbsCurveTransformed()
inCount = data.inputValue(CpCurveToPoly.aCount).asInt() #
useCvs = data.inputValue(CpCurveToPoly.aControlPts).asBool()
curveFn = om.MFnNurbsCurve(inCurve)
curveForm = curveFn.form
curveDegree = curveFn.degree
polyPts = None
meshDataFn = om.MFnMeshData()
meshDataFn.create()
if useCvs:
if curveForm == curveFn.kPeriodic:
polyPts = curveFn.cvPositions()[:-curveDegree]
else:
polyPts = curveFn.cvPositions()
else:
polyPts = []
domain = curveFn.knotDomain
param = domain[1] / float(inCount)
t = 0.0
for i in range(inCount):
polyPts.append(curveFn.getPointAtParam(t))
t += param
if reverseNormal:
polyPts = [pt for pt in reversed(polyPts)]
# create(vertices, polygonCounts, polygonConnects, uValues=None, vValues=None, parent=kNullObj) -> MObject
meshFn = om.MFnMesh()
meshFn.create(
polyPts, [len(polyPts)], [i for i in range(len(polyPts))],
parent=meshDataFn.object())
out = data.outputValue(CpCurveToPoly.aOutput)
out.setMObject(meshDataFn.object())
out.setClean()
class CpHeIds(MayaNodeT):
kNodeName = "creasePlusBevelHe"
kNodeId = om.MTypeId(0x1157)
aForceComp = None
aInputMesh = None
aIds = None
def __init__(self):
super(CpHeIds, self).__init__()
self.numVertices = 0
self.numPolygons = 0
self.numNormals = 0
self.dummycompute = False
@classmethod
def creator(cls):
return cls()
@classmethod
def initialize(cls):
tAttr = om.MFnTypedAttribute()
nAttr = om.MFnNumericAttribute()
cls.aForceComp = nAttr.create("forceCompute", "fc",
om.MFnNumericData.kBoolean, 0)
nAttr.default = False
nAttr.keyable = True
cls.aInputMesh = tAttr.create("inMesh", "i", om.MFnData.kMesh)
cls.aIds = tAttr.create("componentList", "cl",
om.MFnComponentListData.kComponentList)
tAttr.storable = False
tAttr.writable = False
MayaNodeT.addAttribute(cls.aForceComp)
MayaNodeT.addAttribute(cls.aInputMesh)
MayaNodeT.addAttribute(cls.aIds)
def attrToPlug(self, attr):
return om.MPlug(self.thisMObject(), attr)
def setDependentsDirty(self, plug, affect):
if plug == CpHeIds.aForceComp:
if self.dummycompute == False:
self.dummycompute = True
affect.append(self.attrToPlug(CpHeIds.aIds))
else:
self.dummycompute = False
elif plug == CpHeIds.aInputMesh:
affect.append(self.attrToPlug(CpHeIds.aIds))
def compute(self, plug, data):
if plug != CpHeIds.aIds:
return None
doingit = False
data.inputValue(CpHeIds.aForceComp)
inMeshHandle = data.inputValue(CpHeIds.aInputMesh)
inmesh = inMeshHandle.asMesh()
meshFn = om.MFnMesh(inmesh)
if self.dummycompute == True:
doingit = True
elif (self.numVertices != meshFn.numVertices
or self.numPolygons != meshFn.numPolygons
or self.numNormals != meshFn.numNormals):
doingit = True
if doingit == True:
heIds = cPhardEdgeIds(inmesh)
compObj = cPidsToComp(heIds, om.MFn.kMeshEdgeComponent)[1]
outIdsHandle = data.outputValue(CpHeIds.aIds)
outIdsHandle.setMObject(compObj)
outIdsHandle.setClean()
if self.dummycompute == True:
forceCompplug = self.attrToPlug(CpHeIds.aForceComp)
forceCompplug.setBool(False)
def initializePlugin(obj):
mplugin = om.MFnPlugin(obj, "Baidhir Hidair", "1.0")
nodeName = None
try:
nodeName = CpHeIds.kNodeName
mplugin.registerNode(CpHeIds.kNodeName, CpHeIds.kNodeId,
CpHeIds.creator, CpHeIds.initialize)
nodeName = CpCurveBevel.kNodeName
mplugin.registerNode(CpCurveBevel.kNodeName, CpCurveBevel.kNodeId,
CpCurveBevel.creator, CpCurveBevel.initialize)
nodeName = CpCurveToPoly.kNodeName
mplugin.registerNode(CpCurveToPoly.kNodeName, CpCurveToPoly.kNodeId,
CpCurveToPoly.creator, CpCurveToPoly.initialize)
except:
raise Exception('failed to register node: ' + nodeName)
def uninitializePlugin(obj):
mplugin = om.MFnPlugin(obj)
nodeName = None
try:
nodeName = CpHeIds.kNodeName
mplugin.deregisterNode(CpHeIds.kNodeId)
nodeName = CpCurveBevel.kNodeName
mplugin.deregisterNode(CpCurveBevel.kNodeId)
nodeName = CpCurveToPoly.kNodeName
mplugin.deregisterNode(CpCurveToPoly.kNodeId)
except:
raise Exception('failed to deregister node: ' + nodeName)

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 676 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 769 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,17 @@
from functools import wraps
import maya.cmds as cmds
def mayaUndoRun(func):
""" Puts the wrapped `func` into a single Maya Undo action, then
undoes it when the function enters the finally: block """
@wraps(func)
def _undofunc(*args, **kwargs):
try:
# start an undo chunk
cmds.undoInfo(openChunk=True)
return func(*args, **kwargs)
finally:
# after calling the func, end the undo chunk
cmds.undoInfo(closeChunk=True)
return _undofunc

View File

@ -0,0 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Empty Init file
from . import *

View File

@ -0,0 +1,2 @@
@echo off
del *.pyc

View File

@ -0,0 +1,40 @@
_____ _____ ______ _____ ______
/ ____| __ \| ____| /\ / ____| ____|_
| | | |__) | |__ / \ | (___ | |__ _| |_
| | | _ /| __| / /\ \ \___ \| __|_ _|
| |____| | \ \| |____ / ____ \ ____) | |____|_|
\_____|_| \_\______/_/ \_\_____/|______|
This version of CreasePlus is rewritten entirely in python cmds(maya) and API's.
thus there is no longer support for MayaLT. And the run-in lang is Python.
install :
extract, then just place icon and main folders in documents/maya/(maya_version)/scripts/
restart maya if opened.
#
# attach it as python script / runtime command to hotkey:
import maya.cmds as cmds
from CreasePlus import CreasePlusMain
CreasePlusMain.start()
if not cmds.pluginInfo("CreasePlusNodes", q=True, loaded=True):
cmds.loadPlugin("CreasePlusNodes.py")
# attach it as python script / runtime command to hotkey, for attribute iteration in context (optional):
from CreasePlus import CreasePlusMain
CreasePlusMain.crepcore.creasePlusLastCtx()
# attach it as python script / runtime command to hotkey, for edge soft/hard toggle (optional):
from CreasePlus import CreasePlusMain
CreasePlusMain.crepcore.creasePlusToggleEdgeSmooth()
# attach it as python script / runtime command to hotkey, for edge makeUV (optional):
from CreasePlus import CreasePlusMain
CreasePlusMain.crepcore.creasePlusMakeUv()
# commands / defs for bindings / scripts can be found in def_sheet file
# thank you

View File

@ -0,0 +1,201 @@
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.1 Studio
Copyright 2023, George Sladkovsky (Yehor Sladkovskyi)
All Rights Reserved
Autodesk Maya is a property of Autodesk, Inc.
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 AGREEMENTS TERMS, INCLUDING THE DISCLAIMERS OF WARRANTIES AND LIMITATIONS ON DAMAGES, USE AND TRANSFERABILITY. IF YOU DO NOT ACCEPT THIS AGREEMENTS 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 Agreements 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 clients 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 anyones 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 Epics 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 Agreements 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).

View File

@ -0,0 +1,35 @@
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.

View File

@ -0,0 +1,10 @@
from . import *
from . import fonts
from . import icons
from . import plugins
from . import utils
from . import core
from . import init
from . import main
from . import ui
from . import uv_editor

View File

@ -0,0 +1,69 @@
"""
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.1 Studio
Copyright 2023, George Sladkovsky (Yehor Sladkovskyi)
All Rights Reserved
Autodesk Maya is a property of Autodesk, Inc.
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
"""
import base64 as __B64
from datetime import datetime as __DT
import maya.cmds as __mc
from PySide2 import QtWidgets as __QTW
def __getMayaOS():
"""Get Maya version and parent OS"""
maya = str(__mc.about(api=1))[:4]
os = str(__mc.about(os=1))
return [int(maya), os]
MAYA_VER = __getMayaOS()[0]
OS = __getMayaOS()[1]
DEBUG = True
VERSION = __B64.b64decode(b"R1MgQ3VydmVUb29scyB2MS4zLjEKU3R1ZGlvIEVkaXRpb24=").decode('utf-8')
try:
YEAR = __DT.now().year
except BaseException:
YEAR = 2023
MAIN_WINDOW_NAME = 'GSCT_CurveTools'
MAIN_WINDOW_LABEL = 'GS CurveTools'
CURVE_CONTROL_NAME = 'GSCT_CurveControl'
CURVE_CONTROL_LABEL = 'GS Curve Control'
UV_EDITOR_NAME = 'GSCT_UVEditor'
UV_EDITOR_LABEL = 'GS Curve Tools UV Editor'
SCALE_FACTOR_UI = 'GSCT_ScaleFactorWindow'
UI_SCRIPT = '''
import gs_curvetools.utils.utils as ct_ut
ct_ut.logger.logger.info("Uninitializing Workspace Control")
maya.cmds.evalDeferred(ct_ut.stopUI)
'''
FIXED_POLICY = __QTW.QSizePolicy(__QTW.QSizePolicy.Fixed,
__QTW.QSizePolicy.Fixed)
PREFERRED_POLICY = __QTW.QSizePolicy(__QTW.QSizePolicy.Preferred,
__QTW.QSizePolicy.Preferred)
EXPANDING_POLICY = __QTW.QSizePolicy(__QTW.QSizePolicy.Expanding,
__QTW.QSizePolicy.Expanding)

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,161 @@
"""
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.1 Studio
Copyright 2023, George Sladkovsky (Yehor Sladkovskyi)
All Rights Reserved
Autodesk Maya is a property of Autodesk, Inc.
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
"""
import csv
import io
import os
from imp import reload
import maya.cmds as mc
import maya.mel as mel
from .constants import *
from .utils import utils
reload(utils)
def getHotkeys():
path = os.path.dirname(os.path.realpath(__file__)) + '/utils/hotkeys.csv'
with io.open(path, 'r', encoding='utf-8') as f:
reader = csv.reader(f, delimiter='|')
return list(reader)
class Init:
def __init__(self):
if mc.workspaceControl('GSCT_CurveTools', q=1, ex=1):
utils.stopUI(True)
if mc.workspaceControlState('GSCT_CurveTools', q=1, ex=1):
mc.workspaceControlState('GSCT_CurveTools', r=1)
utils.resetOptionVars()
# Init Runtime Command
self.delRuntimeCmds()
# Get hotkeys from csv file
hotkeys = getHotkeys()
for i in range(len(hotkeys)):
cmd = 'import gs_curvetools\ntry:\n ' + hotkeys[i][1] + '\nexcept:\n print("This function requires GS CurveTools.")'
commandName = 'GSCT_' + hotkeys[i][0].strip().replace(' ', '_')
if not mc.runTimeCommand(commandName, ex=1):
mc.runTimeCommand(
commandName,
c=cmd,
ann=hotkeys[i][2],
cat=hotkeys[i][3],
cl=hotkeys[i][4]
)
else:
mc.runTimeCommand(commandName, e=1, delete=1)
mc.runTimeCommand(
commandName,
c=cmd,
ann=hotkeys[i][2],
cat=hotkeys[i][3],
cl=hotkeys[i][4]
)
# Shelf
folder = utils.getFolder.icons()
shelf = mel.eval('$gsTempShelfTopLevel = $gShelfTopLevel')
ui = 'import gs_curvetools.main as ct_main\nct_main.main()'
uiIcon = folder + 'gsCurveToolsIcon_ui.png'
reset = 'import gs_curvetools.utils.utils as ct_ut\nfrom imp import reload\nreload(ct_ut)\nct_ut.resetUI()'
resetIcon = folder + 'gsCurveToolsIcon_reset.png'
stop = 'import gs_curvetools.utils.utils as ct_ut\nfrom imp import reload\nreload(ct_ut)\nct_ut.stopUI()'
stopIcon = folder + 'gsCurveToolsIcon_stop.png'
if MAYA_VER <= 2018:
uiIcon = folder + 'gsCurveToolsIcon_ui_legacy.png'
resetIcon = folder + 'gsCurveToolsIcon_reset_legacy.png'
stopIcon = folder + 'gsCurveToolsIcon_stop_legacy.png'
if mc.tabLayout(shelf, ex=1):
allTabs = mc.tabLayout(shelf, q=1, tl=1)
ex = False
for ele in allTabs:
if ele == 'GS':
ex = True
break
if not ex:
mel.eval('addNewShelfTab ("GS");')
mc.tabLayout(shelf, e=1, st='GS')
buttons = mc.shelfLayout(shelf + '|GS', q=1, ca=1)
if buttons:
for button in buttons:
if mc.shelfButton(button, q=1, l=1) == 'GS_CurveTools'\
or mc.shelfButton(button, q=1, l=1) == 'GS_CurveTools_Reset'\
or mc.shelfButton(button, q=1, l=1) == 'GS_CurveTools_Stop':
mc.deleteUI(button, ctl=1)
mel.eval('shelfTabRefresh;')
currentShelf = mc.tabLayout(shelf, q=1, st=1)
mc.setParent(currentShelf)
mc.shelfButton(style=mc.shelfLayout(currentShelf, query=1, style=1),
sourceType="python",
label="GS_CurveTools",
width=mc.shelfLayout(currentShelf, query=1, cellWidth=1),
command=ui,
image1=uiIcon,
height=mc.shelfLayout(currentShelf, query=1, cellHeight=1),
annotation="Toggle GS CurveTools Window")
mc.shelfButton(style=mc.shelfLayout(currentShelf, query=1, style=1),
sourceType="python",
label="GS_CurveTools_Reset",
width=mc.shelfLayout(currentShelf, query=1, cellWidth=1),
command=reset,
image1=resetIcon,
height=mc.shelfLayout(currentShelf, query=1, cellHeight=1),
annotation="GS CurveTools Reset to Defaults")
mc.shelfButton(style=mc.shelfLayout(currentShelf, query=1, style=1),
sourceType="python",
label="GS_CurveTools_Stop",
width=mc.shelfLayout(currentShelf, query=1, cellWidth=1),
command=stop,
image1=stopIcon,
height=mc.shelfLayout(currentShelf, query=1, cellHeight=1),
annotation="GS CurveTools Stop Scripts")
msg = '<div style="text-align: center;">GS CurveTools Initialized Successfully!\n\
Welcome!\n</div>\
<div style="text-align: left;"><img src="%s" width="36" height="32"/> - Launches the menu.\n\
<span style="color: #5285a6;"><img src="%s" width="36" height="32"/></span> - Resets the menu to default values.\n\
<span style="color: #5285a6;"><img src="%s" width="36" height="32"/></span> - Closes the menu and stops all background scripts.\n</div>\
<div style="text-align: center;">To close this message simply click on it.\n\
Have fun! :)</div>\
<div>\n</div>\
<div>\n</div>\
<div>\n</div>\
<div>\n</div>\
<div>\n</div>' % (uiIcon, resetIcon, stopIcon)
mc.inViewMessage(msg=msg, a=1, pos='topCenter', fade=True, ck=1, fst=60000)
def __repr__(self):
return 'GS CurveTools Initialized'
def delRuntimeCmds(self):
commands = mc.runTimeCommand(q=1, ca=1)
for command in commands:
if 'GSCT_' in command:
mc.runTimeCommand(command, e=1, delete=1)

View File

@ -0,0 +1,132 @@
[15:47:11,248 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[15:47:11,249 | DEBUG | main | 54]: <module>() : 25/10/2024, 15:47:11
[15:47:19,392 | INFO | utils | 447]: stopUI() : Deleting window state for GSCT_CurveTools
[15:47:19,393 | INFO | utils | 447]: stopUI() : Deleting window state for GSCT_CustomLayerColorsWindow
[15:47:19,393 | INFO | utils | 447]: stopUI() : Deleting window state for GSCT_CardToCurvePopOut
[15:52:10,648 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[15:52:10,648 | DEBUG | main | 54]: <module>() : 25/10/2024, 15:52:10
[15:55:45,945 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[15:55:45,946 | DEBUG | main | 54]: <module>() : 25/10/2024, 15:55:45
[15:55:49,786 | INFO | main | 78]: checkScriptJobs() : scriptJobs created for "main" UI!
[15:55:52,238 | INFO | utils | 447]: stopUI() : Deleting window state for GSCT_CurveTools
[15:55:52,994 | INFO | main | 78]: checkScriptJobs() : scriptJobs created for "main" UI!
[15:55:56,505 | INFO | main | 78]: checkScriptJobs() : scriptJobs created for "main" UI!
[15:56:50,105 | ERROR | utils | 109]: warningInView() : Wrong Selection: extractedGeo_1 is skipped
[15:56:50,109 | DEBUG | utils | 111]: warningInView() : File "D:\P4/Art/Tools/MetaBox\Scripts\Modeling\Edit\gs_curvetools\core.py", line 5659, in curveAddToLayer
[15:56:54,291 | ERROR | utils | 109]: warningInView() : Select at least one Curve
[15:56:54,291 | DEBUG | utils | 111]: warningInView() : File "D:\P4/Art/Tools/MetaBox\Scripts\Modeling\Edit\gs_curvetools\core.py", line 5341, in extractSelectedCurves
[15:56:56,160 | ERROR | utils | 109]: warningInView() : Wrong Selection: extractedGeo_2 is skipped
[15:56:56,161 | DEBUG | utils | 111]: warningInView() : File "D:\P4/Art/Tools/MetaBox\Scripts\Modeling\Edit\gs_curvetools\core.py", line 5659, in curveAddToLayer
[15:57:13,014 | INFO | core | 4036]: saveOptions() : Saving options
[15:57:13,979 | INFO | core | 4036]: saveOptions() : Saving options
[15:59:57,188 | INFO | utils | 447]: stopUI() : Deleting window state for GSCT_CurveTools
[15:59:57,964 | INFO | main | 78]: checkScriptJobs() : scriptJobs created for "main" UI!
[16:00:00,991 | INFO | main | 78]: checkScriptJobs() : scriptJobs created for "main" UI!
[16:02:50,064 | ERROR | utils | 109]: warningInView() : Layer is Empty
[16:02:50,067 | DEBUG | utils | 111]: warningInView() : File "D:\P4/Art/Tools/MetaBox\Scripts\Modeling\Edit\gs_curvetools\core.py", line 5599, in curveGeometryEditToggle
[16:03:31,403 | ERROR | utils | 109]: warningInView() : Select at least two Curves
[16:03:31,405 | DEBUG | utils | 111]: warningInView() : File "D:\P4/Art/Tools/MetaBox\Scripts\Modeling\Edit\gs_curvetools\core.py", line 358, in transferUV
[16:06:58,025 | ERROR | utils | 109]: warningInView() : Layer is Empty
[16:06:58,027 | DEBUG | utils | 111]: warningInView() : File "D:\P4/Art/Tools/MetaBox\Scripts\Modeling\Edit\gs_curvetools\core.py", line 5582, in curveLayerSelectObj
[16:07:00,139 | ERROR | utils | 109]: warningInView() : Layer is Empty
[16:07:00,142 | DEBUG | utils | 111]: warningInView() : File "D:\P4/Art/Tools/MetaBox\Scripts\Modeling\Edit\gs_curvetools\core.py", line 5582, in curveLayerSelectObj
[16:07:08,822 | ERROR | utils | 109]: warningInView() : Layer is Empty
[16:07:08,824 | DEBUG | utils | 111]: warningInView() : File "D:\P4/Art/Tools/MetaBox\Scripts\Modeling\Edit\gs_curvetools\core.py", line 5582, in curveLayerSelectObj
[16:07:23,048 | ERROR | utils | 109]: warningInView() : Layer is Empty
[16:07:23,050 | DEBUG | utils | 111]: warningInView() : File "D:\P4/Art/Tools/MetaBox\Scripts\Modeling\Edit\gs_curvetools\core.py", line 5582, in curveLayerSelectObj
[16:28:53,631 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[16:28:53,631 | DEBUG | main | 54]: <module>() : 25/10/2024, 16:28:53
[16:30:21,253 | INFO | main | 78]: checkScriptJobs() : scriptJobs created for "main" UI!
[16:30:22,539 | INFO | utils | 447]: stopUI() : Deleting window state for GSCT_CurveTools
[16:30:23,312 | INFO | main | 78]: checkScriptJobs() : scriptJobs created for "main" UI!
[16:30:25,548 | INFO | utils | 447]: stopUI() : Deleting window state for GSCT_CurveTools
[16:30:26,317 | INFO | main | 78]: checkScriptJobs() : scriptJobs created for "main" UI!
[16:31:38,714 | INFO | core | 4036]: saveOptions() : Saving options
[16:35:38,164 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[16:35:38,165 | DEBUG | main | 54]: <module>() : 25/10/2024, 16:35:38
[16:35:57,855 | INFO | main | 78]: checkScriptJobs() : scriptJobs created for "main" UI!
[16:37:07,264 | ERROR | utils | 109]: warningInView() : Select at least two curves
[16:37:07,268 | DEBUG | utils | 111]: warningInView() : File "D:\P4/Art/Tools/MetaBox\Scripts\Modeling\Edit\gs_curvetools\core.py", line 774, in fill
[16:37:09,026 | ERROR | utils | 109]: warningInView() : Select one curve and one geometry or Select one target curve and any number of curveCards/Tubes
[16:37:09,027 | DEBUG | utils | 111]: warningInView() : File "D:\P4/Art/Tools/MetaBox\Scripts\Modeling\Edit\gs_curvetools\core.py", line 1356, in bind
[16:37:10,565 | ERROR | utils | 109]: warningInView() : Select at least one compatible curve (Bound/Warp curves or geometry)
[16:37:10,565 | DEBUG | utils | 111]: warningInView() : File "D:\P4/Art/Tools/MetaBox\Scripts\Modeling\Edit\gs_curvetools\core.py", line 1614, in unbind
[16:37:11,893 | ERROR | utils | 217]: wrapper() : 'NoneType' object is not iterable
Traceback (most recent call last):
File "D:\P4/Art/Tools/MetaBox\Scripts\Modeling\Edit\gs_curvetools\utils\utils.py", line 208, in wrapper
rv = func(*args, **kwargs)
File "D:\P4/Art/Tools/MetaBox\Scripts\Modeling\Edit\gs_curvetools\core.py", line 5454, in extractAllCurves
parentGrps = set(mc.listRelatives(allGeo, p=1))
TypeError: 'NoneType' object is not iterable
[16:37:22,299 | INFO | utils | 117]: printInView() : Regrouped 0 curve(s)
[16:37:22,497 | INFO | utils | 117]: printInView() : Regrouped 0 curve(s)
[16:55:35,765 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[16:55:35,766 | DEBUG | main | 54]: <module>() : 25/10/2024, 16:55:35
[16:59:15,547 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[16:59:15,548 | DEBUG | main | 54]: <module>() : 25/10/2024, 16:59:15
[17:02:46,653 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[17:02:46,653 | DEBUG | main | 54]: <module>() : 25/10/2024, 17:02:46
[17:03:34,706 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[17:03:34,707 | DEBUG | main | 54]: <module>() : 25/10/2024, 17:03:34
[17:05:26,126 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[17:05:26,126 | DEBUG | main | 54]: <module>() : 25/10/2024, 17:05:26
[17:08:00,366 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[17:08:00,366 | DEBUG | main | 54]: <module>() : 25/10/2024, 17:08:00
[17:11:21,232 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[17:11:21,232 | DEBUG | main | 54]: <module>() : 25/10/2024, 17:11:21
[17:12:35,318 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[17:12:35,318 | DEBUG | main | 54]: <module>() : 25/10/2024, 17:12:35
[17:27:17,603 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[17:27:17,603 | DEBUG | main | 54]: <module>() : 25/10/2024, 17:27:17
[12:28:49,045 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[12:28:49,046 | DEBUG | main | 54]: <module>() : 29/10/2024, 12:28:49
[12:31:52,237 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[12:31:52,237 | DEBUG | main | 54]: <module>() : 29/10/2024, 12:31:52
[12:32:55,923 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[12:32:55,924 | DEBUG | main | 54]: <module>() : 29/10/2024, 12:32:55
[12:43:17,785 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[12:43:17,786 | DEBUG | main | 54]: <module>() : 29/10/2024, 12:43:17
[12:56:52,636 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[12:56:52,637 | DEBUG | main | 54]: <module>() : 29/10/2024, 12:56:52
[13:12:57,427 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[13:12:57,427 | DEBUG | main | 54]: <module>() : 29/10/2024, 13:12:57
[13:26:02,083 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[13:26:02,083 | DEBUG | main | 54]: <module>() : 29/10/2024, 13:26:02
[13:31:31,387 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[13:31:31,387 | DEBUG | main | 54]: <module>() : 29/10/2024, 13:31:31
[13:33:05,154 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[13:33:05,155 | DEBUG | main | 54]: <module>() : 29/10/2024, 13:33:05
[13:37:03,867 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[13:37:03,868 | DEBUG | main | 54]: <module>() : 29/10/2024, 13:37:03
[13:38:50,115 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[13:38:50,116 | DEBUG | main | 54]: <module>() : 29/10/2024, 13:38:50
[13:41:58,626 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[13:41:58,626 | DEBUG | main | 54]: <module>() : 29/10/2024, 13:41:58
[13:52:12,721 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[13:52:12,721 | DEBUG | main | 54]: <module>() : 29/10/2024, 13:52:12
[13:56:05,229 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[13:56:05,230 | DEBUG | main | 54]: <module>() : 29/10/2024, 13:56:05
[13:58:23,170 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[13:58:23,170 | DEBUG | main | 54]: <module>() : 29/10/2024, 13:58:23
[20:53:31,681 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[20:53:31,681 | DEBUG | main | 54]: <module>() : 02/01/2025, 20:53:31
[20:55:41,532 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[20:55:41,532 | DEBUG | main | 54]: <module>() : 02/01/2025, 20:55:41
[20:57:14,643 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[20:57:14,643 | DEBUG | main | 54]: <module>() : 02/01/2025, 20:57:14
[21:02:25,417 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[21:02:25,417 | DEBUG | main | 54]: <module>() : 02/01/2025, 21:02:25
[21:03:03,240 | INFO | main | 78]: checkScriptJobs() : scriptJobs created for "main" UI!
[09:58:36,078 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[09:58:36,078 | DEBUG | main | 54]: <module>() : 10/01/2025, 09:58:36
[09:59:04,960 | INFO | main | 78]: checkScriptJobs() : scriptJobs created for "main" UI!
[10:29:44,404 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[10:29:44,404 | DEBUG | main | 54]: <module>() : 10/01/2025, 10:29:44
[10:31:50,217 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[10:31:50,218 | DEBUG | main | 54]: <module>() : 10/01/2025, 10:31:50
[21:31:51,516 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[21:31:51,516 | DEBUG | main | 54]: <module>() : 10/01/2025, 21:31:51
[18:19:43,116 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[18:19:43,116 | DEBUG | main | 54]: <module>() : 11/01/2025, 18:19:43
[01:08:10,709 | DEBUG | main | 53]: <module>() : -----------------Starting Log Session-----------------
[01:08:10,728 | DEBUG | main | 54]: <module>() : 14/01/2025, 01:08:10

View File

@ -0,0 +1,878 @@
"""
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.1 Studio
Copyright 2023, George Sladkovsky (Yehor Sladkovskyi)
All Rights Reserved
Autodesk Maya is a property of Autodesk, Inc.
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
"""
import os
import sys
from datetime import datetime
from functools import partial as pa
from imp import reload
import maya.cmds as mc
from PySide2 import QtCore, QtGui, QtWidgets
from . import core, ui
from . import constants
from .constants import *
from .utils import style, tooltips, utils, wrap
from .utils.utils import deferred, deferredLp, noUndo, undo
from .utils.wrap import WIDGETS
reload(core)
reload(utils)
reload(style)
reload(wrap)
reload(ui)
reload(tooltips)
reload(constants)
# Loggers
MESSAGE = utils.logger
LOGGER = utils.logger.logger
LOGGER.debug('-----------------Starting Log Session-----------------')
LOGGER.debug(' {} '.format(datetime.now().strftime("%d/%m/%Y, %H:%M:%S")))
### Interface Script Jobs ###
def checkScriptJobs(controlName):
""" Checks if script jobs exist and adds them if necessary """
jobNumbers = list()
workspaceExists = False
UI = 'control'
if mc.workspaceControl(controlName, exists=1):
workspaceExists = True
if controlName == MAIN_WINDOW_NAME:
UI = 'main'
elif controlName == UV_EDITOR_NAME:
UI = 'uv_editor'
elif controlName == SCALE_FACTOR_UI:
UI = 'scale_factor_ui'
jobList = mc.scriptJob(lj=1)
for job in jobList:
if controlName in job:
jobNumbers.append(job.split()[0][0:-1])
if len(jobNumbers) == 0 and workspaceExists:
scriptJobsInit(UI)
LOGGER.info('scriptJobs created for "%s" UI!' % UI)
elif len(jobNumbers) > 0 and not workspaceExists:
LOGGER.info('scriptJobs deleted!')
for number in jobNumbers:
mc.scriptJob(k=number)
def scriptJobsInit(uiName):
if uiName == 'main':
mc.scriptJob(per=1, p=MAIN_WINDOW_NAME, event=['SceneOpened', pa(checkScriptJobs, MAIN_WINDOW_NAME)])
mc.scriptJob(per=1, p=MAIN_WINDOW_NAME, event=['SceneOpened', pa(deferred(core.updateMainUI), True)])
mc.scriptJob(per=1, p=MAIN_WINDOW_NAME, event=['SceneOpened', deferredLp(core.onSceneOpenedUpdateLayerCount)])
mc.scriptJob(per=1, p=MAIN_WINDOW_NAME, event=['SelectionChanged', core.updateMainUI])
mc.scriptJob(per=1, p=MAIN_WINDOW_NAME, event=['Undo', core.updateMainUI])
elif uiName == 'control':
mc.scriptJob(per=1, p=CURVE_CONTROL_NAME, event=['SelectionChanged', core.curveControlUI.updateUI])
mc.scriptJob(per=1, p=CURVE_CONTROL_NAME, event=['Undo', deferred(core.curveControlUI.updateUI)])
elif uiName == 'uv_editor':
def updateUVs():
if (mc.workspaceControl(UV_EDITOR_NAME, q=1, ex=1) and
mc.workspaceControl(UV_EDITOR_NAME, q=1, r=1)):
ui.uveditor.updateEditor()
mc.scriptJob(per=1, p=UV_EDITOR_NAME, event=['SelectionChanged', updateUVs])
mc.scriptJob(per=1, p=UV_EDITOR_NAME, event=['Undo', updateUVs])
def main():
utils.checkNativePlugins(['curveWarp'], MAIN_WINDOW_NAME)
if mc.workspaceControl(MAIN_WINDOW_NAME, q=1, ex=1):
if MAYA_VER >= 2018:
if not mc.workspaceControl(MAIN_WINDOW_NAME, q=1, vis=1):
mc.workspaceControl(MAIN_WINDOW_NAME, e=1, rs=1)
core.updateMainUI()
else:
mc.workspaceControl(MAIN_WINDOW_NAME, e=1, vis=0)
else:
mc.workspaceControl(MAIN_WINDOW_NAME, e=1, fl=1)
mc.deleteUI(MAIN_WINDOW_NAME)
else:
CurveToolsUI()
mc.workspaceControl(MAIN_WINDOW_NAME, e=1, ui=UI_SCRIPT)
try:
core.toggleColor.checkColorStorageNode()
checkScriptJobs(MAIN_WINDOW_NAME)
utils.deferred(core.onSceneOpenedUpdateLayerCount)() # Also updates the UI
except Exception as e:
LOGGER.exception(e)
# Main UI
class CurveToolsUI(QtWidgets.QWidget):
def __init__(self):
WIDGETS.clear()
# Maya Native Workspace
parent = ui.mayaWorkspaceControl(name=MAIN_WINDOW_NAME, label=MAIN_WINDOW_LABEL)
# Resolve Fonts
fontDatabase = QtGui.QFontDatabase()
fontDatabase.removeAllApplicationFonts()
fonts = os.listdir(utils.getFolder.fonts())
for font in fonts:
fontDatabase.addApplicationFont(utils.getFolder.fonts() + font)
# Dockable Workspace Connection
super(CurveToolsUI, self).__init__(parent)
self.ui()
parent.layout().addWidget(self)
self.scrollWidget.setFocus()
checkScriptJobs(MAIN_WINDOW_NAME)
def ui(self):
# Layout
mainLayout = QtWidgets.QVBoxLayout(self)
mainLayout.setContentsMargins(*style.scale([2, 0, 2, 0]))
self.scrollWidget = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout(self.scrollWidget)
scrollArea = QtWidgets.QScrollArea()
scrollArea.setWidget(self.scrollWidget)
mainLayout.addWidget(scrollArea)
# Layout Settings
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(style.scale(2))
layout.setMargin(0)
layout.setAlignment(QtCore.Qt.AlignTop)
scrollArea.setFrameShape(QtWidgets.QFrame.NoFrame)
scrollArea.setWidgetResizable(True)
# Menu buttons
menuBarWidget = QtWidgets.QWidget()
menuBarLayout = QtWidgets.QHBoxLayout(menuBarWidget)
menuBarLayout.setContentsMargins(0, 0, 0, 0)
menuBarLayout.setAlignment(QtCore.Qt.AlignCenter)
menuBar = QtWidgets.QMenuBar()
menuBar.setSizePolicy(PREFERRED_POLICY)
menuBarLayout.addWidget(menuBar)
layout.addWidget(menuBarWidget)
# Options Menu
with wrap.Menu('Options', menuBar) as menu:
menu.triggered.connect(core.saveOptions)
menu.addSection('Import / Export')
importCurves = wrap.MenuItem('importCurves', 'Import Curves', menu)
importCurves.triggered.connect(noUndo(core.importExport.importCurves))
exportCurves = wrap.MenuItem('exportCurves', 'Export Curves', menu)
exportCurves.triggered.connect(core.importExport.exportCurves)
menu.addSection('Global Modifiers')
changeScaleFactor = wrap.MenuItem('changeScaleFactor', 'Change Scale Factor', menu)
changeScaleFactor.triggered.connect(ui.scaleFactorWindow)
globalCurveThickness = wrap.MenuItem('globalCurveThickness', 'Global Curve Thickness', menu)
globalCurveThickness.triggered.connect(ui.curveThicknessWindow)
menu.addSection('Viewport Commands')
setAOSettings = wrap.MenuItem('setAOSettings', 'Set AO Settings', menu)
setAOSettings.triggered.connect(core.setAOSettings)
with wrap.Menu('Transparency Settings', menu) as transparencySettingsMenu:
simpleTransparency = wrap.MenuItem(
'setSimpleTransparency',
'Simple Transparency (Fast, Inncaurate)',
transparencySettingsMenu
)
simpleTransparency.triggered.connect(noUndo(pa(core.setTransparencySettings, 0)))
objectSortingTransparency = wrap.MenuItem(
'setObjectSortingTransparency',
'Object Sorting Transparency (Average)',
transparencySettingsMenu
)
objectSortingTransparency.triggered.connect(noUndo(pa(core.setTransparencySettings, 1)))
setDepthTransparency = wrap.MenuItem(
'setDepthTransparency',
'Depth Transparency (Accurate, Slow)',
transparencySettingsMenu
)
setDepthTransparency.triggered.connect(noUndo(pa(core.setTransparencySettings, 2)))
menu.addSection('Convert Curves')
with wrap.Menu('Convert Curves', menu) as convertCurvesSubmenu:
convertToWarpCard = wrap.MenuItem('convertToWarpCard', "Convert to Warp Card", convertCurvesSubmenu)
convertToWarpCard.triggered.connect(pa(undo(core.convertSelectionTo), 0))
convertToWarpTube = wrap.MenuItem('convertToWarpTube', "Convert to Warp Tube", convertCurvesSubmenu)
convertToWarpTube.triggered.connect(pa(undo(core.convertSelectionTo), 1))
convertToExtrudeCard = wrap.MenuItem('convertToExtrudeCard', "Convert to Extrude Card", convertCurvesSubmenu)
convertToExtrudeCard.triggered.connect(pa(undo(core.convertSelectionTo), 2))
convertToExtrudeTube = wrap.MenuItem('convertToExtrudeTube', "Convert to Extrude Tube", convertCurvesSubmenu)
convertToExtrudeTube.triggered.connect(pa(undo(core.convertSelectionTo), 3))
menu.addSection('General Options')
wrap.MenuItem('keepCurveAttributes', 'Keep Curve Attributes', menu, True, core.getOption('keepCurveAttributes'))
wrap.MenuItem('populateBlendAttributes', 'Add Cards/Tubes Blend Attributes',
menu, True, core.getOption('populateBlendAttributes'))
wrap.MenuItem('convertInstances', 'Auto Convert Instances', menu, True, core.getOption('convertInstances'))
wrap.MenuItem('useAutoRefineOnNewCurves', 'Use Auto-Refine on New Curves',
menu, True, core.getOption('useAutoRefineOnNewCurves'))
wrap.MenuItem('flipUVsAfterMirror', 'Flip UVs After Mirror',
menu, True, core.getOption('flipUVsAfterMirror'))
enableTooltipsMenu = wrap.MenuItem('enableTooltips', 'Enable Tooltips', menu, True, core.getOption('enableTooltips'))
enableTooltipsMenu.triggered.connect(self.toggleTooltips)
menu.addSection('Color Options')
syncColor = wrap.MenuItem('syncCurveColor', 'Sync Curve Color to Layer Color', menu, True, core.getOption('syncCurveColor'))
syncColor.triggered.connect(core.toggleColor.syncCurveColors)
wrap.MenuItem('colorizedRegroup', 'Colorize Regrouped Layers', menu, True, core.getOption('colorizedRegroup'))
colorOnlyDiffuse = wrap.MenuItem('colorOnlyDiffuse', 'Color Only Affects Diffuse',
menu, True, core.getOption('colorOnlyDiffuse'))
colorOnlyDiffuse.triggered.connect(core.toggleColor.updateColorOptions)
checkerPattern = wrap.MenuItem('checkerPattern', 'Checker Pattern for Color Mode', menu, True, core.getOption('checkerPattern'))
checkerPattern.triggered.connect(core.toggleColor.updateColorOptions)
menu.addSection('Bind Options')
wrap.MenuItem('boundCurvesFollowParent', 'Bound Curves Follow Parent', menu, True, core.getOption('boundCurvesFollowParent'))
wrap.MenuItem('massBindOption', 'Bind to All Available Empty Curves', menu, True, core.getOption('massBindOption'))
wrap.MenuItem('bindDuplicatesCurves', 'Duplicate Curves Before Bind', menu, True, core.getOption('bindDuplicatesCurves'))
wrap.MenuItem('bindFlipUVs', 'Flip UVs before Bind', menu, True, core.getOption('bindFlipUVs'))
menu.addSection('Layer Options')
wrap.MenuItem('ignoreLastLayer', 'Ignore Last Layer', menu, True, core.getOption('ignoreLastLayer'))
wrap.MenuItem('syncOutlinerLayerVis', 'Sync Outliner/Layer Visibility', menu, True, core.getOption('syncOutlinerLayerVis'))
wrap.MenuItem('replacingCurveLayerSelection', 'Replacing Curve Layer Selection', menu, True,
core.getOption('replacingCurveLayerSelection'))
onlyNumbersInLayers = wrap.MenuItem('layerNumbersOnly', 'Use Only Numbers in Layers', menu, True,
core.getOption('layerNumbersOnly'))
onlyNumbersInLayers.triggered.connect(core.changeLayersToNumbers)
onlyNumbersInLayers.triggered.connect(self.updateLayerList)
with wrap.Menu('Number of Active Layers', menu) as layerNumberMenu:
with wrap.ActionGroup('layerRowsActionGroup', layerNumberMenu) as actionGroup:
wrap.MenuItem('2layerRows', '20 Layers', layerNumberMenu, True,
core.getOption('2layerRows'), collection=actionGroup)
wrap.MenuItem('3layerRows', '30 Layers', layerNumberMenu, True,
core.getOption('3layerRows'), collection=actionGroup)
wrap.MenuItem('4layerRows', '40 Layers', layerNumberMenu, True,
core.getOption('4layerRows'), collection=actionGroup)
wrap.MenuItem('6layerRows', '60 Layers', layerNumberMenu, True,
core.getOption('6layerRows'), collection=actionGroup)
wrap.MenuItem('8layerRows', '80 Layers', layerNumberMenu, True,
core.getOption('8layerRows'), collection=actionGroup)
actionGroup.triggered.connect(core.updateMainUI)
menu.addSection('Layer Collection Options')
wrap.MenuItem('ignoreTemplateCollections', 'Ignore "Template" Collection Names', menu, True, core.getOption('ignoreTemplateCollections'))
wrap.MenuItem('groupTemplateCollections', 'Group "Template" Collections Together', menu, True, core.getOption('groupTemplateCollections'))
layerCollectionsToggle = wrap.MenuItem('showLayerCollectionsMenu', 'Show Layer Collections Menu',
menu, True, core.getOption('showLayerCollectionsMenu'))
layerCollectionsToggle.triggered.connect(core.layerCollections.toggleLayerCollectionsWidget)
wrap.MenuItem('importIntoANewCollection', 'Import Into a New Collection',
menu, True, core.getOption('importIntoANewCollection'))
menu.addSection('Other Options')
with wrap.Menu('Other Options', menu) as otherOptionsMenu:
otherOptionsMenu.addSection('Utility Commands')
convertToNewLayerSystem = wrap.MenuItem('convertToNewLayerSystem', 'Convert to New Layer System', otherOptionsMenu)
convertToNewLayerSystem.triggered.connect(utils.convertToNewLayerSystem)
updateLayers = wrap.MenuItem('updateLayers', 'Update Layers', otherOptionsMenu)
updateLayers.triggered.connect(undo(core.deleteUnusedLayers)) # TODO: Check if undo breaks anything
resetToDefaults = wrap.MenuItem('resetToDefaults', 'Reset to Defaults', otherOptionsMenu)
resetToDefaults.triggered.connect(utils.resetUI)
otherOptionsMenu.addSection('Fixes')
maya2020UVFix = wrap.MenuItem('maya2020UVFix', 'Fix Maya 2020-2022 UV Bug', otherOptionsMenu)
maya2020UVFix.triggered.connect(undo(utils.fixMaya2020UVs))
fixBrokenGraphs = wrap.MenuItem('mayaFixBrokenGraphs', 'Fix Broken Graphs', otherOptionsMenu)
fixBrokenGraphs.triggered.connect(undo(utils.fixBrokenGraphs))
convertBezierToNurbs = wrap.MenuItem('convertBezierToNurbs', 'Convert Selected Bezier to NURBS', otherOptionsMenu)
convertBezierToNurbs.triggered.connect(undo(utils.convertBezierToNurbs))
maya2020TwistFix = wrap.MenuItem('maya2020TwistAttribute', 'Fix Maya 2020.4 Twist Attribute', otherOptionsMenu)
maya2020TwistFix.triggered.connect(undo(utils.fixMaya2020Twist))
maya2020UnbindFix = wrap.MenuItem('maya2020UnbindFix', 'Fix Maya 2020.4 Unbind Function', otherOptionsMenu)
maya2020UnbindFix.triggered.connect(undo(utils.fixMaya2020Unbind))
deleteAllAnimationKeys = wrap.MenuItem('deleteAllAnimationKeys', 'Delete All Animation Keys', otherOptionsMenu)
deleteAllAnimationKeys.triggered.connect(undo(utils.deleteKeysOnAllObjects))
# Help Menu
with wrap.Menu('Help', menuBar) as menu:
openLogFile = wrap.MenuItem('openLogFile', 'Open Log File', menu)
openLogFile.triggered.connect(utils.logger.openLogFile)
openOnlineDocumentation = wrap.MenuItem('openOnlineDocumentation', 'Open Online Documentation', menu)
openOnlineDocumentation.triggered.connect(utils.openDocs)
usefulLinks = wrap.MenuItem('usefulLinks', 'Useful Links and Contacts', menu)
usefulLinks.triggered.connect(ui.about.socialWindow)
# About Menu
with wrap.Menu('About', menuBar) as menu:
aboutAction = wrap.MenuItem('gsAbout', 'About', menu)
aboutAction.triggered.connect(ui.about.aboutWindow)
menu.addSeparator()
menu.addAction('Made by George Sladkovsky (%s)' % YEAR).setEnabled(False)
# MAIN UI
layout.addWidget(wrap.separator())
# Extrude Warp Switch
with wrap.Row(layout) as row:
# Switch Group
extrudeWarpSwitchGroup = QtWidgets.QButtonGroup(layout)
WIDGETS['gsExtrudeWarpSwitchGroup'] = extrudeWarpSwitchGroup
extrudeWarpSwitchGroup.buttonToggled.connect(self.extrudeWarpToggle)
extrudeWarpSwitchGroup.buttonClicked.connect(core.saveOptions)
warpSwitch = wrap.Button(row.layout(), 'warpSwitch')
warpSwitch.setLabel('Warp', lineHeight=100)
warpSwitch.setButtonStyle('small')
warpSwitch.setCheckable(True)
extrudeSwitch = wrap.Button(row.layout(), 'extrudeSwitch')
extrudeSwitch.setLabel('Extrude', lineHeight=100)
extrudeSwitch.setButtonStyle('small')
extrudeSwitch.setCheckable(True)
extrudeWarpSwitchGroup.addButton(warpSwitch, 0)
extrudeWarpSwitchGroup.addButton(extrudeSwitch, 1)
# New Card and New Tube
with wrap.Row(layout) as row:
newCard = wrap.Button(row.layout(), 'newCard')
newCard.setLabel('New Card')
newCard.clicked.connect(pa(undo(core.create.new), 0))
newTube = wrap.Button(row.layout(), 'newTube')
newTube.setLabel('New Tube')
newTube.clicked.connect(pa(undo(core.create.new), 1))
# Convert Curve to Card and Convert Curve to Tube
with wrap.Row(layout) as row:
curveCard = wrap.Button(row.layout(), 'curveCard')
curveCard.setLabel('Curve Card')
curveCard.clicked.connect(pa(undo(core.create.multiple), 0))
curveTube = wrap.Button(row.layout(), 'curveTube')
curveTube.setLabel('Curve Tube')
curveTube.clicked.connect(pa(undo(core.create.multiple), 1))
# Bind and Unbind
with wrap.Row(layout) as row:
bind = wrap.Button(row.layout(), 'gsBind')
bind.setLabel('Bind')
bind.setIcon('mod-top')
bind.clicked.connect(undo(core.create.bind))
unbind = wrap.Button(row.layout(), 'gsUnbind')
unbind.setLabel('Unbind')
unbind.clicked.connect(undo(core.create.unbind))
layout.addWidget(wrap.separator())
# Add Curve and Add Tube
with wrap.Row(layout) as row:
addCards = wrap.Button(row.layout(), 'addCards')
addCards.setLabel('Add Card')
addCards.setIcon('mod-top')
addCards.clicked.connect(pa(undo(core.create.populate), 0))
addTubes = wrap.Button(row.layout(), 'addTubes')
addTubes.setLabel('Add Tube')
addTubes.setIcon('mod-top')
addTubes.clicked.connect(pa(undo(core.create.populate), 1))
# Fill and Multiply
with wrap.Row(layout) as row:
fill = wrap.Button(row.layout(), 'gsFill')
fill.setLabel('Fill')
fill.setIcon('mod-top')
fill.clicked.connect(pa(undo(core.create.fill)))
subdivide = wrap.Button(row.layout(), 'gsSubdivide')
subdivide.setLabel('Subdivide')
subdivide.setIcon('mod-top')
subdivide.clicked.connect(pa(undo(core.subdivideCurve)))
# Add Slider
with wrap.Row(layout) as row:
m_addCardsSlider = mc.intSliderGrp('gsCurvesSlider', l='Add', f=1, adj=3,
cw=[(1, 20), (2, 25), (3, 1)],
min=1, max=10, v=3)
addCardsSlider = wrap.mayaSlider(m_addCardsSlider, layout=row.layout())
WIDGETS['gsCurvesSlider'] = addCardsSlider
addCardsSlider.setContentsMargins(0, 0, 0, 0)
addCardsSlider.children()[2].setFocusPolicy(QtCore.Qt.NoFocus)
layout.addWidget(wrap.separator())
# Edge to Curve
with wrap.Row(layout) as row:
edgeToCurve = wrap.Button(row.layout(), 'gsEdgeToCurve')
edgeToCurve.setLabel('Edge To Curve')
edgeToCurve.clicked.connect(pa(undo(core.edgeToCurve)))
cardToCurve = wrap.Button(row.layout(), 'gsCardToCurve')
cardToCurve.setLabel('Card To Curve')
cardToCurve.clicked.connect(ui.cardToCurveWindow.openUI)
layout.addWidget(wrap.separator())
# Layer Collections Menu
with wrap.Row(layout, objName='LayerCollectionsLayout', spacing=1) as layerCollectionsLayout:
layerComboBox = wrap.LayerCollectionWidget('layerCollectionsComboBox')
layerComboBox.setStyleSheet(style.smallComboBox)
layerComboBox.currentIndexChanged.connect(lambda *_: deferred(core.updateMainUI)())
layerComboBox.setSizeAdjustPolicy(layerComboBox.AdjustToMinimumContentsLength)
layerComboBox.setDuplicatesEnabled(False)
layerComboBox.setMinimumWidth(0)
layerComboBox.setFixedHeight(style.scale(16))
mc.popupMenu(mm=1, p=layerComboBox.objectName())
mc.menuItem(rp='N', l='Clear', c=lambda *_: undo(core.layerCollections.clear)())
mc.menuItem(rp='E', l='Copy', c=lambda *_: noUndo(core.layerCollections.copy)())
mc.menuItem(rp='NE', l='Move Up', c=lambda *_: noUndo(core.layerCollections.moveUp)())
mc.menuItem(rp='NW', l='Merge Up', c=lambda *_: noUndo(core.layerCollections.mergeUp)())
mc.menuItem(rp='W', l='Paste', c=lambda *_: undo(core.layerCollections.paste)())
mc.menuItem(rp='SW', l='Merge Down', c=lambda *_: noUndo(core.layerCollections.mergeDown)())
mc.menuItem(rp='SE', l='Move Down', c=lambda *_: noUndo(core.layerCollections.moveDown)())
mc.menuItem(rp='S', l='Rename', c=lambda *_: noUndo(core.layerCollections.rename)())
with wrap.Row(layerCollectionsLayout.layout(), margins=style.scale([1, 0, 0, 0])) as plusMinusButtonsLayout:
layerComboPlus = wrap.Button(objName="layerCollectionsPlus", layout=plusMinusButtonsLayout.layout())
layerComboPlus.setFixedHeight(style.scale(16))
layerComboPlus.setLabel("+", lineHeight=100)
layerComboPlus.setMinimumWidth(1)
layerComboPlus.setButtonStyle("small-filled")
layerComboPlus.clicked.connect(core.layerCollections.createLayerCollection)
layerComboMinus = wrap.Button(objName="layerCollectionsMinus", layout=plusMinusButtonsLayout.layout())
layerComboMinus.setEnabled(False)
layerComboMinus.setFixedHeight(style.scale(16))
layerComboMinus.setMinimumWidth(1)
layerComboMinus.setLabel("-", lineHeight=100)
layerComboMinus.setButtonStyle("small-filled")
layerComboMinus.clicked.connect(undo(core.layerCollections.deleteLayerCollection))
layerCollectionsLayout.layout().addWidget(layerComboBox, 3)
layerCollectionsLayout.layout().addWidget(plusMinusButtonsLayout, 1)
layerComboBox.insertItem(0, "Main")
# Layer Filters
with wrap.Row(layout) as row:
allFilter = wrap.Button(row.layout(), 'gsAllFilter')
allFilter.setButtonStyle('small-filled')
allFilter.setLabel('All', lineHeight=100)
allFilter.setIcon('mod-top')
allFilter.clicked.connect(pa(undo(core.layersFilterToggle), True, True))
curveFilter = wrap.Button(row.layout(), 'gsCurveFilter')
curveFilter.setButtonStyle('small-filled')
curveFilter.setLabel('Curve', lineHeight=100)
curveFilter.clicked.connect(pa(undo(core.layersFilterToggle), True, False, ignore=["Shift+Ctrl"]))
mc.popupMenu(mm=1, p=curveFilter.objectName())
mc.menuItem(rp='N', l='Toggle Always on Top', c=lambda *_: undo(core.alwaysOnTopToggle)())
mc.menuItem(rp='S', l='Auto-Hide Curves on Inactive Collections',
cb=mc.optionVar(q='GSCT_AutoHideCurvesOnInactiveCollections'),
c=lambda cb: undo(core.collectionVisibilityToggle)(cb))
geoFilter = wrap.Button(row.layout(), 'gsGeoFilter')
geoFilter.setButtonStyle('small-filled')
geoFilter.setLabel('Geo', lineHeight=100)
geoFilter.clicked.connect(pa(undo(core.layersFilterToggle), False, True, ignore=["Shift+Ctrl"]))
colorMode = wrap.Button(row.layout(), 'colorMode')
colorMode.setButtonStyle('small-filled')
colorMode.setCheckable(True)
colorMode.setChecked(False)
colorMode.setLabel('Color', lineHeight=100)
colorMode.clicked.connect(pa(undo(core.toggleColor.toggleColorVis)))
mc.popupMenu(mm=1, p=colorMode.objectName())
mc.menuItem(rp='N', l='Randomize Colors', c=lambda *_: undo(core.toggleColor.randomizeColors)())
mc.menuItem(rp='E', l='Reset Curve Colors', c=lambda *_: core.toggleColor.resetCurveColors())
mc.menuItem(rp='W', l='Apply Curve Colors', c=lambda *_: core.toggleColor.syncCurveColors(True))
mc.menuItem(rp='S', l='Custom Colors Window', c=ui.customLayerColors.window)
# Layers
with wrap.Layout(layout, objName='LayerLayout') as layerLayout:
layerButtonGrp = QtWidgets.QButtonGroup(layerLayout.layout())
layerButtonGrp.setObjectName(wrap.getUniqueName('LayerGroup'))
layerButtonGrp.setExclusive(True)
WIDGETS['LayerGroup'] = layerButtonGrp
# First Layer Row
with wrap.Row(layerLayout.layout(), 'layerRow0', spacing=0) as row:
for i in range(10):
layerButtonGrp.addButton(self.selectionSets(i, row.layout(), i))
# Second Layer Row
with wrap.Row(layerLayout.layout(), 'layerRow1', spacing=0) as row:
letter = ord('A')
letters = [chr(i) for i in range(letter, letter + 10)]
if WIDGETS['layerNumbersOnly'].isChecked():
letters = [i for i in range(10, 20)]
for i in range(10):
layerButtonGrp.addButton(self.selectionSets(i + 10, row.layout(), letters[i]))
# Third Layer Row
with wrap.Row(layerLayout.layout(), 'layerRow2', spacing=0) as row:
for i in range(10):
layerButtonGrp.addButton(self.selectionSets(i + 20, row.layout(), i + 20))
WIDGETS['layerRow2'].setHidden(True)
# Fourth Layer Row
with wrap.Row(layerLayout.layout(), 'layerRow3', spacing=0) as row:
for i in range(10):
layerButtonGrp.addButton(self.selectionSets(i + 30, row.layout(), i + 30))
WIDGETS['layerRow3'].setHidden(True)
# Fifth Layer Row
with wrap.Row(layerLayout.layout(), 'layerRow4', spacing=0) as row:
for i in range(10):
layerButtonGrp.addButton(self.selectionSets(i + 40, row.layout(), i + 40))
WIDGETS['layerRow4'].setHidden(True)
# Sixth Layer Row
with wrap.Row(layerLayout.layout(), 'layerRow5', spacing=0) as row:
for i in range(10):
layerButtonGrp.addButton(self.selectionSets(i + 50, row.layout(), i + 50))
WIDGETS['layerRow5'].setHidden(True)
# Seventh Layer Row
with wrap.Row(layerLayout.layout(), 'layerRow6', spacing=0) as row:
for i in range(10):
layerButtonGrp.addButton(self.selectionSets(i + 60, row.layout(), i + 60))
WIDGETS['layerRow6'].setHidden(True)
# Eighth Layer Row
with wrap.Row(layerLayout.layout(), 'layerRow7', spacing=0) as row:
for i in range(10):
layerButtonGrp.addButton(self.selectionSets(i + 70, row.layout(), i + 70))
WIDGETS['layerRow7'].setHidden(True)
WIDGETS['curveGrp0'].setChecked(True)
# Extract Selected and Extract All
with wrap.Row(layout) as row:
extractSelected = wrap.Button(row.layout(), 'gsExtractSelected')
extractSelected.setLabel('Extract<br>Selected')
extractSelected.setIcon('mod-bottom')
extractSelected.clicked.connect(pa(undo(core.extractSelectedCurves)))
extractAll = wrap.Button(row.layout(), 'gsExtractAll')
extractAll.setLabel('Extract<br>All')
extractAll.setIcon('mod-bottom')
extractAll.clicked.connect(undo(core.extractAllCurves))
layout.addWidget(wrap.separator())
# Select Curve, Geo and Group
with wrap.Row(layout) as row:
selectCurve = wrap.Button(row.layout(), 'gsSelectCurve')
selectCurve.setLabel('Select<br>Curve')
selectCurve.clicked.connect(pa(undo(core.selectPart), 1))
selectGeo = wrap.Button(row.layout(), 'gsSelectGeo')
selectGeo.setLabel('Select<br>Geo')
selectGeo.clicked.connect(pa(undo(core.selectPart), 2))
selectGroup = wrap.Button(row.layout(), 'gsSelectGroup')
selectGroup.setLabel('Select<br>Group')
selectGroup.clicked.connect(pa(undo(core.selectPart), 0))
# Group Curves Button
with wrap.Row(layout) as row:
groupCurves = wrap.Button(row.layout(), 'gsGroupCurves')
groupCurves.setLabel('Group<br>Curves')
groupCurves.clicked.connect(undo(core.groupCurves))
regroupByLayer = wrap.Button(row.layout(), 'gsRegroupByLayer')
regroupByLayer.setLabel('Regroup<br>by Layer')
regroupByLayer.clicked.connect(undo(core.regroupByLayer))
# Group Name Input Field
groupName = wrap.LineEdit('gsGroupNameTextField', layout)
groupName.setAlignment(QtCore.Qt.AlignCenter)
groupName.setAutoFormat(True)
groupName.setClearButtonEnabled(True)
groupName.setPlaceholderText("Group Name")
# Custom Layer Names Window
customLayerNamesColors = wrap.Button(layout, 'gsCustomLayerNamesAndColors')
customLayerNamesColors.setButtonStyle('small-filled')
customLayerNamesColors.setLabel('Layer Names & Colors')
customLayerNamesColors.clicked.connect(ui.customLayerColors.window)
layout.addWidget(wrap.separator())
# Select CVs Label
layout.addWidget(wrap.wrapControl(mc.text(l='Select CVs')))
# Select CVs Slider
sliderWidget = wrap.wrapControl(
mc.floatSliderGrp(
'gsSelectCVSlider',
w=1, min=0, max=1, step=0.05,
dc=core.sliders.selectCVSlider,
cc=core.sliders.release
)
)
WIDGETS['gsSelectCVSlider'] = sliderWidget
layout.addWidget(sliderWidget)
# Select Curve, Geo and Group
with wrap.Row(layout) as row:
transferAttributes = wrap.Button(row.layout(), 'gsTransferAttributes')
transferAttributes.setLabel('Transfer<br>Attr.')
transferAttributes.setIcon('mod-bottom')
transferAttributes.clicked.connect(undo(core.attributes.transferAttr))
mc.popupMenu(mm=1, p=transferAttributes.objectName())
mc.menuItem('gsCopyAttributes', rp='N', l='Copy Attrs', aob=1, c=lambda _: core.attributes.copyAttributes())
mc.menuItem(ob=1, c=lambda _: ui.attributesFilter.openUI())
mc.menuItem('gsPasteAttributes', rp='S', l='Paste Attrs', aob=1, c=lambda _: core.attributes.pasteAttributes())
mc.menuItem(ob=1, c=lambda _: ui.attributesFilter.openUI())
transferUVs = wrap.Button(row.layout(), 'gsTransferUVs')
transferUVs.setLabel('Transfer<br>UVs')
transferUVs.setIcon('mod-bottom')
transferUVs.clicked.connect(undo(core.attributes.transferUV))
mc.popupMenu(mm=1, p=transferUVs.objectName())
mc.menuItem('gsCopyUVs', aob=1, rp='N', l='Copy UVs', c=lambda _: core.attributes.copyUVs())
mc.menuItem(ob=1, c=lambda _: ui.attributesFilter.openUI())
mc.menuItem('gsPasteUVs', aob=1, rp='S', l='Paste UVs', c=lambda _: core.attributes.pasteUVs())
mc.menuItem(ob=1, c=lambda _: ui.attributesFilter.openUI())
resetPivot = wrap.Button(row.layout(), 'gsResetPivot')
resetPivot.setLabel('Reset<br>Pivot')
resetPivot.clicked.connect(undo(core.resetCurvePivotPoint))
resetPivot.setIcon('mod-bottom')
mc.popupMenu(mm=1, p=resetPivot.objectName())
mc.menuItem('gsResetPivotToRoot', rp='N', l='Reset to Root', c=lambda _: core.resetCurvePivotPoint())
mc.menuItem('gsResetPivotToTip', rp='S', l='Reset to Tip', c=lambda _: core.resetCurvePivotPoint(2))
layout.addWidget(wrap.separator())
# Rebuild Curve
def rebuildSliderRelease(_):
core.sliders.rebuildSliderRelease()
core.sliders.release()
def rebuildButtonClicked():
core.sliders.rebuildSliderDrag()
rebuildSliderRelease(None)
with wrap.Row(layout, margins=style.scale([1.5, 0, 1.5, 0])) as rebuildResetRow:
rebuildButton = wrap.Button(objName='gsRebuildWithCurrentValue')
rebuildButton.setButtonStyle('small-filled')
rebuildButton.setLabel("R", lineHeight=100)
rebuildButton.setMinimumWidth(1)
rebuildButton.setMaximumSize(*style.scale([16, 16]))
rebuildButton.clicked.connect(rebuildButtonClicked)
resetButton = wrap.Button(objName='gsResetRebuildSliderRange')
resetButton.setButtonStyle('small-filled')
resetButton.setIcon('reset')
resetButton.setMinimumWidth(1)
resetButton.setMaximumSize(*style.scale([16, 16]))
resetButton.clicked.connect(lambda: mc.intSliderGrp('gsRebuildSlider', e=1, min=1, max=50))
rebuildResetRow.layout().addWidget(rebuildButton, 1)
rebuildResetRow.layout().addWidget(wrap.wrapControl(mc.text(l='Rebuild Curve')), 3)
rebuildResetRow.layout().addWidget(resetButton, 1)
rebuildCurveSlider = wrap.wrapControl(mc.intSliderGrp(
'gsRebuildSlider', f=1, cw=[(1, 32), (2, 28)], min=1, max=50, fmx=999, v=1,
dc=core.sliders.rebuildSliderDrag,
cc=rebuildSliderRelease))
WIDGETS['gsRebuildSlider'] = rebuildCurveSlider
layout.addWidget(rebuildCurveSlider)
# Duplicate and Randomize
with wrap.Row(layout) as row:
duplicateCurve = wrap.Button(row.layout(), 'gsDuplicateCurve')
duplicateCurve.setLabel('Duplicate')
duplicateCurve.clicked.connect(undo(core.duplicateCurve))
randomizeCurve = wrap.Button(row.layout(), 'gsRandomizeCurve')
randomizeCurve.setLabel('Randomize')
randomizeCurve.clicked.connect(ui.randomizeCurveWindow)
# Extend and Reduce
with wrap.Row(layout) as row:
extendCurve = wrap.Button(row.layout(), 'gsExtendCurve')
extendCurve.setLabel('Extend')
extendCurve.clicked.connect(undo(core.extendCurve))
reduceCurve = wrap.Button(row.layout(), 'gsReduceCurve')
reduceCurve.setLabel('Reduce')
reduceCurve.clicked.connect(undo(core.reduceCurve))
# Smooth
with wrap.Row(layout) as row:
smooth = wrap.Button(row.layout(), 'gsSmooth')
smooth.setLabel('Smooth')
smooth.clicked.connect(undo(core.smoothCurve))
smooth.setIcon('marking-top')
mc.popupMenu(mm=1, p=smooth.objectName())
mc.radioMenuItemCollection()
mc.menuItem('gsSmoothMult1', rp='N', rb=1, l='x1')
mc.menuItem('gsSmoothMult3', rp='E', rb=0, l='x3')
mc.menuItem('gsSmoothMult5', rp='S', rb=0, l='x5')
mc.menuItem('gsSmoothMult10', rp='W', rb=0, l='x10')
# Smooth Slider
factorSlider = wrap.wrapControl(mc.floatSliderGrp(
'gsFactorSlider', l='Factor', adj=3, w=1, cw=[(1, 32), (2, 28)], min=1, max=100))
WIDGETS['gsFactorSlider'] = factorSlider
layout.addWidget(factorSlider)
layout.addWidget(wrap.separator())
# Mirroring
with wrap.Frame(layout, objName='MirrorFrame', label='Mirroring', margins=[1, 1, 1, 1]) as frame:
with wrap.Row(frame.getFrameLayout()) as row:
mirrorX = wrap.Button(row.layout(), 'mirrorX')
mirrorX.setLabel('X')
mirrorX.setFontSize(16)
mirrorX.clicked.connect(pa(undo(core.mirrorHair), 0))
mirrorY = wrap.Button(row.layout(), 'mirrorY')
mirrorY.setLabel('Y')
mirrorY.setFontSize(16)
mirrorY.clicked.connect(pa(undo(core.mirrorHair), 1))
mirrorZ = wrap.Button(row.layout(), 'mirrorZ')
mirrorZ.setLabel('Z')
mirrorZ.setFontSize(16)
mirrorZ.clicked.connect(pa(undo(core.mirrorHair), 2))
with wrap.Row(frame.getFrameLayout()) as row:
mirrorFlipGrp = QtWidgets.QButtonGroup(row.layout())
mirror = wrap.Button(row.layout(), 'mirrorRadio')
mirror.setLabel('Mirror')
mirror.setButtonStyle('small')
mirror.setCheckable(True)
mirror.setChecked(True)
flip = wrap.Button(row.layout(), 'flipRadio')
flip.setLabel('Flip')
flip.setButtonStyle('small')
flip.setCheckable(True)
mirrorFlipGrp.addButton(mirror)
mirrorFlipGrp.addButton(flip)
layout.addWidget(wrap.separator())
# Control Curve and Apply
with wrap.Row(layout) as row:
controlCurve = wrap.Button(row.layout(), 'gsControlCurve')
controlCurve.setLabel('Control Curve')
controlCurve.clicked.connect(undo(core.controlCurveCreate))
applyControlCurve = wrap.Button(row.layout(), 'gsApplyControlCurve')
applyControlCurve.setLabel('Apply')
applyControlCurve.setFixedWidth(style.scale(48))
applyControlCurve.clicked.connect(undo(core.controlCurveApply))
layout.addWidget(wrap.separator())
# Curve Control Window
with wrap.Row(layout) as row:
curveControlWindow = wrap.Button(row.layout(), 'gsCurveControlWindow')
curveControlWindow.setLabel('Curve Control Window')
curveControlWindow.pressed.connect(ui.curveControlWorkspace)
layout.addWidget(wrap.separator())
# UV Editor Window
with wrap.Row(layout) as row:
uvEditor = wrap.Button(row.layout(), 'gsUVEditorMain')
uvEditor.setLabel('UV Editor Window')
uvEditor.pressed.connect(ui.uvEditorWorkspace)
layout.addWidget(wrap.separator())
# Version
layout.addWidget(wrap.wrapControl(mc.text(l=core.VERSION)))
# Toggling the correct switch
warpSwitch.setChecked(core.getOption('warpSwitch'))
extrudeSwitch.setChecked(not core.getOption('warpSwitch'))
# Toggling layer collections widget
core.layerCollections.toggleLayerCollectionsWidget()
# Setting the custom tooltips
tooltips.toggleCustomTooltipsMain(core.getOption('enableTooltips'))
def selectionSets(self, i, layout, label): # Creates layer button
def toggleGeometryEdit(*_):
core.curveGeometryEditToggle(i)
core.updateMainUI()
def toggleCurveVisibility(*_):
core.toggleObjVisibility(i, 0)
core.updateMainUI()
def toggleGeoVisibility(*_):
core.toggleObjVisibility(i, 1)
core.updateMainUI()
def toggleLayerVisibility(*_):
core.toggleLayerVisibility(i)
core.updateMainUI()
selSet = wrap.Layer(layout=layout, objName='curveGrp%s' % i)
selSet.setStyleSheet(style.layer())
selSet.setLabel(str(label))
mc.popupMenu(mm=1, p=selSet.objectName())
mc.menuItem(rp='N', l='Add Selection to Layer', c=lambda _: core.curveAddToLayer(i))
mc.menuItem(rp='NW', l='Extract Geometry', c=lambda _: core.extractCurveGeo(i))
mc.menuItem(rp='NE', l='Toggle Geometry Edit', c=toggleGeometryEdit)
mc.menuItem(rp='W', l='Select Curves', c=lambda _: core.curveLayerSelectObj(i, 0))
mc.menuItem(rp='E', l='Select Geometry', c=lambda _: core.curveLayerSelectObj(i, 1))
mc.menuItem(rp='SW', l='Toggle Curve Visibility', c=toggleCurveVisibility)
mc.menuItem(rp='SE', l='Toggle Geo Visibility', c=toggleGeoVisibility)
mc.menuItem(rp='S', l='Toggle Layer Visibility', c=toggleLayerVisibility)
selSet.clicked.connect(pa(undo(core.layerClicked), i))
return selSet
def extrudeWarpToggle(self):
buttons = ['newCard', 'newTube', 'curveCard', 'curveTube', 'addCards', 'addTubes']
buttonStyle = style.buttonNormal
if WIDGETS['extrudeSwitch'].isChecked():
buttonStyle = style.buttonNormalBlueBorder
for button in buttons:
WIDGETS[button].setStyleSheet(buttonStyle)
def updateLayerList(self):
if 'gsLayerSelector' in WIDGETS:
WIDGETS['gsLayerSelector'].updateLayerList()
core.curveControlUI.updateUI()
def toggleTooltips(self):
for widget in WIDGETS:
if hasattr(WIDGETS[widget], "enableTooltip") and callable(getattr(WIDGETS[widget], "enableTooltip")):
WIDGETS[widget].enableTooltip(core.getOption('enableTooltips'))
tooltips.toggleCustomTooltipsMain(core.getOption('enableTooltips'))
tooltips.toggleCustomTooltipsCurveControl(core.getOption('enableTooltips'))

View File

@ -0,0 +1,80 @@
"""
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.1 Studio
Copyright 2023, George Sladkovsky (Yehor Sladkovskyi)
All Rights Reserved
Autodesk Maya is a property of Autodesk, Inc.
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
"""
import sys
from imp import reload
import maya.api.OpenMaya as om
import maya.api.OpenMayaRender as omr
from gs_curvetools.plugins import cv_manip_src # pyright: ignore
reload(cv_manip_src)
# API parameters
maya_useNewAPI = True
# ------------ Init & UnInit Plugin ------------
def initializePlugin(obj):
plugin = om.MFnPlugin(obj, "GeorgeSladkovsky", "1.3.1", "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

View File

@ -0,0 +1,770 @@
"""
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.1 Studio
Copyright 2023, George Sladkovsky (Yehor Sladkovskyi)
All Rights Reserved
Autodesk Maya is a property of Autodesk, Inc.
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
"""
import maya.api.OpenMaya as om
import maya.api.OpenMayaRender as omr
import maya.api.OpenMayaUI as omui
import maya.cmds as mc
# API parameters
maya_useNewAPI = True
# Cache and globals
CALLBACK_IDS = [] # Callback IDs cache
# ------------ Functions ------------
def onSelectionChanged(*_):
DrawOverride.selectionList.clear()
newSelection = om.MSelectionList()
try:
newSelection = om.MGlobal.getRichSelection(False).getSelection()
DrawOverride.softSelectionActive = True
except BaseException:
DrawOverride.softSelectionActive = False
newSelection = om.MGlobal.getActiveSelectionList()
hilite = om.MGlobal.getHiliteList() # type: om.MSelectionList
if not hilite.isEmpty():
newSelection.merge(hilite)
DrawOverride.selectionList = newSelection
# ------------ Data Classes ----------------------
# Holds all the data for a single CV point
PointData = {
"curveIndex": 0,
"cvIndex": 0,
"position": om.MPoint(),
"isSelected": False,
"hasWeight": False,
"weight": 1.0,
"colorMult": 1.0,
"alphaMult": 1.0,
"distance": 0.0,
}
# Holds all the data for a single curve point
CurveData = {
"curveIndex": 0,
"position": om.MPoint(),
"color": om.MColor(),
"pointIndex": 0,
"distance": 0.0,
}
# ------------ Draw Manager (Locator) ------------
class DrawManagerNode(omui.MPxLocatorNode):
id = om.MTypeId(0x0013bc41)
drawDbClassification = "drawdb/geometry/GSCT_CurveTools_DrawManager"
drawRegistrantId = "GSCT_CurveTools_DrawManagerPlugin"
# Curve attributes
aDrawAlwaysOnTop = om.MObject()
aPointSize = om.MObject()
aLineWidth = om.MObject()
aHullWidth = om.MObject()
aCurveDivisions = om.MObject()
aDeselectedPointColor = om.MObject()
aDeselectedPointAlpha = om.MObject()
aSelectedPointColor = om.MObject()
aSelectedPointAlpha = om.MObject()
aCurveColor = om.MObject()
aCurveAlpha = om.MObject()
aHullColor = om.MObject()
aHullAlpha = om.MObject()
aUseCVDistanceColor = om.MObject()
aUseCurveDistanceColor = om.MObject()
aUseHullDistanceColor = om.MObject()
aDistanceColorMin = om.MObject()
aDistanceColorMax = om.MObject()
aOccluderMeshName = om.MObject()
aUseCVOcclusion = om.MObject()
aShowCurve = om.MObject()
aShowHull = om.MObject()
aLazyUpdate = om.MObject()
def __init__(self):
CALLBACK_IDS.append(om.MEventMessage.addEventCallback("SelectionChanged", onSelectionChanged))
super(DrawManagerNode, self).__init__()
@staticmethod
def creator(): # Creator method to be passed in initializePlugin
return DrawManagerNode()
@staticmethod
def initialize(): # Init method to be passed in initializePlugin
# Init plugs attributes
numericAttr = om.MFnNumericAttribute()
typedAttr = om.MFnTypedAttribute()
# Add attributes
DrawManagerNode.aDrawAlwaysOnTop = numericAttr.create("drawOnTop", "dot", om.MFnNumericData.kBoolean, True)
om.MPxNode.addAttribute(DrawManagerNode.aDrawAlwaysOnTop)
DrawManagerNode.aPointSize = numericAttr.create("pointSize", "psize", om.MFnNumericData.kFloat, 10.0)
numericAttr.setMin(1)
numericAttr.setMax(100)
om.MPxNode.addAttribute(DrawManagerNode.aPointSize)
DrawManagerNode.aLineWidth = numericAttr.create("lineWidth", "lwidth", om.MFnNumericData.kFloat, 4.0)
numericAttr.setMin(1)
numericAttr.setMax(100)
om.MPxNode.addAttribute(DrawManagerNode.aLineWidth)
DrawManagerNode.aHullWidth = numericAttr.create("hullWidth", "hwidth", om.MFnNumericData.kFloat, 3.0)
numericAttr.setMin(1)
numericAttr.setMax(100)
om.MPxNode.addAttribute(DrawManagerNode.aHullWidth)
DrawManagerNode.aCurveDivisions = numericAttr.create("curveDivisions", "crvdiv", om.MFnNumericData.kInt, 5)
numericAttr.setMin(2)
numericAttr.setMax(64)
om.MPxNode.addAttribute(DrawManagerNode.aCurveDivisions)
DrawManagerNode.aDeselectedPointColor = numericAttr.create("deselectedPointColor", "dpcolor", om.MFnNumericData.k3Float)
numericAttr.default = (1.0, 0, 0)
numericAttr.usedAsColor = True
om.MPxNode.addAttribute(DrawManagerNode.aDeselectedPointColor)
DrawManagerNode.aDeselectedPointAlpha = numericAttr.create("deselectedPointAlpha", "dpalpha", om.MFnNumericData.kFloat, 1.0)
numericAttr.setMin(0.0)
numericAttr.setMax(1.0)
om.MPxNode.addAttribute(DrawManagerNode.aDeselectedPointAlpha)
DrawManagerNode.aSelectedPointColor = numericAttr.create("selectedPointColor", "spcolor", om.MFnNumericData.k3Float)
numericAttr.default = (0, 1.0, 0)
numericAttr.usedAsColor = True
om.MPxNode.addAttribute(DrawManagerNode.aSelectedPointColor)
DrawManagerNode.aSelectedPointAlpha = numericAttr.create("selectedPointAlpha", "spalpha", om.MFnNumericData.kFloat, 1.0)
numericAttr.setMin(0.0)
numericAttr.setMax(1.0)
om.MPxNode.addAttribute(DrawManagerNode.aSelectedPointAlpha)
DrawManagerNode.aCurveColor = numericAttr.create("curveColor", "ccolor", om.MFnNumericData.k3Float)
numericAttr.default = (0, 0, 1.0)
numericAttr.usedAsColor = True
om.MPxNode.addAttribute(DrawManagerNode.aCurveColor)
DrawManagerNode.aCurveAlpha = numericAttr.create("curveAlpha", "calpha", om.MFnNumericData.kFloat, 1.0)
numericAttr.setMin(0.0)
numericAttr.setMax(1.0)
om.MPxNode.addAttribute(DrawManagerNode.aCurveAlpha)
DrawManagerNode.aHullColor = numericAttr.create("hullColor", "hcolor", om.MFnNumericData.k3Float)
numericAttr.default = (0.5, 0, 0.5)
numericAttr.usedAsColor = True
om.MPxNode.addAttribute(DrawManagerNode.aHullColor)
DrawManagerNode.aHullAlpha = numericAttr.create("hullAlpha", "halpha", om.MFnNumericData.kFloat, 1.0)
numericAttr.setMin(0.0)
numericAttr.setMax(1.0)
om.MPxNode.addAttribute(DrawManagerNode.aHullAlpha)
DrawManagerNode.aUseCVDistanceColor = numericAttr.create("useCVDistanceColor", "usecvdcolor", om.MFnNumericData.kBoolean, True)
om.MPxNode.addAttribute(DrawManagerNode.aUseCVDistanceColor)
DrawManagerNode.aUseHullDistanceColor = numericAttr.create(
"useHullDistanceColor", "usehulldcolor", om.MFnNumericData.kBoolean, True)
om.MPxNode.addAttribute(DrawManagerNode.aUseHullDistanceColor)
DrawManagerNode.aUseCurveDistanceColor = numericAttr.create(
"useCurveDistanceColor", "usecurvedcolor", om.MFnNumericData.kBoolean, True)
om.MPxNode.addAttribute(DrawManagerNode.aUseCurveDistanceColor)
DrawManagerNode.aDistanceColorMin = numericAttr.create("distanceColorMin", "dcolormin", om.MFnNumericData.kFloat, 0.25)
numericAttr.setMin(0.0)
numericAttr.setMax(1.0)
om.MPxNode.addAttribute(DrawManagerNode.aDistanceColorMin)
DrawManagerNode.aDistanceColorMax = numericAttr.create("distanceColorMax", "dcolormax", om.MFnNumericData.kFloat, 1.0)
numericAttr.setMin(0.0)
numericAttr.setMax(1.0)
om.MPxNode.addAttribute(DrawManagerNode.aDistanceColorMax)
DrawManagerNode.aOccluderMeshName = typedAttr.create("occluderMeshName", "oclmeshname", om.MFnData.kString)
om.MPxNode.addAttribute(DrawManagerNode.aOccluderMeshName)
DrawManagerNode.aUseCVOcclusion = numericAttr.create("useCVOcclusion", "usecvocclusion", om.MFnNumericData.kBoolean, False)
om.MPxNode.addAttribute(DrawManagerNode.aUseCVOcclusion)
DrawManagerNode.aShowCurve = numericAttr.create("showCurve", "showcurve", om.MFnNumericData.kBoolean, True)
om.MPxNode.addAttribute(DrawManagerNode.aShowCurve)
DrawManagerNode.aShowHull = numericAttr.create("showHull", "showhull", om.MFnNumericData.kBoolean, False)
om.MPxNode.addAttribute(DrawManagerNode.aShowHull)
DrawManagerNode.aLazyUpdate = numericAttr.create("lazyUpdate", "lazyupdate", om.MFnNumericData.kBoolean, False)
om.MPxNode.addAttribute(DrawManagerNode.aLazyUpdate)
# ------------ Draw Manager Data ------------
class UserData(om.MUserData):
def __init__(self):
super(UserData, self).__init__(False)
self.curveDataArray = [] # type: list[dict]
self.processedCurvePoints = [] # type: list[om.MPointArray]
self.processedCurveColors = [] # type: list[om.MColorArray]
self.pointDataArray = [] # type: list[dict]
# Sorted and colored CV arrays
self.cvPosArray = om.MPointArray()
self.cvColorArray = om.MColorArray()
self.rootCVsArray = om.MPointArray()
self.rootCVsColors = om.MColorArray()
# Paired and colored hull arrays
self.hullPosArray = [] # List of MPointArray
self.hullColorArray = [] # list of MColorArray
self.pointSize = 10.0
self.lineWidth = 4.0
self.hullWidth = 3.0
self.curveDivisions = 5 # type: int
self.selectedPointColor = om.MColor((0, 1.0, 0.0, 1.0))
self.deselectedPointColor = om.MColor((1.0, 0.0, 0.0, 1.0))
self.curveColor = om.MColor((0, 0, 1.0, 1.0))
self.hullColor = om.MColor((.5, 0, .5, 1.0))
self.drawAlwaysOnTop = True # type: bool
self.drawCurve = False # type: bool
self.drawHull = False # type: bool
def clear(self):
self.pointDataArray = []
self.processedCurvePoints = []
self.processedCurveColors = []
self.curveDataArray = []
self.hullPosArray = []
self.hullColorArray = []
self.cvPosArray.clear()
self.cvColorArray.clear()
self.rootCVsArray.clear()
self.rootCVsColors.clear()
# ------------ Draw Manager Draw Override ------------
class DrawOverride(omr.MPxDrawOverride):
nodeState = 0
selectionInitialized = False
softSelectionActive = False
selectionList = om.MSelectionList()
previousSelectionString = ""
def __init__(self, obj):
super(DrawOverride, self).__init__(obj, None)
self.thisMObject = obj # type: om.MObject
# Cache
self.CVCache = {} # type: dict[str, om.MPointArray]
self.splinePointsCache = {} # type: dict[str, om.MPointArray]
self.oldCurveDivisions = 0 # type: int
@staticmethod
def creator(obj):
return DrawOverride(obj)
def supportedDrawAPIs(self):
return omr.MRenderer.kAllDevices
def hasUIDrawables(self):
return True
def prepareForDraw(self, objPath, cameraPath, frameContext, oldData):
# type: (om.MDagPath, om.MDagPath, omr.MFrameContext, om.MUserData) -> om.MUserData
if self.thisMObject.isNull():
return
data = oldData
if not isinstance(data, UserData):
data = UserData()
data.clear()
lazyUpdate = om.MPlug(self.thisMObject, DrawManagerNode.aLazyUpdate).asBool() # type: bool
# Get selection list from callback
if not DrawOverride.selectionList or not lazyUpdate:
onSelectionChanged()
sel = DrawOverride.selectionList
# Clear cache if no selection exists
if sel.isEmpty():
self.CVCache.clear()
self.splinePointsCache.clear()
return data
# Getting the node state and bypassing if not "Normal"
if mc.getAttr(objPath.fullPathName() + '.nodeState') != 0:
return data
# Get the data from plugs
data = self.updateDataFromPlugs(data)
# Check if selection strings are the same
if data.drawCurve:
selectionString = "".join(self.selectionList.getSelectionStrings()) # type: str
if selectionString != self.previousSelectionString:
# print("Clearing CV and SplinePointsCache")
self.CVCache.clear()
self.splinePointsCache.clear()
self.previousSelectionString = selectionString
# Iterate over curves
processedCurves = set()
curveIndex = 0 # type: int
for selIndex in range(sel.length()):
try:
dag = sel.getDagPath(selIndex).extendToShape() # type: om.MDagPath
except BaseException:
continue
if dag.apiType() != 267:
continue
fullName = dag.fullPathName() # type: str
# Check if current curve was processed already
if fullName in processedCurves:
continue
# Create curve function set and get CVs and curve points
curve = om.MFnNurbsCurve(dag)
cvPos, curvePoints = self.processMayaCurve(curve, fullName, data, curveIndex)
data.curveDataArray += curvePoints
processedCurves.add(fullName) # Prevent from double processing of curves
# Check selection display status (selection context)
displayStatus = omui.M3dView.displayStatus(dag) # type: int
if displayStatus != 4 and displayStatus != 2:
curveIndex += 1
continue
# Check if selection list item has components
comps = sel.getComponent(selIndex) # type: tuple[om.MDagPath, om.MObject]
if comps[1] != om.MObject.kNullObj:
compFn = om.MFnSingleIndexedComponent(comps[1])
if compFn.componentType != om.MFn.kCurveCVComponent:
data.clear()
return data
selectedIDs = om.MIntArray()
selectedWeights = om.MFloatArray()
# Getting soft selection component weights and IDs
for i in range(compFn.elementCount):
weight = 1.0
if compFn.hasWeights:
weight = compFn.weight(i).influence # type: float
selectedIDs.append(compFn.element(i))
selectedWeights.append(weight)
# Adding selected CVs and their colors to data
for i, index in enumerate(selectedIDs):
pointData = PointData.copy()
pointData["curveIndex"] = curveIndex
pointData["cvIndex"] = selectedIDs[i]
pointData["position"] = om.MPoint(cvPos[index])
pointData["isSelected"] = True
pointData["hasWeight"] = True
pointData["weight"] = selectedWeights[i]
data.pointDataArray.append(pointData)
# Adding deselected CVs and their colors to data
for i in range(len(cvPos)):
if i not in selectedIDs:
pointData = PointData.copy()
pointData["curveIndex"] = curveIndex
pointData["cvIndex"] = i
pointData["position"] = om.MPoint(cvPos[i])
data.pointDataArray.append(pointData)
else:
# If no components selected, just add all the CVs with deselected colors
for i in range(len(cvPos)):
pointData = PointData.copy()
pointData["curveIndex"] = curveIndex
pointData["cvIndex"] = i
pointData["position"] = om.MPoint(cvPos[i])
data.pointDataArray.append(pointData)
curveIndex += 1
# Skip everything else if there were no processed curves
if not processedCurves:
data.clear()
return data
# Process draw indices (depth sorting)
camera = om.MFnCamera(cameraPath)
camTransform = om.MFnDagNode(camera.parent(0))
camTransformMatrix = camTransform.transformationMatrix() # type: om.MMatrix
cameraPoint = om.MPoint(
camTransformMatrix.getElement(3, 0),
camTransformMatrix.getElement(3, 1),
camTransformMatrix.getElement(3, 2)
)
# Add camera distances
for point in data.pointDataArray:
point["distance"] = point["position"].distanceTo(cameraPoint)
data.pointDataArray.sort(key=lambda p: p["distance"], reverse=True)
# Ray-casting for occlusion of verts
useOcclusion = om.MPlug(self.thisMObject, DrawManagerNode.aUseCVOcclusion).asBool() # type: bool
if useOcclusion:
self.calculateOcclusion(data, cameraPoint)
# Optional color changed based on the distance from camera
useCVDistanceColor = om.MPlug(self.thisMObject, DrawManagerNode.aUseCVDistanceColor).asBool() # type: bool
useHullDistanceColor = om.MPlug(self.thisMObject, DrawManagerNode.aUseHullDistanceColor).asBool() # type: bool
useCurveDistanceColor = om.MPlug(self.thisMObject, DrawManagerNode.aUseCurveDistanceColor).asBool() # type: bool
distanceColorMin = om.MPlug(self.thisMObject, DrawManagerNode.aDistanceColorMin).asFloat() # type: float
distanceColorMax = om.MPlug(self.thisMObject, DrawManagerNode.aDistanceColorMax).asFloat() # type: float
if distanceColorMax < distanceColorMin:
distanceColorMax = distanceColorMin
# Process color multiplier
if not self.softSelectionActive and (useCVDistanceColor or useHullDistanceColor or useCurveDistanceColor):
for i, point in enumerate(data.pointDataArray):
point["colorMult"] = i / float(len(data.pointDataArray))
# Process curve points
if data.drawCurve:
self.processCurvePoints(data, cameraPoint, useCurveDistanceColor, distanceColorMin, distanceColorMax)
# Process CVs
for point in data.pointDataArray:
if point["cvIndex"] == 0:
x, y, _ = omui.M3dView.active3dView().worldToView(point["position"])
data.rootCVsArray.append(om.MPoint(x, y, 0))
rootColor = om.MColor()
if point["isSelected"]:
rootColor = data.selectedPointColor
if useCVDistanceColor:
rootColor = data.selectedPointColor * max(point["weight"] * point["colorMult"] * distanceColorMax, distanceColorMin)
else:
rootColor = data.deselectedPointColor
if useCVDistanceColor:
rootColor = data.deselectedPointColor * max(point["colorMult"] * distanceColorMax, distanceColorMin)
data.rootCVsColors.append(rootColor)
data.cvPosArray.append(point["position"])
color = om.MColor()
if point["isSelected"]:
if useCVDistanceColor:
color = data.selectedPointColor * max(point["weight"] * point["colorMult"] *
distanceColorMax, distanceColorMin) # type: om.MColor
else:
color = data.selectedPointColor * max(point["weight"] * distanceColorMax, distanceColorMin) # type: om.MColor
else:
color = data.deselectedPointColor
if useCVDistanceColor:
color = data.deselectedPointColor * max(point["colorMult"] * distanceColorMax, distanceColorMin)
color.a = color.a * point["alphaMult"]
data.cvColorArray.append(color)
# Process hull points
if data.drawHull:
self.processHullPoints(data, useHullDistanceColor, distanceColorMin, distanceColorMax)
else:
data.hullPosArray = []
data.hullColorArray = []
return data
def addUIDrawables(self, objPath, drawManager, frameContext, userData):
# type: (om.MDagPath, omr.MUIDrawManager, omr.MFrameContext, om.MUserData) -> None
if not isinstance(userData, UserData):
return
if mc.getAttr(objPath.fullPathName() + '.nodeState') != 0:
return
if userData.drawAlwaysOnTop:
drawManager.beginDrawInXray()
else:
drawManager.beginDrawable()
# Draw curve
if userData.drawCurve:
drawManager.setLineWidth(userData.lineWidth)
for i in range(len(userData.processedCurvePoints)):
drawManager.mesh(drawManager.kLines, userData.processedCurvePoints[i], None,
userData.processedCurveColors[i], None, None)
# Draw hull
if userData.drawHull:
for i in range(len(userData.hullPosArray)):
drawManager.setLineWidth(userData.hullWidth)
drawManager.mesh(drawManager.kLines,
userData.hullPosArray[i],
None,
userData.hullColorArray[i])
# Draw CVs
if len(userData.cvPosArray) > 0:
drawManager.setPointSize(userData.pointSize)
drawManager.mesh(drawManager.kPoints,
userData.cvPosArray,
None,
userData.cvColorArray)
# Draw root CV
drawManager.setLineWidth(1.0)
for i in range(len(userData.rootCVsArray)):
drawManager.setColor(userData.rootCVsColors[i])
drawManager.circle2d(userData.rootCVsArray[i], userData.pointSize / 2.0 + 4.0, False)
if userData.drawAlwaysOnTop:
drawManager.endDrawInXray()
else:
drawManager.endDrawable()
def updateDataFromPlugs(self, data):
# type: (UserData) -> UserData
# Getting attributes
data.drawAlwaysOnTop = om.MPlug(self.thisMObject, DrawManagerNode.aDrawAlwaysOnTop).asBool()
data.pointSize = om.MPlug(self.thisMObject, DrawManagerNode.aPointSize).asFloat()
data.lineWidth = om.MPlug(self.thisMObject, DrawManagerNode.aLineWidth).asFloat()
data.hullWidth = om.MPlug(self.thisMObject, DrawManagerNode.aHullWidth).asFloat()
data.curveDivisions = om.MPlug(self.thisMObject, DrawManagerNode.aCurveDivisions).asInt()
# Selected points color
selectedPointColorPlug = om.MPlug(self.thisMObject, DrawManagerNode.aSelectedPointColor).asMObject() # type: om.MObject
selectedPointColorData = om.MFnNumericData(selectedPointColorPlug)
data.selectedPointColor = om.MColor(selectedPointColorData.getData())
pointAlphaPlug = om.MPlug(self.thisMObject, DrawManagerNode.aSelectedPointAlpha)
data.selectedPointColor.a = pointAlphaPlug.asFloat()
# Deselected points color
deselectedPointColorPlug = om.MPlug(self.thisMObject, DrawManagerNode.aDeselectedPointColor).asMObject() # type: om.MObject
deselectedPointColorData = om.MFnNumericData(deselectedPointColorPlug)
data.deselectedPointColor = om.MColor(deselectedPointColorData.getData())
pointAlphaPlug = om.MPlug(self.thisMObject, DrawManagerNode.aDeselectedPointAlpha)
data.deselectedPointColor.a = pointAlphaPlug.asFloat()
# Curve color
curveColorPlug = om.MPlug(self.thisMObject, DrawManagerNode.aCurveColor).asMObject() # type: om.MObject
curveColorData = om.MFnNumericData(curveColorPlug)
data.curveColor = om.MColor(curveColorData.getData())
curveAlphaPlug = om.MPlug(self.thisMObject, DrawManagerNode.aCurveAlpha)
data.curveColor.a = curveAlphaPlug.asFloat()
# Hull color
hullColorPlug = om.MPlug(self.thisMObject, DrawManagerNode.aHullColor).asMObject() # type: om.MObject
hullColorPlugData = om.MFnNumericData(hullColorPlug)
data.hullColor = om.MColor(hullColorPlugData.getData())
hullAlphaPlug = om.MPlug(self.thisMObject, DrawManagerNode.aCurveAlpha)
data.hullColor.a = hullAlphaPlug.asFloat()
data.drawCurve = om.MPlug(self.thisMObject, DrawManagerNode.aShowCurve).asBool()
data.drawHull = om.MPlug(self.thisMObject, DrawManagerNode.aShowHull).asBool()
return data
def processMayaCurve(self, curve, name, data, curveIndex):
# type: (om.MFnNurbsCurve, str, UserData, int) -> tuple[om.MPointArray, list]
cvPos = curve.cvPositions(space=om.MSpace.kWorld) # type: om.MPointArray
# Check if curve is being drawn at all
if not data.drawCurve:
return cvPos, []
needsUpdate = False
if self.oldCurveDivisions != data.curveDivisions:
needsUpdate = True
self.oldCurveDivisions = data.curveDivisions
if self.CVCache and name in self.CVCache and len(self.CVCache[name]) == len(cvPos) and not needsUpdate:
for i, cv in enumerate(cvPos):
if not cv.isEquivalent(self.CVCache[name][i]):
needsUpdate = True
break
else:
needsUpdate = True
if not needsUpdate:
return cvPos, self.splinePointsCache[name]
self.CVCache.update({name: cvPos})
splinePoints = []
knotDomainMin = curve.knotDomain[0] # type: float
knotDomainMax = curve.knotDomain[1] # type: float
r = knotDomainMin # type: float
step = knotDomainMax / (curve.numSpans * data.curveDivisions) # type: float
index = 0
while r < knotDomainMax:
pointAtParam = curve.getPointAtParam(r, space=om.MSpace.kWorld) # type: om.MPoint
curveData = CurveData.copy()
curveData["curveIndex"] = curveIndex
curveData["position"] = pointAtParam
curveData["color"] = data.curveColor
curveData["pointIndex"] = index
splinePoints.append(curveData)
if r + step >= knotDomainMax:
index += 1
curveData = CurveData.copy()
pointAtParam = curve.getPointAtParam(knotDomainMax - 0.0001, space=om.MSpace.kWorld)
curveData["curveIndex"] = curveIndex
curveData["position"] = pointAtParam
curveData["color"] = data.curveColor
curveData["pointIndex"] = index
splinePoints.append(curveData)
break
r += step
index += 1
self.splinePointsCache.update({name: splinePoints})
return cvPos, splinePoints
def calculateOcclusion(self, data, cameraPoint):
# type: (UserData, om.MPoint) -> UserData
try:
occluderMeshName = om.MPlug(self.thisMObject, DrawManagerNode.aOccluderMeshName).asString()
meshSel = om.MSelectionList().add(occluderMeshName) # type: om.MSelectionList
meshSel = meshSel.getDagPath(0)
occluderMesh = om.MFnMesh(meshSel)
params = occluderMesh.autoUniformGridParams() # type: om.MMeshIsectAccelParams
for pointData in data.pointDataArray:
result = occluderMesh.anyIntersection(om.MFloatPoint(cameraPoint),
om.MFloatVector(pointData["position"]) - om.MFloatVector(cameraPoint),
om.MSpace.kWorld,
1.0,
False,
accelParams=params)
if result:
pointData["alphaMult"] = 0.0
except BaseException:
pass
return data
def processCurvePoints(self, data, cameraPoint, useCurveDistanceColor, colorMin, colorMax):
# type: (UserData, om.MPoint, bool, float, float) -> UserData
for curve in data.curveDataArray:
curve["distance"] = curve["position"].distanceTo(cameraPoint)
min_e = min(data.curveDataArray, key=lambda x: x["distance"]) # type: dict
max_e = max(data.curveDataArray, key=lambda x: x["distance"]) # type: dict
splitCurves = dict() # type: dict[int, list[dict]]
for curvePoint in data.curveDataArray:
if useCurveDistanceColor:
invLerp = 1 - (curvePoint["distance"] - min_e["distance"]) / (max_e["distance"] - min_e["distance"])
curvePoint["color"] = data.curveColor * max(invLerp * colorMax, colorMin)
splitCurves.setdefault(curvePoint["curveIndex"], []).append(curvePoint)
processedPointsArray = om.MPointArray()
processedColorsArray = om.MColorArray()
for curve in splitCurves.values():
for i in range(1, len(curve)):
processedPointsArray.append(om.MPoint(curve[i - 1]["position"]))
processedPointsArray.append(om.MPoint(curve[i]["position"]))
processedColorsArray.append(curve[i - 1]["color"])
processedColorsArray.append(curve[i]["color"])
data.processedCurvePoints.append(processedPointsArray)
data.processedCurveColors.append(processedColorsArray)
def processHullPoints(self, data, distanceColors, colorMin, colorMax):
# type: (UserData, bool, float, float) -> UserData
# Split points into different curves
filteredPoints = dict() # type: dict[int, dict[int, om.MPoint]]
filteredColors = dict() # type: dict[int, dict[int, om.MPoint]]
for point in data.pointDataArray:
filteredPoints.setdefault(point["curveIndex"], dict()).update({point["cvIndex"]: point["position"]})
color = om.MColor()
if point["isSelected"]:
if distanceColors:
color = data.selectedPointColor * max(point["weight"] * point["colorMult"] * colorMax, colorMin) # type: om.MColor
else:
color = data.selectedPointColor * max(point["weight"] * colorMax, colorMin) # type: om.MColor
else:
color = data.hullColor
if distanceColors:
color = data.hullColor * max(point["colorMult"] * colorMax, colorMin)
color.a = point["alphaMult"] * data.hullColor.a
filteredColors.setdefault(point["curveIndex"], dict()).update({point["cvIndex"]: color})
for i in range(len(filteredPoints)): # Curve index
try:
pointArray = om.MPointArray()
colorArray = om.MColorArray()
for j in range(1, len(filteredPoints[i])): # CV index
pointArray.append(om.MPoint(filteredPoints[i][j - 1]))
pointArray.append(om.MPoint(filteredPoints[i][j]))
colorArray.append(filteredColors[i][j - 1])
colorArray.append(filteredColors[i][j])
data.hullPosArray.append(pointArray)
data.hullColorArray.append(colorArray)
except BaseException:
pass
'''
# ------------ Init & UnInit Plugin ------------
def initializePlugin(obj):
plugin = om.MFnPlugin(obj, "GeorgeSladkovsky", "1.3", "Any")
try:
plugin.registerNode(
"GSCT_CurveTools_DrawManagerNode",
DrawManagerNode.id,
DrawManagerNode.creator,
DrawManagerNode.initialize,
om.MPxNode.kLocatorNode,
DrawManagerNode.drawDbClassification)
except BaseException:
sys.stderr.write("Failed to register node\n")
raise
try:
omr.MDrawRegistry.registerDrawOverrideCreator(
DrawManagerNode.drawDbClassification,
DrawManagerNode.drawRegistrantId,
DrawOverride.creator)
except BaseException:
sys.stderr.write("Failed to register override\n")
raise
def uninitializePlugin(obj):
global CALLBACK_IDS
om.MMessage.removeCallbacks(CALLBACK_IDS)
CALLBACK_IDS = []
plugin = om.MFnPlugin(obj)
try:
plugin.deregisterNode(DrawManagerNode.id)
except BaseException:
sys.stderr.write("Failed to deregister node\n")
raise
try:
omr.MDrawRegistry.deregisterGeometryOverrideCreator(DrawManagerNode.drawDbClassification, DrawManagerNode.drawRegistrantId)
except BaseException:
sys.stderr.write("Failed to deregister override\n")
raise
'''

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,62 @@
"""
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.1 Studio
Copyright 2023, George Sladkovsky (Yehor Sladkovskyi)
All Rights Reserved
Autodesk Maya is a property of Autodesk, Inc.
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
"""
from PySide2 import QtGui
def lerp(x, y0, y1, x0=0, x1=1):
""" Returns the value between y0 and y1 based on x in range of x0 and x1
https://en.wikipedia.org/wiki/Linear_interpolation"""
return float((y0 * (x1 - x) + y1 * (x - x0)) / (x1 - x0))
def quad(y1, y2, y3, fPoint, x1=0, x2=0.5, x3=1):
""" Takes three points (y1, y2, y3) and returns a point on computed curve with fPoint """
A = (x2 * (y1 - y3) + x3 * (y2 - y1)) / ((x1 - x2) * (x1 - x3) * (x2 - x3))
B = (y2 - y1) / (x2 - x1) - A * (x1 + x2)
C = y1
return (A * (fPoint**2)) + (B * fPoint) + C
def dot(v1, v2):
""" Dot product of two QVector2D """
return QtGui.QVector2D.dotProduct(v1, v2)
def projectPoint(A, B, P):
""" Project a point P onto a line made of two points A and B """
A = QtGui.QVector2D(A)
B = QtGui.QVector2D(B)
P = QtGui.QVector2D(P)
AP = A - P
AB = A - B
return (A - dot(AP, AB) / dot(AB, AB) * AB).toPointF()
def angleDiff(a1, a2):
""" Find an absolute angle difference within 360 degrees"""
a1 = a1 % 360
a2 = a2 % 360
return abs(a1) - abs(a2)

View File

@ -0,0 +1,79 @@
Show Hide UI|import gs_curvetools.main as ct_main;ct_main.main()|Show/Hide UI|GS.GS_CurveTools.UI|python
Reset to Defaults|import gs_curvetools.utils.utils as ct_utils;from imp import reload;reload(ct_utils);ct_utils.resetUI()|Reset the UI to Defaults|GS.GS_CurveTools.UI|python
Stop UI|import gs_curvetools.utils.utils as ct_utils;from imp import reload;reload(ct_utils);ct_utils.stopUI()|Close the UI and Stop Scripts|GS.GS_CurveTools.UI|python
New Card|gs_curvetools.core.create.new(0)|Create New Card|GS.GS_CurveTools.Create|python
New Tube|gs_curvetools.core.create.new(1)|Create New Tube|GS.GS_CurveTools.Create|python
Curve Card|gs_curvetools.core.create.multiple(0)|Convert to Card|GS.GS_CurveTools.Create|python
Curve Tube|gs_curvetools.core.create.multiple(1)|Convert to Tube|GS.GS_CurveTools.Create|python
Bind|gs_curvetools.core.create.bind(1)|Bind Curves or Geo|GS.GS_CurveTools.Create|python
Bind Duplicate|gs_curvetools.core.create.bind(2)|Bind Curves or Geo with duplication|GS.GS_CurveTools.Create|python
Unbind|gs_curvetools.core.create.unbind()|Unbind Curves|GS.GS_CurveTools.Create|python
Add Card|gs_curvetools.core.create.populate(0,1)|Add Cards between selection|GS.GS_CurveTools.Create|python
Add Card No Blend|gs_curvetools.core.create.populate(0,2)|Add Cards between selection without attribute blend|GS.GS_CurveTools.Create|python
Add Tube|gs_curvetools.core.create.populate(1,1)|Add Tubes between selection|GS.GS_CurveTools.Create|python
Add Tube No Blend|gs_curvetools.core.create.populate(1,2)|Add Tubes between selection without attribute blend|GS.GS_CurveTools.Create|python
Fill|gs_curvetools.core.create.fill(1)|Duplicate Fill between selection|GS.GS_CurveTools.Create|python
Fill No Blend|gs_curvetools.core.create.fill(2)|Duplicate Fill between selection without attribute blend|GS.GS_CurveTools.Create|python
Subdivide|gs_curvetools.core.subdivideCurve(1)|Subdivide Selected Card/Tube|GS.GS_CurveTools.Create|python
Subdivide Duplicate|gs_curvetools.core.subdivideCurve(2)|Subdivide Selected Card/Tube and Duplicate|GS.GS_CurveTools.Create|python
Edge to Curve|gs_curvetools.core.edgeToCurve()|Convert Edges to Curves|GS.GS_CurveTools.Create|python
Card to Curve|gs_curvetools.core.cardToCurve()|Convert Cards to Curves|GS.GS_CurveTools.Create|python
Add Layer Collection|gs_curvetools.core.layerCollections.createLayerCollection()|Adds a New Layer Collection With the Specified Name|GS.GS_CurveTools.LayerCollections|python
Delete Layer Collection|gs_curvetools.core.layerCollections.deleteLayerCollection()|Deletes Current Layer Collection|GS.GS_CurveTools.LayerCollections|python
Clear Layer Collection|gs_curvetools.core.layerCollections.clear()|Deletes All The Curves from the Current Layer Collection|GS.GS_CurveTools.LayerCollections|python
Rename Layer Collection|gs_curvetools.core.layerCollections.rename()|Renames the Current Layer Collection|GS.GS_CurveTools.LayerCollections|python
Merge Up Layer Collection|gs_curvetools.core.layerCollections.mergeUp()|Merges the Current Layer Collection With One Above It|GS.GS_CurveTools.LayerCollections|python
Merge Down Layer Collection|gs_curvetools.core.layerCollections.mergeDown()|Merges the Current Layer Collection With One Below It|GS.GS_CurveTools.LayerCollections|python
Move Up Layer Collection|gs_curvetools.core.layerCollections.moveUp()|Moves the Current Layer Collection Up One Index|GS.GS_CurveTools.LayerCollections|python
Move Down Layer Collection|gs_curvetools.core.layerCollections.moveDown()|Moves the Current Layer Collection Down One Index|GS.GS_CurveTools.LayerCollections|python
Copy Layer Collection|gs_curvetools.core.layerCollections.copy()|Copies Curves from Current Layer Collection|GS.GS_CurveTools.LayerCollections|python
Paste Layer Collection|gs_curvetools.core.layerCollections.paste()|Pastes Copied Curves to Current Layer Collection|GS.GS_CurveTools.LayerCollections|python
Filter All Show|gs_curvetools.core.layersFilterToggle(True, True, hotkey=True)|Show All Layers|GS.GS_CurveTools.Layers|python
Filter All Show All Collections|"gs_curvetools.core.layersFilterToggle(True, True, hotkey=True, mod=""Ctrl"")"|Show All Layers in All Collections|GS.GS_CurveTools.Layers|python
Filter All Hide|gs_curvetools.core.layersFilterToggle(False, False, hotkey=True)|Hide All Layers|GS.GS_CurveTools.Layers|python
Filter All Hide All Collections|"gs_curvetools.core.layersFilterToggle(True, True, hotkey=True, mod=""Shift+Ctrl"")"|Hide All Layers in All Collections|GS.GS_CurveTools.Layers|python
Filter Curve|gs_curvetools.core.layersFilterToggle(True, False, hotkey=True)|Show Only Curve Component|GS.GS_CurveTools.Layers|python
Filter Curve in All Collections|"gs_curvetools.core.layersFilterToggle(True, False, hotkey=True, mod=""Ctrl"")"|Show Only Curve Component in All Collections|GS.GS_CurveTools.Layers|python
Toggle Always on Top|gs_curvetools.core.alwaysOnTopToggle()|Toggle Always on Top Mode for Curves|GS.GS_CurveTools.Layers|python
Filter Geo|gs_curvetools.core.layersFilterToggle(False, True, hotkey=True)|Show Only Geo Component|GS.GS_CurveTools.Layers|python
Filter Geo in All Collections|"gs_curvetools.core.layersFilterToggle(False, True, hotkey=True, mod=""Ctrl"")"|Show Only Geo Component in All Collections|GS.GS_CurveTools.Layers|python
Toggle Color|gs_curvetools.core.toggleColor.toggleColorVis()|Toggle Color Mode|GS.GS_CurveTools.Layers|python
Extract Selected|"gs_curvetools.core.extractSelectedCurves(""Shift"", hotkey=True)"|Extract Selected Geo|GS.GS_CurveTools.Layers|python
Extract Selected Medged|gs_curvetools.core.extractSelectedCurves(hotkey=True)|Extract Selected Geo Merged|GS.GS_CurveTools.Layers|python
Extract All|"gs_curvetools.core.extractAllCurves(""Shift"", hotkey=True)"|Extract All Geo|GS.GS_CurveTools.Layers|python
Extract All Medged|gs_curvetools.core.extractAllCurves(hotkey=True)|Extract All Geo Merged|GS.GS_CurveTools.Layers|python
Select Curve|gs_curvetools.core.selectPart(1)|Select Curve Component|GS.GS_CurveTools.Selection|python
Select Geo|gs_curvetools.core.selectPart(2)|Select Geo Component|GS.GS_CurveTools.Selection|python
Select Group|gs_curvetools.core.selectPart(0)|Select Group|GS.GS_CurveTools.Selection|python
Group Curves|gs_curvetools.core.groupCurves()|Group Selected Curves|GS.GS_CurveTools.Selection|python
Regroup by Layer|gs_curvetools.core.regroupByLayer()|Regroup using Layers|GS.GS_CurveTools.Selection|python
Transfer Attr Forward|gs_curvetools.core.attributes.transferAttr(1)|Transfer Attributes Forward|GS.GS_CurveTools.Utilities|python
Transfer Attr Backwards|gs_curvetools.core.attributes.transferAttr(2)|Transfer Attributes Backwards|GS.GS_CurveTools.Utilities|python
Copy Attributes|gs_curvetools.core.attributes.copyAttributes()|Copy attributes from selected curves|GS.GS_CurveTools.Utilities|python
Paste Attributes|gs_curvetools.core.attributes.pasteAttributes()|Paste attributes to selected curves|GS.GS_CurveTools.Utilities|python
Transfer UVs Forward|gs_curvetools.core.attributes.transferUV(1)|Transfer UVs Forward|GS.GS_CurveTools.Utilities|python
Transfer UVs Backwards|gs_curvetools.core.attributes.transferUV(2)|Transfer UVs Backwards|GS.GS_CurveTools.Utilities|python
Copy UVs|gs_curvetools.core.attributes.copyUVs()|Copy UVs from selected curves|GS.GS_CurveTools.Utilities|python
Paste UVs|gs_curvetools.core.attributes.pasteUVs()|Paste UVs to selected curves|GS.GS_CurveTools.Utilities|python
Reset Pivot to Root|gs_curvetools.core.resetCurvePivotPoint(1)|Reset Pivot Point to Root|GS.GS_CurveTools.Utilities|python
Reset Pivot to Tip|gs_curvetools.core.resetCurvePivotPoint(2)|Reset Pivot Point to Tip|GS.GS_CurveTools.Utilities|python
Rebuild Curves|gs_curvetools.core.sliders.rebuildButtonClicked()|Rebuild Selected Curves|GS.GS_CurveTools.Utilities|python
Duplicate|gs_curvetools.core.duplicateCurve()|Duplicate Selected Curves|GS.GS_CurveTools.Utilities|python
Delete Curves|gs_curvetools.core.deleteSelectedCurves()|Delete selected curves and objects|GS.GS_CurveTools.Utilities|python
Extend|gs_curvetools.core.extendCurve()|Extend Curves|GS.GS_CurveTools.Utilities|python
Reduce|gs_curvetools.core.reduceCurve()|Reduce Curves|GS.GS_CurveTools.Utilities|python
Smooth|gs_curvetools.core.smoothCurve()|Smooth Curves|GS.GS_CurveTools.Utilities|python
Orient to Normal|gs_curvetools.core.orientToFaceNormals()|Orients Selected Curve to Scalp Normals|GS.GS_CurveTools.Utilities|python
Mirror X|gs_curvetools.core.mirrorHair(0)|Mirror on X Axis|GS.GS_CurveTools.Mirror|python
Mirror Y|gs_curvetools.core.mirrorHair(1)|Mirror on Y Axis|GS.GS_CurveTools.Mirror|python
Mirror Z|gs_curvetools.core.mirrorHair(2)|Mirror on Z Axis|GS.GS_CurveTools.Mirror|python
Flip X|gs_curvetools.core.mirrorHair(0,1)|Flip on X Axis|GS.GS_CurveTools.Mirror|python
Flip Y|gs_curvetools.core.mirrorHair(1,1)|Flip on Y Axis|GS.GS_CurveTools.Mirror|python
Flip Z|gs_curvetools.core.mirrorHair(2,1)|Flip on Z Axis|GS.GS_CurveTools.Mirror|python
Add Control Curve|gs_curvetools.core.controlCurveCreate()|Add Control Curve|GS.GS_CurveTools.Control|python
Apply Control Cuve|gs_curvetools.core.controlCurveApply()|Apply Control Curve|GS.GS_CurveTools.Control|python
Curve Control Window|gs_curvetools.ui.curveControlWorkspace()|Open Curve Control Window|GS.GS_CurveTools.Control|python
UV Editor Window|gs_curvetools.ui.uvEditorWorkspace()|Open UV Editor Window|GS.GS_CurveTools.Control|python
AO Toggle|gs_curvetools.utils.utils.AOToggle()|Toggle Viewport AO|GS.GS_CurveTools.Misc|python
Advanced Visibility Toggle|gs_curvetools.core.advancedVisibility.toggleCurveHighlightFromUI(True)|Toggle Advanced Visibility|GS.GS_CurveTools.Misc|python
Geometry Highlight Toggle|gs_curvetools.core.advancedVisibility.geometryHighlightCommand()|Toggle Geometry Highlight|GS.GS_CurveTools.Misc|python
1 Show Hide UI import gs_curvetools.main as ct_main;ct_main.main() Show/Hide UI GS.GS_CurveTools.UI python
2 Reset to Defaults import gs_curvetools.utils.utils as ct_utils;from imp import reload;reload(ct_utils);ct_utils.resetUI() Reset the UI to Defaults GS.GS_CurveTools.UI python
3 Stop UI import gs_curvetools.utils.utils as ct_utils;from imp import reload;reload(ct_utils);ct_utils.stopUI() Close the UI and Stop Scripts GS.GS_CurveTools.UI python
4 New Card gs_curvetools.core.create.new(0) Create New Card GS.GS_CurveTools.Create python
5 New Tube gs_curvetools.core.create.new(1) Create New Tube GS.GS_CurveTools.Create python
6 Curve Card gs_curvetools.core.create.multiple(0) Convert to Card GS.GS_CurveTools.Create python
7 Curve Tube gs_curvetools.core.create.multiple(1) Convert to Tube GS.GS_CurveTools.Create python
8 Bind gs_curvetools.core.create.bind(1) Bind Curves or Geo GS.GS_CurveTools.Create python
9 Bind Duplicate gs_curvetools.core.create.bind(2) Bind Curves or Geo with duplication GS.GS_CurveTools.Create python
10 Unbind gs_curvetools.core.create.unbind() Unbind Curves GS.GS_CurveTools.Create python
11 Add Card gs_curvetools.core.create.populate(0,1) Add Cards between selection GS.GS_CurveTools.Create python
12 Add Card No Blend gs_curvetools.core.create.populate(0,2) Add Cards between selection without attribute blend GS.GS_CurveTools.Create python
13 Add Tube gs_curvetools.core.create.populate(1,1) Add Tubes between selection GS.GS_CurveTools.Create python
14 Add Tube No Blend gs_curvetools.core.create.populate(1,2) Add Tubes between selection without attribute blend GS.GS_CurveTools.Create python
15 Fill gs_curvetools.core.create.fill(1) Duplicate Fill between selection GS.GS_CurveTools.Create python
16 Fill No Blend gs_curvetools.core.create.fill(2) Duplicate Fill between selection without attribute blend GS.GS_CurveTools.Create python
17 Subdivide gs_curvetools.core.subdivideCurve(1) Subdivide Selected Card/Tube GS.GS_CurveTools.Create python
18 Subdivide Duplicate gs_curvetools.core.subdivideCurve(2) Subdivide Selected Card/Tube and Duplicate GS.GS_CurveTools.Create python
19 Edge to Curve gs_curvetools.core.edgeToCurve() Convert Edges to Curves GS.GS_CurveTools.Create python
20 Card to Curve gs_curvetools.core.cardToCurve() Convert Cards to Curves GS.GS_CurveTools.Create python
21 Add Layer Collection gs_curvetools.core.layerCollections.createLayerCollection() Adds a New Layer Collection With the Specified Name GS.GS_CurveTools.LayerCollections python
22 Delete Layer Collection gs_curvetools.core.layerCollections.deleteLayerCollection() Deletes Current Layer Collection GS.GS_CurveTools.LayerCollections python
23 Clear Layer Collection gs_curvetools.core.layerCollections.clear() Deletes All The Curves from the Current Layer Collection GS.GS_CurveTools.LayerCollections python
24 Rename Layer Collection gs_curvetools.core.layerCollections.rename() Renames the Current Layer Collection GS.GS_CurveTools.LayerCollections python
25 Merge Up Layer Collection gs_curvetools.core.layerCollections.mergeUp() Merges the Current Layer Collection With One Above It GS.GS_CurveTools.LayerCollections python
26 Merge Down Layer Collection gs_curvetools.core.layerCollections.mergeDown() Merges the Current Layer Collection With One Below It GS.GS_CurveTools.LayerCollections python
27 Move Up Layer Collection gs_curvetools.core.layerCollections.moveUp() Moves the Current Layer Collection Up One Index GS.GS_CurveTools.LayerCollections python
28 Move Down Layer Collection gs_curvetools.core.layerCollections.moveDown() Moves the Current Layer Collection Down One Index GS.GS_CurveTools.LayerCollections python
29 Copy Layer Collection gs_curvetools.core.layerCollections.copy() Copies Curves from Current Layer Collection GS.GS_CurveTools.LayerCollections python
30 Paste Layer Collection gs_curvetools.core.layerCollections.paste() Pastes Copied Curves to Current Layer Collection GS.GS_CurveTools.LayerCollections python
31 Filter All Show gs_curvetools.core.layersFilterToggle(True, True, hotkey=True) Show All Layers GS.GS_CurveTools.Layers python
32 Filter All Show All Collections gs_curvetools.core.layersFilterToggle(True, True, hotkey=True, mod="Ctrl") Show All Layers in All Collections GS.GS_CurveTools.Layers python
33 Filter All Hide gs_curvetools.core.layersFilterToggle(False, False, hotkey=True) Hide All Layers GS.GS_CurveTools.Layers python
34 Filter All Hide All Collections gs_curvetools.core.layersFilterToggle(True, True, hotkey=True, mod="Shift+Ctrl") Hide All Layers in All Collections GS.GS_CurveTools.Layers python
35 Filter Curve gs_curvetools.core.layersFilterToggle(True, False, hotkey=True) Show Only Curve Component GS.GS_CurveTools.Layers python
36 Filter Curve in All Collections gs_curvetools.core.layersFilterToggle(True, False, hotkey=True, mod="Ctrl") Show Only Curve Component in All Collections GS.GS_CurveTools.Layers python
37 Toggle Always on Top gs_curvetools.core.alwaysOnTopToggle() Toggle Always on Top Mode for Curves GS.GS_CurveTools.Layers python
38 Filter Geo gs_curvetools.core.layersFilterToggle(False, True, hotkey=True) Show Only Geo Component GS.GS_CurveTools.Layers python
39 Filter Geo in All Collections gs_curvetools.core.layersFilterToggle(False, True, hotkey=True, mod="Ctrl") Show Only Geo Component in All Collections GS.GS_CurveTools.Layers python
40 Toggle Color gs_curvetools.core.toggleColor.toggleColorVis() Toggle Color Mode GS.GS_CurveTools.Layers python
41 Extract Selected gs_curvetools.core.extractSelectedCurves("Shift", hotkey=True) Extract Selected Geo GS.GS_CurveTools.Layers python
42 Extract Selected Medged gs_curvetools.core.extractSelectedCurves(hotkey=True) Extract Selected Geo Merged GS.GS_CurveTools.Layers python
43 Extract All gs_curvetools.core.extractAllCurves("Shift", hotkey=True) Extract All Geo GS.GS_CurveTools.Layers python
44 Extract All Medged gs_curvetools.core.extractAllCurves(hotkey=True) Extract All Geo Merged GS.GS_CurveTools.Layers python
45 Select Curve gs_curvetools.core.selectPart(1) Select Curve Component GS.GS_CurveTools.Selection python
46 Select Geo gs_curvetools.core.selectPart(2) Select Geo Component GS.GS_CurveTools.Selection python
47 Select Group gs_curvetools.core.selectPart(0) Select Group GS.GS_CurveTools.Selection python
48 Group Curves gs_curvetools.core.groupCurves() Group Selected Curves GS.GS_CurveTools.Selection python
49 Regroup by Layer gs_curvetools.core.regroupByLayer() Regroup using Layers GS.GS_CurveTools.Selection python
50 Transfer Attr Forward gs_curvetools.core.attributes.transferAttr(1) Transfer Attributes Forward GS.GS_CurveTools.Utilities python
51 Transfer Attr Backwards gs_curvetools.core.attributes.transferAttr(2) Transfer Attributes Backwards GS.GS_CurveTools.Utilities python
52 Copy Attributes gs_curvetools.core.attributes.copyAttributes() Copy attributes from selected curves GS.GS_CurveTools.Utilities python
53 Paste Attributes gs_curvetools.core.attributes.pasteAttributes() Paste attributes to selected curves GS.GS_CurveTools.Utilities python
54 Transfer UVs Forward gs_curvetools.core.attributes.transferUV(1) Transfer UVs Forward GS.GS_CurveTools.Utilities python
55 Transfer UVs Backwards gs_curvetools.core.attributes.transferUV(2) Transfer UVs Backwards GS.GS_CurveTools.Utilities python
56 Copy UVs gs_curvetools.core.attributes.copyUVs() Copy UVs from selected curves GS.GS_CurveTools.Utilities python
57 Paste UVs gs_curvetools.core.attributes.pasteUVs() Paste UVs to selected curves GS.GS_CurveTools.Utilities python
58 Reset Pivot to Root gs_curvetools.core.resetCurvePivotPoint(1) Reset Pivot Point to Root GS.GS_CurveTools.Utilities python
59 Reset Pivot to Tip gs_curvetools.core.resetCurvePivotPoint(2) Reset Pivot Point to Tip GS.GS_CurveTools.Utilities python
60 Rebuild Curves gs_curvetools.core.sliders.rebuildButtonClicked() Rebuild Selected Curves GS.GS_CurveTools.Utilities python
61 Duplicate gs_curvetools.core.duplicateCurve() Duplicate Selected Curves GS.GS_CurveTools.Utilities python
62 Delete Curves gs_curvetools.core.deleteSelectedCurves() Delete selected curves and objects GS.GS_CurveTools.Utilities python
63 Extend gs_curvetools.core.extendCurve() Extend Curves GS.GS_CurveTools.Utilities python
64 Reduce gs_curvetools.core.reduceCurve() Reduce Curves GS.GS_CurveTools.Utilities python
65 Smooth gs_curvetools.core.smoothCurve() Smooth Curves GS.GS_CurveTools.Utilities python
66 Orient to Normal gs_curvetools.core.orientToFaceNormals() Orients Selected Curve to Scalp Normals GS.GS_CurveTools.Utilities python
67 Mirror X gs_curvetools.core.mirrorHair(0) Mirror on X Axis GS.GS_CurveTools.Mirror python
68 Mirror Y gs_curvetools.core.mirrorHair(1) Mirror on Y Axis GS.GS_CurveTools.Mirror python
69 Mirror Z gs_curvetools.core.mirrorHair(2) Mirror on Z Axis GS.GS_CurveTools.Mirror python
70 Flip X gs_curvetools.core.mirrorHair(0,1) Flip on X Axis GS.GS_CurveTools.Mirror python
71 Flip Y gs_curvetools.core.mirrorHair(1,1) Flip on Y Axis GS.GS_CurveTools.Mirror python
72 Flip Z gs_curvetools.core.mirrorHair(2,1) Flip on Z Axis GS.GS_CurveTools.Mirror python
73 Add Control Curve gs_curvetools.core.controlCurveCreate() Add Control Curve GS.GS_CurveTools.Control python
74 Apply Control Cuve gs_curvetools.core.controlCurveApply() Apply Control Curve GS.GS_CurveTools.Control python
75 Curve Control Window gs_curvetools.ui.curveControlWorkspace() Open Curve Control Window GS.GS_CurveTools.Control python
76 UV Editor Window gs_curvetools.ui.uvEditorWorkspace() Open UV Editor Window GS.GS_CurveTools.Control python
77 AO Toggle gs_curvetools.utils.utils.AOToggle() Toggle Viewport AO GS.GS_CurveTools.Misc python
78 Advanced Visibility Toggle gs_curvetools.core.advancedVisibility.toggleCurveHighlightFromUI(True) Toggle Advanced Visibility GS.GS_CurveTools.Misc python
79 Geometry Highlight Toggle gs_curvetools.core.advancedVisibility.geometryHighlightCommand() Toggle Geometry Highlight GS.GS_CurveTools.Misc python

View File

@ -0,0 +1,786 @@
"""
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.1 Studio
Copyright 2023, George Sladkovsky (Yehor Sladkovskyi)
All Rights Reserved
Autodesk Maya is a property of Autodesk, Inc.
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
"""
import math
from imp import reload
import maya.cmds as mc
from . import utils
reload(utils)
# Scaling
MULT = 1
if mc.about(mac=True):
MULT = 1
else:
MULT = mc.mayaDpiSetting(q=1, rsv=1)
# mult = 1
def scale(a):
# types (int\float) -> (float)
if isinstance(a, list):
return [i * MULT for i in a]
else:
return a * MULT
# Fonts
NORMAL_FONT = 11 * MULT
SMALL_FONT = 10 * MULT
# Normal Button
BUTTON_HEIGHT = 24 * MULT
BORDER_RADIUS = 4 * MULT
BORDER_WIDTH = 2 * MULT
BACKGROUND_COLOR = 'rgb(93,93,93)'
PRESSED_BACKGROUND_COLOR = 'rgb(128,128,128)'
HOVER_BACKGROUND_COLOR = 'rgb(100,100,100)'
# Active Button (Orange)
ORANGE = 'rgb(219,148,86)'
ORANGE_HOVER = 'rgb(225,155,90)'
ORANGE_PRESSED = 'rgb(235,170,105)'
# Active Button (Blue)
BLUE = 'rgb(82,133,166)'
BLUE_HOVER = 'rgb(95,145,185)'
# Active Button (White)
BORDER_WIDTH_WHITE = 2 * MULT
# Small Button
SMALL_BORDER_RADIUS = 4 * MULT
SMALL_BORDER_WIDTH = 1 * MULT
SMALL_BORDER_COLOR = 'rgb(93,93,93)'
SMALL_BACKGROUND_COLOR = 'rgba(93,93,93,0)'
SMALL_PRESSED_COLOR = 'rgba(128,128,128,255)'
SMALL_HOVER_COLOR = 'rgb(100,100,100)'
# Layer/Set Button
LAYER_BOTTOM_PADDING = 1 * MULT
LAYER_BORDER_RADIUS = 4 * MULT
LAYER_HEIGHT = 16 * MULT
LAYER_BORDER_WIDTH = 1 * MULT
# Images/Icons
DROP_DOWN_ARROW_IMAGE = utils.getFolder.icons() + 'drop-down-arrow.png'
### Regular Buttons ###
buttonNormal = '''
QPushButton {{
background-color: {bg_clr};
border-radius: {br};
min-height: {h}px;
max-height: {h}px;
}}
QPushButton:pressed {{
background-color: {pr_bg_clr};
}}
QPushButton:checked {{
background-color: {ac_bg_clr};
}}
QPushButton:hover {{
background-color: {h_bg_clr};
}}
}}
'''.format(br=int(BORDER_RADIUS),
h=int(BUTTON_HEIGHT),
bg_clr=str(BACKGROUND_COLOR),
pr_bg_clr=str(PRESSED_BACKGROUND_COLOR),
h_bg_clr=str(HOVER_BACKGROUND_COLOR),
ac_bg_clr=BLUE)
buttonNormalBlueBorder = '''
QPushButton
{{
background-color: {bg_clr};
border-radius: {br};
border-color: {borderBlue};
border-style: solid;
border-width: {bw}px;
padding: -{bw}px;
min-height: {h}px;
max-height: {h}px;
}}
QPushButton:pressed
{{
background-color: {pr_bg_clr};
}}
QPushButton:checked
{{
background-color: {ac_bg_clr};
}}
QPushButton:hover
{{
background-color: {h_bg_clr};
}}
'''.format(br=int(BORDER_RADIUS),
h=int(BUTTON_HEIGHT),
bw=int(BORDER_WIDTH),
bg_clr=str(BACKGROUND_COLOR),
pr_bg_clr=str(PRESSED_BACKGROUND_COLOR),
h_bg_clr=str(HOVER_BACKGROUND_COLOR),
borderBlue=str(BLUE),
ac_bg_clr=BLUE)
buttonIcon = '''
QPushButton
{{
background-color: none;
border-radius: {br}px;
border:none;
}}
QPushButton:checked
{{
background-color: none;
border:none;
}}
QPushButton:pressed
{{
background-color: {pr_bg_clr};
}}
QPushButton:checked:pressed
{{
background-color: {pr_bg_clr};
}}
QPushButton:checked:hover
{{
background-color: {h_bg_clr};
}}
QPushButton:hover
{{
background-color: {h_bg_clr};
}}
}}
'''.format(br=int(BORDER_RADIUS / 2),
h=int(BUTTON_HEIGHT),
bg_clr=str(BACKGROUND_COLOR),
pr_bg_clr=str(PRESSED_BACKGROUND_COLOR),
h_bg_clr=str(HOVER_BACKGROUND_COLOR),
ac_bg_clr=BLUE)
buttonActiveOrange = '''
QPushButton
{{
background-color: {bg_clr};
border-radius: {br}px;
min-height: {h}px;
max-height: {h}px;
}}
QPushButton:pressed
{{
background-color: {pr_bg_clr};
}}
QPushButton:hover
{{
background-color: {h_bg_clr};
}}
'''.format(br=int(BORDER_RADIUS),
h=int(BUTTON_HEIGHT),
bg_clr=ORANGE,
pr_bg_clr=str(ORANGE_PRESSED),
h_bg_clr=str(ORANGE_HOVER),
ac_bg_clr=BLUE)
buttonActiveBlue = '''
QPushButton
{{
background-color: {bg_clr};
border-radius: {br}px;
min-height: {h}px;
max-height: {h}px;
}}
QPushButton:pressed
{{
background-color: {pr_bg_clr};
}}
QPushButton:hover
{{
background-color: {h_bg_clr};
}}
'''.format(br=int(BORDER_RADIUS),
h=int(BUTTON_HEIGHT),
bg_clr=BLUE,
pr_bg_clr=str(PRESSED_BACKGROUND_COLOR),
h_bg_clr=str(HOVER_BACKGROUND_COLOR),
ac_bg_clr=BLUE)
buttonActiveWhite = '''
QPushButton
{{
background-color: {bg_clr};
border-radius: {br}px;
border-width: {bw}px;
border-color: white;
border-style: solid;
padding: -{bw}px;
min-height: {h}px;
max-height: {h}px;
}}
QPushButton:pressed
{{
background-color: {pr_bg_clr};
}}
QPushButton:hover
{{
background-color: {bg_clr};
}}
'''.format(br=int(BORDER_RADIUS),
h=int(BUTTON_HEIGHT),
bw=int(BORDER_WIDTH_WHITE),
bg_clr=ORANGE,
pr_bg_clr=str(PRESSED_BACKGROUND_COLOR))
frameButton = '''
QPushButton
{{
background-color: {bg_clr};
border-width: {bw}px;
border-radius: {br}px;
border-color: {br_clr};
border-style: solid;
}}
QPushButton:pressed
{{
background-color: {pr_bg_clr};
}}
QPushButton:checked
{{
background-color: {ac_bg_clr};
border-color: lightgrey;
border-style: solid;
border-width: {bw}px;
border-top-left-radius: {br}px;
border-top-right-radius: {br}px;
border-bottom-left-radius: 0px;
border-bottom-right-radius: 0px;
}}
QPushButton:disabled
{{
border-style: none;
}}
QPushButton:disabled:checked
{{
background-color: {bg_clr};
border-style: none;
}}
QPushButton:hover
{{
background-color: {h_bg_clr};
}}
}}
'''.format(br=4 * MULT,
br_clr=SMALL_BORDER_COLOR,
bw=int(SMALL_BORDER_WIDTH),
bg_clr=BACKGROUND_COLOR,
pr_bg_clr=SMALL_PRESSED_COLOR,
h_bg_clr=SMALL_HOVER_COLOR,
ac_bg_clr=BLUE)
frameButtonNotCollapsable = '''
QPushButton
{{
background-color: {bg_clr};
border-style: solid;
border-width: {bw}px;
border-top-left-radius: {br}px;
border-top-right-radius: {br}px;
border-bottom-left-radius: 0px;
border-bottom-right-radius: 0px;
}}
'''.format(br=4 * MULT,
bw=int(SMALL_BORDER_WIDTH),
bg_clr=BACKGROUND_COLOR)
smallNormal = '''
QPushButton {{
background-color: {bg_clr};
border-width: {bw}px;
border-radius: {br}px;
border-color: {br_clr};
border-style: solid;
}}
QPushButton:pressed {{
background-color: {pr_bg_clr};
}}
QPushButton:checked {{
background-color: {ac_bg_clr};
border-color: lightgrey;
border-style: solid;
border-width: {bw}px;
}}
QPushButton:disabled {{
border-style: none;
}}
QPushButton:disabled:checked {{
background-color: {bg_clr};
border-style: none;
}}
QPushButton:hover {{
background-color: {h_bg_clr};
}}
QPushButton:checked:hover {{
background-color: {h_bg_checked_clr};
}}
}}
'''.format(br=int(SMALL_BORDER_RADIUS),
br_clr=SMALL_BORDER_COLOR,
bw=int(SMALL_BORDER_WIDTH),
bg_clr=SMALL_BACKGROUND_COLOR,
pr_bg_clr=SMALL_PRESSED_COLOR,
h_bg_clr=SMALL_HOVER_COLOR,
h_bg_checked_clr=BLUE_HOVER,
ac_bg_clr=BLUE)
smallFilled = '''
QPushButton
{{
background-color: {bg_clr};
border-width: {bw}px;
border-radius: {br}px;
border-color: {br_clr};
border-style: solid;
}}
QPushButton:pressed
{{
background-color: {pr_bg_clr};
}}
QPushButton:checked
{{
background-color: {ac_bg_clr};
border-color: lightgrey;
border-style: solid;
border-width: {bw}px;
}}
QPushButton:disabled
{{
border-style: none;
}}
QPushButton:disabled:checked
{{
background-color: {bg_clr};
border-style: none;
}}
QPushButton:hover
{{
background-color: {h_bg_clr};
}}
}}
'''.format(br=int(SMALL_BORDER_RADIUS),
br_clr=SMALL_BORDER_COLOR,
bw=int(SMALL_BORDER_WIDTH),
bg_clr=BACKGROUND_COLOR,
pr_bg_clr=SMALL_PRESSED_COLOR,
h_bg_clr=SMALL_HOVER_COLOR,
ac_bg_clr=BLUE)
smallFilledBlue = '''
QPushButton
{{
background-color: {bg_clr};
border-width: {bw}px;
border-radius: {br}px;
border-color: {br_clr};
border-style: solid;
}}
QPushButton:pressed
{{
background-color: {pr_bg_clr};
}}
QPushButton:checked
{{
background-color: {ac_bg_clr};
border-color: lightgrey;
border-style: solid;
border-width: {bw}px;
}}
QPushButton:disabled
{{
border-style: none;
}}
QPushButton:disabled:checked
{{
background-color: {bg_clr};
border-style: none;
}}
QPushButton:hover
{{
background-color: {h_bg_clr};
}}
'''.format(br=int(SMALL_BORDER_RADIUS),
br_clr=BLUE,
bw=int(SMALL_BORDER_WIDTH),
bg_clr=BLUE,
pr_bg_clr=SMALL_PRESSED_COLOR,
h_bg_clr=BLUE_HOVER,
ac_bg_clr=BLUE)
smallNoBorder = '''
QPushButton
{{
background-color: {bg_clr};
border-width: 0px;
border-radius: {br}px;
border-color: {br_clr};
border-style: solid;
}}
QPushButton:pressed
{{
background-color: {pr_bg_clr};
}}
QPushButton:checked
{{
color: rgb(255,0,0)
background-color: {ac_bg_clr};
border-color: lightgrey;
border-style: solid;
border-width: {bw}px;
}}
QPushButton:disabled
{{
color: rgb(255,0,0);
border-style: none;
}}
QPushButton:disabled:checked
{{
background-color: {bg_clr};
border-style: none;
}}
QPushButton:hover
{{
background-color: {h_bg_clr};
}}
'''.format(br=int(SMALL_BORDER_RADIUS),
br_clr=SMALL_BORDER_COLOR,
bw=int(SMALL_BORDER_WIDTH),
bg_clr=SMALL_BACKGROUND_COLOR,
pr_bg_clr=SMALL_PRESSED_COLOR,
h_bg_clr=SMALL_HOVER_COLOR,
ac_bg_clr=BLUE)
sliderLabel = '''
QPushButton
{{
background-color: {bg_clr};
border-width: 0px;
border-radius: {br}px;
border-color: {br_clr};
border-style: solid;
}}
QPushButton:pressed
{{
background-color: {bg_clr};
}}
QPushButton:checked
{{
color: rgb(255,0,0)
background-color: {ac_bg_clr};
border-color: lightgrey;
border-style: solid;
border-width: {bw}px;
}}
QPushButton:disabled
{{
color: rgb(255,0,0);
border-style: none;
}}
QPushButton:disabled:checked
{{
background-color: {bg_clr};
border-style: none;
}}
QPushButton:hover
{{
background-color: {h_bg_clr};
}}
'''.format(br=int(SMALL_BORDER_RADIUS),
br_clr=SMALL_BORDER_COLOR,
bw=int(SMALL_BORDER_WIDTH),
bg_clr=SMALL_BACKGROUND_COLOR,
pr_bg_clr=SMALL_PRESSED_COLOR,
h_bg_clr=SMALL_HOVER_COLOR,
ac_bg_clr=BLUE)
### Compound Buttons ###
smallCompoundTopLeft = '''
QPushButton {{
background-color: {bg_clr};
border-width: {bw}px;
border-top-left-radius: {br}px;
border-top-right-radius: 0px;
border-bottom-left-radius: 0px;
border-bottom-right-radius: 0px;
border-color: {br_clr};
border-style: solid;
}}
QPushButton:pressed {{
background-color: {pr_bg_clr};
}}
QPushButton:checked {{
background-color: {ac_bg_clr};
border-color: lightgrey;
border-style: solid;
border-width: {bw}px;
}}
QPushButton:disabled {{
border-style: none;
}}
QPushButton:disabled:checked {{
background-color: {bg_clr};
border-style: none;
}}
QPushButton:hover {{
background-color: {h_bg_clr};
}}
QPushButton:checked:hover {{
background-color: {h_bg_checked_clr};
}}
}}
'''.format(br=int(SMALL_BORDER_RADIUS),
br_clr=SMALL_BORDER_COLOR,
bw=int(SMALL_BORDER_WIDTH),
bg_clr=SMALL_BACKGROUND_COLOR,
pr_bg_clr=SMALL_PRESSED_COLOR,
h_bg_clr=SMALL_HOVER_COLOR,
h_bg_checked_clr=BLUE_HOVER,
ac_bg_clr=BLUE)
smallCompoundTopRight = '''
QPushButton {{
background-color: {bg_clr};
border-width: {bw}px;
border-top-left-radius: 0px;
border-top-right-radius: {br}px;
border-bottom-left-radius: 0px;
border-bottom-right-radius: 0px;
border-color: {br_clr};
border-style: solid;
}}
QPushButton:pressed {{
background-color: {pr_bg_clr};
}}
QPushButton:checked {{
background-color: {ac_bg_clr};
border-color: lightgrey;
border-style: solid;
border-width: {bw}px;
}}
QPushButton:disabled {{
border-style: none;
}}
QPushButton:disabled:checked {{
background-color: {bg_clr};
border-style: none;
}}
QPushButton:hover {{
background-color: {h_bg_clr};
}}
QPushButton:checked:hover {{
background-color: {h_bg_checked_clr};
}}
}}
'''.format(br=int(SMALL_BORDER_RADIUS),
br_clr=SMALL_BORDER_COLOR,
bw=int(SMALL_BORDER_WIDTH),
bg_clr=SMALL_BACKGROUND_COLOR,
pr_bg_clr=SMALL_PRESSED_COLOR,
h_bg_clr=SMALL_HOVER_COLOR,
h_bg_checked_clr=BLUE_HOVER,
ac_bg_clr=BLUE)
smallFilledCompoundBottom = '''
QPushButton
{{
background-color: {bg_clr};
border-width: {bw}px;
border-top-left-radius: 0px;
border-top-right-radius: 0px;
border-bottom-left-radius: {br}px;
border-bottom-right-radius: {br}px;
border-color: {br_clr};
border-style: solid;
}}
QPushButton:pressed
{{
background-color: {pr_bg_clr};
}}
QPushButton:checked
{{
background-color: {ac_bg_clr};
border-color: lightgrey;
border-style: solid;
border-width: {bw}px;
}}
QPushButton:disabled
{{
border-style: none;
}}
QPushButton:disabled:checked
{{
background-color: {bg_clr};
border-style: none;
}}
QPushButton:hover
{{
background-color: {h_bg_clr};
}}
}}
'''.format(br=int(SMALL_BORDER_RADIUS),
br_clr=SMALL_BORDER_COLOR,
bw=int(SMALL_BORDER_WIDTH),
bg_clr=BACKGROUND_COLOR,
pr_bg_clr=SMALL_PRESSED_COLOR,
h_bg_clr=SMALL_HOVER_COLOR,
ac_bg_clr=BLUE)
### Layer Button ###
def layer(background_color='rbga(0,0,0,0)', outline='rgb(93,93,93)'):
styleSheet = '''
QPushButton
{{
background-color: {bg};
border-width: {bw}px;
border-color: {bc};
border-style: solid;
min-height: {h}px;
max-height: {h}px;
padding: -{bw}px;
}}
QPushButton:hover
{{
border-width: {bw}px;
border-color: {bc_hover};
border-style: solid;
}}
QPushButton:pressed
{{
border-width: {bw}px;
border-color: {bc_checked};
border-style: solid;
}}
QPushButton:checked
{{
background-color: {bg};
border-width: {bw}px;
border-color: {bc_checked};
border-radius: {radius};
border-style: solid;
min-height: {h}px;
max-height: {h}px;
padding: -{bw}px;
}}
QPushButton:checked:hover
{{
border-width: {bw}px;
border-color: {bc_checked};
border-style: solid;
}}
'''.format(bw=math.ceil(LAYER_BORDER_WIDTH),
h=math.ceil(LAYER_HEIGHT),
bg=background_color,
bc=outline,
bc_hover=ORANGE,
radius=BORDER_RADIUS,
bc_checked='white')
return styleSheet
### Labels ###
subDLabel = '''
QLabel
{{
color : {orange};
}}
'''.format(orange=ORANGE)
layerLabel = '''
QLabel
{{
font: {weight} {f_size}px;
padding-bottom: {p}px;
}}
'''.format(f_size=int(NORMAL_FONT),
weight='bold',
p=int(LAYER_BOTTOM_PADDING))
### Input Fields ###
inputField = '''
QSpinBox
{
border-style: solid;
border-radius: 4px;
}
'''
### Combo Box ###
smallComboBox = '''
QComboBox
{{
background-color: {bg_clr};
border-radius: {br}px;
}}
QComboBox:hover
{{
background-color: {bc_hover};
}}
QComboBox:on
{{
border-top-left-radius: {br}px;
border-top-right-radius: {br}px;
border-bottom-left-radius: 0px;
border-bottom-right-radius: 0px;
}}
QComboBox::drop-down
{{
background-color: {bg_clr};
border-radius: {br}px;
width: {drop_down_width};
}}
QComboBox::down-arrow
{{
image: url("{path}");
width: {icon_size}px;
height: {icon_size}px;
}}
'''.format(br=int(SMALL_BORDER_RADIUS),
br_clr=SMALL_BORDER_COLOR,
bg_clr=BACKGROUND_COLOR,
bw=int(SMALL_BORDER_WIDTH),
path=DROP_DOWN_ARROW_IMAGE,
icon_size=int(scale(8)),
bc_hover=HOVER_BACKGROUND_COLOR,
drop_down_width=int(scale(16)))

View File

@ -0,0 +1,888 @@
<!--
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.
Scale factor determines the initial scale 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.
NOTE: Using the correct scale factor can help with the control of the curves and helps in avoiding bugs due to very large or very small scenes.
# 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.
# 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.
# populateBlendAttributes
Enables blending of the attributes when using Add Cards/Tubes or Fill functions.
# 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 cant 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
UnBinds geometry or Cards/Tubes from selected Bound object.
Geometry and Cards/Tubes will be placed at the origin.
# 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.
# 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.
# gsCardToCurve
Opens the Card-to-Curve UI
Card to curve algorithm will attempt to generate CurveTools cards from selected geometry cards.
Selected geometry cards should be one sided meshes.
Selected geometry cards should be separate objects.
# 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 markdown menu (Hold RMB):
***
Markdown 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.
# curveSmooth
Smoothing of the geometry that is attached to a curve.
# 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 additinally 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 --->
# gsCardToCurve_outputTypeSwitch
Controls the output of Card-to-Curve algorithm
# gsCardToCurve_generateCards
Generate cards from selected one-sided geometry
# gsCardToCurve_generateCurves
Generate curves from selected one-sided geometry
# gsCardToCurve_cardType
Controls the type of generated cards
# gsCardToCurve_warp
Generate Warp cards
# gsCardToCurve_extrude
Generate Extrude cards
# gsCardToCurve_matchAttributes
Controls which attributes on the new cards should be approximated from the original geometry.
NOTE: This process is not perfect and final result can be inaccurate.
# gsCardToCurve_orientation
Match orientation attribute during the generation process
# gsCardToCurve_width
Match orientation attribute during the generation process
# gsCardToCurve_taper
Match taper attribute during the generation process
# gsCardToCurve_twist
Match twist attribute during the generation process
# gsCardToCurve_profile
Match profile attribute during the generation process
# gsCardToCurve_material
Copy material (shader) from the original geometry
# gsCardToCurve_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.
# gsCardToCurve_UVMatchOptions
Controls UV matching behaviour
# gsCardToCurve_verticalFlip
Vertically flip matched UVs
# gsCardToCurve_horizontalFlip
Horizontally flip matched UVs
# gsCardToCurve_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.
# gsCardToCurve_convertSelected
Convert selected geometry to cards or curves based on the options

View File

@ -0,0 +1,102 @@
"""
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.1 Studio
Copyright 2023, George Sladkovsky (Yehor Sladkovskyi)
All Rights Reserved
Autodesk Maya is a property of Autodesk, Inc.
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
"""
import io
import os
gs_curvetools_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))).replace('\\', '/')
def processTooltips():
# type: () -> dict
""" Processes the tooltips markdown file and returns a dict {"name": "tooltip", ...} """
from . import utils
filePath = os.path.join(gs_curvetools_path, 'utils', 'tooltips.md').replace('\\', '/')
finalDict = {}
with io.open(filePath, "r", encoding="utf-8") as f:
lines = f.readlines()
title = ""
tooltip = ""
commentBlock = False
for line in lines:
line = line.strip()
if not line or "":
continue
# Exclude comments
if "<!--" in line and "-->" in line:
continue
if "<!--" in line and not commentBlock:
commentBlock = True
continue
if "-->" in line and commentBlock:
commentBlock = False
continue
if commentBlock:
continue
# Process tooltips
if '#' in line:
if title and tooltip:
finalDict[title] = tooltip.strip()
title = line.replace("#", "").strip()
tooltip = ""
else:
tooltip += line + "\n"
# Final title/tooltip pair
if title and tooltip:
finalDict[title] = tooltip.strip()
return finalDict
def toggleCustomTooltipsMain(enable=True):
# Custom Tooltips that can't be automatically processed from markdown files (mostly Maya sliders)
from . import wrap
customTooltips = {
'gsCurvesSlider': 'Selects the number of added curves.\nUsed by Add Card, Add Tube, Fill and Subdivide functions',
'gsSelectCVSlider': 'Selects the CVs of the selected curves based on the position of the slider.\n<- Left is root of the curve and right is the tip ->\nModes:\nNormal Drag is single CV selection based on the position of the slider.\nShift+Drag is additive selection.\nAlt+Drag is subtractive selection.\nCtrl+Drag to move the slider without selection change.',
'gsRebuildSlider': 'Interactively rebuilds the selected curves. Slider controls the target number of CVs.\nNOTE: very small scale curves can have issues with distortion. If it is the case, try using Maya curve rebuild command.',
'gsFactorSlider': 'Adjusts the intensity of the Smooth slider and the power of Extend and Reduce functions',
}
for widget in customTooltips:
if hasattr(wrap.WIDGETS[widget], "setToolTip") and callable(getattr(wrap.WIDGETS[widget], "setToolTip")):
if enable:
wrap.WIDGETS[widget].setToolTip(customTooltips[widget])
else:
wrap.WIDGETS[widget].setToolTip('')
def toggleCustomTooltipsCurveControl(enable=True):
# Custom Tooltips that can't be automatically processed from markdown files (mostly Maya sliders) for Curve Control Window
from . import wrap
customTooltips = {
'gsPointSizeSlider': 'Controls the size of a CV point highlight',
'gsCurveWidthSlider': 'Controls the width of the curve highlight',
'gsHullWidthSlider': 'Controls the width of the hull highlight',
}
for widget in customTooltips:
if hasattr(wrap.WIDGETS[widget], "setToolTip") and callable(getattr(wrap.WIDGETS[widget], "setToolTip")):
if enable:
wrap.WIDGETS[widget].setToolTip(customTooltips[widget])
else:
wrap.WIDGETS[widget].setToolTip('')

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff