MetaBox/Scripts/Modeling/Edit/gs_curvetools/utils/utils.py
2025-01-14 02:59:09 +08:00

1427 lines
51 KiB
Python

"""
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 cProfile
import functools
import inspect
import itertools
import logging
import math
import os
import pstats
import re
import subprocess
import sys
import traceback
try:
from collections.abc import Iterable
except BaseException:
from collections import Iterable
from functools import partial as pa
from functools import wraps
from imp import reload
from os import path
import maya.api.OpenMaya as om
import maya.cmds as mc
import maya.mel as mel
from ..constants import *
gs_curvetools_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))).replace('\\', '/')
### Logging ###
class Logger:
""" Create logger, log and print messages"""
def __init__(self):
logFilePath = os.path.join(gs_curvetools_path, 'log.log').replace('\\', '/') # type: str
# Check if file already there and its size. Delete if too large.
if os.path.exists(logFilePath):
size = os.stat(logFilePath).st_size
if size >= 1024 * 1024: # 1 mb max log size
try:
os.remove(logFilePath)
except BaseException:
pass
# Create logger
self.logger = logging.getLogger("GS_CurveTools")
self.logger.setLevel(logging.DEBUG)
self.logger.propagate = False
# File handler
fileLog = logging.FileHandler(filename=logFilePath, encoding='utf-8')
fileLog.set_name("File Output")
fileLog.setLevel(logging.DEBUG)
# Console handler
consoleLog = logging.StreamHandler(stream=sys.stdout)
consoleLog.set_name("Console Output")
consoleLog.setLevel(logging.INFO)
# Formatter
fileFormatter = logging.Formatter(
fmt='[%(asctime)s,%(msecs)03d | %(levelname)5.5s | %(module)8.8s | %(lineno)5d]: %(funcName)s() : %(message)s',
datefmt='%H:%M:%S')
consoleFormatter = logging.Formatter(
fmt='[GS CurveTools|%(levelname)s]: %(message)s')
fileLog.setFormatter(fileFormatter)
consoleLog.setFormatter(consoleFormatter)
# Add handlers to the logger
self.logger.handlers = []
self.logger.addHandler(fileLog)
self.logger.addHandler(consoleLog)
def inView(self, message):
""" InView Message """
self.logger.debug(message)
mc.inViewMessage(smg=str(message), pos='topCenter', f=1, fit=50, fot=50)
def warning(self, message):
""" Print Warning Message """
self.logger.error(message)
trace = traceback.format_stack(limit=2)
self.logger.debug(trace[0].splitlines()[0].strip())
mc.warning(message)
def warningInView(self, message):
""" Print Warning and InView message """
self.logger.error(message)
trace = traceback.format_stack(limit=2)
self.logger.debug(trace[0].splitlines()[0].strip())
mc.warning(message)
mc.inViewMessage(smg=message, pos='topCenter', f=1, fit=50, fot=50)
def printInView(self, message):
""" Print to Script Editor and InView message """
self.logger.info(message)
mc.inViewMessage(smg=message, pos='topCenter', f=1, fit=50, fot=50)
def openLogFile(self):
if OS == "mac":
self.warning('Opening log file is not supported on Mac. Please open it manually from gs_curvetools folder:')
self.warning('{}'.format(path.join(gs_curvetools_path, 'log.log').replace('\\', '/')))
return
subprocess.call('notepad "{}"'.format(path.join(gs_curvetools_path, 'log.log').replace('\\', '/')), creationflags=0x00000008)
logger = Logger()
MESSAGE = logger
LOGGER = logger.logger
# Some version specific compatibility stuff
if sys.version_info >= (3, 0):
from io import StringIO
fixedWraps = wraps
else:
# Old python import for StringIO
from io import BytesIO as StringIO
# Legacy wraps for python 2.7 to fix some errors
def wrapsSafely(obj, attr_names=functools.WRAPPER_ASSIGNMENTS):
return wraps(obj, assigned=itertools.ifilter(functools.partial(hasattr, obj), attr_names))
fixedWraps = wrapsSafely
### Decorators ###
def deferred(func):
"""Deferred execution"""
@fixedWraps(func)
def wrapper(*args, **kwargs):
try:
mc.evalDeferred(pa(func, *args, **kwargs))
except Exception as e:
LOGGER.exception(e)
return wrapper
def deferredLp(func):
"""Deferred execution (lowest priority)"""
@fixedWraps(func)
def wrapper(*args, **kwargs):
try:
rv = mc.evalDeferred(pa(func, *args, **kwargs), lp=1)
except Exception as e:
LOGGER.exception(e)
return rv
return wrapper
def executeNext(func):
@fixedWraps(func)
def wrapper(*args, **kwargs):
try:
mc.evalDeferred(pa(func, *args, **kwargs), en=1)
except Exception as e:
LOGGER.exception(e)
return wrapper
def parseMelCommand(pythonCmd, *args, **_):
""" Convert python function call to MEL compatible call with all the args"""
if inspect.ismethod(pythonCmd):
instName = pythonCmd.__self__.name
else:
instName = False
module = pythonCmd.__module__
function = pythonCmd.__name__
arguments = "({})".format(','.join(str(x) if not isinstance(x, str) else "'{}'".format(str(x)) for x in args))
if instName:
cmd = "{}.{}.{}{}".format(module, instName, function, arguments)
else:
cmd = "{}.{}{}".format(module, function, arguments)
melCommand = 'python("import {};{}");'.format(module, cmd)
return melCommand
def undo(func):
""" Safe Undo Open/Close Chunk """
@fixedWraps(func)
def wrapper(*args, **kwargs):
funcName = str(func.__name__)
mc.undoInfo(ock=1, cn=funcName)
rv = None
try:
rv = func(*args, **kwargs)
try:
mc.repeatLast(
ac=parseMelCommand(func, *args, **kwargs),
acl=funcName
)
except Exception as e:
LOGGER.exception(e)
except Exception as e:
LOGGER.exception(e)
finally:
mc.undoInfo(cck=1)
return rv
return wrapper
def noUndo(func):
""" Safe "State Without Flush" """
@fixedWraps(func)
def wrapper(*args, **kwargs):
mc.undoInfo(swf=0)
rv = None
try:
rv = func(*args, **kwargs)
except Exception as e:
LOGGER.exception(e)
finally:
mc.undoInfo(swf=1)
return rv
return wrapper
### Time classes ###
class Timer:
""" Timer """
t0 = 0
@classmethod
def increment(cls, sec):
cls.t = mc.timerX()
if cls.t >= cls.t0 + sec:
cls.t0 = cls.t
return True
else:
return False
### Debug ###
class Profiler():
def __init__(self):
self.pr = cProfile.Profile()
self.pr.enable()
def end(self):
self.pr.disable()
s = StringIO()
sort = 'tottime'
ps = pstats.Stats(self.pr, stream=s).sort_stats(sort)
ps.print_stats()
print(s.getvalue()) # pylint: disable=print-statement
def profile(func):
"""Profiles the execution speed of the function or method"""
@fixedWraps(func)
def wrapper(*args, **kwargs):
p = Profiler()
rv = func(*args, **kwargs)
p.end()
return rv
return wrapper
class DebugDraw:
timer = Timer()
@staticmethod
def returnMaterial(color):
matName = 'GS_DEBUG_Material_{}_{}_{}'.format(
str(color[0]).replace('.', ''),
str(color[1]).replace('.', ''),
str(color[2]).replace('.', '')
)
if not mc.objExists(matName):
lambert = mc.shadingNode('lambert', asShader=1, n=matName, ss=1)
mc.setAttr(lambert + '.color', color[0], color[1], color[2])
mc.setAttr(lambert + '.incandescence', color[0], color[1], color[2])
mc.setAttr(lambert + '.ambientColor', color[0], color[1], color[2])
mc.setAttr(lambert + '.transparency', 0.5, 0.5, 0.5)
lambert_set = mc.sets(r=1, nss=1, em=1, n=matName + '_set')
mc.connectAttr(lambert + '.outColor', lambert_set + '.surfaceShader')
return lambert_set
else:
lambert_set = mc.listConnections(matName + '.outColor', t='shadingEngine')[0]
return lambert_set
@classmethod
def sceneCleanup(cls, ignoreTimer=False):
# Don't cleanup the scene if multiple objects are created at the same time
if not cls.timer.increment(5) and not ignoreTimer:
return
allNodes = mc.ls()
for node in allNodes:
if 'GS_DEBUG_' in node and mc.objExists(node) and mc.nodeType(node) == 'transform':
mc.delete(node)
@classmethod
def setGeoStyle(cls, geo, color):
# type: (list[tuple[str, str]], tuple[float, float, float]) -> None
""" Apply debug render style to list of transforms """
if not isinstance(geo, list) and not isinstance(geo, tuple):
geo = [geo]
for tr in geo:
mc.setAttr(tr + '.overrideEnabled', 1)
mc.setAttr(tr + '.overrideDisplayType', 2)
mc.setAttr(tr + '.overrideRGBColors', 1)
mc.setAttr(tr + '.overrideColorA', 0)
mat = cls.returnMaterial(color)
mc.sets(tr, e=1, fe=mat, nw=1)
@classmethod
def sphere(cls, center, radius=0.1, dir=(0, 1, 0), color=(0, 1, 0), label=''):
cls.sceneCleanup()
center = (center[0], center[1], center[2])
sphere = mc.polySphere(n="GS_DEBUG_Sphere#", ax=dir, r=radius, sx=8, sy=8, ch=0, o=1, cuv=0)
mc.move(center[0], center[1], center[2], sphere, xyz=1)
cls.setGeoStyle(sphere, color)
if label:
mc.headsUpMessage(label, o=sphere[0], time=100, vp=1)
@classmethod
def arrow(cls, origin=(0, 0, 0), target=(1, 0, 0), size=0.1, color=(0, 1, 0), label=''):
cls.sceneCleanup()
origSel = mc.ls(sl=1)
origin = (origin[0], origin[1], origin[2])
target = (target[0], target[1], target[2])
cylinderTransform, cylinderCreator = mc.polyCylinder(n='GS_DEBUG_Arrow#', ch=1, ax=(0, 1, 0), r=0.1, h=1, sh=1, sc=1, sa=8)
pyramidTransform, pyramidCreator = mc.polyPyramid(ch=1, ax=(0, 1, 0), w=1, ns=4)
mc.setAttr(pyramidTransform + '.inheritsTransform', 0)
multX = mc.createNode('multDoubleLinear')
mc.setAttr(multX + '.input2', 0.3)
mc.connectAttr(cylinderTransform + '.scaleX', multX + '.input1', f=1)
mc.connectAttr(multX + '.output', pyramidTransform + '.scaleX')
multZ = mc.createNode('multDoubleLinear')
mc.setAttr(multZ + '.input2', 0.3)
mc.connectAttr(cylinderTransform + '.scaleZ', multZ + '.input1', f=1)
mc.connectAttr(multZ + '.output', pyramidTransform + '.scaleZ')
add = mc.createNode('addDoubleLinear')
mc.connectAttr(cylinderTransform + '.scaleX', add + '.input1')
mc.connectAttr(cylinderTransform + '.scaleZ', add + '.input2')
divide = mc.createNode('multDoubleLinear')
mc.setAttr(divide + '.input2', .5)
mc.connectAttr(add + '.output', divide + '.input1')
mc.connectAttr(divide + '.output', pyramidTransform + '.scaleY')
mc.setAttr(pyramidCreator + '.heightBaseline', -1)
mc.setAttr(cylinderCreator + '.heightBaseline', -1)
mc.select(cylinderTransform + '.vtx[17]', pyramidTransform, r=1)
mc.pointOnPolyConstraint(w=1)
mc.parent(pyramidTransform, cylinderTransform)
mc.move(origin[0], origin[1], origin[2], cylinderTransform, xyz=1)
locator = mc.spaceLocator(p=(0, 0, 0))
mc.xform(locator, cp=1, t=target, a=1)
aim = mc.aimConstraint(locator, cylinderTransform, aim=(0, 1, 0))
dist = mc.createNode('distanceBetween')
mc.connectAttr(locator[0] + '.translate', dist + '.point1')
mc.connectAttr(cylinderTransform + '.translate', dist + '.point2')
mult = mc.createNode('multDoubleLinear')
mc.connectAttr(divide + '.output', mult + '.input1')
mc.setAttr(mult + '.input2', -0.7)
subtract = mc.createNode('addDoubleLinear')
mc.connectAttr(mult + '.output', subtract + '.input2')
mc.connectAttr(dist + '.distance', subtract + '.input1')
mc.connectAttr(subtract + '.output', cylinderTransform + '.scaleY')
mc.setAttr(cylinderTransform + ".sx", size)
mc.setAttr(cylinderTransform + ".sz", size)
cls.setGeoStyle([pyramidTransform, cylinderTransform], color)
mc.delete(aim, locator)
mc.select(cylinderTransform, r=1)
mc.select(origSel, r=1)
if label:
mc.headsUpMessage(label, o=cylinderTransform, time=100, vp=1)
### Misc utils ###
def stopUI(state=False):
from .. import core
windows = [
core.MAIN_WINDOW_NAME,
core.CURVE_CONTROL_NAME,
core.UV_EDITOR_NAME,
core.SCALE_FACTOR_UI,
'GSCT_RandomizeCurvePopOut',
'GSCT_AttributesFilterPopOut',
'GSCT_TwistGraphPopOut',
'GSCT_WidthGraphPopOut',
'GSCT_CurveThicknessWindow',
'GSCT_LayerEditorWindow',
'GSCT_CustomLayerColorsWindow',
'GSCT_OrientToNormalsWindow',
'GSCT_CardToCurvePopOut',
]
# Delete advanced visibility node
if all(mc.getClassification("GSCT_CurveTools_DrawManagerNode")):
sel = mc.ls(typ="GSCT_CurveTools_DrawManagerNode")
for n in sel:
if "GSCT_CurveTools_DrawManager" in n:
mc.delete(mc.listRelatives(n, p=1, pa=1))
# Delete workspaces
for window in windows:
if MAYA_VER <= 2017:
if mc.workspaceControl(window, q=1, ex=1):
mc.workspaceControl(window, e=1, floating=1)
mc.deleteUI(window)
return 0
if mc.workspaceControl(window, q=1, ex=1):
if mc.workspaceControl(window, q=1, vis=1):
if mc.workspaceControl(window, q=1, fl=1):
mc.workspaceControl(window, e=1, clp=False)
mc.workspaceControl(window, e=1, cl=1)
elif mc.workspaceControl(window, q=1, clp=1):
mc.workspaceControl(window, e=1, clp=False)
mc.workspaceControl(window, e=1, cl=1)
else:
mc.workspaceControl(window, e=1, cl=1)
mc.deleteUI(window)
if state:
if mc.workspaceControlState(window, q=1, ex=1):
LOGGER.info("Deleting window state for %s" % window)
mc.workspaceControlState(window, r=1)
@deferred
def resetUI():
resetOptionVars()
if MAYA_VER >= 2018:
stopUI(True)
# Reload all files
from importlib import import_module
files = [
'.constants',
'.core',
'.main',
'.ui',
'.uv_editor',
'.utils.gs_math',
'.utils.style',
'.utils.tooltips',
'.utils.utils',
'.utils.wrap',
]
modules = {}
for file in files:
modules[file] = import_module(file, 'gs_curvetools')
for module in modules:
reload(modules[module])
# Run main function
if '.main' in modules:
modules['.main'].main()
else:
try:
from .. import main
main.main()
except ImportError as e:
LOGGER.exception(e)
raise ImportError('Main not found!')
def resetOptionVars():
mc.optionVar(fv=["GSCT_globalScaleFactor", 1])
mc.optionVar(fv=["GSCT_globalCurveThickness", -1])
mc.optionVar(iv=['GSCT_warpSwitch', 1])
mc.optionVar(iv=["GSCT_syncCurveColor", 0])
mc.optionVar(iv=['GSCT_colorOnlyDiffuse', 1])
mc.optionVar(iv=["GSCT_colorizedRegroup", 0])
mc.optionVar(iv=["GSCT_checkerPattern", 0])
mc.optionVar(iv=["GSCT_ignoreLastLayer", 1])
mc.optionVar(iv=["GSCT_ignoreTemplateCollections", 1])
mc.optionVar(iv=["GSCT_groupTemplateCollections", 1])
mc.optionVar(iv=["GSCT_syncOutlinerLayerVis", 1])
mc.optionVar(iv=["GSCT_keepCurveAttributes", 1])
mc.optionVar(iv=["GSCT_massBindOption", 0])
mc.optionVar(iv=["GSCT_boundCurvesFollowParent", 1])
mc.optionVar(iv=["GSCT_bindDuplicatesCurves", 0])
mc.optionVar(iv=["GSCT_bindFlipUVs", 1])
mc.optionVar(iv=["GSCT_populateBlendAttributes", 1])
mc.optionVar(iv=["GSCT_convertInstances", 1])
mc.optionVar(iv=["GSCT_replacingCurveLayerSelection", 1])
mc.optionVar(iv=["GSCT_flipUVsAfterMirror", 1])
mc.optionVar(iv=["GSCT_useAutoRefineOnNewCurves", 1])
mc.optionVar(iv=["GSCT_layerNumbersOnly", 0])
mc.optionVar(iv=['GSCT_2layerRows', 1])
mc.optionVar(iv=['GSCT_3layerRows', 0])
mc.optionVar(iv=['GSCT_4layerRows', 0])
mc.optionVar(iv=['GSCT_6layerRows', 0])
mc.optionVar(iv=['GSCT_8layerRows', 0])
mc.optionVar(iv=['GSCT_UVBugMessageDismissed', 0])
mc.optionVar(iv=['GSCT_UVEditorTransparencyToggle', 0])
mc.optionVar(iv=['GSCT_UVEditorAlphaOnly', 0])
mc.optionVar(iv=['GSCT_enableTooltips', 1])
mc.optionVar(iv=['GSCT_showLayerCollectionsMenu', 1])
mc.optionVar(iv=['GSCT_importIntoANewCollection', 0])
mc.optionVar(iv=['GSCT_AutoHideCurvesOnInactiveCollections', 0])
mc.optionVar(sv=['GSCT_UVEditorBGColor', "(36, 36, 36)"])
mc.optionVar(sv=['GSCT_UVEditorGridColor', "(50, 50, 50)"])
mc.optionVar(sv=['GSCT_UVEditorFrameColor', "(160, 75, 75)"])
mc.optionVar(sv=['GSCT_UVEditorUVFrameSelectedColor', "(255, 255, 255)"])
mc.optionVar(sv=['GSCT_UVEditorUVFrameDeselectedColor', "(128, 128, 128)"])
mc.optionVar(sv=['GSCT_UVEditorUVCardFillColor', "(96, 100, 160)"])
mc.optionVar(sv=['GSCT_AttributesFilter', "{'Orientation': False}"])
mc.optionVar(iv=['GSCT_CardToCurveOutputType', 0])
mc.optionVar(iv=['GSCT_CardToCurveCardType', 0])
mc.optionVar(iv=['GSCT_GeometryHighlightEnabled', 0])
mc.optionVar(
sv=['GSCT_CardToCurveOptions',
"{'gsCardToCurve_horizontalFlip': False, 'gsCardToCurve_verticalFlip': False}"]
)
# Advanced visibility options
mc.optionVar(fv=['GSCT_' + 'gsPointSizeSlider', 10.5])
mc.optionVar(fv=['GSCT_' + 'gsCurveWidthSlider', 4.0])
mc.optionVar(fv=['GSCT_' + 'gsHullWidthSlider', 3.0])
mc.optionVar(sv=['GSCT_' + 'gsDeselectedCVColor', "[1, 0, 0]"])
mc.optionVar(fv=['GSCT_' + 'gsDeselectedCVAlpha', 1.0])
mc.optionVar(sv=['GSCT_' + 'gsSelectedCVColor', "[0, 1, 0]"])
mc.optionVar(fv=['GSCT_' + 'gsSelectedCVAlpha', 1.0])
mc.optionVar(sv=['GSCT_' + 'gsCurveHighlightColor', "[0, 0, 1]"])
mc.optionVar(fv=['GSCT_' + 'gsCurveHighlightAlpha', 1.0])
mc.optionVar(sv=['GSCT_' + 'gsHullHighlightColor', "[0.5, 0, 0.5]"])
mc.optionVar(fv=['GSCT_' + 'gsHullHighlightAlpha', 1.0])
mc.optionVar(iv=['GSCT_' + 'gsCurveVisibilityToggle', 1])
mc.optionVar(iv=['GSCT_' + 'gsHullVisibilityToggle', 0])
mc.optionVar(iv=['GSCT_' + 'gsLazyUpdateToggle', 0])
mc.optionVar(iv=['GSCT_' + 'gsAlwaysOnTopToggle', 1])
mc.optionVar(iv=['GSCT_' + 'gsCVDistanceColor', 1])
mc.optionVar(iv=['GSCT_' + 'gsHullDistanceColor', 1])
mc.optionVar(iv=['GSCT_' + 'gsCurveDistanceColor', 1])
mc.optionVar(fv=['GSCT_' + 'gsDistanceColorMinValue', 0.25])
mc.optionVar(fv=['GSCT_' + 'gsDistanceColorMaxValue', 1.0])
mc.optionVar(iv=['GSCT_' + 'gsEnableCVOcclusion', 0])
mc.optionVar(sv=['GSCT_' + 'gsOccluderMeshName', ""])
def fixMaya2020UVs():
success = 0
total = 0
for i in range(80):
try:
group = 'curveGrp_%s_Geo' % i
if mc.objExists(group):
geometry = mc.editDisplayLayerMembers(group, q=1, fn=1, nr=1)
for geo in geometry:
total += 1
allUVNodes = list()
history = mc.listHistory(geo, il=0)
for node in history:
if mc.nodeType(node) == 'polyMoveUV':
allUVNodes.append(node)
for node in allUVNodes:
mc.setAttr(node + '.inputComponents', 1, 'map[*]', type='componentList')
success += 1
except BaseException:
pass
additionalMessage = ''
if success != total:
additionalMessage = ' Some cards were not fixed!'
MESSAGE.printInView('Fixed %s/%s Cards.%s' % (success, total, additionalMessage))
def fixMaya2020Twist():
dialog = mc.confirmDialog(
title='Fix Maya 2020.4 Twist',
message='This command will fix Maya 2020.4 Twist Attribute Bug\n\
Only use it if you have issues with Twist and Inv.Twist Attributes\n\n\
Proceed?',
button=['Yes', 'No'],
defaultButton='Yes',
cancelButton='No',
dismissString='No',
icon='information'
)
if dialog == 'No':
return
success = 0
total = 0
for i in range(80):
try:
group = 'curveGrp_%s_Curve' % i
if mc.objExists(group):
curve = mc.editDisplayLayerMembers(group, q=1, fn=1, nr=1)
for crv in curve:
if mc.attributeQuery('invTwist', n=crv, ex=1):
total += 1
twist = mc.listConnections(crv + '.invTwist', d=1, scn=1)
twistHandle = mc.listConnections(twist[0] + '.deformerData', s=1, scn=1)
twistHandleShape = mc.listRelatives(twistHandle[0], c=1, pa=1)
connectionCheck = mc.isConnected(twist[0] + '.startAngle', twistHandleShape[0] + '.startAngle')
if not connectionCheck:
mc.connectAttr(twist[0] + '.startAngle', twistHandleShape[0] + '.startAngle', f=1)
mc.connectAttr(twist[0] + '.endAngle', twistHandleShape[0] + '.endAngle', f=1)
success += 1
except BaseException:
pass
MESSAGE.printInView('Fixed %s/%s Cards.' % (success, total))
def fixMaya2020Unbind():
dialog = mc.confirmDialog(
title='Fix Maya 2020.4 Unbind',
message='This command will fix Maya 2020.4 Unbind Function Bug\n\
Only use it if you have issues with Unbind Function\n\n\
Proceed?',
button=['Yes', 'No'],
defaultButton='Yes',
cancelButton='No',
dismissString='No',
icon='information'
)
if dialog == 'No':
return
success = 0
total = 0
for i in range(80):
try:
group = 'curveGrp_%s_Curve' % i
if mc.objExists(group):
curve = mc.editDisplayLayerMembers(group, q=1, fn=1, nr=1)
for crv in curve:
if mc.attributeQuery('profileMagnitude', n=crv, ex=1):
total += 1
ffd = mc.listConnections(crv + '.profileMagnitude', s=0, d=1)
origGeo = mc.listConnections(ffd[0] + '.originalGeometry[0]', s=1, d=0, p=1)
if origGeo:
mc.disconnectAttr(origGeo[0], ffd[0] + '.originalGeometry[0]')
success += 1
except BaseException:
pass
MESSAGE.printInView('Fixed %s/%s Cards.' % (success, total))
class GetFolder:
""" Get various folders from Maya """
def scripts(self):
return mc.internalVar(usd=1)
def root(self):
return path.join(gs_curvetools_path, '')
def fonts(self):
return path.join(gs_curvetools_path, 'fonts', '')
def icons(self):
return path.join(gs_curvetools_path, 'icons', '')
def plugins(self):
return path.join(gs_curvetools_path, 'plugins', '')
getFolder = GetFolder()
def attrExists(obj, attr):
""" Check if attribute exists """
if mc.objExists(obj):
if mc.attributeQuery(attr, n=obj, ex=1):
return 1
else:
return 0
else:
return 0
def getAttr(obj, attr):
""" Check if obj exist, check if attr exist, return attr """
if mc.objExists(obj):
if mc.attributeQuery(attr, n=obj, ex=1):
return mc.getAttr(obj + '.' + attr)
else:
return None
else:
return None
class ProgressBar():
"""Progress bar for Maya UI"""
def __init__(self, name, maxValue):
self.mainProgressBar = mel.eval('$tmp = $gMainProgressBar')
self.end()
mc.progressBar(self.mainProgressBar, edit=True, beginProgress=True, isInterruptable=True,
status=name, minValue=0, maxValue=maxValue)
def tick(self, step):
""" Returns "True" if ESC is pressed """
if mc.progressBar(self.mainProgressBar, query=True, isCancelled=True):
self.end()
MESSAGE.warning('Function is Cancelled!')
return True
mc.progressBar(self.mainProgressBar, edit=True, step=step)
return False
def end(self):
mc.progressBar(self.mainProgressBar, edit=True, endProgress=True)
def objectExists(obj):
""" Check if object exists """
if obj and len(obj):
return mc.objExists(obj[0])
else:
return 0
def addAtIndex(addTo, index, add):
""" Adds to source list at index. If list is empty, appends. """
if len(addTo) <= index:
addTo.append(add)
else:
addTo[index] = add
def getClosestPointAndNormal(targetMesh, pointPos=(0, 0, 0)):
"""
(targetMesh, givenPointPosition) -> (MPoint, MVector, int)
Returns a tuple containing the closest point on the mesh to the given point,
the normal at that point, and the ID of the face in which that point lies.
"""
pointPos = om.MPoint(pointPos)
sel = om.MSelectionList()
sel.add(targetMesh)
fnMesh = om.MFnMesh(sel.getDagPath(0))
pointAndNormal = fnMesh.getClosestPointAndNormal(pointPos, space=om.MSpace.kWorld)
return pointAndNormal
def getClosestVertexAndNormal(targetMesh, pointPos=(0, 0, 0)):
# type: (str, om.MVector | om.MPoint) -> tuple[om.MPoint, float, str, int, om.MVector]
"""
returns: (vertPos, vertDist, vertName, vertIndex, faceNormal)
Returns a tuple containing the closest to the pointPos vert on the mesh, the distance
between this vert and pointPos, vertex name and vertex index
"""
pos = om.MPoint(pointPos)
sel = om.MSelectionList()
sel.add(targetMesh)
fn_mesh = om.MFnMesh(sel.getDagPath(0))
index = fn_mesh.getClosestPoint(pos, space=om.MSpace.kWorld)[1]
faceNormal = fn_mesh.getPolygonNormal(index, space=om.MSpace.kWorld)
faceVerts = fn_mesh.getPolygonVertices(index)
vertexDistances = ((vertex, fn_mesh.getPoint(vertex, om.MSpace.kWorld).distanceTo(pos))
for vertex in faceVerts)
vertIndex, vertDistance = min(vertexDistances, key=lambda t: t[1])
vertPosition = fn_mesh.getPoint(vertIndex, om.MSpace.kWorld)
vertName = "{}.vtx[{}]".format(targetMesh, vertIndex)
return (vertPosition, vertDistance, vertName, vertIndex, faceNormal)
def resetAttributes(inputCurve, inputGeo):
# type: (str, str) -> None
"""
Resets attributes to a default state suitable for mirroring and orientation adjustment
"""
from .. import core
if mc.attributeQuery('lengthDivisions', n=inputCurve, ex=1):
mc.setAttr(inputCurve + '.lengthDivisions', 10)
try:
mc.setAttr(inputCurve + '.widthDivisions', 3)
except BaseException:
mc.setAttr(inputCurve + '.widthDivisions', 5)
if mc.attributeQuery('Profile', n=inputCurve, ex=1):
mc.setAttr(inputCurve + '.Profile', 0)
core.updateLattice("0, 0.5, 0.333, 0.5, 0.667, 0.5, 1, 0.5", inputCurve)
if mc.attributeQuery('Twist', n=inputCurve, ex=1):
mc.setAttr(inputCurve + '.Twist', 0)
# if mc.attributeQuery('invTwist', n=inputCurve, ex=1):
# mc.setAttr(inputCurve + '.invTwist', 0)
if mc.attributeQuery('Length', n=inputCurve, ex=1):
targetNode = mc.ls(mc.listHistory(inputGeo, ac=1, il=0), typ='curveWarp')[0]
core.attributes.resetMultiInst(targetNode, 'scaleCurve')
core.attributes.resetMultiInst(targetNode, 'twistCurve')
def resetAndReturnAttrs(curve):
# type: (str) -> tuple[str, om.MVector, int, int, float, float, list[list[str]]]
"""
Resets the attributes on a curve and returns original values as a list:
returns [curve, origVec, lengthDivisions, widthDivisions, twist, invTwist, graphs]
"""
from .. import core
crvAttr = core.attributes.getAttr(curve)
lengthDivisions = crvAttr['lengthDivisions'] if 'lengthDivisions' in crvAttr else None
widthDivisions = crvAttr['widthDivisions'] if 'widthDivisions' in crvAttr else None
twist = crvAttr['Twist'] if 'Twist' in crvAttr else None
invTwist = crvAttr['invTwist'] if 'invTwist' in crvAttr else None
if lengthDivisions and lengthDivisions > 10:
mc.setAttr(curve + '.lengthDivisions', 10)
if widthDivisions and widthDivisions > 2:
try:
mc.setAttr(curve + '.widthDivisions', 2)
except BaseException:
mc.setAttr(curve + '.widthDivisions', 4)
if twist and twist != 0:
mc.setAttr(curve + '.Twist', 0)
if invTwist and invTwist != 0:
mc.setAttr(curve + '.invTwist', 0)
firstCv = '%s.cv[0]' % curve
pos = mc.pointPosition(firstCv)
geo = core.selectPart(2, True, curve)[0]
graphs = None
if mc.attributeQuery('Length', n=curve, ex=1):
targetNode = mc.ls(mc.listHistory(geo, ac=1, il=0), typ='curveWarp')[0]
graphs = core.attributes.getMultiInst(curve)
core.attributes.resetMultiInst(targetNode, 'scaleCurve')
core.attributes.resetMultiInst(targetNode, 'twistCurve')
origVec = getClosestPointAndNormal(geo, pos)[1]
return [curve, origVec, lengthDivisions, widthDivisions, twist, invTwist, graphs]
def getMiddleVertAndNormal(inputCurve, inputGeo, inputFirstCv):
# type: (str, str, om.MVector) -> tuple[om.MVector, om.MVector]
"""Returns the middle profile vert of the card and normals of the flat card"""
from .. import core
# Getting original values
origAttrs = core.attributes.getAttr(inputCurve)
origGraphs = core.attributes.getMultiInst(inputCurve)
origLattice = core.getLatticeValues(inputCurve)
# Reset attributes
resetAttributes(inputCurve, inputGeo)
# Set a new temporary values
if mc.attributeQuery('Profile', n=inputCurve, ex=1):
mc.setAttr(inputCurve + ".Profile", 0)
if origLattice:
core.updateLattice("0, 0.5, 0.333, 0.5, 0.667, 0.5, 1, 0.5", inputCurve)
mc.setAttr(inputCurve + '.lengthDivisions', 10)
if mc.attributeQuery('Width', n=inputCurve, ex=1):
mc.setAttr(inputCurve + '.widthDivisions', 3)
else:
mc.setAttr(inputCurve + '.widthDivisions', 5)
# Get closest vert and normal value
closestVertAndNormal = getClosestVertexAndNormal(inputGeo, inputFirstCv)
# Get a position value for the closest vert
if mc.attributeQuery('Profile', n=inputCurve, ex=1):
profileScale = 2 * mc.getAttr(inputCurve + '.Width')
if mc.attributeQuery('scaleFactor', n=inputCurve, ex=1):
profileScale *= mc.getAttr(inputCurve + '.scaleFactor')
mc.setAttr(inputCurve + '.Profile', math.copysign(profileScale, origAttrs['Profile']))
vertexPos = mc.pointPosition("{}.vtx[{}]".format(inputGeo, closestVertAndNormal[3]))
# Set the original attributes back
core.attributes.setAttr(inputCurve, origAttrs)
if origGraphs:
for graph in origGraphs:
core.attributes.setMultiInst(inputCurve, graph)
if origLattice:
core.updateLattice(fromDouble2ToString(origLattice), inputCurve)
return (om.MVector(vertexPos), closestVertAndNormal[4])
def getUnitMult():
mult = float(mc.convertUnit("1cm", fromUnit="cm", toUnit=mc.currentUnit(q=1, linear=1)))
print("Current Mult:", mult)
return mult
def polySelectSp(comps, obj=None):
# type: (list[str], str) -> list[str]
"""polySelectSp wrapper"""
if MAYA_VER >= 2023:
return mc.ls(mc.polySelectSp(comps, q=1, loop=1), fl=1)
else:
return selectVertLoop(obj, comps)
def selectVertLoop(obj, comps):
# type: (str, list[str]) -> list[str]
"""
Returns vert loop list between two verts on the border
For older Maya versions that had polySelectSp fail do to that
"""
if len(comps) != 2:
raise Exception("Wrong number of input verts")
allFaces = "{}.f[*]".format(obj)
def expandToEdges(verts):
if MAYA_VER == 2018:
if isinstance(verts, set):
verts = list(verts)
return set(mc.ls(mc.polyListComponentConversion(verts, fv=1, te=1), fl=1))
def expandToVerts(edges):
if MAYA_VER == 2018:
if isinstance(edges, set):
edges = list(edges)
return set(mc.ls(mc.polyListComponentConversion(edges, fe=1, tv=1), fl=1))
perimeterEdges = set(mc.ls(mc.polyListComponentConversion(allFaces, ff=1, te=1, bo=1)))
if MAYA_VER == 2018:
perimeterEdges = list(perimeterEdges)
perimeterVerts = set(mc.ls(mc.polyListComponentConversion(perimeterEdges, fe=1, tv=1), fl=1))
sourceVert = comps[0] # type: str
targetVert = comps[1] # type: str
pathOne = set()
pathTwo = set()
# Initial expand to find two possible paths
initVerts = expandToVerts(expandToEdges(sourceVert))
initVerts.remove(sourceVert)
pathOne.add(initVerts.pop())
pathTwo.add(initVerts.pop())
# Check first path
limit = 1000
count = 0
currentVert_one = pathOne.copy().pop()
pathOne.add(sourceVert)
currentVert_two = pathTwo.copy().pop()
pathTwo.add(sourceVert)
while count < limit:
count += 1
# Expand first path
toEdges_one = expandToEdges(currentVert_one)
toVerts_one = expandToVerts(toEdges_one)
nonPerimeterVerts_one = toVerts_one.intersection(perimeterVerts) # Filter out non-perimeter verts
nextVert_one = nonPerimeterVerts_one.difference(pathOne)
pathOne.update(nextVert_one)
currentVert_one = nextVert_one.pop()
if targetVert in pathOne:
return list(pathOne)
# Expand second path
toEdges_two = expandToEdges(currentVert_two)
toVerts_two = expandToVerts(toEdges_two)
nonPerimeterVerts_two = toVerts_two.intersection(perimeterVerts) # Filter out non-perimeter verts
nextVert_two = nonPerimeterVerts_two.difference(pathTwo)
pathTwo.update(nextVert_two)
currentVert_two = nextVert_two.pop()
if targetVert in pathTwo:
return list(pathTwo)
return None
def deleteKeysOnAllObjects():
"""Deletes all the keys on all nurbs curve in the scene"""
for obj in mc.ls(typ='nurbsCurve'):
deleteKeys(obj)
def deleteKeys(target):
# type: (list[str]|str) -> None
"""Deletes keys from target curves and their CVs"""
mc.cutKey(target, cl=1, t=(), hi='both', cp=1, s=1)
def colorFrom255to1(clr):
if isinstance(clr, Iterable):
clr = list(clr)
for i in range(len(clr)):
if isinstance(clr[i], float) or isinstance(clr[i], int):
clr[i] = clr[i] / 255.0
if clr:
return clr
else:
MESSAGE.warning("Invalid color conversion")
elif isinstance(clr, float) or isinstance(clr, int):
return clr / 255.0
else:
MESSAGE.warning("Invalid color conversion")
def colorFrom1to255(clr):
if isinstance(clr, Iterable):
clr = list(clr)
for i in range(len(clr)):
if isinstance(clr[i], float) or isinstance(clr[i], int):
clr[i] = clr[i] * 255.0
if clr:
return clr
else:
MESSAGE.warning("Invalid color conversion")
elif isinstance(clr, float) or isinstance(clr, int):
return clr * 255.0
else:
MESSAGE.warning("Invalid color conversion")
def fromStringToDouble2(inputString):
splitPoints = inputString.split(',')
splitPoints = filter(None, splitPoints)
splitPoints = [float(point) for point in splitPoints]
arrangedValues = [[splitPoints[i], splitPoints[i + 1]] for i in range(0, len(splitPoints), 2)]
return arrangedValues
def fromDouble2ToString(inputList):
newString = ''
for i in range(len(inputList)):
newString += '%s,%s,' % (inputList[i][0],
inputList[i][1])
return newString
def setDouble2Attr(node, attr, values):
for i in range(len(values)):
mc.setAttr('%s.%s[%s]' % (node, attr, i), values[i][0], values[i][1], typ='double2')
def convertInstanceToObj(objects=list()):
""" Accepts a list and converts any found instances to objects """
convert = mc.menuItem('gsConvertInstances', q=1, rb=1)
finalList = list()
if not objects:
return finalList
for obj in objects:
if mc.attributeQuery('Orientation', n=obj, ex=1) and mc.connectionInfo(obj + '.Orientation', isSource=1):
LOGGER.info(obj + ' is skipped. It is a part of existing curveCard/Tube.')
continue
par = mc.listRelatives(mc.listRelatives(obj, pa=1, c=1)[0], pa=1, ap=1)
if len(par) > 1 and convert:
dup = mc.duplicate(obj)
mc.delete(obj)
mc.rename(dup[0], obj)
LOGGER.info(obj + ' is an instance. Converted to object.')
finalList.append(obj)
elif len(par) > 1 and not convert:
LOGGER.info(obj + ' is skipped. It is an instance.')
else:
finalList.append(obj)
mc.evalDeferred("print(' ')")
return finalList
def mergeDicts(dict1, dict2, rd=False):
""" Merge dictionaries and keep values of common keys in list. rd - remove duplicates """
dict3 = dict()
dict3.update(dict1)
dict3.update(dict2)
for key, value in dict3.items():
if key in dict1 and key in dict2:
if isinstance(dict1[key], list) and isinstance(dict2[key], list):
for geo in dict1[key]:
dict3[key].append(geo)
elif isinstance(dict1[key], list) and not isinstance(dict2[key], list):
dict3[key] = [dict3[key]] + dict1[key]
elif not isinstance(dict1[key], list) and isinstance(dict2[key], list):
dict3[key].append(dict1[key])
else:
dict3[key] = [value, dict1[key]]
if rd:
for key in dict3:
if isinstance(dict3[key], list):
tmp = dict.fromkeys(dict3[key])
dict3[key] = list(tmp)
return dict3
def cleanDict(dict0):
"""Removes objects from the dict if they were not found in the scene"""
for key in dict0:
tempList = list()
for geo in dict0[key]:
if mc.objExists(geo):
tempList = tempList + [geo]
dict0[key] = tempList
return dict0
def getShader(inputGeo):
""" Returns a dict of shadingEngines from list of geo in format {shader: [geo1,geo2...]} """
inputGeo = mc.filterExpand(inputGeo, sm=12)
if not inputGeo:
return {}
shaderDict = dict()
for geo in inputGeo:
dag = mc.ls(geo, dag=1, s=1)
shader = mc.listConnections(dag, d=1, s=1, t='shadingEngine')[0]
shaderDict.setdefault(shader, [])
shaderDict[shader].append(geo)
return shaderDict
def connectMessage(source, target, message):
"""Creates messages in both nodes based on message name and connects them source->target\n
Target node will be a multi-instance message"""
if source == target:
return
if not mc.attributeQuery(message, n=source, ex=1):
mc.addAttr(source, ln=message, at='message', k=0)
if mc.attributeQuery(message, n=target, ex=1) and not mc.attributeQuery(message, n=target, m=1):
mc.deleteAttr('{}.{}'.format(target, message))
if not mc.attributeQuery(message, n=target, ex=1):
mc.addAttr(target, ln=message, at='message', k=0, m=1, im=0)
mc.connectAttr('{}.{}'.format(source, message), '{}.{}'.format(target, message), na=1)
def getMod():
""" Get modifiers (Shift, Alt, Ctrl and '+' combinations) """
mods = mc.getModifiers()
if (mods & 1) > 0 and (mods & 4) > 0 and (mods & 8) > 0:
return 'Shift+Ctrl+Alt'
if (mods & 1) > 0 and (mods & 4) > 0:
return 'Shift+Ctrl'
if (mods & 1) > 0 and (mods & 8) > 0:
return 'Shift+Alt'
if (mods & 4) > 0 and (mods & 8) > 0:
return 'Ctrl+Alt'
if (mods & 1) > 0:
return 'Shift'
if (mods & 4) > 0:
return 'Ctrl'
if (mods & 8) > 0:
return 'Alt'
def getHotkeyMod(hk, key):
# type: (int, str) -> bool
"""Gets key modifiers and checks if hotkey was used"""
return True if hk == 2 or (hk is None and getMod() == key) else False
def fixDuplicateNames(nodes=None):
""" Fixes duplicate names conflicts in input nodes or all nodes in the scene """
if not nodes:
nodes = mc.ls()
duplicates = [node for node in nodes if '|' in node]
duplicates.sort(key=lambda obj: obj.count('|'), reverse=True)
newNames = []
if not duplicates:
return []
for obj in duplicates:
regEx1 = re.compile("[^|]*$").search(obj)
shortName = regEx1.group(0)
regEx2 = re.compile(".*[^0-9]").match(shortName)
if regEx2:
stripSuffix = regEx2.group(0)
else:
stripSuffix = shortName
newName = mc.rename(obj, (stripSuffix + "#"))
newNames.append(newName)
return newNames
def checkIfBezier(inputCurve):
if mc.nodeType(mc.listRelatives(inputCurve, c=1, pa=1)) == "bezierCurve":
origSel = mc.ls(sl=1)
mc.select(inputCurve, r=1)
result = mc.bezierCurveToNurbs()
origSel.append(result[0])
if result:
mc.rebuildCurve(result, kr=2, kcp=1, fr=1)
mc.select(origSel, r=1)
return result
return inputCurve
def convertBezierToNurbs():
sel = mc.ls(sl=1, dag=1, typ="bezierCurve")
for crv in sel:
oldConnections = mc.listConnections(crv, c=1, p=1, scn=1)
try:
mc.disconnectAttr(oldConnections[0], oldConnections[1])
mc.select(crv, r=1)
result = mc.bezierCurveToNurbs()
mc.connectAttr(oldConnections[0], oldConnections[1])
if result:
mc.rebuildCurve(result, kr=2, kcp=1, fr=1)
except Exception as e:
LOGGER.exception(e)
LOGGER.info("Curve %s was not converted" % crv)
@undo
def resetSingleGraph(graph, target=None):
sel = target if target else mc.filterExpand(mc.ls(sl=1, tr=1), sm=9)
if not sel:
sel = mc.filterExpand(mc.listRelatives(mc.ls(sl=1, o=1), p=1, pa=1), sm=9)
if not sel:
return
for curve in sel:
if mc.attributeQuery('Length', n=curve, ex=1) and mc.connectionInfo(curve + '.Length', isSource=1):
warp = mc.listConnections(curve + '.Length')
if warp:
from .. import core
from .wrap import WIDGETS
if graph == 'scale' or graph == 'width':
core.attributes.resetMultiInst(warp[0], 'scaleCurve')
WIDGETS['scaleCurve'].resetGraph()
if graph == 'twist':
core.attributes.resetMultiInst(warp[0], 'twistCurve')
WIDGETS['twistCurve'].resetGraph()
def fixBrokenGraphs():
sel = mc.filterExpand(mc.ls(typ='nurbsCurve'), sm=9)
for curve in sel:
if mc.attributeQuery('Length', n=curve, ex=1) and mc.connectionInfo(curve + '.Length', isSource=1):
warp = mc.listConnections(curve + '.Length')
if warp:
from .. import core
correctScale = None
correctTwist = None
if mc.attributeQuery('scaleCurve', n=curve, ex=1) and mc.attributeQuery('scaleCurve', n=warp[0], ex=1):
correctScale = mc.getAttr(curve + '.scaleCurve')
if mc.attributeQuery('twistCurve', n=curve, ex=1) and mc.attributeQuery('twistCurve', n=warp[0], ex=1):
correctTwist = mc.getAttr(curve + '.twistCurve')
if correctScale:
core.attributes.resetMultiInst(warp[0], 'scaleCurve')
setDouble2Attr(warp[0], 'scaleCurve', fromStringToDouble2(correctScale))
if correctTwist:
core.attributes.resetMultiInst(warp[0], 'twistCurve')
setDouble2Attr(warp[0], 'twistCurve', fromStringToDouble2(correctTwist))
@undo
def convertToNewLayerSystem():
"""Converts from the old layer system to the new one"""
layers = mc.ls(typ='displayLayer')
from .. import core
if mc.objExists(core.toggleColor.STORAGE_NODE):
mc.delete(core.toggleColor.STORAGE_NODE)
for layer in layers:
if 'curveGrp_' in layer:
connection = mc.listConnections(layer + '.identification', s=1, et=1, t='displayLayerManager', p=1)
if connection:
connectionOut = mc.listConnections(layer + '.drawInfo', d=1, scn=1, et=1, t='transform', p=1)
if connectionOut:
newNode = mc.createNode('displayLayer')
mc.copyAttr(layer, newNode, ic=0, oc=1, v=1, at=['drawInfo'])
mc.delete(layer)
mc.rename(newNode, layer)
def createNewDisplayLayer(name=None, objects=None):
# type: (str, list[str]) -> str
"""
Creates a new display layer independent from Maya display layer system.
name: The name of the Layer
objects: The list of objects to add to the Layer
returns: Name of the new layer node
"""
newLayer = mc.createNode('displayLayer')
if name:
newLayer = mc.rename(newLayer, name)
if objects:
mc.editDisplayLayerMembers(name, objects, nr=1)
return newLayer
def getFormattedLayerNames(collectionID, layerID):
# type: (int, int) -> tuple[str, str, str]
"""Gets layer names from the current collection id and layer id"""
id = ''
if collectionID:
id = '%s_' % collectionID
grpCurve = 'curveGrp_%s%s_Curve' % (id, layerID)
grpGeo = 'curveGrp_%s%s_Geo' % (id, layerID)
grpInst = 'curveGrp_%s%s_Inst' % (id, layerID)
return (grpCurve, grpGeo, grpInst)
def getFormattedCollectionByID(id):
# type: (int) -> str
"""Returns a formatted collection ID, suitable for string concat"""
collection = ''
if int(id) > 0:
collection = '%s_' % id
return collection
def getCollectionsSet():
# type: () -> set
"""Returns active collections indexes in a set"""
allLayers = mc.ls(typ='displayLayer')
collections = set()
for layer in allLayers:
if 'curveGrp_' in layer and ('_Geo' in layer or '_Curve' in layer or '_Inst' in layer):
c = layer.split('_')
if len(c) == 4:
collections.add(c[1])
return collections
def AOToggle():
if (mc.getAttr('hardwareRenderingGlobals.ssaoEnable')):
mc.setAttr('hardwareRenderingGlobals.ssaoEnable', 0)
else:
mc.setAttr('hardwareRenderingGlobals.ssaoEnable', 1)
def disableEcho():
""" Disable Echo All Commands """
if mc.commandEcho(q=1, st=1) and not mc.optionVar(q='gsEchoAllCommands'):
mc.commandEcho(st=0)
LOGGER.info('Echo All Commands function is disabled for performance reasons.')
LOGGER.info('To disable this functionality, set internalVariable "gsEchoAllCommands" to 1')
LOGGER.info('Example(MEL): optionVar -iv "gsEchoAllCommands" 1')
def checkNativePlugins(plugInList, myPluginName):
"""Checks if all the required NATIVE maya plugins are loaded"""
for plugin in plugInList:
if mc.pluginInfo(plugin, q=1, loaded=1) == 0:
try:
mc.loadPlugin(plugin, qt=1)
LOGGER.info('{0} plug-in was loaded successfully!'.format(plugin))
except Exception as e:
message = 'Warning!\n\nMaya native Plug-in "{0}" was not detected and can\'t be activated.\
\nYou can\'t use some of the functionality of {1} without {0} plug-in.\
\n\nTry to restart Maya and check your Maya installation.\
\n\nNOTE: This error can also be triggered if you have maya Plug-in Manager opened. Try to close it and repeat.'\
.format(plugin, myPluginName)
mc.confirmDialog(
title='{0} Plug-in Not Detected'.format(plugin),
message=message,
button=['OK'],
defaultButton='OK',
cancelButton='OK',
dismissString='OK',
icn='critical')
errorMessage = 'Maya "{0}" plug-in can\'t be loaded. Please check if your Maya install is correct.'.format(plugin)
LOGGER.exception(e)
MESSAGE.warning(errorMessage)
def loadCustomPlugin(plugin):
"""Checks if all the required CUSTOM maya plugins are loaded"""
if mc.pluginInfo(plugin, q=1, loaded=1) == 0:
split = os.path.split(plugin)
try:
mc.loadPlugin(plugin, qt=1)
LOGGER.info("{0} plug-in was loaded successfully!".format(split[-1]))
return True
except BaseException:
return False
else:
return True
def userSetup():
""" Rewrite userSetup file """
fileName = mc.internalVar(usd=1) + 'userSetup.mel'
if os.path.exists(fileName):
fileID = open(fileName, 'r')
allLines = fileID.readlines()
fileID.close()
newList = list()
lineDetected = 0
# Remove old code if any exist
for i in range(len(allLines)):
if 'gs_curvetools' not in allLines[i]:
newList.append(allLines[i])
else:
lineDetected = 1
# Write to userSetup if needed
if lineDetected == 1:
LOGGER.info('userSetup.mel was modified successfully!')
newFileID = open(fileName, 'w')
newFileID.writelines(newList)
newFileID.close()
def getDPI():
return mc.mayaDpiSetting(q=1, sd=1)
def openDocs():
""" Open User Documentation """
if OS == "mac":
os.system('open https://gs-curvetools.readthedocs.io/')
return 1
os.system('start https://gs-curvetools.readthedocs.io/')
def openLink(link):
""" Open User Documentation """
if OS == "mac":
os.system('open %s' % link)
return 1
os.system('start %s' % link)