771 lines
32 KiB
Python
771 lines
32 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 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
|
|
'''
|