Update
479
Scripts/Modeling/Edit/CreasePlus/CreasePlusBase.py
Normal 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()
|
2125
Scripts/Modeling/Edit/CreasePlus/CreasePlusCore.py
Normal file
BIN
Scripts/Modeling/Edit/CreasePlus/CreasePlusEULA.pdf
Normal file
6
Scripts/Modeling/Edit/CreasePlus/CreasePlusExcept.py
Normal file
@ -0,0 +1,6 @@
|
||||
import maya.api.OpenMaya as om
|
||||
|
||||
|
||||
def cPexcept(excepstr=""):
|
||||
om.MGlobal.displayError(excepstr)
|
||||
return Exception(excepstr)
|
1057
Scripts/Modeling/Edit/CreasePlus/CreasePlusMain.py
Normal file
507
Scripts/Modeling/Edit/CreasePlus/CreasePlusNodes.py
Normal 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)
|
BIN
Scripts/Modeling/Edit/CreasePlus/CreasePlus_def_sheet.pdf
Normal file
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_bevel_ico.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_bevellive_ico.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_bool_ico.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_close_ico.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_crease_ico.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_curveattach_ico.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_curvebevel_ico.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_curveclose_ico.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_curvedraw_ico.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_curveint_ico.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_curvemult_ico.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_curvepoly_ico.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_displayhe_ico.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_eye_ico.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_hardedge_ico.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_menu_ico.png
Normal file
After Width: | Height: | Size: 676 B |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_meshslicer_ico.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_mirror_ico.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_next_ico.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_panelbool_ico.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_physcrease_ico.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_shapeshifter_ico.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_smoothangle_ico.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_subd_ico.png
Normal file
After Width: | Height: | Size: 502 B |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_uv_ico.png
Normal file
After Width: | Height: | Size: 769 B |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_weighttool_ico.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
Scripts/Modeling/Edit/CreasePlus/Icons/crep_zbrush_ico.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
17
Scripts/Modeling/Edit/CreasePlus/MayaUndoRun.py
Normal 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
|
5
Scripts/Modeling/Edit/CreasePlus/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Empty Init file
|
||||
from . import *
|
2
Scripts/Modeling/Edit/CreasePlus/delpyc.bat
Normal file
@ -0,0 +1,2 @@
|
||||
@echo off
|
||||
del *.pyc
|
40
Scripts/Modeling/Edit/CreasePlus/readit.txt
Normal 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
|
201
Scripts/Modeling/Edit/gs_curvetools/LICENSE.txt
Normal 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 AGREEMENT’S TERMS, INCLUDING THE DISCLAIMERS OF WARRANTIES AND LIMITATIONS ON DAMAGES, USE AND TRANSFERABILITY. IF YOU DO NOT ACCEPT THIS AGREEMENT’S TERMS, DO NOT DOWNLOAD, INSTALL OR USE THE DIGITAL PRODUCT OR RECEIVE OR USE THE PROFESSIONAL SERVICES.
|
||||
|
||||
This end-user agreement (“Agreement”) is a legally binding agreement between you, the licensee and customer (“you” or “your”), and the provider (“we” or “us” or “our”) of the digital products (“Products”) or instructional, training, mentorship or other professional service packages (“Professional Services”) that you purchase through the ArtStation Marketplace, regarding your rights and obligations regarding those Products and Professional Services.
|
||||
|
||||
1. Your Status
|
||||
In this Agreement, “you” means the person or entity acquiring rights in the Products or purchasing Professional Services. That may be a natural person, or a corporate or business entity or organization.
|
||||
|
||||
(a) If you are a natural person then you must be, and you confirm that you are, at least 13 years old. If you are between 13 years and the age of majority in your jurisdiction of residence, you confirm that your parent or legal guardian has reviewed and agrees to this Agreement and is happy for you to access and use the Product or receive the Professional Services.
|
||||
|
||||
(b) If you are a corporate entity then: (i) the rights granted under this Agreement are granted to that entity; (ii) you represent and warrant that the individual completing and accepting this Agreement is an authorized your representative and has the authority to legally bind that you to the Agreement; and (iii) to the extent that one or more of your employees are granted any rights in the Product or to receive Professional Services under this Agreement, you will ensure that your employees comply with this Agreement and you will be responsible and liable for any breach of this Agreement by any employee.
|
||||
|
||||
2. ArtStation
|
||||
ArtStation is a division of Epic Games, Inc., You acknowledge and agree that Epic is a third-party beneficiary of this Agreement and therefore will be entitled to directly enforce and rely upon any provision in this Agreement that confers a benefit on, or rights in favour of, Epic. In addition, you authorize Epic to act as your authorized representative to file a lawsuit or other formal action against a licensor in a court or with any other governmental authority if Epic knows or suspects that a licensor breached any representations or warranties under this Agreement. The foregoing authorization is nonexclusive, and Epic shall be under no obligation to pursue any claim. Epic will not initiate any such action on your behalf without first consulting with and obtaining your approval.
|
||||
|
||||
Products
|
||||
The following sections 3 through 9 apply to any Products you acquire from us through the ArtStation Marketplace:
|
||||
|
||||
3. Product Licence
|
||||
Subject to this Agreement’s terms and conditions, we hereby grant you a limited, non-exclusive, worldwide, non-transferable right and licence to (which will be perpetual unless the licence terminates as set out in this Agreement): (a) download the Product; and (b) copy and use the Product. We reserve all rights not expressly granted to you under this Agreement.
|
||||
|
||||
4. Licence Scope and Restrictions
|
||||
(a) Tutorials
|
||||
You are purchasing ONE licence to create ONE copy of the Product for use by you only (or, if you are a corporate entity, for use by a single authorized employee).
|
||||
|
||||
If this Product is bundled with a stock digital asset then you receive a limited personal use licence regarding that stock digital asset, and you may use that stock digital asset for your personal use only. You will not use that stock digital asset in any commercial manner unless you purchase a separate commercial licence.
|
||||
|
||||
(b) Installable Tools
|
||||
You may purchase one or more licences for the Product. A single licence allows you to install the Product on a single computer at a time for use by a single authorized user. If you are a corporate entity and the authorized employee completing the transaction on your behalf purchases multiple licences, you may choose to store the Product on a single server or shared hard drive for use by a single authorized employee at a time for each licence purchased.
|
||||
|
||||
Provided that you comply with the restrictions on users set out above, you may use the Product on an unlimited number of projects.
|
||||
|
||||
(c) Stock Assets
|
||||
Subject to the restrictions set out in this Agreement, you may copy, use, modify, adapt, translate, distribute, publicly display, transmit, broadcast, and create derivative works from the Product in works you create (“Works”), which may include things like films, videos, multi-media projects, computer games, models, images, publications, broadcasts, documents, and presentations.
|
||||
|
||||
If you are a corporate entity, you may make the Product available for use by your employees in accordance with this Agreement (for example, by storing the Product on a network server).
|
||||
|
||||
You may only share the Product with external people or entities where:
|
||||
|
||||
You are collaborating with the external parties in the creation of your Work and you need to share the Product for that purpose, provided that any external party that receives the Product may only use it in your Work and must secure and limit access to the Product for that purpose;
|
||||
You are working as a contractor for a client in the creation of a Work and need to share the Product with your client, or any external parties working with your client, provided that your client and any such external parties may use the Product only for your client’s Work, and all parties secure and limit access to the Product for that purpose.
|
||||
For any other use of the Product by any other party, that party must purchase a licence to the Product.
|
||||
|
||||
In addition to any other restrictions in this Agreement, you will not:
|
||||
|
||||
publish, sell, license, offer or make available for sale or licensing, or otherwise distribute the Product except as part of a Work or through a form of sharing that is authorized in this Agreement; or
|
||||
publish, distribute or make available the Product through any online clearinghouse platform.
|
||||
FURTHER SPECIFIC TERMS
|
||||
In addition to the restrictions set out above, the following terms and conditions apply to the following forms of commercial licences for the Product:
|
||||
|
||||
Standard Commercial Licence
|
||||
If you have purchased a Standard Commercial licence then you may exercise your rights under that licence:
|
||||
|
||||
for personal use on an unlimited number of personal projects that are not used or distributed in any commercial manner; and
|
||||
respect to one commercial Work, with up to a maximum of, as applicable, 2,000 sales of the Work or 20,000 monthly views of the Work.
|
||||
Extended Commercial Licence
|
||||
If you have purchased an Extended Commercial licence then you may exercise your rights under that licence:
|
||||
|
||||
for personal use on an unlimited number of personal projects that are not used or distributed in any commercial manner; and
|
||||
with respect to any number of commercial Works, with no limit on sales or views.
|
||||
5. Additional Restrictions
|
||||
Except as expressly permitted under this Agreement, you will not:
|
||||
|
||||
(a) make any copy of the Product except for archival or backup purposes;
|
||||
|
||||
(b) circumvent or disable any access control technology, security device, procedure, protocol, or technological protection mechanism that may be included or established in or as part of the Product;
|
||||
|
||||
(c) hack, reverse engineer, decompile, disassemble, modify or create derivative works of the Product or any part of the Product;
|
||||
|
||||
(d) publish, sell distribute or otherwise make the Product available to others to use, download or copy;
|
||||
|
||||
(e) transfer or sub-license the Product or any rights under this Agreement to any third party, whether voluntarily or by operation of law;
|
||||
|
||||
(f) use the Product for any purpose that may be defamatory, threatening, abusive, harmful or invasive of anyone’s privacy, or that may otherwise violate any law or give rise to civil or other liability;
|
||||
|
||||
(g) misrepresent yourself as the creator or owner of the Property;
|
||||
|
||||
(h) remove or modify any proprietary notice, symbol or label in or on the Product;
|
||||
|
||||
(i) directly or indirectly assist, facilitate or encourage any third party to carry on any activity prohibited by this Agreement.
|
||||
|
||||
6. Proprietary Rights
|
||||
The Product is protected by copyright laws and international copyright treaties, as well as other intellectual property laws and treaties. You are licensing the Product and the right to access, install and use the Product in accordance with this Agreement, not buying the Product. As between you and us, we own all right, title and interest in and to the Product, and you are not acquiring any ownership of or rights in the Product except the limited rights granted under this Agreement.
|
||||
|
||||
7. No Epic Support
|
||||
You acknowledge and agree that you are licensing the Product from us (the Provider), not from Epic, and that Epic has no obligation to support the Product.
|
||||
|
||||
8. Interruptions and Errors
|
||||
Your use of the Product might be interrupted and might not be free of errors.
|
||||
|
||||
9. Updates
|
||||
We have no obligation to update the Product.
|
||||
|
||||
Professional Services
|
||||
The following sections 10 and 11 apply to any Professional Services you purchase from us through the ArtStation Marketplace:
|
||||
|
||||
10. Provision of Professional Services
|
||||
We will provide the Professional Services directly to you and, subject to this Agreement, will assume all responsibility for all aspects of the Professional Services. We represent and warrant that we have the right to offer and provide the Professional Services and that we have appropriate qualifications and experience to provide the Professional Services.
|
||||
|
||||
11. Epic is not Involved
|
||||
You acknowledge and agree that:
|
||||
|
||||
(a) Epic is only a provider of the online ArtStation Marketplace where you purchased the Professional Services, and does not provide or exercise any control or oversight over us or the Professional Services, and is not responsible for us or the Professional Services or any shortcomings in them, including any damages, losses or legal issues caused by us or the Professional Services;
|
||||
|
||||
(b) this Agreement (and any dispute under it) is an agreement between us and you only, and not with Epic, and Epic is not a party to this Agreement;
|
||||
|
||||
(c) we are not Epic’s employee, agent or subcontractor;
|
||||
|
||||
(d) Epic does not have any obligation to attempt to resolve any dispute between us and you; and
|
||||
|
||||
(e) we will provide the Professional Services directly to you, and we (and not Epic) are solely responsible for the Professional Services, and Epic has no obligation or liability to you with respect to the Professional Services.
|
||||
|
||||
Both Products and Services
|
||||
The following sections 12 through 25 apply to all Products or Services you purchase from us through the ArtStation Marketplace:
|
||||
|
||||
12. Disclaimer
|
||||
ANY PRODUCTS OR PROFESSIONAL SERVICES ARE PROVIDED ON AN “AS IS” AND “AS AVAILABLE” BASIS, WITHOUT ANY REPRESENTATIONS, WARRANTIES OR CONDITIONS OF ANY KIND.
|
||||
|
||||
TO THE FULLEST EXTENT PERMITTED BY APPLICABLE LAW WE DISCLAIM, AND YOU WAIVE (WITH REGARD TO US AND ALSO TO EPIC, ITS AFFILIATES, AND ITS AND THEIR LICENSORS AND SERVICE PROVIDERS (COLLECTIVELY, THE “EPIC PARTIES”), ALL TERMS, CONDITIONS, GUARANTEES, REPRESENTATIONS AND WARRANTIES (EXPRESS, IMPLIED, STATUTORY AND OTHERWISE), IN RESPECT OF THE PRODUCTS AND PROFESSIONAL SERVICES, INCLUDING THOSE OF MERCHANTABILITY, NON-INFRINGEMENT, TITLE, QUALITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
NEITHER WE NOR ANY OF THE EPIC PARTIES REPRESENT OR WARRANT THAT: (A) ANY PRODUCT OR PROFESSIONAL SERVICE IS ACCURATE, COMPLETE, RELIABLE, CURRENT OR ERROR-FREE; (B) ANY PRODUCT OR PROFESSIONAL SERVICE WILL MEET YOUR REQUIREMENTS OR EXPECTATIONS; (C) ANY PRODUCT OR PROFESSIONAL SERVICES IS FREE OF VIRUSES OR OTHER HARMFUL COMPONENTS; OR (D) ANY DEFECTS IN ANY PRODUCT OR PROFESSIONAL SERVICE WILL BE CORRECTED.
|
||||
|
||||
13. Exclusion and Limitation of Liability
|
||||
(a) YOU DOWNLOAD, INSTALL AND OTHERWISE USE ALL PRODUCTS, AND RECEIVE AND USE ALL PROFESSIONAL SERVICES, AT YOUR OWN RISK. YOU AGREE TO, AND HEREBY DO:
|
||||
|
||||
(i) WAIVE ANY CLAIMS THAT YOU MAY HAVE AGAINST US OR THE EPIC PARTIES OR OUR RESPECTIVE DIRECTORS, OFFICERS, EMPLOYEES, AGENTS, REPRESENTATIVES, LICENSORS, SUCCESSORS AND ASSIGNS (COLLECTIVELY THE “RELEASEES”) ARISING FROM OR RELATING TO ANY PRODUCTS OR PROFESSIONAL SERVICES, AND
|
||||
|
||||
(ii) RELEASE THE RELEASEES FROM ANY LIABILITY FOR ANY LOSS, DAMAGE, EXPENSE OR INJURY ARISING FROM OR RELATING TO YOUR USE OF ANY PRODUCT OR PROFESSIONAL SERVICE, WHETHER ARISING IN TORT (INCLUDING NEGLIGENCE), CONTRACT OR OTHERWISE, EVEN IF THE RELEASEES ARE EXPRESSLY ADVISED OF THE POSSIBILITY OF SUCH LOSS, INJURY OR DAMAGE AND EVEN IF THAT LOSS, INJURY OR DAMAGE IS FORESEEABLE.
|
||||
|
||||
(b) NEITHER WE NOR THE EPIC PARTIES WILL BE LIABLE FOR ANY LOSSES, DAMAGES, CLAIMS OR EXPENSES THAT CONSTITUTE: (I) LOSS OF INTEREST, PROFIT, BUSINESS, CUSTOMERS OR REVENUE; (II) BUSINESS INTERRUPTIONS; (III) COST OF REPLACEMENT PRODUCTS OR SERVICES; OR (IV) LOSS OF OR DAMAGE TO REPUTATION OR GOODWILL.
|
||||
|
||||
(c) NEITHER WE NOR THE EPIC PARTIES WILL BE LIABLE FOR ANY LOSSES, DAMAGES, CLAIMS OR EXPENSES THAT CONSTITUTE INCIDENTAL, CONSEQUENTIAL, SPECIAL, PUNITIVE, EXEMPLARY, MULTIPLE OR INDIRECT DAMAGES, EVEN IF WE HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH LOSSES, DAMAGES, CLAIMS OR EXPENSES.
|
||||
|
||||
(d) MAXIMUM LIABILITY: IF, DESPITE THE LIMITATIONS SET OUT ABOVE, WE OR ANY EPIC PARTY BECOME LIABLE TO YOU IN RESPECT OF ANY PRODUCT OR PROFESSIONAL SERVICE OR OTHERWISE UNDER THIS AGREEMENT, THE ENTIRE CUMULATIVE LIABILITY OF US AND THE EPIC PARTIES, AND YOUR EXCLUSIVE AND CUMULATIVE REMEDY, FOR ANY DAMAGES (REGARDLESS OF THE CAUSE OR FORM OR ACTION), WILL BE LIMITED TO CAD$10.
|
||||
|
||||
14. Indemnity
|
||||
As a condition of your use of any Product or any Professional Services, you agree to hold harmless and indemnify the Releasees from any liability for any loss or damage to any third party resulting from your access to, installation or use of the Product or your receipt and use of the Professional Services.
|
||||
|
||||
15. Term and Termination
|
||||
This Agreement is effective until terminated. Your rights under this Agreement will terminate automatically without notice if: (a) you breach any terms of this Agreement; or (b) you do not complete payment for the Product or Professional Services, or any payment you make is refunded, reversed or cancelled for any reason. Upon this Agreement’s termination, you will cease all use of the Product and destroy all copies, full or partial, of the Product in your possession. Sections 11 through 25 will survive the termination of this Agreement.
|
||||
|
||||
16. Compliance with Laws
|
||||
You will comply with all applicable laws when using any Product or Professional Services (including intellectual property and export control laws).
|
||||
|
||||
17. Entire Agreement
|
||||
This Agreement supersedes all prior agreements of the parties regarding the Product or Professional Services, and constitutes the whole agreement with respect to the Product or Professional Services.
|
||||
|
||||
18. Disputes
|
||||
If you have any concerns about the Product or Professional Services, please contact us through our ArtStation Marketplace account and we will work with you to try to resolve the issue. You acknowledge and agree that any such dispute is between you and us, and that Epic will not be involved in the dispute and has no obligation to try to resolve the dispute.
|
||||
|
||||
19. Persons Bound
|
||||
This Agreement will enure to the benefit of and be binding upon the parties and their heirs, executors, administrators, legal representatives, lawful successors and permitted assigns.
|
||||
|
||||
20. Assignment
|
||||
We may assign this Agreement without notice to you. You may not assign this Agreement or any of your rights under it without our prior written consent, which we will not withhold unreasonably.
|
||||
|
||||
21. Waiver
|
||||
No waiver, delay, or failure to act by us regarding any particular default or omission will prejudice or impair any of our rights or remedies regarding that or any subsequent default or omission that are not expressly waived in writing.
|
||||
|
||||
22. Applicable Law and Jurisdiction
|
||||
You agree that this Agreement will be deemed to have been made and executed in the State of North Carolina, U.S.A., and any dispute will be resolved in accordance with the laws of North Carolina, excluding that body of law related to choice of laws, and of the United States of America. Any action or proceeding brought to enforce the terms of this Agreement or to adjudicate any dispute must be brought in the Superior Court of Wake County, State of North Carolina or the United States District Court for the Eastern District of North Carolina. You agree to the exclusive jurisdiction and venue of these courts. You waive any claim of inconvenient forum and any right to a jury trial. The Convention on Contracts for the International Sale of Goods will not apply. Any law or regulation which provides that the language of a contract shall be construed against the drafter will not apply to this Agreement.
|
||||
|
||||
23. Legal Effect
|
||||
This Agreement describes certain legal rights. You may have other rights under the laws of your country. This Agreement does not change your rights under the laws of your country if the laws of your country do not permit it to do so.
|
||||
|
||||
24. Interpretation
|
||||
In this Agreement, "we", "us", and "our" refer to the licensor of the Product alone and never refer to the combination of you and that licensor (that combination is referred to as "the parties"), or the combination of you or the licensor with Epic.
|
||||
|
||||
25. Artificial Intelligence
|
||||
For purposes of this Agreement, “Generative AI Programs” means artificial intelligence, machine learning, deep learning, neural networks, or similar technologies designed to automate the generation of or aid in the creation of new content, including but not limited to audio, visual, or text-based content.
|
||||
|
||||
We (the licensor of the Product) represent and warrant that where the Product was created using Generative AI Programs, we have applied the “CreatedWithAI” tag. Under this Agreement, a Product is considered to be created using Generative AI Programs where a material portion of a Product is generated with Generative AI Programs, whether characters, backgrounds, or other material elements. A Product is not considered to be created using Generative AI Programs merely for use of features that solely operate on a Product (e.g., AI-based upscaling or content-aware fill).
|
35
Scripts/Modeling/Edit/gs_curvetools/README.txt
Normal 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.
|
10
Scripts/Modeling/Edit/gs_curvetools/__init__.py
Normal 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
|
69
Scripts/Modeling/Edit/gs_curvetools/constants.py
Normal 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)
|
6729
Scripts/Modeling/Edit/gs_curvetools/core.py
Normal file
BIN
Scripts/Modeling/Edit/gs_curvetools/fonts/Roboto-Bold.ttf
Normal file
BIN
Scripts/Modeling/Edit/gs_curvetools/fonts/Roboto-Italic.ttf
Normal file
BIN
Scripts/Modeling/Edit/gs_curvetools/fonts/Roboto-Regular.ttf
Normal file
BIN
Scripts/Modeling/Edit/gs_curvetools/icons/drop-down-arrow.png
Normal file
After Width: | Height: | Size: 234 B |
After Width: | Height: | Size: 10 KiB |
After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 1.2 KiB |
BIN
Scripts/Modeling/Edit/gs_curvetools/icons/marking.png
Normal file
After Width: | Height: | Size: 118 B |
BIN
Scripts/Modeling/Edit/gs_curvetools/icons/mod.png
Normal file
After Width: | Height: | Size: 178 B |
BIN
Scripts/Modeling/Edit/gs_curvetools/icons/reset.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.4 KiB |
161
Scripts/Modeling/Edit/gs_curvetools/init.py
Normal 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)
|
132
Scripts/Modeling/Edit/gs_curvetools/log.log
Normal 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
|
878
Scripts/Modeling/Edit/gs_curvetools/main.py
Normal 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'))
|
BIN
Scripts/Modeling/Edit/gs_curvetools/plugins/2018/cv_manip.mll
Normal file
BIN
Scripts/Modeling/Edit/gs_curvetools/plugins/2019/cv_manip.mll
Normal file
BIN
Scripts/Modeling/Edit/gs_curvetools/plugins/2020/cv_manip.mll
Normal file
BIN
Scripts/Modeling/Edit/gs_curvetools/plugins/2022/cv_manip.mll
Normal file
BIN
Scripts/Modeling/Edit/gs_curvetools/plugins/2023/cv_manip.mll
Normal file
BIN
Scripts/Modeling/Edit/gs_curvetools/plugins/2024/cv_manip.mll
Normal file
80
Scripts/Modeling/Edit/gs_curvetools/plugins/cv_manip.py
Normal 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
|
770
Scripts/Modeling/Edit/gs_curvetools/plugins/cv_manip_src.py
Normal 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
|
||||
'''
|
2924
Scripts/Modeling/Edit/gs_curvetools/ui.py
Normal file
62
Scripts/Modeling/Edit/gs_curvetools/utils/gs_math.py
Normal 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)
|
79
Scripts/Modeling/Edit/gs_curvetools/utils/hotkeys.csv
Normal 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
|
|
786
Scripts/Modeling/Edit/gs_curvetools/utils/style.py
Normal 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)))
|
888
Scripts/Modeling/Edit/gs_curvetools/utils/tooltips.md
Normal 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 can’t be fixed in GS CurveTools plug-in.
|
||||
|
||||
# mayaFixBrokenGraphs
|
||||
This function will attempt to fix all the broken graphs in the scene.
|
||||
Use if one of the graphs (Width, Twist or Profile) is in a broken state.
|
||||
|
||||
# convertBezierToNurbs
|
||||
Converts the selected Bezier curves to NURBS curves.
|
||||
Bezier curves are not supported by the GS CurveTools.
|
||||
|
||||
# maya2020TwistAttribute
|
||||
This function will fix any broken cards created in Maya 2020.4 before v1.2.2 update.
|
||||
|
||||
# maya2020UnbindFix
|
||||
This function will fix any cards that are not unbinding properly and were created before v1.2.3 update in Maya 2020.4.
|
||||
|
||||
# deleteAllAnimationKeys
|
||||
This function will delete all the animation keys on all the curves present in the scene.
|
||||
This can fix deformation issues when using duplicate or other GS CurveTools functions.
|
||||
Some functions (like duplicate) will delete the keys automatically, however the keys can still cause issues.
|
||||
|
||||
<!-- Main Buttons -->
|
||||
|
||||
# warpSwitch
|
||||
Advanced cards and tubes suitable for longer hair.
|
||||
Additional options and controls.
|
||||
Slower than Extrude (viewport performance).
|
||||
Can have issues on very small scales.
|
||||
|
||||
# extrudeSwitch
|
||||
Simple cards and tubes suitable for shorter hair and brows.
|
||||
Has limited controls.
|
||||
Much faster than Warp (viewport performance).
|
||||
Better for small scales.
|
||||
|
||||
# newCard
|
||||
Creates a new Card in the middle of the world. Used at the beginning of the project to create templates.
|
||||
|
||||
# newTube
|
||||
Creates a new Tube in the middle of the world. Used at the beginning of the project to create templates.
|
||||
|
||||
# curveCard
|
||||
Converts selected Maya curve to CurveTools Card.
|
||||
|
||||
# curveTube
|
||||
Converts selected Maya curve to CurveTools Tube.
|
||||
|
||||
# gsBind
|
||||
Binds selection to a single curve. Creates a Bind Group. Selection options:
|
||||
1. Single "empty" curve (default Maya curve) and single combined geometry.
|
||||
2. Single "empty" curve (default Maya curve) and any number of Curve Cards and Curve Tubes.
|
||||
***
|
||||
Shift + Click will duplicate the original curves/geo before binding it to the empty curve.
|
||||
Same option is available in the Options menu (Duplicate Curves Before Bind).
|
||||
|
||||
# gsUnbind
|
||||
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
|
102
Scripts/Modeling/Edit/gs_curvetools/utils/tooltips.py
Normal 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('')
|