├── README.md ├── argile ├── Qt.py ├── __init__.py ├── argile.py ├── argileAddPose.py ├── extraWidgets.py ├── filepickerwidget.py ├── img │ ├── Add.png │ ├── Delete.png │ ├── addFrame.png │ ├── backUp.png │ ├── cancelEdit.png │ ├── disconnect.png │ ├── edit.png │ ├── empty.png │ ├── fromScene.png │ ├── gear.png │ ├── publish.png │ ├── refresh.png │ ├── restore.png │ ├── sculpting.png │ └── toFrame.png ├── pluginCPP │ ├── 2016 │ │ └── blurPostDeform.mll │ ├── 2017 │ │ └── blurPostDeform.mll │ └── 2016.5 │ │ └── blurPostDeform.mll ├── storeXml.py ├── ui │ ├── argile.ui │ ├── argileAddPose.ui │ └── storeXml.ui └── utils.py └── argileCppCode ├── CMakeLists.txt ├── blurPostDeformCmd.cpp ├── blurPostDeformCmd.h ├── blurPostDeformNode.cpp ├── blurPostDeformNode.h ├── blurPostDeformPlugin.cpp ├── common.cpp ├── common.h └── example.py /README.md: -------------------------------------------------------------------------------- 1 | # Argile 2 | 3 | 4 | **Argile** is a maya plugin and python script used for postSculpt. 5 | 6 | It is used by Blur studios 7 | 8 | Pronounce it **aʀʒil** (french for clay) 9 | 10 | This package include 11 | - argile (python module for ui) 12 | - cpp *blurPostDeform.mll* for maya 2016, 2016.5, 2017 13 | 14 | ### Installation 15 | 16 | Copy the argile folder in maya scripts directory : 17 | ```sh 18 | C:\Users\...\Documents\maya\scripts\argile 19 | ``` 20 | It already contains the .mll plugins 21 | 22 | ### CPP 23 | The *argileCppCode* folder contains the code to compile blurPostDeform cpp plugin 24 | 25 | ### Run in Maya 26 | To open the argile tool in maya run the following command : 27 | ```sh 28 | from argile import runArgileDeformUI 29 | runArgileDeformUI() 30 | ``` 31 | -------------------------------------------------------------------------------- /argile/__init__.py: -------------------------------------------------------------------------------- 1 | ## 2 | # :remarks GUI to work with the blurSculpt plugin 3 | # 4 | # :author [author::email] 5 | # :author [author::company] 6 | # :date 03/22/17 7 | # 8 | from Qt.QtWidgets import QApplication, QSplashScreen, QDialog, QMainWindow 9 | 10 | def loadPluginCpp (): 11 | from maya import cmds 12 | import os 13 | 14 | fileVar = os.path.realpath(__file__) 15 | uiFolder, filename = os.path.split(fileVar) 16 | dicVersionPth = {'2016 Extension 2' : '2016.5', 17 | '2016' : '2016', 18 | '2017' : '2017', 19 | } 20 | mayaVersion = cmds.about (v=True) 21 | if mayaVersion in dicVersionPth : 22 | if cmds.pluginInfo("blurPostDeform", q=True, loaded=True) : 23 | return True 24 | try : 25 | cmds.loadPlugin("blurPostDeform") 26 | return True 27 | except : 28 | plugPath = os.path.join ( uiFolder, "pluginCPP", dicVersionPth[mayaVersion],"blurPostDeform.mll") 29 | return cmds.loadPlugin(plugPath) 30 | else : 31 | cmds.error( "No plugin for maya version ", mayaVersion , " \nFAIL") 32 | return False 33 | 34 | def getQTObject (mayaWindow): 35 | from maya import cmds 36 | if not cmds.window("tmpWidgetsWindow", q=True,ex=True) : 37 | cmds.window ("tmpWidgetsWindow") 38 | cmds.formLayout("qtLayoutObjects") 39 | for ind, el in enumerate (mayaWindow.children()): 40 | try : 41 | title =el.windowTitle () 42 | if title == "tmpWidgetsWindow" : 43 | break 44 | except : 45 | continue 46 | return el 47 | 48 | def runArgileDeformUI(): 49 | from .argile import ArgileDeformDialog 50 | from maya.cmds import optionVar 51 | if loadPluginCpp () : 52 | mayaWin = rootWindow() 53 | rootWin = ArgileDeformDialog (mayaWin, getQTObject(mayaWin)) 54 | rootWin.show() 55 | 56 | vals = optionVar (q="argileDeformWindow") 57 | if vals : 58 | rootWin.move(vals[0], vals[1]) 59 | rootWin.resize(vals[2], vals[3]) 60 | else : 61 | rootWin.move (0,0) 62 | 63 | def rootWindow(): 64 | """ 65 | Returns the currently active QT main window 66 | Only works for QT UI's like Maya 67 | """ 68 | # for MFC apps there should be no root window 69 | window = None 70 | if QApplication.instance(): 71 | inst = QApplication.instance() 72 | window = inst.activeWindow() 73 | # Ignore QSplashScreen's, they should never be considered the root window. 74 | if isinstance(window, QSplashScreen): 75 | return None 76 | # If the application does not have focus try to find A top level widget 77 | # that doesn't have a parent and is a QMainWindow or QDialog 78 | if window == None: 79 | windows = [] 80 | dialogs = [] 81 | for w in QApplication.instance().topLevelWidgets(): 82 | if w.parent() == None: 83 | if isinstance(w, QMainWindow): 84 | windows.append(w) 85 | elif isinstance(w, QDialog): 86 | dialogs.append(w) 87 | if windows: 88 | window = windows[0] 89 | elif dialogs: 90 | window = dialogs[0] 91 | 92 | # grab the root window 93 | if window: 94 | while True: 95 | parent = window.parent() 96 | if not parent: 97 | break 98 | if isinstance(parent, QSplashScreen): 99 | break 100 | window = parent 101 | 102 | return window 103 | -------------------------------------------------------------------------------- /argile/argileAddPose.py: -------------------------------------------------------------------------------- 1 | from Qt import QtGui, QtCore, QtWidgets, QtCompat 2 | from Qt.QtWidgets import QDialog 3 | from utils import toPyObject, getUiFile 4 | 5 | from maya import cmds, mel 6 | 7 | 8 | class ArgileAddPose(QDialog): 9 | 10 | def closeEvent (self, event): 11 | for el in self.parentWindow.toRestore: 12 | el.setEnabled (True) 13 | super(ArgileAddPose, self).closeEvent(event) 14 | 15 | def addNewPose (self): 16 | newName = str(self.uiPoseNameLE.text()) 17 | if not self.parentWindow.isValidName (newName) : 18 | self.uiWarningExistPoseNameLabel.show() 19 | self.uiPoseNameLE.selectAll() 20 | else : 21 | self.parentWindow.addNewPose ( 22 | newName, 23 | local = self.uiLocalDeformationRB.isChecked(), 24 | poseTransform = str(self.uiTransformLE.text())) 25 | self.close () 26 | 27 | def getSelectedTransform (self) : 28 | selection = cmds.ls (sl=True, tr=True) 29 | if selection : 30 | self.uiTransformLE.setText (selection[0]) 31 | 32 | def refreshWindow (self) : 33 | self.uiWarningExistPoseNameLabel.hide() 34 | parentWindGeom = self.parentWindow.geometry() 35 | currentGeom = self.geometry() 36 | XPos = parentWindGeom.x () + .5*(parentWindGeom.width () - currentGeom.width ()) 37 | YPos = parentWindGeom.y () + .5*(parentWindGeom.height () - currentGeom.height ()) 38 | self.move (XPos, YPos) 39 | self.setEnabled (True) 40 | self.activateWindow () 41 | self.uiPoseNameLE.setFocus() 42 | self.uiTransformLE.setText ("N/A") 43 | self.uiPoseNameLE.selectAll() 44 | 45 | #------------------- INIT ---------------------------------------------------- 46 | def __init__(self, parent=None): 47 | super(ArgileAddPose, self).__init__(parent) 48 | # load the ui 49 | 50 | QtCompat.loadUi(getUiFile(__file__), self) 51 | self.parentWindow = parent 52 | 53 | self.parentWindow.addPoseWin = self 54 | 55 | self.uiTransformLE.setText ("N/A") 56 | self.uiPoseNameLE.setText ("newPose") 57 | 58 | self.uiAddPoseBTN.clicked.connect (self.addNewPose) 59 | self.uiPickTransformBTN.clicked.connect (self.getSelectedTransform) 60 | 61 | self.uiLocalDeformationRB.toggled.connect ( self.uiTransformLE.setEnabled) 62 | self.uiLocalDeformationRB.toggled.connect (self.uiPickTransformBTN.setEnabled) 63 | self.uiLocalDeformationRB.toggled.connect ( self.uiUseTransformLBL.setEnabled) 64 | self.uiTangentDeformationRB.toggled.connect (self.uiWarningLabel.setVisible) 65 | 66 | 67 | self.uiWarningLabel.setVisible (False) 68 | self.uiWarningExistPoseNameLabel.hide() 69 | 70 | self.setWindowFlags (QtCore.Qt.Tool|QtCore.Qt.WindowStaysOnTopHint ) 71 | self.setWindowTitle ("Add New Pose") 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /argile/extraWidgets.py: -------------------------------------------------------------------------------- 1 | from maya import cmds 2 | from Qt import QtGui, QtCore, QtWidgets, QtCompat 3 | from utils import toPyObject, getUiFile 4 | from maya import OpenMayaUI 5 | 6 | def toQt(mayaName, QtClass): 7 | """ 8 | Given the name of a Maya UI element of any type, return the corresponding QWidget or QAction. 9 | If the object does not exist, returns None 10 | """ 11 | ptr = OpenMayaUI.MQtUtil.findControl(mayaName) 12 | if ptr is None: 13 | ptr = OpenMayaUI.MQtUtil.findLayout(mayaName) 14 | if ptr is None: 15 | ptr = OpenMayaUI.MQtUtil.findMenuItem(mayaName) 16 | if ptr is not None: 17 | return QtCompat.wrapInstance(long(ptr), QtClass) 18 | 19 | 20 | class toggleBlockSignals(object): 21 | def __init__(self, listWidgets, raise_error=True): 22 | self.listWidgets = listWidgets 23 | 24 | def __enter__(self): 25 | for widg in self.listWidgets: 26 | widg.blockSignals (True) 27 | def __exit__(self, exc_type, exc_val, exc_tb): 28 | for widg in self.listWidgets: 29 | widg.blockSignals (False) 30 | 31 | # spinner connected to an attribute 32 | class OLDspinnerWidget(QtWidgets.QWidget): 33 | 34 | def offsetSpin_mousePressEvent ( self,event): 35 | if cmds.objExists (self.theAttr) : 36 | val = cmds.getAttr (self.theAttr) 37 | self.theSpinner.setValue ( val ) 38 | QtWidgets.QDoubleSpinBox.mousePressEvent(self.theSpinner,event) 39 | 40 | def offsetSpin_wheelEvent ( self,event): 41 | if cmds.objExists (self.theAttr) : 42 | val = cmds.getAttr (self.theAttr) 43 | self.theSpinner.setValue ( val ) 44 | QtWidgets.QDoubleSpinBox.wheelEvent(self.theSpinner,event) 45 | 46 | def valueChangedFn(self,newVal) : 47 | if cmds.objExists (self.theAttr) and cmds.getAttr (self.theAttr, settable=True): 48 | cmds.setAttr (self.theAttr, newVal) 49 | 50 | def createWidget (self,singleStep = .1, precision = 2) : 51 | #theWindowForQtObjects = getQTObject () 52 | cmds.setParent ("tmpWidgetsWindow|qtLayoutObjects") 53 | self.floatField = cmds.floatField( pre=precision , step = singleStep) 54 | #QtCompat.wrapInstance (self.floatField,QtWidgets.QWidget ) 55 | self.theQtObject = toQt (self.floatField,QtWidgets.QWidget) 56 | #self.theQtObject = self.theWindowForQtObjects .children() [-1] 57 | """ 58 | if qtLayoutObject : 59 | self.theQtObject = qtLayoutObject 60 | else : 61 | self.theQtObject = toQtObject (self.floatField) 62 | """ 63 | 64 | self.theQtObject.setParent (self) 65 | self.theSpinner.lineEdit().hide() 66 | self.theQtObject.move (self.theSpinner.pos()) 67 | self.theQtObject.show() 68 | 69 | #QtCore.QObject.connect(self.theSpinner, QtCore.SIGNAL("valueChanged(double)"), self.valueChangedFn) 70 | self.theSpinner.valueChanged.connect (self.valueChangedFn) 71 | # set before click 72 | self.theSpinner.mousePressEvent = self.offsetSpin_mousePressEvent 73 | # set before spin 74 | self.theSpinner.wheelEvent = self.offsetSpin_wheelEvent 75 | self.theSpinner.resize (self.size()) 76 | wdth = self.theSpinner.lineEdit().width () + 3 77 | self.theQtObject.resize (wdth, self.height() ) 78 | 79 | def doConnectAttrSpinner (self, theAttr ) : 80 | self.theAttr = theAttr 81 | if cmds.objExists (self.theAttr) : 82 | cmds.connectControl( self.floatField, self.theAttr ) 83 | minValue, maxValue = -16777214,16777215 84 | 85 | listAtt = theAttr.split (".") 86 | att = listAtt [-1] 87 | node = ".".join(listAtt [:-1]) 88 | 89 | if cmds.attributeQuery (att, node = node, maxExists=True): 90 | maxValue, = cmds.attributeQuery (att, node = node, maximum=True) 91 | if cmds.attributeQuery (att, node = node, minExists=True) : 92 | minValue, = cmds.attributeQuery (att, node = node, minimum=True) 93 | 94 | self.theSpinner.setRange (minValue,maxValue) 95 | 96 | def resizeEvent (self, event): 97 | self.theSpinner.resize (self.size()) 98 | wdth = self.theSpinner.lineEdit().width () + 3 99 | self.theQtObject.resize (wdth, self.height() ) 100 | 101 | def __init__ (self, theAttr, singleStep = .1, precision = 2, theWindowForQtObjects=None): 102 | self.theWindowForQtObjects = theWindowForQtObjects 103 | super(spinnerWidget, self).__init__ () 104 | self.theAttr = theAttr 105 | self.theSpinner = QtWidgets.QDoubleSpinBox (self) 106 | self.theSpinner .setRange (-16777214,16777215) 107 | self.theSpinner .setSingleStep (singleStep) 108 | self.theSpinner .setDecimals(precision) 109 | 110 | self.theSpinner .move(0,0) 111 | #self.setMinimumWidth(50) 112 | self.createWidget ( singleStep=singleStep, precision =precision) 113 | self.doConnectAttrSpinner (theAttr) 114 | 115 | class spinnerWidget2(QtWidgets.QDoubleSpinBox): 116 | 117 | def updateValToAttr (self) : 118 | if cmds.objExists (self.theAttr) : 119 | self.setValue (cmds.getAttr (self.theAttr)) 120 | 121 | def doValueChanged (self, newVal) : 122 | if cmds.objExists (self.theAttr) : 123 | cmds.setAttr (self.theAttr, newVal) 124 | print self.theAttr, newVal 125 | 126 | def __init__ (self, theAttr, singleStep = .1, precision = 2): 127 | super(spinnerWidget2 , self).__init__(None) 128 | self.theAttr = theAttr 129 | self.setSingleStep (singleStep) 130 | self.setDecimals(precision) 131 | self.updateValToAttr () 132 | self.valueChanged.connect (self.doValueChanged) 133 | 134 | # spinner connected to an attribute 135 | 136 | class KeyFrameBtn (QtWidgets.QPushButton): 137 | _colors = { 138 | "redColor" :'background-color: rgb(154, 10, 10);', 139 | "redLightColor" : 'background-color: rgb(255, 153, 255);', 140 | "blueColor" :'background-color: rgb(10, 10, 154);', 141 | "blueLightColor" : 'background-color: rgb(153,255, 255);' 142 | } 143 | pressedColor = 'background-color: rgb(255, 255, 255);' 144 | 145 | def delete (self) : 146 | self.theTimeSlider.listKeys.remove(self) 147 | self.deleteLater () 148 | #sip.delete(self) 149 | 150 | def mouseMoveEvent(self,event): 151 | #print "begin mouseMove event" 152 | 153 | controlShitPressed = event.modifiers () == QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier 154 | shiftPressed = controlShitPressed or event.modifiers () == QtCore.Qt.KeyboardModifiers(QtCore.Qt.ShiftModifier) 155 | 156 | Xpos = event.globalX () - self.globalX + self.prevPos.x() 157 | theKey = (Xpos - self.startPos )/self.oneKeySize 158 | if not shiftPressed : theKey = int(theKey) 159 | theTime = theKey + self.startpb 160 | 161 | if theTime < self.start : theTime = self.start 162 | elif theTime > self.end : theTime = self.end 163 | 164 | if shiftPressed : self.theTime = round(theTime,3) 165 | else : self.theTime = int (theTime) 166 | self.updatePosition() 167 | 168 | #print "end mouseMove event" 169 | super (KeyFrameBtn, self ).mouseMoveEvent(event) 170 | 171 | def mousePressEvent(self,event): 172 | #print "begin mousePress event" 173 | controlShitPressed = event.modifiers () == QtCore.Qt.ControlModifier | QtCore.Qt.ShiftModifier 174 | controlPressed = controlShitPressed or event.modifiers () == QtCore.Qt.ControlModifier 175 | shiftPressed = controlShitPressed or event.modifiers () == QtCore.Qt.KeyboardModifiers(QtCore.Qt.ShiftModifier) 176 | 177 | if shiftPressed : offsetKey = 0.001 178 | else : offsetKey = 1 179 | 180 | self.duplicateMode = controlPressed 181 | 182 | if not self.checked : 183 | self.select(addSel = controlPressed ) 184 | if event.button() == QtCore.Qt.RightButton : 185 | index = self.theTimeSlider.listKeys.index (self) 186 | itemFrame = self.mainWindow.uiFramesTW.topLevelItem (index) 187 | 188 | self.mainWindow.clickedItem =itemFrame 189 | self.mainWindow.popup_menu.exec_(event.globalPos()) 190 | 191 | elif event.button() == QtCore.Qt.LeftButton : 192 | 193 | self.globalX = event.globalX () 194 | self.prevPos = self.pos() 195 | self.prevTime = self.theTime 196 | 197 | startpb = cmds.playbackOptions (q=True,minTime=True) 198 | endpb = cmds.playbackOptions (q=True,maxTime=True) 199 | 200 | self.startPos = self.theTimeSlider .width () / 100. * .5 201 | self.oneKeySize = (self.theTimeSlider .width () - self.startPos *2.) / (endpb-startpb+1.) 202 | 203 | self.setStyleSheet (self.pressedColor) 204 | 205 | #self.mainWindow.listKeysToMove = [(el, el.theTime) for el in self.theTimeSlider.getSortedListKeysObj() if el.checked ] 206 | 207 | self.start = startpb 208 | self.end = endpb 209 | self.startpb = startpb 210 | 211 | if self.duplicateMode : 212 | self.theTimeSlider.addDisplayKey (self.prevTime, isEmpty = self.isEmpty) 213 | 214 | """ 215 | super (KeyFrameBtn, self ).mousePressEvent (event) 216 | else : 217 | super (KeyFrameBtn, self ).mousePressEvent (event) 218 | """ 219 | #print "end mousePress event" 220 | 221 | def mouseReleaseEvent(self,event): 222 | super (KeyFrameBtn, self ).mouseReleaseEvent (event) 223 | if self.prevTime != self.theTime : 224 | 225 | if self.duplicateMode : 226 | self.mainWindow.duplicateFrame (self.prevTime,self.theTime) 227 | else : 228 | #poseName = cmds.getAttr (self.mainWindow.currentPose+".poseName") 229 | #listDeformationsFrame = cmds.blurSculpt (self.mainWindow.currentBlurNode,query = True,listFrames = True, poseName=str(poseName) ) 230 | listDeformationsFrame = self.mainWindow.getListDeformationFrames () 231 | 232 | if self.theTime in listDeformationsFrame : 233 | self.mainWindow.refresh () 234 | else : 235 | cmds.undoInfo (undoName="moveSeveralKeys",openChunk = True) 236 | self.updatePosition () 237 | self.doChangeTime () 238 | cmds.undoInfo (undoName="moveSeveralKeys",closeChunk = True) 239 | 240 | def doChangeTime (self): 241 | index = self.theTimeSlider.listKeys.index (self) 242 | itemFrame = self.mainWindow.uiFramesTW.topLevelItem (index) 243 | itemFrame.setText (0,str(self.theTime)) 244 | # check if refresh is necessary 245 | self.mainWindow.refreshListFramesAndSelect ( self.theTime) 246 | 247 | def enterEvent(self,event): 248 | super (KeyFrameBtn, self ).enterEvent (event) 249 | self.setFocus() 250 | self.setStyleSheet (self.lightColor) 251 | 252 | def leaveEvent(self,event): 253 | super (KeyFrameBtn, self ).leaveEvent (event) 254 | 255 | if self.checked : self.setStyleSheet (self.lightColor) 256 | else : self.setStyleSheet (self.baseColor) 257 | 258 | def select (self, addSel = False, selectInTree=True): 259 | if not addSel : 260 | for el in self.theTimeSlider.listKeys : 261 | el.checked = False 262 | el.setStyleSheet (el.baseColor) 263 | self.checked = True 264 | cmds.evalDeferred (self.setFocus) 265 | 266 | # select in parent : 267 | if selectInTree : 268 | with toggleBlockSignals ([self.mainWindow.uiFramesTW] ): 269 | index = self.theTimeSlider.listKeys.index (self) 270 | itemFrame = self.mainWindow.uiFramesTW.topLevelItem (index) 271 | self.mainWindow.uiFramesTW.setCurrentItem(itemFrame) 272 | self.setStyleSheet (self.lightColor) 273 | 274 | def updatePosition (self, startPos=None,oneKeySize=None, start=None, end = None ): 275 | if start == None or end == None : 276 | start = cmds.playbackOptions (q=True,minTime=True) 277 | end = cmds.playbackOptions (q=True,maxTime=True) 278 | 279 | isVisible = self.theTime >= start and self.theTime <= end 280 | 281 | self.setVisible (isVisible ) 282 | if isVisible: 283 | if oneKeySize == None or startPos == None : 284 | theTimeSlider_width = self.theTimeSlider .width () 285 | startPos = theTimeSlider_width / 100. * .5 286 | oneKeySize = (theTimeSlider_width - startPos*2.) / (end -start+1.) 287 | 288 | Xpos = (self.theTime - start ) * oneKeySize + startPos 289 | self.move (Xpos , 15) 290 | if oneKeySize < 6 : self. resize (6, 40) 291 | else : self. resize (oneKeySize, 40) 292 | 293 | def __init__ (self, theTime, theTimeSlider, isEmpty = False): 294 | super(KeyFrameBtn , self).__init__(None) 295 | self.checked = False 296 | if isEmpty : 297 | self.baseColor = self._colors["blueColor"] 298 | self.lightColor = self._colors["blueLightColor"] 299 | else: 300 | self.baseColor = self._colors["redColor"] 301 | self.lightColor = self._colors["redLightColor"] 302 | 303 | self.isEmpty = isEmpty 304 | self.duplicateMode = False 305 | 306 | self.setCursor (QtGui.QCursor (QtCore.Qt.SplitHCursor)) 307 | if theTime == int(theTime) : self.theTime = int(theTime) 308 | else : self.theTime = theTime 309 | 310 | self.theTimeSlider = theTimeSlider 311 | self.mainWindow = theTimeSlider.mainWindow 312 | 313 | self.setParent (self.theTimeSlider) 314 | self.resize (6,40) 315 | self.setStyleSheet (self.baseColor) 316 | 317 | cmds.evalDeferred (self.updatePosition ) 318 | self.show() 319 | 320 | class TheTimeSlider (QtWidgets.QWidget): 321 | 322 | def deleteKeys (self): 323 | #print "deleteKeys" 324 | toDelete = []+self.listKeys 325 | for keyFrameBtn in toDelete: 326 | keyFrameBtn.delete () 327 | self.listKeys = [] 328 | 329 | def getSortedListKeysObj (self): 330 | return sorted (self.listKeys, key=lambda ky : ky.theTime ) 331 | 332 | def addDisplayKey (self, theTime, isEmpty=False): 333 | keyFrameBtn = KeyFrameBtn (theTime, self, isEmpty=isEmpty) 334 | self.listKeys .append ( keyFrameBtn ) 335 | return keyFrameBtn 336 | 337 | def updateKeys (self): 338 | #print "updateKeys" 339 | listKeys = self.getSortedListKeysObj () 340 | listKeys .reverse() 341 | theTimeSlider_width = self.width () 342 | 343 | start = cmds.playbackOptions (q=True,minTime=True) 344 | end = cmds.playbackOptions (q=True,maxTime=True) 345 | startPos = theTimeSlider_width / 100. * .5 346 | oneKeySize = (theTimeSlider_width - startPos*2.) / (end -start+1.) 347 | 348 | for keyObj in listKeys : keyObj.updatePosition (startPos = startPos ,oneKeySize = oneKeySize, start =start, end = end) 349 | 350 | def resizeEvent (self,e): 351 | self.theTimePort.resize(e.size().width(),30) 352 | self.updateKeys () 353 | super(TheTimeSlider, self).resizeEvent (e) 354 | 355 | def __init__ (self, mainWindow): 356 | super(TheTimeSlider , self).__init__(None) 357 | self.mainWindow = mainWindow 358 | 359 | self.listKeys = [] 360 | #self.theTimePort = timePort.theTimePort 361 | #self.mayaMainWindow = timePort.mayaWindowLayout 362 | 363 | #theWindowForQtObjects = getQTObject () 364 | theWindowForQtObjects = self.mainWindow.theWindowForQtObjects 365 | cmds.setParent ("tmpWidgetsWindow|qtLayoutObjects") 366 | # cmds.setParent ("MayaWindow|formLayout1|qtLayoutObjects") 367 | cmdsTimePort = cmds.timePort( 'skinFixingTimePort', w=10, h=20, snap=True, globalTime=True,enableBackground=True, bgc = [.5,.5,.6]) 368 | # self.theTimePort = gui_utils.qtLayoutObject.children() [-1] 369 | self.theTimePort = theWindowForQtObjects .children() [-1] 370 | 371 | 372 | self.theTimePort.setParent (self) 373 | self.theTimePort.show() 374 | 375 | self.setMaximumHeight (40) 376 | self.setMinimumHeight (40) 377 | 378 | class WaitCursorCtxt(object): 379 | def __init__(self, raise_error=True): 380 | self.raise_error = raise_error 381 | def __enter__(self): 382 | cmds.waitCursor (state=True) 383 | def __exit__(self, exc_type, exc_val, exc_tb): 384 | if cmds.waitCursor (q=True, state=True) : cmds.waitCursor (state=False) 385 | 386 | 387 | 388 | """ 389 | 390 | theWindowForQtObjects = getQTObject () 391 | 392 | cmds.setParent ("tmpWidgetsWindow|qtLayoutObjects") 393 | # cmds.setParent ("MayaWindow|formLayout1|qtLayoutObjects") 394 | cmdsTimePort = cmds.timePort( 'skinFixingTimePort', w=10, h=20, snap=True, globalTime=True,enableBackground=True, bgc = [.5,.5,.6]) 395 | # self.theTimePort = gui_utils.qtLayoutObject.children() [-1] 396 | self.theTimePort = theWindowForQtObjects .children() [-1] 397 | 398 | 399 | self.theTimePort.setParent (self) 400 | """ -------------------------------------------------------------------------------- /argile/filepickerwidget.py: -------------------------------------------------------------------------------- 1 | ## 2 | # :namespace python.blurdev.gui.widgets.filepickerwidget.filepickerwidget 3 | # 4 | # :remarks Defines the FilePickerWidget class 5 | # 6 | # :author beta@blur.com 7 | # :author Blur Studio 8 | # :date 10/06/10 9 | # 10 | 11 | from Qt.QtCore import Qt, Property, Signal, Slot 12 | from Qt.QtGui import QColor 13 | from Qt.QtWidgets import QApplication, QFileDialog, QHBoxLayout, QLineEdit, QToolButton, QWidget 14 | from Qt import QtCompat 15 | import os.path 16 | 17 | resolvedStylesheetDefault = """QLineEdit {color: rgba%(fg)s; 18 | background: rgba%(bg)s; 19 | }""" 20 | 21 | class LineEdit(QLineEdit): 22 | def dragEnterEvent(self, event): 23 | if not self.isReadOnly(): 24 | event.acceptProposedAction() 25 | else: 26 | super(LineEdit, self).dragEnterEvent(event) 27 | 28 | def dropEvent(self, event): 29 | mimeData = event.mimeData() 30 | if not self.isReadOnly() and mimeData.hasUrls(): 31 | urlList = mimeData.urls() 32 | if urlList: 33 | fname = urlList[0].toLocalFile() 34 | self.setText(fname) 35 | event.acceptProposedAction() 36 | 37 | class FilePickerWidget( QWidget ): 38 | filenamePicked = Signal(str) 39 | filenameChanged = Signal(str) 40 | filenameEdited = Signal(str) 41 | 42 | def __init__( self, parent=None ): 43 | self._correctBackground = QColor(156, 206, 156, 255) 44 | self._correctForeground = QColor(Qt.white) 45 | self._inCorrectBackground = QColor(210, 156, 156, 255) 46 | self._inCorrectForeground = QColor(Qt.white) 47 | self._defaultLocation = '' 48 | QWidget.__init__( self, parent ) 49 | 50 | self.uiFilenameTXT = LineEdit(self) 51 | self.uiPickFileBTN = QToolButton(self) 52 | self.uiPickFileBTN.setText('...') 53 | self.uiPickFileBTN.setToolTip('

Browse to a file path.

Ctrl + LMB: Explore to current path.

') 54 | # Make this widget focusable and pass the widget focus to uiFilenameTXT 55 | self.setFocusProxy(self.uiFilenameTXT) 56 | self.setFocusPolicy(Qt.StrongFocus) 57 | layout = QHBoxLayout(self) 58 | layout.addWidget(self.uiFilenameTXT) 59 | layout.addWidget(self.uiPickFileBTN) 60 | layout.setContentsMargins(0, 0, 0, 0) 61 | self.setLayout(layout) 62 | 63 | self._caption = "Pick file..." 64 | self._filters = "All Files (*.*)" 65 | self._pickFolder = False 66 | self._openFile = False 67 | self._resolvePath = False 68 | #self._imageSequence = False 69 | self._resolved = False 70 | self._chosenPath = None 71 | #self._imageSequenceFormat = '{pre}[{firstNum}:{lastNum}]{post}' 72 | 73 | self.uiFilenameTXT.textChanged.connect( self.emitFilenameChanged ) 74 | 75 | self.uiFilenameTXT.editingFinished.connect( self.emitFilenameEdited ) 76 | self.uiPickFileBTN.clicked.connect( self.pickPath ) 77 | self.resolvedStylesheet = resolvedStylesheetDefault 78 | 79 | self.resolve() 80 | 81 | def caption( self ): 82 | return self._caption 83 | 84 | def emitFilenameChanged( self ): 85 | self.resolve() 86 | if ( not self.signalsBlocked() ): 87 | self.filenameChanged.emit( self.uiFilenameTXT.text() ) 88 | 89 | def emitFilenameEdited( self ): 90 | if ( not self.signalsBlocked() ): 91 | self.filenameEdited.emit( self.uiFilenameTXT.text() ) 92 | 93 | def filePath( self ): 94 | # if it's an image sequence, return the last chosen image path 95 | return self._chosenPath or self.uiFilenameTXT.text() 96 | def filters( self ): 97 | return self._filters 98 | 99 | def isResolved( self ): 100 | return self._resolved 101 | 102 | def openFile( self ): 103 | return self._openFile 104 | 105 | def pickFolder( self ): 106 | return self._pickFolder 107 | 108 | def pickPath( self ): 109 | initialPath = self.uiFilenameTXT.text() or self.defaultLocation 110 | initialPath = str (initialPath) 111 | while not os.path.exists(initialPath): 112 | if os.path.dirname(initialPath) == initialPath: 113 | break 114 | else: 115 | initialPath = os.path.dirname(initialPath) 116 | if QApplication.keyboardModifiers() == Qt.ControlModifier: 117 | import blurdev 118 | blurdev.osystem.explore(initialPath) 119 | else: 120 | if self._pickFolder: 121 | filepath = QFileDialog.getExistingDirectory(self, self._caption, initialPath) 122 | elif self._openFile: 123 | filepath, _ = QtCompat.QFileDialog.getOpenFileName(self, self._caption, initialPath, self._filters) 124 | else: 125 | filepath, _ = QtCompat.QFileDialog.getSaveFileName(self, self._caption, initialPath, self._filters) 126 | if filepath: 127 | self.uiFilenameTXT.setText( filepath ) 128 | if ( not self.signalsBlocked() ): 129 | self.filenamePicked.emit( filepath ) 130 | 131 | def resolve( self ): 132 | if self.resolvePath(): 133 | path = self.uiFilenameTXT.text() 134 | if self._pickFolder: 135 | valid = os.path.isdir(path) 136 | else: 137 | valid = os.path.isfile(path) 138 | if valid: 139 | fg = self.correctForeground 140 | bg = self.correctBackground 141 | self._resolved = True 142 | else: 143 | fg = self.inCorrectForeground 144 | bg = self.inCorrectBackground 145 | self._resolved = False 146 | 147 | style = self.resolvedStylesheet % {'bg':bg.getRgb(), 'fg':fg.getRgb()} 148 | else: 149 | style = '' 150 | self._resolved = False 151 | 152 | self.uiFilenameTXT.setStyleSheet(style) 153 | 154 | def resolvePath( self ): 155 | return self._resolvePath 156 | 157 | def setCaption( self, caption ): 158 | self._caption = caption 159 | 160 | @Slot(str) 161 | def setFilePath( self, filePath ): 162 | self.uiFilenameTXT.setText( filePath ) 163 | self.resolve() 164 | 165 | def setFilters( self, filters ): 166 | self._filters = filters 167 | 168 | def setOpenFile( self, state ): 169 | self._openFile = state 170 | 171 | def setPickFolder( self, state ): 172 | self._pickFolder = state 173 | 174 | @Slot(bool) 175 | def setNotResolvePath( self, state ): 176 | """ Set resolvePath to the oposite of state. """ 177 | self.setResolvePath( not state ) 178 | 179 | @Slot(bool) 180 | def setResolvePath( self, state ): 181 | self._resolvePath = state 182 | self.resolve() 183 | 184 | pyCaption = Property( "QString", caption, setCaption ) 185 | pyFilters = Property( "QString", filters, setFilters ) 186 | pyPickFolder = Property( "bool", pickFolder, setPickFolder ) 187 | pyOpenFile = Property( "bool", openFile, setOpenFile ) 188 | pyResolvePath = Property( "bool", resolvePath, setResolvePath ) 189 | #pyImageSequence = Property( "bool", imageSequence, setImageSequence ) 190 | pyFilePath = Property( "QString", filePath, setFilePath ) 191 | 192 | # Load the colors from the stylesheets 193 | @Property(QColor) 194 | def correctBackground(self): 195 | return self._correctBackground 196 | 197 | @correctBackground.setter 198 | def correctBackground(self, color): 199 | self._correctBackground = color 200 | self.resolve() 201 | 202 | @Property(QColor) 203 | def correctForeground(self): 204 | return self._correctForeground 205 | 206 | @correctForeground.setter 207 | def correctForeground(self, color): 208 | self._correctForeground = color 209 | self.resolve() 210 | 211 | @Property(QColor) 212 | def inCorrectBackground(self): 213 | return self._inCorrectBackground 214 | 215 | @inCorrectBackground.setter 216 | def inCorrectBackground(self, color): 217 | self._inCorrectBackground = color 218 | self.resolve() 219 | 220 | @Property(QColor) 221 | def inCorrectForeground(self): 222 | return self._inCorrectForeground 223 | 224 | @inCorrectForeground.setter 225 | def inCorrectForeground(self, color): 226 | self._inCorrectForeground = color 227 | self.resolve() 228 | 229 | @Property("QString") 230 | def defaultLocation(self): 231 | return self._defaultLocation 232 | 233 | @defaultLocation.setter 234 | def defaultLocation(self, value): 235 | self._defaultLocation = str(value) 236 | -------------------------------------------------------------------------------- /argile/img/Add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guibab/argile/b36273ecbc14ec2cc40080303f35c79926455d0e/argile/img/Add.png -------------------------------------------------------------------------------- /argile/img/Delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guibab/argile/b36273ecbc14ec2cc40080303f35c79926455d0e/argile/img/Delete.png -------------------------------------------------------------------------------- /argile/img/addFrame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guibab/argile/b36273ecbc14ec2cc40080303f35c79926455d0e/argile/img/addFrame.png -------------------------------------------------------------------------------- /argile/img/backUp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guibab/argile/b36273ecbc14ec2cc40080303f35c79926455d0e/argile/img/backUp.png -------------------------------------------------------------------------------- /argile/img/cancelEdit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guibab/argile/b36273ecbc14ec2cc40080303f35c79926455d0e/argile/img/cancelEdit.png -------------------------------------------------------------------------------- /argile/img/disconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guibab/argile/b36273ecbc14ec2cc40080303f35c79926455d0e/argile/img/disconnect.png -------------------------------------------------------------------------------- /argile/img/edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guibab/argile/b36273ecbc14ec2cc40080303f35c79926455d0e/argile/img/edit.png -------------------------------------------------------------------------------- /argile/img/empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guibab/argile/b36273ecbc14ec2cc40080303f35c79926455d0e/argile/img/empty.png -------------------------------------------------------------------------------- /argile/img/fromScene.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guibab/argile/b36273ecbc14ec2cc40080303f35c79926455d0e/argile/img/fromScene.png -------------------------------------------------------------------------------- /argile/img/gear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guibab/argile/b36273ecbc14ec2cc40080303f35c79926455d0e/argile/img/gear.png -------------------------------------------------------------------------------- /argile/img/publish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guibab/argile/b36273ecbc14ec2cc40080303f35c79926455d0e/argile/img/publish.png -------------------------------------------------------------------------------- /argile/img/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guibab/argile/b36273ecbc14ec2cc40080303f35c79926455d0e/argile/img/refresh.png -------------------------------------------------------------------------------- /argile/img/restore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guibab/argile/b36273ecbc14ec2cc40080303f35c79926455d0e/argile/img/restore.png -------------------------------------------------------------------------------- /argile/img/sculpting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guibab/argile/b36273ecbc14ec2cc40080303f35c79926455d0e/argile/img/sculpting.png -------------------------------------------------------------------------------- /argile/img/toFrame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guibab/argile/b36273ecbc14ec2cc40080303f35c79926455d0e/argile/img/toFrame.png -------------------------------------------------------------------------------- /argile/pluginCPP/2016.5/blurPostDeform.mll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guibab/argile/b36273ecbc14ec2cc40080303f35c79926455d0e/argile/pluginCPP/2016.5/blurPostDeform.mll -------------------------------------------------------------------------------- /argile/pluginCPP/2016/blurPostDeform.mll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guibab/argile/b36273ecbc14ec2cc40080303f35c79926455d0e/argile/pluginCPP/2016/blurPostDeform.mll -------------------------------------------------------------------------------- /argile/pluginCPP/2017/blurPostDeform.mll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/guibab/argile/b36273ecbc14ec2cc40080303f35c79926455d0e/argile/pluginCPP/2017/blurPostDeform.mll -------------------------------------------------------------------------------- /argile/storeXml.py: -------------------------------------------------------------------------------- 1 | from Qt import QtGui, QtCore, QtWidgets, QtCompat 2 | from Qt.QtWidgets import QDialog 3 | from utils import toPyObject, getUiFile 4 | 5 | from . import extraWidgets, filepickerwidget 6 | import codecs, re 7 | import os 8 | 9 | from maya import cmds, mel 10 | from xml.dom import minidom 11 | import xml.etree.ElementTree as ET 12 | 13 | 14 | 15 | class StoreXml(QDialog): 16 | storewin = True 17 | 18 | def closeEvent (self, event): 19 | for el in self.parentWindow.toRestore: 20 | el.setEnabled (True) 21 | super(StoreXml, self).closeEvent(event) 22 | 23 | def setUpFilePicker (self, store = True): 24 | # prepare the file picker 25 | with extraWidgets.toggleBlockSignals ([self.uiXmlStoreFile]) : 26 | self.uiXmlStoreFile.setFilePath("") 27 | 28 | sceneName = cmds.file(q=True,sceneName=True) 29 | splt = sceneName.split("/") 30 | startDir = "/".join(splt [:-1]) 31 | self.uiXmlStoreFile._defaultLocation = startDir 32 | 33 | if store : 34 | self.storewin = True 35 | self.uiDoStoreBTN.setText ("store xml") 36 | self.uiXmlStoreFile.pyOpenFile = False 37 | self.uiXmlStoreFile.pyCaption = u'Store xml...' #QtCore.QString(u'Store xml...') 38 | self.uiDoStoreBTN.setEnabled (False) 39 | self.setWindowTitle ("Store xml file") 40 | 41 | else : 42 | self.storewin = False 43 | self.uiDoStoreBTN.setText ("restore selected frames") 44 | self.uiXmlStoreFile.pyOpenFile = True 45 | self.uiXmlStoreFile.pyCaption = u'select file to load from...'#QtCore.QString(u'select file to load from...') 46 | self.setWindowTitle ("Restore from xml file") 47 | 48 | 49 | self.uiAllFramesTW.setEnabled (self.storewin) 50 | self.uiAllFramesTW.clear() 51 | self.uiAllFramesTW.setColumnCount(3) 52 | self.uiAllFramesTW.setHeaderLabels(["blurSculpt", u"pose", "frame", "isEmpty"]) 53 | self.setTreePretty () 54 | 55 | def refreshTree(self, blurNode) : 56 | # fill the tree of frames 57 | 58 | dicVal = {"blurNode" : blurNode} 59 | listPoses = cmds.blurSculpt (blurNode,query = True, listPoses=True) 60 | if not listPoses : return 61 | 62 | posesIndices = map(int,cmds.getAttr (blurNode+".poses",mi=True)) 63 | 64 | # first store positions 65 | for logicalInd in posesIndices: 66 | dicVal ["indPose"] = logicalInd 67 | 68 | thePose = cmds.getAttr ("{blurNode}.poses[{indPose}].poseName".format (**dicVal)) 69 | listDeformationsIndices = cmds.getAttr ("{blurNode}.poses[{indPose}].deformations".format (**dicVal), mi=True) 70 | if not listDeformationsIndices : 71 | continue 72 | 73 | toAdd = [] 74 | for logicalFrameIndex in listDeformationsIndices : 75 | dicVal["frameInd"] = logicalFrameIndex 76 | frame = cmds.getAttr ("{blurNode}.poses[{indPose}].deformations[{frameInd}].frame".format (**dicVal)) 77 | mvtIndices = cmds.getAttr ("{blurNode}.poses[{indPose}].deformations[{frameInd}].vectorMovements".format (**dicVal), mi=True) 78 | 79 | frameItem = QtWidgets.QTreeWidgetItem() 80 | frameItem.setText (0, str(blurNode)) 81 | frameItem.setText (1, str(thePose)) 82 | frameItem.setText (2, str(frame)) 83 | if not mvtIndices : 84 | frameItem.setText (3, u"\u00D8") 85 | frameItem.setTextAlignment(3 , QtCore.Qt.AlignCenter ) 86 | 87 | frameItem.setData (0, QtCore.Qt.UserRole, "{blurNode}.poses[{indPose}].deformations[{frameInd}]".format (**dicVal)) 88 | toAdd.append ((frame, frameItem)) 89 | 90 | for frame, frameItem in sorted (toAdd) : 91 | self.uiAllFramesTW.addTopLevelItem (frameItem) 92 | self.setTreePretty () 93 | 94 | def setTreePretty (self) : 95 | self.uiAllFramesTW.setEnabled (True) 96 | for i in range (4) : self.uiAllFramesTW.resizeColumnToContents(i) 97 | vh = self.uiAllFramesTW.header () 98 | self.uiAllFramesTW.selectAll() 99 | for i in range (4): 100 | wdt = self.uiAllFramesTW.columnWidth (i) 101 | self.uiAllFramesTW.setColumnWidth (i,wdt + 10) 102 | 103 | def buttonAction(self) : 104 | if self.storewin : self.doStoreXml () 105 | else : 106 | self.doRetrieveSelection () 107 | QtCore.QTimer.singleShot (0, self.parentWindow.refresh) 108 | 109 | self.close () 110 | 111 | def doRetrieveSelection (self) : 112 | selectedItems = self.uiAllFramesTW.selectedItems () 113 | dicFrames = {} 114 | listPoses = [] 115 | for frameItem in selectedItems: 116 | #frame_tag = frameItem.data (0, QtCore.Qt.UserRole)#.toPyObject() 117 | #pose_tag = frameItem.data (1, QtCore.Qt.UserRole)#.toPyObject() 118 | frameKey = frameItem.data (0, QtCore.Qt.UserRole)#.toPyObject() 119 | frame_tag = self.dicTag [frameKey] 120 | poseKey = frameItem.data (1, QtCore.Qt.UserRole)#.toPyObject() 121 | pose_tag = self.dicTag [poseKey] 122 | poseName = pose_tag .get ("poseName") 123 | 124 | if poseName not in dicFrames : 125 | dicFrames [poseName] = [frame_tag] 126 | listPoses .append ( pose_tag ) 127 | else : 128 | dicFrames [poseName].append(frame_tag) 129 | 130 | print "do retrieve done" 131 | with extraWidgets.WaitCursorCtxt (): 132 | self.retrieveblurXml ( dicFrames, listPoses) 133 | 134 | 135 | 136 | def retrieveblurXml (self, dicFrames, listPoses) : 137 | dicVal = {"blurNode" : self.parentWindow.currentBlurNode} 138 | 139 | pses = cmds.getAttr (self.parentWindow.currentBlurNode+".poses",mi=True) 140 | dicPoses = {} 141 | newInd = 0 142 | if pses: 143 | posesIndices = map(int,pses) 144 | for logicalInd in posesIndices: 145 | dicVal ["indPose"] = logicalInd 146 | poseName = cmds.getAttr ("{blurNode}.poses[{indPose}].poseName".format (**dicVal)) 147 | dicPoses [poseName] = logicalInd 148 | newInd = max (posesIndices) + 1 149 | 150 | 151 | for pose_tag in listPoses: 152 | poseName = pose_tag .get ("poseName") 153 | print poseName 154 | # access the pose Index 155 | if poseName not in dicPoses: # create it 156 | dicVal ["indPose"] = newInd 157 | cmds.setAttr ("{blurNode}.poses[{indPose}].poseName".format (**dicVal), poseName, type = "string") 158 | dicPoses[poseName] = newInd 159 | newInd += 1 160 | # do the connection and type 161 | poseEnabled = pose_tag .get ("poseEnabled" ) == "True" 162 | poseGain = float(pose_tag .get ("poseGain")) 163 | poseOffset = float(pose_tag .get ("poseOffset")) 164 | cmds.setAttr ("{blurNode}.poses[{indPose}].poseEnabled".format (**dicVal), poseEnabled) 165 | cmds.setAttr ("{blurNode}.poses[{indPose}].poseGain".format (**dicVal), poseGain) 166 | cmds.setAttr ("{blurNode}.poses[{indPose}].poseOffset".format (**dicVal), poseOffset) 167 | 168 | deformType = int(pose_tag .get ("deformType")) 169 | cmds.setAttr ("{blurNode}.poses[{indPose}].deformationType".format (**dicVal), deformType) 170 | poseMatrixConn = pose_tag .get ("poseMatrix") 171 | if cmds.objExists (poseMatrixConn ) : 172 | try : 173 | cmds.connectAttr (poseMatrixConn,"{blurNode}.poses[{indPose}].poseMatrix".format (**dicVal), f=True) 174 | except : pass 175 | else : 176 | dicVal ["indPose"] = dicPoses [poseName] 177 | 178 | for poseName, listFrameTags in dicFrames.items(): 179 | dicVal ["indPose"] = dicPoses [poseName] 180 | dicFrames = {} 181 | newFrameInd = 0 182 | listDeformationsIndices = cmds.getAttr ("{blurNode}.poses[{indPose}].deformations".format (**dicVal), mi=True) 183 | if listDeformationsIndices : 184 | for logicalFrameIndex in listDeformationsIndices : 185 | dicVal ["frameInd"] = logicalFrameIndex 186 | frame = cmds.getAttr ("{blurNode}.poses[{indPose}].deformations[{frameInd}].frame".format (**dicVal)) 187 | dicFrames [frame]= logicalFrameIndex 188 | newFrameInd = max (listDeformationsIndices) + 1 189 | 190 | for frame_tag in listFrameTags : 191 | frame = float(frame_tag .get ("frame") ) 192 | if frame not in dicFrames : 193 | dicVal["frameInd"] = newFrameInd 194 | newFrameInd +=1 195 | 196 | gain = float(frame_tag .get ("gain") ) 197 | offset = float(frame_tag .get ("offset")) 198 | frameEnabled = frame_tag .get ("frameEnabled") == "True" 199 | cmds.setAttr ("{blurNode}.poses[{indPose}].deformations[{frameInd}].gain".format (**dicVal), gain) 200 | cmds.setAttr ("{blurNode}.poses[{indPose}].deformations[{frameInd}].offset".format (**dicVal), offset) 201 | cmds.setAttr ("{blurNode}.poses[{indPose}].deformations[{frameInd}].frameEnabled".format (**dicVal), frameEnabled) 202 | cmds.setAttr ("{blurNode}.poses[{indPose}].deformations[{frameInd}].frame".format (**dicVal), frame) 203 | else : 204 | dicVal["frameInd"] = dicFrames [frame] 205 | # first clear 206 | frameName = "{blurNode}.poses[{indPose}].deformations[{frameInd}]".format (**dicVal) 207 | mvtIndices = cmds.getAttr (frameName+".vectorMovements", mi=True) 208 | if mvtIndices: 209 | mvtIndices = map (int, mvtIndices ) 210 | for indVtx in mvtIndices: 211 | cmds.removeMultiInstance(frameName+".vectorMovements[{0}]".format (indVtx), b=True) 212 | 213 | vector_tag = frame_tag.getchildren ()[0] 214 | for vectag in vector_tag.getchildren (): 215 | index =int(vectag.get ("index" )) 216 | dicVal["vecInd"] = index 217 | value = vectag.get ("value") 218 | floatVal = map (float, value [1:-1].split(", ")) 219 | cmds.setAttr ("{blurNode}.poses[{indPose}].deformations[{frameInd}].vectorMovements[{vecInd}]".format (**dicVal), *floatVal) 220 | 221 | 222 | 223 | def doStoreXml (self): 224 | inputPoseFramesIndices = {} 225 | selectedItems = self.uiAllFramesTW.selectedItems () 226 | destinationFile = str (self.uiXmlStoreFile.filePath()) 227 | 228 | if selectedItems : 229 | for frameItem in selectedItems: 230 | fullName = str(frameItem.data (0, QtCore.Qt.UserRole)) 231 | poseInd, frameInd = [int (ind) for ind in re.findall(r'\b\d+\b', fullName)] 232 | if poseInd not in inputPoseFramesIndices : 233 | inputPoseFramesIndices [poseInd] = [frameInd] 234 | else : 235 | inputPoseFramesIndices [poseInd].append (frameInd) 236 | 237 | with extraWidgets.WaitCursorCtxt (): 238 | doc = minidom.Document() 239 | ALL_tag = doc.createElement("ALL") 240 | doc.appendChild(ALL_tag ) 241 | 242 | created_tag = self.parentWindow.storeInfoBlurSculpt(doc, self.parentWindow.currentBlurNode,inputPoseFramesIndices = inputPoseFramesIndices ) 243 | ALL_tag .appendChild (created_tag ) 244 | 245 | with codecs.open(destinationFile, "w", "utf-8") as out: 246 | doc.writexml(out,indent="\n",addindent="\t",newl="") 247 | 248 | 249 | def readXmlFile (self) : 250 | with extraWidgets.WaitCursorCtxt (): 251 | if os.path.isfile(self.sourceFile ) : 252 | tree = ET.parse(self.sourceFile ) 253 | root = tree.getroot() 254 | self.refreshTreeFromRoot (root) 255 | 256 | 257 | def refreshTreeFromRoot (self, root) : 258 | self.dicTag = {} 259 | for blurNode_tag in root.getchildren(): 260 | blurName = blurNode_tag.get ("name") 261 | for pose_tag in blurNode_tag.getchildren(): 262 | poseName = pose_tag .get ("poseName") 263 | toAdd = [] 264 | for frame_tag in pose_tag.getchildren (): 265 | frame = float(frame_tag .get ("frame") ) 266 | vector_tag = frame_tag.getchildren ()[0] 267 | 268 | frameItem = QtWidgets.QTreeWidgetItem() 269 | frameItem.setText (0, str(blurName)) 270 | frameItem.setText (1, str(poseName)) 271 | frameItem.setText (2, str(frame)) 272 | if not vector_tag.getchildren() : 273 | frameItem.setText (3, u"\u00D8") 274 | 275 | toAdd.append ((frame, frameItem)) 276 | 277 | frameKey = "_".join ([str(blurName),str(poseName), str(frame), "frame" ]) 278 | self.dicTag [frameKey] = frame_tag 279 | poseKey = "_".join ([str(blurName),str(poseName), str(frame), "pose" ]) 280 | self.dicTag [poseKey] = pose_tag 281 | frameItem.setData (0, QtCore.Qt.UserRole, frameKey) 282 | frameItem.setData (1, QtCore.Qt.UserRole, poseKey) 283 | 284 | for frame, frameItem in sorted (toAdd) : 285 | self.uiAllFramesTW.addTopLevelItem (frameItem) 286 | 287 | self.setTreePretty () 288 | 289 | def fileIsPicked(self): 290 | print "File is Picked" 291 | if not self.storewin : 292 | self.sourceFile = str (self.uiXmlStoreFile.filePath()) 293 | self.readXmlFile () 294 | else : 295 | self.uiDoStoreBTN.setEnabled (True) 296 | 297 | 298 | #------------------- INIT ---------------------------------------------------- 299 | def __init__(self, parent=None): 300 | super(StoreXml, self).__init__(parent) 301 | # load the ui 302 | 303 | #import __main__ 304 | #self.parentWindow = __main__.__dict__["blurDeformWindow"] 305 | #blurdev.gui.loadUi( __file__, self ) 306 | 307 | QtCompat.loadUi(getUiFile(__file__), self) 308 | self.parentWindow = parent 309 | #------------------------ 310 | 311 | 312 | self.parentWindow.saveXmlWin = self 313 | 314 | self.setWindowFlags (QtCore.Qt.Tool|QtCore.Qt.WindowStaysOnTopHint ) 315 | self.setWindowTitle ("Store xml file") 316 | self.uiAllFramesTW.setSelectionMode( QtWidgets.QAbstractItemView.ExtendedSelection) 317 | self.uiAllFramesTW.setAlternatingRowColors(True) 318 | 319 | self.uiDoStoreBTN.clicked.connect ( self.buttonAction ) 320 | 321 | self.uiXmlStoreFile = filepickerwidget.FilePickerWidget (self) 322 | self.botLay.insertWidget (0,self.uiXmlStoreFile) 323 | self.uiXmlStoreFile.filenameChanged.connect (self.fileIsPicked) 324 | #filenameChanged 325 | 326 | -------------------------------------------------------------------------------- /argile/ui/argile.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 582 10 | 593 11 | 12 | 13 | 14 | Blur Deform 15 | 16 | 17 | 18 | 2 19 | 20 | 21 | 2 22 | 23 | 24 | 25 | 26 | Qt::Vertical 27 | 28 | 29 | 30 | 31 | 0 32 | 0 33 | 34 | 35 | 36 | 37 | 150 38 | 80 39 | 40 | 41 | 42 | 43 | 16777215 44 | 150 45 | 46 | 47 | 48 | blur sculpt deformers 49 | 50 | 51 | 52 | 9 53 | 54 | 55 | 2 56 | 57 | 58 | 2 59 | 60 | 61 | 2 62 | 63 | 64 | 65 | 66 | 67 | 24 68 | 24 69 | 70 | 71 | 72 | <html><head/><body><p>from scene selection</p></body></html> 73 | 74 | 75 | <- 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 24 84 | 24 85 | 86 | 87 | 88 | <html><head/><body><p>delete current blurSculpt</p></body></html> 89 | 90 | 91 | X 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 24 100 | 24 101 | 102 | 103 | 104 | <html><head/><body><p>add new blurSculpt to selected mesh</p></body></html> 105 | 106 | 107 | + 108 | 109 | 110 | 111 | 112 | 113 | 114 | true 115 | 116 | 117 | 118 | 1 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 24 128 | 24 129 | 130 | 131 | 132 | <html><head/><body><p>refresh scene</p></body></html> 133 | 134 | 135 | r 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 24 144 | 24 145 | 146 | 147 | 148 | <html><head/><body><p>add new blurSculpt to selected mesh</p></body></html> 149 | 150 | 151 | O 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | Qt::Horizontal 160 | 161 | 162 | 163 | 164 | 4 165 | 166 | 167 | 168 | 169 | 0 170 | 171 | 172 | 0 173 | 174 | 175 | 8 176 | 177 | 178 | 179 | 180 | poses 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 18 189 | 18 190 | 191 | 192 | 193 | <html><head/><body><p>add new pose</p></body></html> 194 | 195 | 196 | + 197 | 198 | 199 | 200 | 18 201 | 18 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 18 211 | 18 212 | 213 | 214 | 215 | <html><head/><body><p>delete pose</p></body></html> 216 | 217 | 218 | X 219 | 220 | 221 | 222 | 24 223 | 24 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | true 234 | 235 | 236 | 237 | 1 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | pose Info 246 | 247 | 248 | true 249 | 250 | 251 | 252 | 2 253 | 254 | 255 | 2 256 | 257 | 258 | 259 | 260 | 261 | 40 262 | 16777215 263 | 264 | 265 | 266 | Qt::LeftToRight 267 | 268 | 269 | matrix : 270 | 271 | 272 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 273 | 274 | 275 | 276 | 277 | 278 | 279 | false 280 | 281 | 282 | local 283 | 284 | 285 | true 286 | 287 | 288 | 289 | 290 | 291 | 292 | false 293 | 294 | 295 | tangent 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 60 304 | 20 305 | 306 | 307 | 308 | select Mat 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 18 317 | 18 318 | 319 | 320 | 321 | <html><head/><body><p>disconnect matrix connection</p></body></html> 322 | 323 | 324 | X 325 | 326 | 327 | 328 | 24 329 | 24 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | true 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 4 350 | 351 | 352 | 353 | 354 | 0 355 | 356 | 357 | 0 358 | 359 | 360 | 8 361 | 362 | 363 | 364 | 365 | frames 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 18 374 | 18 375 | 376 | 377 | 378 | <html><head/><body><p>enter edit mode</p></body></html> 379 | 380 | 381 | + 382 | 383 | 384 | 385 | 16 386 | 16 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 18 396 | 18 397 | 398 | 399 | 400 | <html><head/><body><p>exit edit mode</p></body></html> 401 | 402 | 403 | + 404 | 405 | 406 | 407 | 16 408 | 16 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 18 418 | 18 419 | 420 | 421 | 422 | <html><head/><body><p>add empty frame</p></body></html> 423 | 424 | 425 | [] 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 18 434 | 18 435 | 436 | 437 | 438 | <html><head/><body><p>add new frame form selected mesh</p></body></html> 439 | 440 | 441 | + 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 18 450 | 18 451 | 452 | 453 | 454 | <html><head/><body><p>remove selected frame</p></body></html> 455 | 456 | 457 | X 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | true 467 | 468 | 469 | true 470 | 471 | 472 | true 473 | 474 | 475 | 476 | 1 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | -------------------------------------------------------------------------------- /argile/ui/argileAddPose.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 286 10 | 107 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 2 19 | 20 | 21 | 2 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | Qt::Horizontal 30 | 31 | 32 | 33 | 34 | 35 | 36 | pose Name : 37 | 38 | 39 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 50 48 | 20 49 | 50 | 51 | 52 | <html><head/><body><p>select the matrix</p></body></html> 53 | 54 | 55 | << 56 | 57 | 58 | 59 | 60 | 61 | 62 | local 63 | 64 | 65 | true 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | Arial Black 74 | 50 75 | false 76 | 77 | 78 | 79 | WARNING you need UVS for tangent Space 80 | 81 | 82 | Qt::AlignCenter 83 | 84 | 85 | 86 | 87 | 88 | 89 | deformation : 90 | 91 | 92 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 50 101 | 0 102 | 103 | 104 | 105 | 106 | 30 107 | 16777215 108 | 109 | 110 | 111 | <html><head/><body><p>select the matrix</p></body></html> 112 | 113 | 114 | ADD 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | use transform : 125 | 126 | 127 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 128 | 129 | 130 | 131 | 132 | 133 | 134 | tangent 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | Arial Black 143 | 50 144 | false 145 | 146 | 147 | 148 | poseName already exist, pick new one 149 | 150 | 151 | Qt::AlignCenter 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | -------------------------------------------------------------------------------- /argile/ui/storeXml.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 674 10 | 488 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 19 | 20 | true 21 | 22 | 23 | true 24 | 25 | 26 | 27 | 1 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | Store Xml 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /argile/utils.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Copyright 2016, Blur Studio 3 | 4 | This file is part of Simplex. 5 | 6 | Simplex is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU Lesser General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | Simplex is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU Lesser General Public License for more details. 15 | 16 | You should have received a copy of the GNU Lesser General Public License 17 | along with Simplex. If not, see . 18 | ''' 19 | 20 | """Utility functions.""" 21 | import os 22 | 23 | def toPyObject(thing): 24 | try: 25 | return thing.toPyObject() 26 | except: 27 | return thing 28 | 29 | 30 | def getUiFile(fileVar, subFolder="ui", uiName=None): 31 | """Get the path to the .ui file""" 32 | uiFolder, filename = os.path.split(fileVar) 33 | if uiName is None: 34 | uiName = os.path.splitext(filename)[0] 35 | if subFolder: 36 | uiFile = os.path.join(uiFolder, subFolder, uiName+".ui") 37 | return uiFile 38 | -------------------------------------------------------------------------------- /argileCppCode/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | project(blurPostDeform) 3 | 4 | SET(MAYA_VERSION 2016 CACHE STRING "Maya version number") 5 | SET(SOURCE_FILES 6 | "blurPostDeformPlugin.cpp" 7 | "blurPostDeformNode.cpp" 8 | "blurPostDeformNode.h" 9 | "blurPostDeformCmd.cpp" 10 | "blurPostDeformCmd.h" 11 | "common.cpp" 12 | "common.h" 13 | ) 14 | 15 | FIND_PACKAGE(OpenGL REQUIRED) 16 | 17 | # OS Specific environment setup 18 | SET(CUSTOM_DEFINITIONS "REQUIRE_IOSTREAM;_BOOL") 19 | SET(MAYA_INSTALL_BASE_SUFFIX "") 20 | SET(MAYA_INC_SUFFIX "include") 21 | SET(MAYA_LIB_SUFFIX "lib") 22 | IF(WIN32) 23 | # Windows 24 | SET(MAYA_INSTALL_BASE_DEFAULT "C:/Program Files/Autodesk") 25 | SET(CUSTOM_DEFINITIONS "${CUSTOM_DEFINITIONS};NT_PLUGIN") 26 | ELSEIF(APPLE) 27 | # Apple 28 | SET(MAYA_INSTALL_BASE_DEFAULT "/Applications/Autodesk") 29 | SET(MAYA_INC_SUFFIX "devkit/include") 30 | SET(MAYA_LIB_SUFFIX "Maya.app/Contents/MacOS") 31 | SET(CUSTOM_DEFINITIONS "${CUSTOM_DEFINITIONS};OSMac_") 32 | ELSE(WIN32) 33 | # Linux 34 | SET(MAYA_INSTALL_BASE_DEFAULT "/usr/autodesk") 35 | SET(MAYA_INSTALL_BASE_SUFFIX "-x64") 36 | ENDIF(WIN32) 37 | 38 | SET(MAYA_INSTALL_BASE_PATH ${MAYA_INSTALL_BASE_DEFAULT} CACHE STRING 39 | "Path containing all your maya installations, like /usr/autodesk or /Applications/Autodesk/") 40 | 41 | SET(LIBRARIES ${LIBRARIES} "Foundation" "OpenMaya" "OpenMayaAnim" "OpenMayaUI" "OpenMayaRender" "clew" ${OPENGL_LIBRARIES}) 42 | 43 | SET(_MAYA_LOCATION ${MAYA_INSTALL_BASE_PATH}/maya${MAYA_VERSION}${MAYA_INSTALL_BASE_SUFFIX}) 44 | SET(_PROJECT ${PROJECT_NAME}) 45 | 46 | INCLUDE_DIRECTORIES(${_MAYA_LOCATION}/${MAYA_INC_SUFFIX} ${OPENGL_INCLUDE_DIRS} ${GLUT_INCLUDE_DIRS}) 47 | 48 | LINK_DIRECTORIES(${_MAYA_LOCATION}/${MAYA_LIB_SUFFIX}) 49 | 50 | ADD_LIBRARY(${_PROJECT} SHARED ${SOURCE_FILES}) 51 | TARGET_LINK_LIBRARIES(${_PROJECT} ${LIBRARIES}) 52 | 53 | SET_TARGET_PROPERTIES(${_PROJECT} PROPERTIES COMPILE_DEFINITIONS "${CUSTOM_DEFINITIONS}") 54 | SET_TARGET_PROPERTIES(${_PROJECT} PROPERTIES OUTPUT_NAME ${PROJECT_NAME}) 55 | SET_TARGET_PROPERTIES(${_PROJECT} PROPERTIES CLEAN_DIRECT_OUTPUT 1) 56 | 57 | IF (WIN32) 58 | SET_TARGET_PROPERTIES(${_PROJECT} PROPERTIES SUFFIX ".mll" ) 59 | SET_TARGET_PROPERTIES(${_PROJECT} PROPERTIES LINK_FLAGS "/export:initializePlugin /export:uninitializePlugin" ) 60 | ELSEIF(APPLE) 61 | SET_TARGET_PROPERTIES(${_PROJECT} PROPERTIES PREFIX "" ) 62 | SET_TARGET_PROPERTIES(${_PROJECT} PROPERTIES SUFFIX ".bundle" ) 63 | ELSE(WIN32) 64 | SET_TARGET_PROPERTIES(${_PROJECT} PROPERTIES PREFIX "" ) 65 | ENDIF(WIN32) 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /argileCppCode/blurPostDeformCmd.cpp: -------------------------------------------------------------------------------- 1 | #include "blurPostDeformCmd.h" 2 | #include "blurPostDeformNode.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #define PROGRESS_STEP 100 22 | #define TASK_COUNT 32 23 | 24 | /** 25 | A version number used to support future updates to the binary wrap binding file. 26 | */ 27 | const float kWrapFileVersion = 1.0f; 28 | 29 | const char* blurSculptCmd::kName = "blurSculpt"; 30 | const char* blurSculptCmd::kQueryFlagShort = "-q"; 31 | const char* blurSculptCmd::kQueryFlagLong = "-query"; 32 | const char* blurSculptCmd::kNameFlagShort = "-n"; 33 | const char* blurSculptCmd::kNameFlagLong = "-name"; 34 | const char* blurSculptCmd::kAddPoseNameFlagShort = "-ap"; 35 | const char* blurSculptCmd::kAddPoseNameFlagLong = "-addPose"; 36 | const char* blurSculptCmd::kPoseNameFlagShort = "-pn"; 37 | const char* blurSculptCmd::kPoseNameFlagLong = "-poseName"; 38 | const char* blurSculptCmd::kPoseTransformFlagShort = "-pt"; 39 | const char* blurSculptCmd::kPoseTransformFlagLong = "-poseTransform"; 40 | const char* blurSculptCmd::kListPosesFlagShort = "-lp"; 41 | const char* blurSculptCmd::kListPosesFlagLong = "-listPoses"; 42 | const char* blurSculptCmd::kListFramesFlagShort = "-lf"; 43 | const char* blurSculptCmd::kListFramesFlagLong = "-listFrames"; 44 | const char* blurSculptCmd::kAddFlagShort = "-add"; 45 | const char* blurSculptCmd::kAddFlagLong = "-addAtTime"; 46 | const char* blurSculptCmd::kOffsetFlagShort = "-of"; 47 | const char* blurSculptCmd::kOffsetFlagLong = "-offset"; 48 | const char* blurSculptCmd::kRemoveTimeFlagShort = "-rmv"; 49 | const char* blurSculptCmd::kRemoveTimeFlagLong = "-removeAtTime"; 50 | const char* blurSculptCmd::kHelpFlagShort = "-h"; 51 | const char* blurSculptCmd::kHelpFlagLong = "-help"; 52 | 53 | /** 54 | Displays command instructions. 55 | */ 56 | void DisplayHelp() { 57 | MString help; 58 | help += "Flags:\n"; 59 | help += "-name (-n): String Name of the blurSclupt node to create.\n"; 60 | help += "-query (-q): N/A Query mode.\n"; 61 | help += "-listPoses (-lp): N/A In query mode return the list of poses stored\n"; 62 | help += "-listFrames (-lf): N/A combine with poseName and query mode\n"; 63 | help += " return the list of frame used\n"; 64 | help += "-addPose (-ap): N/A Add a pose, use with poseName \n"; 65 | help += "-poseName (-pn): String the name of the pose we want to add or edit\n"; 66 | help += "-poseTransform (-pt): String the transform node for the pose to add\n"; 67 | help += "-addAtTime (-nbm) String the mesh target to add at the currentTime\n"; 68 | help += " needs pose name\n"; 69 | help += "-offset (-of) Float the offset distance to see if a vertex is moved\n"; 70 | help += " default 0.001 | used in addAtTime\n"; 71 | help += "-removeAtTime (-rmv): N/A Remove this pose at this time\n"; 72 | help += "-help (-h) N/A Display this text.\n"; 73 | MGlobal::displayInfo(help); 74 | } 75 | 76 | blurSculptCmd::blurSculptCmd() 77 | : 78 | name_("blurSculpt#"), 79 | command_(kCommandCreate), 80 | getListPoses_(false), 81 | getListFrames_(false), 82 | connectTransform_(false), 83 | aOffset_ (0.001), 84 | aPoseGain_ (1), 85 | aPoseOffset_(0) 86 | { 87 | } 88 | 89 | MSyntax blurSculptCmd::newSyntax() { 90 | MSyntax syntax; 91 | syntax.addFlag(kQueryFlagShort, kQueryFlagLong); 92 | syntax.addFlag(kListPosesFlagShort, kListPosesFlagLong); 93 | syntax.addFlag(kListFramesFlagShort, kListFramesFlagLong); 94 | syntax.addFlag(kNameFlagShort, kNameFlagLong, MSyntax::kString); 95 | syntax.addFlag(kAddPoseNameFlagShort, kAddPoseNameFlagLong); 96 | syntax.addFlag(kPoseNameFlagShort, kPoseNameFlagLong, MSyntax::kString); 97 | syntax.addFlag(kPoseTransformFlagShort, kPoseTransformFlagLong, MSyntax::kString); 98 | syntax.addFlag(kAddFlagShort, kAddFlagLong, MSyntax::kString); 99 | syntax.addFlag(kOffsetFlagShort, kOffsetFlagLong, MSyntax::kDouble); 100 | syntax.addFlag(kRemoveTimeFlagShort, kRemoveTimeFlagLong); 101 | syntax.addFlag(kHelpFlagShort, kHelpFlagLong); 102 | syntax.setObjectType(MSyntax::kSelectionList, 0, 255); 103 | syntax.useSelectionAsDefault(true); 104 | return syntax; 105 | } 106 | 107 | void* blurSculptCmd::creator() { 108 | return new blurSculptCmd; 109 | } 110 | 111 | bool blurSculptCmd::isUndoable() const { 112 | return command_ == kCommandCreate; // Only creation will be undoable 113 | } 114 | 115 | MStatus blurSculptCmd::doIt(const MArgList& args) { 116 | MStatus status; 117 | 118 | status = GatherCommandArguments(args); 119 | CHECK_MSTATUS_AND_RETURN_IT(status); 120 | 121 | status = GetGeometryPaths(); 122 | CHECK_MSTATUS_AND_RETURN_IT(status); 123 | if (command_ == kCommandHelp) { return MS::kSuccess; } 124 | if (command_ == kCommandAddPoseAtTime) { 125 | //MGlobal::displayInfo(MString("command is : [kCommandAddPoseAtTime]")); 126 | status = GetLatestBlurSculptNode(); 127 | } 128 | if (command_ == kCommandAddPose) { 129 | //MGlobal::displayInfo(MString("command is : [kCommandAddPose]")); 130 | addAPose(); 131 | return MS::kSuccess; 132 | } 133 | MFnDagNode fnMeshDriven(meshDeformed_); 134 | 135 | if (command_ == kCommandCreate) { 136 | //MGlobal::displayInfo(MString("command is : [kCommandCreate]")); 137 | 138 | // Add the blurSculpt creation command to the modifier. 139 | MString command = "deformer -type blurSculpt -n \"" + name_ + "\""; 140 | command += " " + fnMeshDriven.partialPathName(); 141 | //MGlobal::displayInfo(MString("command is : [") + command + MString("]")); 142 | 143 | status = dgMod_.commandToExecute(command); 144 | status = dgMod_.doIt(); 145 | status = GetLatestBlurSculptNode(); 146 | //setFaceVertexRelationShip(); 147 | //computeBarycenters(); 148 | 149 | MFnDependencyNode fnBlurSculptNode(oBlurSculptNode_); 150 | setResult (fnBlurSculptNode.name()); 151 | } 152 | CHECK_MSTATUS_AND_RETURN_IT(status); 153 | status = getListPoses(); 154 | CHECK_MSTATUS_AND_RETURN_IT(status); 155 | if (command_ == kCommandAddPoseAtTime) { 156 | status = GetLatestBlurSculptNode(); 157 | MFnDependencyNode fnBlurSculptNode(oBlurSculptNode_); 158 | 159 | //MGlobal::displayInfo(MString("Adding : [") + targetMeshAdd_ + MString("] to mesh [")+ fnMeshDriven.partialPathName() + MString("]")); 160 | //MGlobal::displayInfo(MString(" fnBlurSculptNode : [") + fnBlurSculptNode.name() + MString("]")); 161 | addAFrame(); 162 | } 163 | else if (command_ == kCommandQuery) { 164 | //MGlobal::displayInfo(MString("Query : getListPoses_ [") + getListPoses_ + MString("] ")); 165 | //MGlobal::displayInfo(MString(" getListFrames_ [") + getListFrames_ + MString("] ") ); 166 | //MGlobal::displayInfo(MString(" poseName_ [") + poseName_ + MString("] ")); 167 | int nb = allPosesNames_.length(); 168 | if (getListPoses_) 169 | { 170 | MString toDisplay("the poses names : "); 171 | MString tst("test"); 172 | 173 | for (int i = 0; i < nb; i++) { 174 | toDisplay += MString("[") + allPosesNames_[i] + MString("]"); 175 | //appendToResult(static_cast(1)); 176 | appendToResult(allPosesNames_[i].asChar () ); 177 | } 178 | //MGlobal::displayInfo(toDisplay); 179 | } 180 | if (getListFrames_) { 181 | int poseIndex = getMStringIndex(allPosesNames_, poseName_); //allPosesNames_.indexOf(poseName_); 182 | if (poseIndex == -1) { 183 | MGlobal::displayError(poseName_+" is not a pose"); 184 | return MS::kFailure; 185 | } 186 | else { 187 | getListFrames(poseIndex); 188 | MString toDisplay("the frame for pose "+ poseName_ +" are : \n"); 189 | for (unsigned int i = 0; i < allFramesFloats_.length(); i++) { 190 | toDisplay += MString(" [") + allFramesFloats_[i] + MString("]"); 191 | appendToResult(static_cast(allFramesFloats_[i])); 192 | } 193 | //MGlobal::displayInfo(toDisplay); 194 | } 195 | } 196 | //cout << "Normal: " << endl; 197 | } 198 | return MS::kSuccess; 199 | } 200 | 201 | MStatus blurSculptCmd::GatherCommandArguments(const MArgList& args) { 202 | MStatus status; 203 | MArgDatabase argData(syntax(), args); 204 | argData.getObjects(selectionList_); 205 | if (argData.isFlagSet(kHelpFlagShort)) { 206 | command_ = kCommandHelp; 207 | DisplayHelp(); 208 | return MS::kSuccess; 209 | } 210 | if (argData.isFlagSet(kNameFlagShort)) { 211 | name_ = argData.flagArgumentString(kNameFlagShort, 0, &status); 212 | CHECK_MSTATUS_AND_RETURN_IT(status); 213 | } 214 | if (argData.isFlagSet(kPoseNameFlagShort)) { 215 | poseName_ = argData.flagArgumentString(kPoseNameFlagShort, 0, &status); 216 | CHECK_MSTATUS_AND_RETURN_IT(status); 217 | } 218 | if (argData.isFlagSet(kQueryFlagShort)) { 219 | command_ = kCommandQuery; 220 | } 221 | if (argData.isFlagSet(kListPosesFlagShort)) { 222 | getListPoses_ = true; 223 | } 224 | if (argData.isFlagSet(kListFramesFlagShort)) { 225 | getListFrames_ = true; 226 | } 227 | if (command_ == kCommandQuery) return MS::kSuccess; 228 | 229 | if (argData.isFlagSet(kPoseTransformFlagShort)) { 230 | poseTransformStr_ = argData.flagArgumentString(kPoseTransformFlagShort, 0, &status); 231 | MSelectionList selListA; 232 | MGlobal::getSelectionListByName(poseTransformStr_, selListA); 233 | selListA.getDependNode(0, poseTransform_); 234 | //selList.getDagPath(0, poseTransform_); 235 | selListA.clear(); 236 | connectTransform_ = true; 237 | } 238 | 239 | if (argData.isFlagSet(kOffsetFlagShort)) { 240 | MString OffsetStr = argData.flagArgumentString(kOffsetFlagShort, 0, &status); 241 | aOffset_ = OffsetStr.asFloat(); 242 | } 243 | 244 | if (argData.isFlagSet(kAddPoseNameFlagShort)) { 245 | command_ = kCommandAddPose; 246 | return MS::kSuccess; 247 | } 248 | if (argData.isFlagSet(kAddFlagShort)) { 249 | targetMeshAdd_ = argData.flagArgumentString(kAddFlagShort, 0, &status); 250 | MSelectionList selList; 251 | MGlobal::getSelectionListByName(targetMeshAdd_, selList); 252 | selList.getDagPath(0, meshTarget_); 253 | selList.clear(); 254 | status = GetShapeNode(meshTarget_); 255 | 256 | command_ = kCommandAddPoseAtTime; 257 | CHECK_MSTATUS_AND_RETURN_IT(status); 258 | } 259 | else { 260 | command_ = kCommandCreate; 261 | } 262 | return MS::kSuccess; 263 | } 264 | 265 | MStatus blurSculptCmd::GetGeometryPaths() { 266 | MStatus status; 267 | if (selectionList_.length() == 0 ) { 268 | MGlobal::displayError("select at least a mesh"); 269 | return MS::kFailure; 270 | } 271 | if (command_ == kCommandQuery || command_ == kCommandHelp || command_ == kCommandAddPose) 272 | { 273 | MObject inputNode ; 274 | status = selectionList_.getDependNode(0, inputNode); 275 | CHECK_MSTATUS_AND_RETURN_IT(status); 276 | //MGlobal::displayInfo("query get node"); 277 | MFnDependencyNode inputNodeDep(inputNode, &status); 278 | if (inputNodeDep.typeId() == blurSculpt::id) { 279 | oBlurSculptNode_ = inputNode; 280 | //MGlobal::displayInfo("query we have the blurSculpt"); 281 | return MS::kSuccess; 282 | } 283 | } 284 | else { 285 | // The driver is selected last 286 | status = selectionList_.getDagPath(0, meshDeformed_); 287 | CHECK_MSTATUS_AND_RETURN_IT(status); 288 | status = GetShapeNode(meshDeformed_); 289 | // The driver must be a mesh for this specific algorithm. 290 | if (command_ == kCommandCreate && !meshDeformed_.hasFn(MFn::kMesh)) { 291 | MGlobal::displayError("blurSculpt works only on mesh."); 292 | return MS::kFailure; 293 | } 294 | } 295 | /* 296 | meshDeformed_ 297 | 298 | if (command_ != kCommandCreate && meshDeformed_.(blurSculpt::id)) { 299 | 300 | } 301 | */ 302 | return MS::kSuccess; 303 | } 304 | /* 305 | MStatus blurSculptCmd::setFaceVertexRelationShip() { 306 | MStatus status; 307 | MFnMesh fnDeformedMesh(meshDeformed_, &status); 308 | int nbDeformedVtx = fnDeformedMesh.numVertices(); 309 | MItMeshVertex vertexIter(meshDeformed_); 310 | 311 | MFnDependencyNode blurSculptDepNode(oBlurSculptNode_); 312 | MPlug vertexFaceIndicesPlug = blurSculptDepNode.findPlug(blurSculpt::vertexFaceIndices); 313 | MPlug vertexVertexIndicesPlug = blurSculptDepNode.findPlug(blurSculpt::vertexVertexIndices); 314 | 315 | MIntArray faces, edges, vertexList; 316 | MPlug theVertexFace, theVertextVertex; 317 | 318 | int vertexInd = 0; 319 | for (; !vertexIter.isDone(); vertexIter.next()) { 320 | vertexIter.getConnectedFaces(faces); 321 | int faceIndex = faces[0]; 322 | vertexIter.getConnectedEdges(edges); 323 | int2 vertexList; 324 | int nextVertex; 325 | fnDeformedMesh.getEdgeVertices(edges[0], vertexList); 326 | if (vertexList[0] == vertexInd) 327 | nextVertex = vertexList[1]; 328 | else 329 | nextVertex = vertexList[0]; 330 | 331 | theVertexFace = vertexFaceIndicesPlug.elementByLogicalIndex(vertexInd, &status); 332 | theVertexFace.setValue(faceIndex); 333 | 334 | theVertextVertex = vertexVertexIndicesPlug.elementByLogicalIndex(vertexInd, &status); 335 | theVertextVertex.setValue(nextVertex); 336 | 337 | vertexInd++; 338 | } 339 | return MS::kSuccess; 340 | 341 | } 342 | */ 343 | MStatus blurSculptCmd::GetLatestBlurSculptNode() { 344 | MStatus status; 345 | MObject oDriven = meshDeformed_.node(); 346 | 347 | // Since we use MDGModifier to execute the deformer command, we can't get 348 | // the created deformer node, so we need to find it in the deformation chain. 349 | MItDependencyGraph itDG(oDriven, 350 | MFn::kGeometryFilt, 351 | MItDependencyGraph::kUpstream, 352 | MItDependencyGraph::kDepthFirst, 353 | MItDependencyGraph::kNodeLevel, 354 | &status); 355 | CHECK_MSTATUS_AND_RETURN_IT(status); 356 | MObject oDeformerNode; 357 | for (; !itDG.isDone(); itDG.next()) { 358 | oDeformerNode = itDG.currentItem(); 359 | MFnDependencyNode fnNode(oDeformerNode, &status); 360 | CHECK_MSTATUS_AND_RETURN_IT(status); 361 | if (fnNode.typeId() == blurSculpt::id) { 362 | oBlurSculptNode_ = oDeformerNode; 363 | return MS::kSuccess; 364 | } 365 | } 366 | return MS::kFailure; 367 | } 368 | 369 | MStatus blurSculptCmd::GetPreDeformedMesh(MObject& blurSculptNode, MDagPath& pathMesh) { 370 | MStatus status; 371 | /* 372 | // Get the bind mesh connected to the message attribute of the wrap deformer 373 | MPlug plugBindMesh(oWrapNode, blurSculpt::aBindDriverGeo); 374 | MPlugArray plugs; 375 | plugBindMesh.connectedTo(plugs, true, false, &status); 376 | CHECK_MSTATUS_AND_RETURN_IT(status); 377 | if (plugs.length() == 0) { 378 | MGlobal::displayError("Unable to rebind. No bind mesh is connected."); 379 | return MS::kFailure; 380 | } 381 | MObject oBindMesh = plugs[0].node(); 382 | status = MDagPath::getAPathTo(oBindMesh, pathBindMesh); 383 | CHECK_MSTATUS_AND_RETURN_IT(status); 384 | */ 385 | return MS::kSuccess; 386 | } 387 | 388 | MStatus blurSculptCmd::getListPoses() { 389 | MStatus status; 390 | allPosesNames_.clear(); 391 | allPosesIndices_.clear(); 392 | 393 | MFnDependencyNode blurSculptDepNode(oBlurSculptNode_); 394 | // get list of poses 395 | MPlug posesPlug = blurSculptDepNode.findPlug(blurSculpt::poses, &status); 396 | unsigned int nbPoses = posesPlug.numElements(&status); 397 | 398 | //MIntArray iarrIndexes; // array to hold each valid index number. 399 | unsigned nEle = posesPlug.getExistingArrayAttributeIndices(allPosesIndices_, &status); 400 | 401 | //for (unsigned int element = 0; element < iarrIndexes.length(); element++) 402 | for (unsigned int element = 0; element < nbPoses; element++) 403 | { 404 | // do not use elementByLogicalIndex 405 | MPlug thePosePlug = posesPlug.elementByPhysicalIndex(element, &status); 406 | MPlug thePoseNamePlug = thePosePlug.child(blurSculpt::poseName); 407 | allPosesNames_.append(thePoseNamePlug.asString()); 408 | //unsigned int logicalIndex = newTargetPlug.logicalIndex(&stat); 409 | } 410 | return MS::kSuccess; 411 | } 412 | 413 | MStatus blurSculptCmd::getListFrames( int poseIndex) { 414 | //MGlobal::displayInfo(MString("\n query list Poses: \n") ); 415 | 416 | MStatus status; 417 | allFramesFloats_.clear(); 418 | 419 | MFnDependencyNode blurSculptDepNode(oBlurSculptNode_); 420 | MPlug posesPlug = blurSculptDepNode.findPlug(blurSculpt::poses, &status); 421 | MPlug thePosePlug = posesPlug.elementByLogicalIndex(poseIndex, &status); 422 | 423 | MPlug deformationsPlug = thePosePlug.child(blurSculpt::deformations); 424 | unsigned int nbDeformations = deformationsPlug.numElements(&status); 425 | // get the frame indices 426 | unsigned nEle = deformationsPlug.getExistingArrayAttributeIndices(allFramesIndices_, &status); 427 | for (unsigned int deformIndex = 0; deformIndex< nbDeformations; deformIndex++) 428 | { 429 | MPlug theDeformPlug = deformationsPlug.elementByPhysicalIndex(deformIndex, &status); 430 | MPlug theFramePlug = theDeformPlug.child(blurSculpt::frame); 431 | allFramesFloats_.append(theFramePlug.asFloat()); 432 | } 433 | //MGlobal::displayInfo(MString("\n END query list Poses: \n")); 434 | return MS::kSuccess; 435 | } 436 | 437 | /* 438 | MStatus blurSculptCmd::computeBarycenters() { 439 | 440 | // http://tech-artists.org/forum/showthread.php?4907-Maya-API-Vertex-and-Face-matrix-tangent-space 441 | 442 | //This is not a very trivial thing. If you construct the tangents by yourself rather than having maya do it you'll be getting the most stable results. A clean UV map is a requirement however... 443 | //Maya's mesh lacks a large amount of self awareness, most data is per face-vertex but there is no proper transitioning between vertex and face-vertices and not good way to get averaged data per vertex. 444 | //Best is to use a MItMeshPolygon to iterate the polygons. 445 | //MObject sInMeshAttr is the typed kMesh attribute, MDataBlock data is the io data as passed to compute 446 | 447 | MStatus status; 448 | MFnMesh fnDeformedMesh(meshDeformed_, &status); 449 | MItMeshPolygon inMeshIter(meshDeformed_); 450 | 451 | MFnDependencyNode blurSculptDepNode(oBlurSculptNode_); 452 | 453 | int initialSize = fnDeformedMesh.numVertices(); 454 | MIntArray parsed (initialSize,-1); 455 | //Then we need to iterate the individual triangles to get accurate tangent data 456 | 457 | MPlug vertexFaceIndicesPlug = blurSculptDepNode.findPlug(blurSculpt::triangleFaceValues); 458 | MPlug vertexTriangleIndicesPlug = blurSculptDepNode.findPlug(blurSculpt::vertexTriangleIndices); 459 | 460 | int triangleInd = 0; 461 | MPlug trianglePlug, vertexTrianglePlug , vertex1Plug, vertex2Plug, vertex3Plug, uValuePlug, vValuePlug; 462 | MGlobal::displayInfo(MString("\n BaryCenters\n")); 463 | 464 | for (int triangleInd = 0; !inMeshIter.isDone(); inMeshIter.next(), ++triangleInd) 465 | { 466 | // get the trianglePlug 467 | trianglePlug = vertexFaceIndicesPlug.elementByLogicalIndex(triangleInd, &status); 468 | vertex1Plug = trianglePlug.child(blurSculpt::vertex1); 469 | vertex2Plug = trianglePlug.child(blurSculpt::vertex2); 470 | vertex3Plug = trianglePlug.child(blurSculpt::vertex3); 471 | uValuePlug = trianglePlug.child(blurSculpt::uValue); 472 | vValuePlug = trianglePlug.child(blurSculpt::vValue); 473 | 474 | MPointArray points; 475 | MIntArray vertices; 476 | inMeshIter.getTriangles(points, vertices); 477 | MFloatArray u; 478 | MFloatArray v; 479 | inMeshIter.getUVs(u, v); 480 | //Now that we know the points, uvs ad vertex indices per triangle vertex we can start getting the tangent per triangle 481 | //and use that tangent for all vertices in the triangle. If a vertex is split and has multiple we can only use one of the 482 | //uv-space-triangles to get a 3D tangent per vertex. 483 | // 484 | //This bit iterates each triangle of the poylgon's triangulation (3 points) 485 | //and extracts the barycentric coordinates for the tangent. 486 | 487 | for (unsigned int i = 0; i < vertices.length(); i += 3) 488 | { 489 | // Taking UV coordinates [i-(i+2)] as our triangle and 490 | // the unitX (1,0) as point to get barycentric coordinates for 491 | // we can get the U direction in barycentric using the function 492 | // from this site: 493 | // http://www.blackpawn.com/texts/pointinpoly/ 494 | double u02 = (u[i + 2] - u[i]); 495 | double v02 = (v[i + 2] - v[i]); 496 | double u01 = (u[i + 1] - u[i]); 497 | double v01 = (v[i + 1] - v[i]); 498 | double dot00 = u02 * u02 + v02 * v02; 499 | double dot01 = u02 * u01 + v02 * v01; 500 | double dot11 = u01 * u01 + v01 * v01; 501 | double d = dot00 * dot11 - dot01 * dot01; 502 | double u = 1.0; 503 | double v = 1.0; 504 | if (d != 0.0) 505 | { 506 | u = (dot11 * u02 - dot01 * u01) / d; 507 | v = (dot00 * u01 - dot01 * u02) / d; 508 | } 509 | 510 | uValuePlug.setDouble(u); 511 | vValuePlug.setDouble(v); 512 | vertex1Plug.setInt(vertices[0]); 513 | vertex2Plug.setInt(vertices[1]); 514 | vertex3Plug.setInt(vertices[2]); 515 | 516 | //Now to get the 3D tangent all we need to do is apply the barycentric coordinates to the 3D points : 517 | MVector tangent = points[i + 2] * u + points[i + 1] * v - points[i] * (u + v); 518 | MVector binormal, normal; 519 | //Next we iterate over the three vertices individually. 520 | //Here we use MFnMesh::getVertexNormal for the average normal (whether you want angle-weighted depends on what you're doing, I often don't use them). 521 | //Having the average normal and triangle tangent we can use the cross product for the binormal, cross the normal & binormal again to get a proper 522 | //perpendicular tangent, because the normal is average and the tangent is not the initial tangent was wrong. 523 | 524 | for (unsigned int j = i; j < i + 3; ++j) { 525 | int theVtx = vertices[j]; 526 | if (parsed[theVtx] == -1) { 527 | // store the triangle index 528 | vertexTrianglePlug = vertexTriangleIndicesPlug.elementByLogicalIndex(theVtx, &status); 529 | vertexTrianglePlug.setValue(triangleInd); 530 | //fnDeformedMesh.getVertexNormal(vertices[j], false, normal); 531 | //binormal = tangent ^ normal; 532 | //binormal.normalize(); 533 | //tangent = binormal ^ normal; 534 | //tangent.normalize(); 535 | // store the vertex 536 | parsed.set (1, theVtx); 537 | } 538 | //the matrix produced 539 | //{ {tangent[0], tangent[1], tangent[2], 0}, 540 | //{ binormal[0], binormal[1], binormal[2], 0 }, 541 | //{ normal[0], normal[1], normal[2], 0 }, 542 | //{ point[0], point[1], point[2], 0 }} 543 | 544 | } 545 | } 546 | 547 | } 548 | return MS::kSuccess; 549 | } 550 | */ 551 | MStatus blurSculptCmd::addAPose() { 552 | MStatus status; 553 | //MGlobal::displayInfo(MString("\n Function add A Pose : \n") + poseName_); 554 | 555 | MFnDependencyNode blurSculptDepNode(oBlurSculptNode_); 556 | // get list of poses 557 | MPlug posesPlug = blurSculptDepNode.findPlug(blurSculpt::poses, &status); 558 | // get the index of the poseName in the array 559 | int tmpInd = getMStringIndex(allPosesNames_, poseName_); //allPosesNames_.indexOf(poseName_); 560 | int poseIndex; 561 | 562 | //MGlobal::displayInfo(MString("indexOfPoseName : ") + poseIndex); 563 | bool doAddName = false; 564 | if (tmpInd == -1) { // if doesn't exists use new one 565 | poseIndex = GetFreeIndex(posesPlug); 566 | doAddName = true; 567 | }else { 568 | poseIndex = allPosesIndices_[tmpInd]; 569 | } 570 | if (doAddName) { 571 | // access the channel 572 | MPlug thePosePlug = posesPlug.elementByLogicalIndex(poseIndex, &status); 573 | // add the channel Name 574 | MDGModifier dgMod; 575 | MPlug thePoseMatrixPlug = thePosePlug.child(blurSculpt::poseMatrix); 576 | 577 | MPlug thePoseNamePlug = thePosePlug.child(blurSculpt::poseName); 578 | thePoseNamePlug.setValue(poseName_); 579 | MPlug thePoseGainPlug = thePosePlug.child(blurSculpt::poseGain); 580 | thePoseGainPlug.setValue(1.0); 581 | 582 | MPlug thePoseOffsetPlug = thePosePlug.child(blurSculpt::poseOffset); 583 | thePoseOffsetPlug.setValue(0.0); 584 | 585 | if (connectTransform_) { 586 | // add the transform 587 | //MDagModifier cmd; 588 | //MGlobal::displayInfo(MString("connection of ") + poseTransformStr_); 589 | MFnDependencyNode poseTransformDep_(poseTransform_); 590 | MPlug worldMatPlug = poseTransformDep_.findPlug("matrix"); 591 | 592 | dgMod.connect(worldMatPlug, thePoseMatrixPlug); 593 | dgMod.doIt(); 594 | } 595 | } 596 | return MS::kSuccess; 597 | 598 | } 599 | 600 | MStatus blurSculptCmd::addAFrame() { 601 | MStatus status; 602 | //MGlobal::displayInfo(MString("\nadd A Frame\n")); 603 | 604 | // get the meshes access 605 | MFnMesh fnDeformedMesh(meshDeformed_, &status); 606 | MFnMesh fnTargetMesh(meshTarget_, &status); 607 | // get access to our node 608 | MFnDependencyNode blurSculptDepNode(oBlurSculptNode_); 609 | // get list of poses 610 | MPlug posesPlug = blurSculptDepNode.findPlug(blurSculpt::poses, &status); 611 | 612 | // get the nb of vertices 613 | int nbDeformedVtx = fnDeformedMesh.numVertices(); 614 | int nbTargetVtx = fnTargetMesh.numVertices(); 615 | 616 | if (nbDeformedVtx != nbTargetVtx) { 617 | MGlobal::displayError("not same number of vertices"); 618 | return MS::kFailure; 619 | } 620 | //MGlobal::displayInfo(MString("same nb vertices : ") + nbTargetVtx); 621 | 622 | // get the current time 623 | MTime currentFrame = MAnimControl::currentTime(); 624 | float currentFrameF = float(currentFrame.value()); 625 | //MGlobal::displayInfo(MString("currentFrame : ") + currentFrameF); 626 | // get the name of the pose 627 | //MGlobal::displayInfo(MString("poseName : ") + poseName_); 628 | MGlobal::displayInfo(MString("offset value : ") + aOffset_); 629 | 630 | // get the mode of deformation 631 | /* 632 | MPlug deformationTypePlug = blurSculptDepNode.findPlug(blurSculpt::deformationType, &status); 633 | int deformationType = deformationTypePlug.asInt(); 634 | */ 635 | // get the index of the poseName in the array 636 | int tmpInd = getMStringIndex(allPosesNames_, poseName_); //allPosesNames_.indexOf(poseName_); 637 | 638 | if (tmpInd == -1) { // if doesn't exists create new one 639 | addAPose(); // add the pose 640 | getListPoses();// get the list 641 | int tmpInd = getMStringIndex(allPosesNames_, poseName_); //allPosesNames_.indexOf(poseName_); 642 | } 643 | int poseIndex = allPosesIndices_[tmpInd]; 644 | 645 | // access the channel 646 | MPlug thePosePlug = posesPlug.elementByLogicalIndex(poseIndex, &status); 647 | 648 | // get the Matrix 649 | MDGModifier dgMod; 650 | MPoint matPoint(0, 0, 0); 651 | MPlug thePoseMatrixPlug = thePosePlug.child(blurSculpt::poseMatrix); 652 | 653 | MObject matrixObj; 654 | thePoseMatrixPlug.getValue(matrixObj); 655 | MFnMatrixData mData(matrixObj); 656 | MMatrix matrixValue = mData.matrix(&status); 657 | matPoint = matPoint * matrixValue; 658 | MMatrix matrixValueInverse = matrixValue.inverse(); 659 | MPlug poseEnabledPlug = thePosePlug.child(blurSculpt::poseEnabled); 660 | 661 | MPlug deformationTypePlug = thePosePlug.child(blurSculpt::deformationType); 662 | int deformationType = deformationTypePlug.asInt(); 663 | 664 | // get the deformations plug 665 | getListFrames(poseIndex); 666 | MPlug theDeformationPlug = thePosePlug.child(blurSculpt::deformations); 667 | 668 | // we get the list of frames for the pose 669 | int deformationIndex = -1; 670 | bool emptyFrameChannel = false; 671 | for (unsigned int i = 0; i < allFramesFloats_.length(); i++) { 672 | if (currentFrameF == allFramesFloats_[i]) { 673 | // work with the indices 674 | deformationIndex = allFramesIndices_[i]; 675 | emptyFrameChannel = true; 676 | break; 677 | } 678 | } 679 | 680 | if (deformationIndex == -1) 681 | deformationIndex = GetFreeIndex(theDeformationPlug); 682 | 683 | //MGlobal::displayInfo(MString("deformationIndex : [") + deformationIndex + MString("] frame : ") + currentFrameF); 684 | 685 | // get the new deformation 686 | MPlug deformPlug = theDeformationPlug.elementByLogicalIndex(deformationIndex, &status); 687 | // set the frame value 688 | MPlug theFramePlug = deformPlug.child(blurSculpt::frame); 689 | theFramePlug.setValue(currentFrameF); 690 | 691 | MPlug theVectorsPlug = deformPlug.child(blurSculpt::vectorMovements); 692 | // get the points from the meshes 693 | MPointArray deformedMeshVerticesPos; 694 | //first set the gain at 0 695 | //float prevGainValue = thePoseGainPlug.asFloat(); 696 | //thePoseGainPlug.setValue(0); 697 | poseEnabledPlug.setValue(false); 698 | fnDeformedMesh.getPoints(deformedMeshVerticesPos, MSpace::kObject); 699 | 700 | //then reset the gain to its value 701 | //thePoseGainPlug.setValue(prevGainValue); 702 | 703 | MPointArray targetMeshVerticesPos; 704 | fnTargetMesh.getPoints(targetMeshVerticesPos, MSpace::kObject); 705 | //MItMeshPolygon faceIter(meshDeformed_); 706 | MPoint offsetPoint; 707 | MMatrix mMatrix; 708 | 709 | 710 | 711 | // if the channel is full first empty it 712 | /* 713 | //in python easier 714 | indices = cmds.getAttr (BSN+".poses[0].deformations[1].vectorMovements", mi=True) 715 | for ind in indices : cmds.removeMultiInstance(BSN+".poses[0].deformations[1].vectorMovements[{0}]".format(ind), b=True) 716 | if (emptyFrameChannel) { 717 | MGlobal::displayInfo (MString("-->DO empty channel<--") ); 718 | 719 | int existingElem = vertexFaceIndicesPlug.numElements(); 720 | for (int indElem = 0; indElem< existingElem; indElem++) { 721 | MGlobal::displayInfo(MString(" vtx : ") + indElem); 722 | MPlug oldVectorsPlugElement = vertexFaceIndicesPlug.elementByPhysicalIndex(indElem); 723 | MFnNumericData fnNumericData; 724 | MObject vectorValues = fnNumericData.create(MFnNumericData::k3Float, &status); 725 | 726 | //MGlobal::displayInfo (MString("setting ") + offsetPoint.x); 727 | fnNumericData.setData3Float(0,0,0); 728 | status = dgMod.newPlugValue(oldVectorsPlugElement, vectorValues); 729 | } 730 | dgMod.doIt(); 731 | 732 | } 733 | 734 | */ 735 | MPlug facePlug, vertexPlug, vertexTrianglePlug, triangleValuesPlug; 736 | MVector normal, tangent, binormal, cross; 737 | MPoint DFV, TV; 738 | 739 | MFloatVectorArray normals; 740 | MVectorArray tangents(nbDeformedVtx), smoothTangents(nbDeformedVtx); 741 | MIntArray tangentFound(nbDeformedVtx, -1); // init at -1 742 | 743 | MVectorArray smoothedNormals (nbDeformedVtx); 744 | fnDeformedMesh.getVertexNormals(false, normals, MSpace::kWorld); 745 | 746 | 747 | //smooth the normals 748 | MItMeshVertex vertexIter(meshDeformed_); 749 | //MIntArray surroundingVertices; 750 | MPlug smoothNormalsPlug = blurSculptDepNode.findPlug(blurSculpt::smoothNormals); 751 | bool useSmoothNormals = smoothNormalsPlug.asBool(); 752 | 753 | std::vector perFaceConnectedVertices; 754 | perFaceConnectedVertices.resize(nbDeformedVtx); 755 | if (useSmoothNormals) { 756 | for (int vtxTmp = 0; !vertexIter.isDone(); vertexIter.next(), ++vtxTmp) { 757 | MIntArray surroundingVertices; 758 | vertexIter.getConnectedVertices(surroundingVertices); 759 | perFaceConnectedVertices[vtxTmp] = surroundingVertices; 760 | int nbSurrounding = surroundingVertices.length(); 761 | //float mult = 1. / (nbSurrounding+1); 762 | MVector sumNormal = MVector(normals[vtxTmp]); 763 | for (int k = 0; k < nbSurrounding; ++k) { 764 | int vtxAround = surroundingVertices[k]; 765 | sumNormal += MVector(normals[vtxAround]); 766 | } 767 | //sumNormal = .5*sumNormal + .5*normals[vtxTmp]; 768 | sumNormal.normalize(); 769 | smoothedNormals.set(sumNormal, vtxTmp); 770 | } 771 | } 772 | // Store vectors values 773 | MPoint zeroPt(0, 0, 0); 774 | for (int indVtx = 0; indVtx < nbTargetVtx; indVtx++) { 775 | DFV = deformedMeshVerticesPos[indVtx]; 776 | TV = targetMeshVerticesPos[indVtx]; 777 | if (DFV.distanceTo(TV) > aOffset_) { 778 | if (deformationType == 0) { 779 | offsetPoint = TV*matrixValueInverse - DFV*matrixValueInverse; 780 | //offsetPoint = offsetPoint * matrixValueInverse + matPoint; 781 | } 782 | else { 783 | if (tangentFound[indVtx] == -1) { 784 | tangents.set(getVertexTangent(fnDeformedMesh, vertexIter, indVtx), indVtx); 785 | tangentFound[indVtx] = 1; 786 | } 787 | tangent = tangents[indVtx]; 788 | if (useSmoothNormals) { 789 | MIntArray surroundingVertices = perFaceConnectedVertices[indVtx]; 790 | int nbSurrounding = surroundingVertices.length(); 791 | for (int k = 0; k < nbSurrounding; ++k) { 792 | int vtxAround = surroundingVertices[k]; 793 | if (tangentFound[vtxAround] == -1) { 794 | tangents.set(getVertexTangent(fnDeformedMesh, vertexIter, vtxAround), vtxAround); 795 | tangentFound[vtxAround] = 1; 796 | } 797 | tangent += tangents[vtxAround]; 798 | } 799 | } 800 | //fnDeformedMesh.getVertexNormal(indVtx, false, normal); 801 | if (useSmoothNormals) { 802 | normal = smoothedNormals[indVtx]; 803 | } 804 | else { 805 | normal = normals[indVtx]; 806 | } 807 | tangent.normalize(); 808 | CreateMatrix(zeroPt, normal, tangent, mMatrix); 809 | offsetPoint = (TV - DFV)* mMatrix.inverse(); 810 | } 811 | MFnNumericData fnNumericData; 812 | MObject vectorValues = fnNumericData.create(MFnNumericData::k3Float, &status); 813 | 814 | //MGlobal::displayInfo (MString("setting ") + offsetPoint.x); 815 | fnNumericData.setData3Float (float(offsetPoint.x), float(offsetPoint.y), float(offsetPoint.z)); 816 | CHECK_MSTATUS_AND_RETURN_IT(status); 817 | MPlug VectorsPlugElement = theVectorsPlug.elementByLogicalIndex(indVtx, &status); 818 | CHECK_MSTATUS_AND_RETURN_IT(status); 819 | status = dgMod.newPlugValue(VectorsPlugElement, vectorValues); 820 | CHECK_MSTATUS_AND_RETURN_IT(status); 821 | //MGlobal::displayInfo(MString("vtx : ") + indVtx); 822 | } 823 | } 824 | poseEnabledPlug.setValue(true); 825 | status = dgMod.doIt(); 826 | CHECK_MSTATUS_AND_RETURN_IT(status); 827 | 828 | return MS::kSuccess; 829 | } 830 | 831 | -------------------------------------------------------------------------------- /argileCppCode/blurPostDeformCmd.h: -------------------------------------------------------------------------------- 1 | #ifndef blurPostDeformCmd_H 2 | #define blurPostDeformCmd_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "common.h" 34 | 35 | class blurSculptCmd : public MPxCommand { 36 | public: 37 | enum CommandMode { kCommandCreate, kCommandQuery, kCommandAddPose, kCommandAddPoseAtTime, kCommandHelp}; 38 | blurSculptCmd(); 39 | virtual MStatus doIt(const MArgList&); 40 | //virtual MStatus undoIt(); 41 | //virtual MStatus redoIt(); 42 | virtual bool isUndoable() const; 43 | static void* creator(); 44 | static MSyntax newSyntax(); 45 | 46 | 47 | const static char* kName; /**< The name of the command. */ 48 | 49 | /** 50 | Specifies the name of the cvWrap node. 51 | */ 52 | const static char* kNameFlagShort; 53 | const static char* kNameFlagLong; 54 | 55 | const static char* kQueryFlagShort; 56 | const static char* kQueryFlagLong; 57 | 58 | const static char* kAddPoseNameFlagShort; 59 | const static char* kAddPoseNameFlagLong; 60 | 61 | const static char* kPoseNameFlagShort; 62 | const static char* kPoseNameFlagLong; 63 | 64 | const static char* kPoseTransformFlagShort; 65 | const static char* kPoseTransformFlagLong; 66 | 67 | const static char* kListPosesFlagShort; 68 | const static char* kListPosesFlagLong; 69 | 70 | const static char* kListFramesFlagShort; 71 | const static char* kListFramesFlagLong; 72 | 73 | const static char* kAddFlagShort; 74 | const static char* kAddFlagLong; 75 | 76 | const static char* kOffsetFlagShort; 77 | const static char* kOffsetFlagLong; 78 | 79 | const static char* kRemoveTimeFlagShort; 80 | const static char* kRemoveTimeFlagLong; 81 | 82 | /** 83 | Displays help. 84 | */ 85 | const static char* kHelpFlagShort; 86 | const static char* kHelpFlagLong; 87 | 88 | private: 89 | /** 90 | Gathers all the command arguments and sets necessary command states. 91 | @param[in] args Maya MArgList. 92 | */ 93 | MStatus GatherCommandArguments(const MArgList& args); 94 | 95 | MStatus GetGeometryPaths(); 96 | 97 | //MStatus computeBarycenters(); // adding a pose 98 | 99 | MStatus GetLatestBlurSculptNode(); 100 | //MStatus setFaceVertexRelationShip(); 101 | MStatus GetPreDeformedMesh(MObject& blurSculptNode, MDagPath& pathMesh); 102 | 103 | 104 | 105 | MStatus addAPose(); // adding a pose 106 | MStatus addAFrame(); // adding a pose 107 | MStatus getListPoses();// get list of poses 108 | MStatus getListFrames(int poseIndex);// get list of frames 109 | 110 | 111 | MString name_; /**< Name of blurSculpt node to create. */ 112 | MString poseName_; /**< name of the pose to work with. */ 113 | MString targetMeshAdd_;/**< name of the target mesh to compute the deformation for the current time */ 114 | MString poseTransformStr_;/**< name of the target mesh to compute the deformation for the current time */ 115 | CommandMode command_; // the command type 116 | MSelectionList selectionList_; /**< Selected command input nodes. */ 117 | MObject oBlurSculpt_; /**< MObject to the BlurSculpt node in focus. */ 118 | MDGModifier dgMod_; // the execute of mel 119 | 120 | MDagPath meshDeformed_; /**< Paths to the shape deformed */ 121 | MDagPath meshTarget_; /**< Paths to the target mesh*/ 122 | MObject poseTransform_; 123 | 124 | MObject oBlurSculptNode_; /**< MObject to the blurSculpt node in focus. */ 125 | bool getListPoses_; 126 | bool getListFrames_; 127 | bool connectTransform_; 128 | float aOffset_; 129 | float aPoseGain_; 130 | float aPoseOffset_; 131 | 132 | MStringArray allPosesNames_; 133 | MFloatArray allFramesFloats_; 134 | MIntArray allFramesIndices_, allPosesIndices_ ; 135 | }; 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /argileCppCode/blurPostDeformNode.cpp: -------------------------------------------------------------------------------- 1 | #include "blurPostDeformNode.h" 2 | 3 | MTypeId blurSculpt::id(0x001226F0); 4 | // local attributes 5 | // 6 | MObject blurSculpt::blurSculptMatrix; 7 | MObject blurSculpt::uvSet; 8 | MObject blurSculpt::smoothNormals; 9 | MObject blurSculpt::deformationType; 10 | MObject blurSculpt::inTime; 11 | 12 | MObject blurSculpt::poses; // array of all the poses 13 | MObject blurSculpt::poseName; 14 | MObject blurSculpt::poseGain; // mult of the pose position 15 | MObject blurSculpt::poseOffset; // add of the pose position 16 | MObject blurSculpt::poseEnabled;// boolean for enable/disable Pose 17 | MObject blurSculpt::poseMatrix; // a matrix to calculate deformation from 18 | MObject blurSculpt::deformations; // array of the deformations containing 19 | MObject blurSculpt::frame; // float for the frame 20 | MObject blurSculpt::frameEnabled; 21 | MObject blurSculpt::gain; // multier 22 | MObject blurSculpt::offset; // added 23 | MObject blurSculpt::vectorMovements; // the vectors of movements 24 | 25 | blurSculpt::blurSculpt() {} 26 | blurSculpt::~blurSculpt() {} 27 | void* blurSculpt::creator() 28 | { 29 | return new blurSculpt(); 30 | 31 | } 32 | void blurSculpt::postConstructor() 33 | { 34 | setExistWithoutInConnections(true); 35 | } 36 | void blurSculpt::getSmoothedNormal (int indVtx, 37 | MIntArray& smoothNormalFound, 38 | MFloatVectorArray& normals, MFloatVectorArray& smoothedNormals ) 39 | { 40 | MIntArray surroundingVertices = connectedVertices[indVtx]; 41 | int nbSurrounding = surroundingVertices.length(); 42 | //float mult = 1. / (nbSurrounding + 1); 43 | MVector sumNormal = MVector(normals[indVtx]); 44 | for (int k = 0; k < nbSurrounding; ++k) { 45 | int vtxAround = surroundingVertices[k]; 46 | sumNormal += MVector(normals[vtxAround]); 47 | } 48 | //sumNormal = .5*sumNormal + .5*normals[vtxTmp]; 49 | sumNormal.normalize(); 50 | smoothNormalFound[indVtx] = 1; 51 | smoothedNormals.set(sumNormal, indVtx); 52 | } 53 | 54 | void blurSculpt::getSmoothedTangent (int indVtx,MFnMesh& fnInputMesh, MIntArray& smoothTangentFound,MIntArray& tangentFound, MFloatVectorArray& tangents, MFloatVectorArray& smoothTangents ){ 55 | // first get the tangent ------------ 56 | if (tangentFound[indVtx] == -1) { 57 | tangents.set(getVertexTangentFromFace(fnInputMesh, connectedFaces[indVtx], indVtx), indVtx); 58 | tangentFound[indVtx] = 1; 59 | } 60 | MVector tangent = MVector(tangents[indVtx]); 61 | 62 | // for all connected vertices ------------ 63 | MIntArray surroundingVertices = connectedVertices[indVtx]; 64 | int nbSurrounding = surroundingVertices.length(); 65 | for (int k = 0; k < nbSurrounding; ++k) { 66 | int vtxAround = surroundingVertices[k]; 67 | // get its tanget ------------ 68 | if (tangentFound[vtxAround] == -1) { 69 | tangents.set(getVertexTangentFromFace(fnInputMesh, connectedFaces[vtxAround], vtxAround), vtxAround); 70 | tangentFound[vtxAround] = 1; 71 | } 72 | //sum it 73 | tangent += tangents[vtxAround]; 74 | } 75 | //normalize 76 | tangent.normalize(); 77 | // set the smoothed tangent 78 | smoothTangentFound[indVtx] = 1; 79 | smoothTangents.set(tangent, indVtx); 80 | } 81 | 82 | 83 | MStatus blurSculpt::sumDeformation (MArrayDataHandle& deformationsHandle, // the current handle of the deformation 84 | MFnMesh& fnInputMesh, // the current mesh 85 | float poseGainValue, float poseOffsetValue, float curentMult,// the pose multiplication of gain and value 86 | MMatrix& poseMat, MPoint& matPoint, 87 | bool useSmoothNormals, int deformType, 88 | MIntArray& tangentFound, MIntArray& smoothTangentFound, MIntArray& smoothNormalFound,// if we already have the tangets or not 89 | MFloatVectorArray& normals, MFloatVectorArray& smoothedNormals, MFloatVectorArray& tangents, MFloatVectorArray& smoothTangents, // the values of tangents and normals 90 | MPointArray& theVerticesSum ) // the output array to fill 91 | { 92 | MStatus returnStatus; 93 | MDataHandle deformationFrameHandle = deformationsHandle.inputValue(&returnStatus); 94 | 95 | float gainValue = deformationFrameHandle.child(gain).asFloat(); 96 | float offsetValue = deformationFrameHandle.child(offset).asFloat(); 97 | 98 | float multiplier = curentMult * (poseGainValue + poseOffsetValue)*(gainValue + offsetValue); 99 | 100 | MArrayDataHandle vectorMovementsHandle = deformationFrameHandle.child(vectorMovements); 101 | int nbVectorMvts = vectorMovementsHandle.elementCount(); 102 | MVector tangent, normal; 103 | int theVertexNumber; 104 | MDataHandle vectorHandle; 105 | 106 | MMatrix mMatrix; 107 | MPoint zeroPt(0, 0, 0); 108 | MPoint theValue; 109 | for (int vectorIndex = 0; vectorIndex < nbVectorMvts; vectorIndex++) { 110 | vectorMovementsHandle.jumpToArrayElement(vectorIndex); 111 | theVertexNumber = vectorMovementsHandle.elementIndex(); 112 | vectorHandle = vectorMovementsHandle.inputValue(&returnStatus); 113 | float3& vtxValue = vectorHandle.asFloat3(); 114 | if (deformType == 0) { 115 | theValue = MPoint(multiplier*vtxValue[0], multiplier*vtxValue[1], multiplier*vtxValue[2]); 116 | theValue = theValue * poseMat - matPoint; 117 | } 118 | else { 119 | if (useSmoothNormals) { 120 | // ---- recompute normal smoothed ----- 121 | if (smoothNormalFound[theVertexNumber] == -1) { 122 | getSmoothedNormal(theVertexNumber, smoothNormalFound, normals, smoothedNormals); 123 | } 124 | normal = smoothedNormals[theVertexNumber]; 125 | 126 | // ---- recompute tangent smoothed ----- 127 | if (smoothTangentFound[theVertexNumber] == -1) { 128 | getSmoothedTangent(theVertexNumber, 129 | fnInputMesh, 130 | smoothTangentFound, tangentFound, 131 | tangents, smoothTangents); 132 | 133 | } 134 | tangent = smoothTangents[theVertexNumber]; 135 | } 136 | else { 137 | // -- get the tangent ------------- 138 | if (tangentFound[theVertexNumber] == -1) { 139 | tangent = getVertexTangentFromFace(fnInputMesh, connectedFaces[theVertexNumber], theVertexNumber); 140 | tangentFound[theVertexNumber] = 1; 141 | tangents.set(tangent, theVertexNumber); 142 | } 143 | tangent = tangents[theVertexNumber]; 144 | // -- directly get the normal ------------- 145 | normal = normals[theVertexNumber]; 146 | } 147 | 148 | CreateMatrix(zeroPt, normal, tangent, mMatrix); 149 | theValue = MPoint(multiplier*vtxValue[0], multiplier*vtxValue[1], multiplier*vtxValue[2]) * mMatrix; 150 | } 151 | theValue += theVerticesSum[theVertexNumber]; 152 | theVerticesSum.set(theValue, theVertexNumber); 153 | } 154 | return returnStatus; 155 | } 156 | 157 | /* 158 | MVector blurSculpt::getTheTangent(MPointArray& deformedMeshVerticesPos, 159 | MArrayDataHandle& vertexTriangleIndicesData, 160 | MArrayDataHandle& triangleFaceValuesData, 161 | MArrayDataHandle& vertexVertexIndicesData, 162 | MArrayDataHandle& vertexFaceIndicesData, 163 | MFnMesh& fnInputMesh, 164 | MItMeshVertex& meshVertIt, 165 | 166 | int theVertexNumber, int deformType) 167 | { 168 | MStatus returnStatus; 169 | MVector tangent; 170 | if (deformType==2) {//use triangle 171 | vertexTriangleIndicesData.jumpToArrayElement(theVertexNumber); 172 | MDataHandle vertTriangleHandle = vertexTriangleIndicesData.inputValue(&returnStatus); 173 | int triangleIndex = vertTriangleHandle.asInt(); 174 | 175 | triangleFaceValuesData.jumpToArrayElement(triangleIndex); 176 | MDataHandle triangleValuesData = triangleFaceValuesData.inputValue(&returnStatus); 177 | int v1 = triangleValuesData.child(vertex1).asInt(); 178 | int v2 = triangleValuesData.child(vertex2).asInt(); 179 | int v3 = triangleValuesData.child(vertex3).asInt(); 180 | double u = triangleValuesData.child(uValue).asDouble(); 181 | double v = triangleValuesData.child(vValue).asDouble(); 182 | 183 | tangent = deformedMeshVerticesPos[v3] * u + deformedMeshVerticesPos[v2] * v - deformedMeshVerticesPos[v1] * (u + v); 184 | } 185 | else if (deformType == 3) {//useVertex 186 | vertexVertexIndicesData.jumpToArrayElement(theVertexNumber); 187 | MDataHandle tangentVertexData = vertexVertexIndicesData.inputValue(&returnStatus); 188 | int tangentVertexIndex = tangentVertexData.asInt(); 189 | tangent = deformedMeshVerticesPos[tangentVertexIndex] - deformedMeshVerticesPos[theVertexNumber]; 190 | } 191 | else { // use maya deformType == 1 192 | tangent = getVertexTangent(fnInputMesh, meshVertIt, theVertexNumber); 193 | //OLD 194 | 195 | //vertexFaceIndicesData.jumpToArrayElement(theVertexNumber); 196 | //MDataHandle vertFaceHandle = vertexFaceIndicesData.inputValue(&returnStatus); 197 | //int faceIndex = vertFaceHandle.asInt(); 198 | // ---- ask the value of the tangent for the wertex 199 | //fnInputMesh.getFaceVertexTangent(faceIndex, theVertexNumber, tangent, MSpace::kWorld); 200 | 201 | } 202 | tangent.normalize(); 203 | return tangent; 204 | } 205 | */ 206 | MStatus blurSculpt::initialize() 207 | { 208 | // local attribute initialization 209 | MStatus stat; 210 | MFnMatrixAttribute mAttr; 211 | MFnStringData stringFn; 212 | MFnTypedAttribute tAttr; 213 | MFnEnumAttribute enumAttr; 214 | MFnUnitAttribute unitAttr; 215 | MFnCompoundAttribute cAttr; 216 | MFnNumericAttribute nAttr; 217 | 218 | blurSculptMatrix=mAttr.create( "locateMatrix", "lm"); 219 | mAttr.setStorable(false); 220 | mAttr.setConnectable(true); 221 | 222 | // deformation attributes 223 | addAttribute( blurSculptMatrix); 224 | // the UV attribute 225 | MObject defaultString; 226 | defaultString = stringFn.create("HIII"); 227 | uvSet = tAttr.create("uvSet", "uvs", MFnData::kString, defaultString); 228 | tAttr.setStorable(true); 229 | tAttr.setKeyable(false); 230 | addAttribute(uvSet); 231 | 232 | // the type of deformation 233 | deformationType = nAttr.create("deformationType", "dt", MFnNumericData::kInt, 0); 234 | nAttr.setStorable(true); 235 | nAttr.setHidden(true); 236 | 237 | inTime = unitAttr.create("inTime", "it", MFnUnitAttribute::kTime); 238 | unitAttr.setStorable(true); 239 | unitAttr.setKeyable(false); 240 | unitAttr.setWritable(true); 241 | unitAttr.setReadable(false); 242 | addAttribute(inTime); 243 | 244 | smoothNormals = nAttr.create("smoothNormals", "smoothNormals", MFnNumericData::kBoolean, true); 245 | nAttr.setKeyable(false); 246 | addAttribute(smoothNormals); 247 | 248 | /* 249 | // relationShip face vertex 250 | vertexFaceIndices = nAttr.create("vertexFaceIndices", "vertexFaceIndices", MFnNumericData::kInt, -1); 251 | nAttr.setArray(true); 252 | nAttr.setStorable(true); 253 | nAttr.setHidden(true); 254 | 255 | addAttribute(vertexFaceIndices); 256 | 257 | // relationShip face vertex 258 | vertexVertexIndices = nAttr.create("vertexVertexIndices", "vertexVertexIndices", MFnNumericData::kInt, -1); 259 | nAttr.setArray(true); 260 | nAttr.setStorable(true); 261 | nAttr.setHidden(true); 262 | addAttribute(vertexVertexIndices); 263 | 264 | // relationShip face vertex 265 | vertexTriangleIndices = nAttr.create("vertexTriangleIndices", "vertexTriangleIndices", MFnNumericData::kInt, -1); 266 | nAttr.setArray(true); 267 | nAttr.setStorable(true); 268 | nAttr.setHidden(true); 269 | addAttribute(vertexTriangleIndices); 270 | 271 | // relationShip triangle vertex 272 | vertex1 = nAttr.create("vertex1", "vertex1", MFnNumericData::kInt, -1); 273 | vertex2 = nAttr.create("vertex2", "vertex2", MFnNumericData::kInt, -1); 274 | vertex3 = nAttr.create("vertex3", "vertex3", MFnNumericData::kInt, -1); 275 | uValue = nAttr.create("uValue", "uValue", MFnNumericData::kDouble ); 276 | vValue = nAttr.create("vValue", "vValue", MFnNumericData::kDouble); 277 | 278 | 279 | triangleFaceValues = cAttr.create("triangleFaceValues", "triangleFaceValues"); 280 | cAttr.setArray(true); 281 | cAttr.setUsesArrayDataBuilder(true); 282 | cAttr.setStorable(true); 283 | cAttr.setHidden(true); 284 | cAttr.addChild(vertex1); cAttr.addChild(vertex2); cAttr.addChild(vertex3); 285 | cAttr.addChild(uValue); cAttr.addChild(vValue); 286 | addAttribute(triangleFaceValues); 287 | */ 288 | 289 | // add the stored poses 290 | // the string for the name of the pose 291 | MObject poseNameS = stringFn.create("name Of pose"); 292 | poseName = tAttr.create("poseName","poseName",MFnData::kString, poseNameS); 293 | tAttr.setStorable(true); 294 | // the global gain of the pose 295 | poseGain = nAttr.create("poseGain", "poseGain", MFnNumericData::kFloat,1.); 296 | // the global offset of the pose 297 | poseOffset = nAttr.create("poseOffset", "poseOffset", MFnNumericData::kFloat,0.); 298 | poseEnabled = nAttr.create("poseEnabled", "poseEnabled", MFnNumericData::kBoolean, true); 299 | nAttr.setKeyable(false); 300 | // matrix to calculate deformation from 301 | poseMatrix = mAttr.create("poseMatrix", "poseMatrix"); 302 | mAttr.setStorable(false); 303 | mAttr.setConnectable(true); 304 | 305 | // the frame for the pose 306 | frame = nAttr.create("frame", "frame", MFnNumericData::kFloat); 307 | // the gain of the deformation 308 | frameEnabled = nAttr.create("frameEnabled", "frameEnabled", MFnNumericData::kBoolean, true); 309 | nAttr.setKeyable(false); 310 | 311 | gain = nAttr.create("gain", "gain", MFnNumericData::kFloat,1.0); 312 | // the offset of the deformation 313 | offset = nAttr.create("offset", "offset", MFnNumericData::kFloat,0.); 314 | // the vectorMovement of the vertices 315 | vectorMovements = nAttr.create("vectorMovements", "vectorMovements", MFnNumericData::k3Float); 316 | nAttr.setArray(true); 317 | 318 | // create the compound object 319 | deformations = cAttr.create("deformations", "deformations"); 320 | cAttr.setArray(true); 321 | cAttr.setUsesArrayDataBuilder(true); 322 | cAttr.setStorable(true); 323 | cAttr.setHidden(true); 324 | cAttr.addChild(frame); 325 | cAttr.addChild(frameEnabled); 326 | cAttr.addChild(gain); 327 | cAttr.addChild(offset); 328 | cAttr.addChild(vectorMovements); 329 | 330 | // create the compound object 331 | poses = cAttr.create("poses", "poses"); 332 | cAttr.setArray(true); 333 | cAttr.setUsesArrayDataBuilder(true); 334 | cAttr.setStorable(true); 335 | cAttr.setHidden(true); 336 | cAttr.addChild(poseName); 337 | cAttr.addChild(poseGain); 338 | cAttr.addChild(poseOffset); 339 | cAttr.addChild(poseEnabled); 340 | cAttr.addChild(poseMatrix); 341 | cAttr.addChild(deformationType); 342 | 343 | cAttr.addChild(deformations); 344 | addAttribute(poses); 345 | // now the attribute affects 346 | 347 | attributeAffects( blurSculpt::blurSculptMatrix, blurSculpt::outputGeom ); 348 | //attributeAffects(blurSculpt::uvSet, blurSculpt::outputGeom); 349 | attributeAffects(blurSculpt::inTime, blurSculpt::outputGeom); 350 | attributeAffects(blurSculpt::deformationType, blurSculpt::outputGeom); 351 | attributeAffects(blurSculpt::vectorMovements, blurSculpt::outputGeom); 352 | attributeAffects(blurSculpt::poseOffset, blurSculpt::outputGeom); 353 | attributeAffects(blurSculpt::poseGain, blurSculpt::outputGeom); 354 | attributeAffects(blurSculpt::poseEnabled, blurSculpt::outputGeom); 355 | attributeAffects(blurSculpt::poseMatrix, blurSculpt::outputGeom); 356 | attributeAffects(blurSculpt::frame, blurSculpt::outputGeom); 357 | attributeAffects(blurSculpt::frameEnabled, blurSculpt::outputGeom); 358 | attributeAffects(blurSculpt::offset, blurSculpt::outputGeom); 359 | attributeAffects(blurSculpt::gain, blurSculpt::outputGeom); 360 | attributeAffects(blurSculpt::smoothNormals, blurSculpt::outputGeom); 361 | 362 | return MStatus::kSuccess; 363 | } 364 | 365 | 366 | 367 | 368 | MStatus 369 | blurSculpt::deform( MDataBlock& block, 370 | MItGeometry& iter, 371 | const MMatrix& /*m*/, 372 | unsigned int multiIndex) 373 | // 374 | // Method: deform 375 | // 376 | // Description: Deform the point with a squash algorithm 377 | // 378 | // Arguments: 379 | // block : the datablock of the node 380 | // iter : an iterator for the geometry to be deformed 381 | // m : matrix to transform the point into world space 382 | // multiIndex : the index of the geometry that we are deforming 383 | { 384 | MStatus returnStatus; 385 | 386 | MArrayDataHandle hInput = block.outputArrayValue(input, &returnStatus); 387 | if (MS::kSuccess != returnStatus) return returnStatus; 388 | returnStatus = hInput.jumpToElement(multiIndex); 389 | if (MS::kSuccess != returnStatus) return returnStatus; 390 | 391 | MObject oInputGeom = hInput.outputValue().child(inputGeom).asMesh(); 392 | MFnMesh fnInputMesh(oInputGeom, &returnStatus); 393 | if (MS::kSuccess != returnStatus) return returnStatus; 394 | 395 | // get the mesh before deformation, not after, or Idk 396 | /* 397 | MObject thisNode = this->thisMObject(); 398 | MPlug inPlug(thisNode, input); 399 | inPlug.selectAncestorLogicalIndex(multiIndex, input); 400 | MDataHandle hInput = block.inputValue(inPlug); 401 | 402 | MDataHandle inputGeomDataH = hInput.inputValue().child(inputGeom); 403 | MDataHandle hOutput = block.outputValue(plug); 404 | hOutput.copy(inputGeomDataH); 405 | 406 | MFnMesh inputMesh(inputGeomDataH.asMesh()); 407 | */ 408 | 409 | // Envelope data from the base class. 410 | // The envelope is simply a scale factor. 411 | // 412 | MDataHandle envData = block.inputValue(envelope, &returnStatus); 413 | if (MS::kSuccess != returnStatus) return returnStatus; 414 | float env = envData.asFloat(); 415 | 416 | // Get the matrix which is used to define the direction and scale 417 | // of the blurSculpt. 418 | // 419 | MDataHandle matData = block.inputValue(blurSculptMatrix, &returnStatus ); 420 | if (MS::kSuccess != returnStatus) return returnStatus; 421 | MMatrix omat = matData.asMatrix(); 422 | MMatrix omatinv = omat.inverse(); 423 | 424 | MDataHandle timeData = block.inputValue(inTime, &returnStatus); 425 | if (MS::kSuccess != returnStatus) return returnStatus; 426 | MTime theTime = timeData.asTime(); 427 | double theTime_value = theTime.value(); 428 | 429 | /* 430 | // relationShip vtx face 431 | MArrayDataHandle vertexFaceIndicesData = block.inputValue(vertexFaceIndices, &returnStatus); 432 | if (MS::kSuccess != returnStatus) return returnStatus; 433 | // relationShip vtx vtx 434 | MArrayDataHandle vertexVertexIndicesData = block.inputValue(vertexVertexIndices, &returnStatus); 435 | if (MS::kSuccess != returnStatus) return returnStatus; 436 | // triangle vtx f 437 | MArrayDataHandle vertexTriangleIndicesData = block.inputValue(vertexTriangleIndices, &returnStatus); 438 | if (MS::kSuccess != returnStatus) return returnStatus; 439 | // triangle data 440 | MArrayDataHandle triangleFaceValuesData = block.inputValue(triangleFaceValues, &returnStatus); 441 | if (MS::kSuccess != returnStatus) return returnStatus; 442 | */ 443 | 444 | 445 | /* 446 | MTime currentFrame = MAnimControl::currentTime(); 447 | float theTime_value = float(currentFrame.value()); 448 | */ 449 | //cout << "time is " << theTime_value << endl; 450 | //MGlobal::displayInfo(MString("time is : ") + theTime_value); 451 | 452 | // READ IN ".uvSet" DATA: 453 | MDataHandle uvSetDataHandle = block.inputValue(uvSet); 454 | MString theUVSet = uvSetDataHandle.asString(); 455 | bool isUvSet(false); 456 | /* 457 | MStringArray setNames; 458 | meshFn.getUVSetNames(setNames); 459 | MString stringToPrint("US sets :"); 460 | unsigned nbUvSets = setNames.length(); 461 | for (unsigned i = 0; i < nbUvSets; i++) { 462 | stringToPrint += " " + setNames[i]; 463 | isUvSet |= theUVSet == setNames[i]; 464 | } 465 | MString currentUvSet = meshFn.currentUVSetName(&status); 466 | if (!isUvSet) 467 | theUVSet = currentUvSet; 468 | MGlobal::displayInfo(currentUvSet ); 469 | MGlobal::displayInfo(stringToPrint); 470 | MGlobal::displayInfo(MString("uvSet to Use is : ") + theUVSet ); 471 | if (isUvSet ) 472 | MGlobal::displayInfo(MString("Found") ); 473 | 474 | */ 475 | int nbVertices = fnInputMesh.numVertices(); 476 | MPointArray theVerticesSum(nbVertices); 477 | 478 | // iterate through each point in the geometry 479 | MArrayDataHandle posesHandle = block.inputValue(poses, &returnStatus); 480 | unsigned int nbPoses = posesHandle.elementCount(); 481 | unsigned int nbDeformations; 482 | MDataHandle poseInputVal, poseNameHandle, poseGainHandle, poseOffsetHandle, poseEnabledHandle; 483 | MDataHandle deformationTypeData; 484 | //int deformType; 485 | //MDataHandle deformationFrameHandle, frameEnabledHandle , frameHandle, gainHandle, offsetHandle, vectorHandle ; 486 | MString thePoseName; 487 | //float theFrame, poseGainValue, poseOffsetValue, gainValue, offsetValue; 488 | //int theVertexNumber; 489 | 490 | //loop for each output conneted curves 491 | //MItMeshVertex vertexIter(oInputGeom); 492 | //MDataHandle vertFaceHandle, vertTriangleHandle, triangleValuesData, tangentVertexData; 493 | //int faceIndex, triangleIndex, tangentVertexIndex; 494 | 495 | 496 | // --- get all normals at once ------------------------ 497 | MFloatVectorArray normals; 498 | fnInputMesh.getVertexNormals(false, normals, MSpace::kWorld); 499 | // --- this data is to be build ------------------------ 500 | MFloatVectorArray tangents(nbVertices), smoothTangents(nbVertices); 501 | MFloatVectorArray triangleTangents(nbVertices); 502 | MPointArray deformedMeshVerticesPos; 503 | fnInputMesh.getPoints(deformedMeshVerticesPos, MSpace::kObject); 504 | 505 | MIntArray tangentFound(nbVertices, -1); // init at -1 506 | MIntArray smoothTangentFound(nbVertices, -1); // init at -1 507 | MIntArray smoothNormalFound(nbVertices, -1); // init at -1 508 | // -------------------- smooth the normals ----------------------------------------------- 509 | //MIntArray surroundingVertices; 510 | MFloatVectorArray smoothedNormals(nbVertices); 511 | MDataHandle smoothNormalsData = block.inputValue(smoothNormals, &returnStatus); 512 | bool useSmoothNormals = smoothNormalsData.asBool(); 513 | 514 | // if init is false means is a new deformed or a recent opend scene, so create cache form scratch 515 | if (!init){ 516 | MItMeshVertex vertexIter(oInputGeom); 517 | connectedVertices.resize(nbVertices); 518 | connectedFaces.resize(nbVertices); 519 | for (int vtxTmp = 0; !vertexIter.isDone(); vertexIter.next(), ++vtxTmp) { 520 | MIntArray surroundingVertices, surroundingFaces; 521 | vertexIter.getConnectedVertices(surroundingVertices); 522 | connectedVertices[vtxTmp] = surroundingVertices; 523 | 524 | vertexIter.getConnectedFaces(surroundingFaces); 525 | connectedFaces[vtxTmp] = surroundingFaces; 526 | } 527 | init=1; 528 | } 529 | 530 | // -------------------- end compute smoothed normals ------------------------------- 531 | int prevFrameIndex = 0, nextFrameIndex = 0; 532 | float prevFrame = 0, nextFrame = 0; 533 | bool hasPrevFrame = false, hasNextFrame = false; 534 | float prevMult = 0., nextMult = 0., multiplier=0.; 535 | int deformType; 536 | float poseGainValue, poseOffsetValue, theFrame; 537 | MDataHandle deformationFrameHandle, frameEnabledHandle, frameHandle; 538 | //MGlobal::displayInfo(MString("useTriangle : ") + useTriangle + MString(" useVertex : ") + useVertex); 539 | for (unsigned int poseIndex = 0; poseIndex < nbPoses; poseIndex++) { 540 | //MPlug posePlug = allPosesPlug.elementByLogicalIndex(poseIndex); 541 | //posesHandle.jumpToArrayElement(poseIndex); 542 | 543 | // use this method for arrays that are sparse 544 | poseInputVal = posesHandle.inputValue(&returnStatus); 545 | poseEnabledHandle = poseInputVal.child(poseEnabled); 546 | bool isPoseEnabled = poseEnabledHandle.asBool(); 547 | if (isPoseEnabled) { 548 | deformationTypeData = poseInputVal.child(deformationType); 549 | deformType = deformationTypeData.asInt(); 550 | 551 | //poseNameHandle = poseInputVal.child(poseName); 552 | //thePoseName = poseNameHandle.asString(); 553 | //MGlobal::displayInfo(MString("poseName : ") + thePoseName); 554 | poseGainHandle = poseInputVal.child(poseGain); 555 | poseGainValue = poseGainHandle.asFloat(); 556 | 557 | poseOffsetHandle = poseInputVal.child(poseOffset); 558 | poseOffsetValue = poseOffsetHandle.asFloat(); 559 | 560 | MArrayDataHandle deformationsHandle = poseInputVal.child(deformations); 561 | nbDeformations = deformationsHandle.elementCount(); 562 | prevFrameIndex = 0; nextFrameIndex = 0; 563 | prevFrame = 0; nextFrame = 0; 564 | hasPrevFrame = false; hasNextFrame = false; 565 | // check the frames in between 566 | for (unsigned int deformIndex = 0; deformIndex < nbDeformations; deformIndex++) { 567 | //deformationsHandle.jumpToArrayElement(deformIndex); 568 | deformationFrameHandle = deformationsHandle.inputValue(&returnStatus); 569 | 570 | frameEnabledHandle = deformationFrameHandle.child(frameEnabled); 571 | bool isFrameEnabled = frameEnabledHandle.asBool(); 572 | if (isFrameEnabled) { 573 | frameHandle = deformationFrameHandle.child(frame); 574 | theFrame = frameHandle.asFloat(); 575 | if (theFrame < theTime_value) { 576 | if ((!hasPrevFrame) || (theFrame > prevFrame)) { 577 | hasPrevFrame = true; 578 | prevFrameIndex = deformIndex; 579 | prevFrame = theFrame; 580 | } 581 | } 582 | else if (theFrame > theTime_value) { 583 | if ((!hasNextFrame) || (theFrame < nextFrame)) { 584 | hasNextFrame = true; 585 | nextFrameIndex = deformIndex; 586 | nextFrame = theFrame; 587 | } 588 | } 589 | else if (theFrame == theTime_value) { // equality 590 | hasPrevFrame = true; 591 | hasNextFrame = false; 592 | prevFrameIndex = deformIndex; 593 | prevFrame = theFrame; 594 | break; 595 | } 596 | } 597 | deformationsHandle.next(); 598 | } 599 | // get the frames multiplication 600 | prevMult = 0.; nextMult = 0.; 601 | if (hasPrevFrame) prevMult = 1.; 602 | if (hasNextFrame) nextMult = 1.; 603 | if (hasPrevFrame && hasNextFrame) { 604 | nextMult = float(theTime_value - prevFrame) / float(nextFrame - prevFrame); 605 | prevMult = float(1. - nextMult); 606 | } 607 | MDataHandle poseMatrixData = poseInputVal.child(poseMatrix); 608 | MMatrix poseMat = poseMatrixData.asMatrix(); 609 | MPoint matPoint = MPoint(0, 0, 0)*poseMat; 610 | if (hasPrevFrame) { 611 | deformationsHandle.jumpToArrayElement(prevFrameIndex); 612 | 613 | sumDeformation (deformationsHandle, // the current handle of the deformation 614 | fnInputMesh, 615 | poseGainValue, poseOffsetValue, prevMult,// the pose multiplication of gain and value 616 | poseMat, matPoint, 617 | useSmoothNormals,deformType, 618 | tangentFound, smoothTangentFound, smoothNormalFound, // if we already have the tangets or not 619 | normals, smoothedNormals, tangents, smoothTangents, // the values of tangents and normals 620 | //perFaceConnectedVertices, // the connected vertices already stored // to remove !!! 621 | theVerticesSum ); // the output array to fill 622 | 623 | } 624 | if (hasNextFrame) { 625 | deformationsHandle.jumpToArrayElement(nextFrameIndex); 626 | 627 | sumDeformation (deformationsHandle, // the current handle of the deformation 628 | fnInputMesh, 629 | poseGainValue, poseOffsetValue, nextMult, // the pose multiplication of gain and value 630 | poseMat, matPoint, 631 | useSmoothNormals,deformType, 632 | tangentFound, smoothTangentFound, smoothNormalFound, // if we already have the tangets or not 633 | normals, smoothedNormals, tangents, smoothTangents, // the values of tangents and normals 634 | //perFaceConnectedVertices, // the connected vertices already stored // to remove !!! 635 | theVerticesSum ); // the output array to fill 636 | } 637 | } 638 | posesHandle.next(); 639 | } 640 | MPoint pt, resPos, toset; 641 | float weight; 642 | for ( ; !iter.isDone(); iter.next()) { 643 | int theindex = iter.index(); 644 | pt = iter.position(); 645 | //pt *= omatinv; 646 | weight = weightValue(block,multiIndex, theindex); 647 | resPos = (pt + theVerticesSum[theindex]) * omat; 648 | toset = double(env*weight) * resPos + double(1. - env*weight) * pt; 649 | iter.setPosition(toset); 650 | } 651 | return returnStatus; 652 | } 653 | 654 | /* override */ 655 | MObject& 656 | blurSculpt::accessoryAttribute() const 657 | // 658 | // Description: 659 | // This method returns a the attribute to which an accessory 660 | // shape is connected. If the accessory shape is deleted, the deformer 661 | // node will automatically be deleted. 662 | // 663 | // This method is optional. 664 | // 665 | { 666 | return blurSculpt::blurSculptMatrix; 667 | } 668 | 669 | /* override */ 670 | MStatus 671 | blurSculpt::accessoryNodeSetup(MDagModifier& cmd) 672 | // 673 | // Description: 674 | // This method is called when the deformer is created by the 675 | // "deformer" command. You can add to the cmds in the MDagModifier 676 | // cmd in order to hook up any additional nodes that your node needs 677 | // to operate. 678 | // 679 | // In this example, we create a locator and attach its matrix attribute 680 | // to the matrix input on the blurSculpt node. The locator is used to 681 | // set the direction and scale of the random field. 682 | // 683 | // Description: 684 | // This method is optional. 685 | // 686 | { 687 | MStatus result; 688 | 689 | // hook up the accessory node 690 | // 691 | /* 692 | MObject objLoc = cmd.createNode(MString("locator"), 693 | MObject::kNullObj, 694 | &result); 695 | 696 | 697 | MFnDependencyNode fnLoc(objLoc); 698 | MString attrName; 699 | attrName.set("matrix"); 700 | MObject attrMat = fnLoc.attribute(attrName); 701 | result = cmd.connect(objLoc, attrMat, this->thisMObject(), blurSculpt::blurSculptMatrix); 702 | 703 | */ 704 | //- Connect time1 node with time of node 705 | MSelectionList selList; 706 | 707 | MObject timeNode; 708 | MGlobal::getSelectionListByName(MString("time1"), selList); 709 | selList.getDependNode(0, timeNode); 710 | selList.clear(); 711 | 712 | MFnDependencyNode fnTimeNode(timeNode); 713 | MObject timeAttr = fnTimeNode.attribute(MString("outTime"), &result); 714 | cmd.connect(timeNode, timeAttr, this->thisMObject(), blurSculpt::inTime); 715 | 716 | return result; 717 | } 718 | 719 | -------------------------------------------------------------------------------- /argileCppCode/blurPostDeformNode.h: -------------------------------------------------------------------------------- 1 | #ifndef __blurPostDeform_H__ 2 | #define __blurPostDeform_H__ 3 | #pragma once 4 | 5 | // MAYA HEADER FILES: 6 | 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "common.h" 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | #include 40 | #include 41 | 42 | #include 43 | #include 44 | #include 45 | 46 | #include 47 | 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | #include 55 | #include 56 | 57 | 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | 64 | #define McheckErr(stat,msg) \ 65 | if ( MS::kSuccess != stat ) { \ 66 | cerr << msg; \ 67 | return MS::kFailure; \ 68 | } 69 | 70 | // MAIN CLASS DECLARATION FOR THE CUSTOM NODE: 71 | class blurSculpt : public MPxDeformerNode 72 | { 73 | public: 74 | blurSculpt(); 75 | virtual ~blurSculpt(); 76 | 77 | static void* creator(); 78 | static MStatus initialize(); 79 | 80 | // deformation function 81 | // 82 | virtual MStatus deform(MDataBlock& block, 83 | MItGeometry& iter, 84 | const MMatrix& mat, 85 | unsigned int multiIndex); 86 | 87 | void postConstructor(); 88 | 89 | // when the accessory is deleted, this node will clean itself up 90 | // 91 | virtual MObject& accessoryAttribute() const; 92 | 93 | // create accessory nodes when the node is created 94 | // 95 | virtual MStatus accessoryNodeSetup(MDagModifier& cmd); 96 | //MStatus setFrameVtx(MArrayDataHandle &deformationsHandle, MMatrix poseMat, MPoint matPoint, int deformType, int frameIndex, int theMult, float poseGainValue, float poseOffsetValue); 97 | 98 | 99 | void getSmoothedTangent (int indVtx, 100 | MFnMesh& fnInputMesh, 101 | MIntArray& smoothTangentFound,MIntArray& tangentFound, 102 | MFloatVectorArray& tangents, MFloatVectorArray& smoothTangents ); 103 | 104 | void getSmoothedNormal (int indVtx, 105 | MIntArray& smoothNormalFound, 106 | MFloatVectorArray& normals, MFloatVectorArray& smoothedNormals ); 107 | 108 | /* 109 | MVector getTheTangent(MPointArray& deformedMeshVerticesPos, 110 | MArrayDataHandle& vertexTriangleIndicesData, 111 | MArrayDataHandle& triangleFaceValuesData, 112 | MArrayDataHandle& vertexVertexIndicesData, 113 | MArrayDataHandle& vertexFaceIndicesData, 114 | MFnMesh& fnInputMesh, 115 | MItMeshVertex& meshVertIt, 116 | int theVertexNumber, int deformType); 117 | */ 118 | MStatus sumDeformation (MArrayDataHandle& deformationsHandle, // the current handle of the deformation 119 | MFnMesh& fnInputMesh, // the current mesh 120 | float poseGainValue, float poseOffsetValue, float curentMult,// the pose multiplication of gain and value 121 | MMatrix& poseMat, MPoint& matPoint, 122 | bool useSmoothNormals, int deformType, 123 | MIntArray& tangentFound, MIntArray& smoothTangentFound, MIntArray& smoothNormalFound, // if we already have the tangets or not 124 | MFloatVectorArray& normals, MFloatVectorArray& smoothedNormals, MFloatVectorArray& tangents, MFloatVectorArray& smoothTangents, // the values of tangents and normals 125 | MPointArray& theVerticesSum ); // the output array to fill 126 | 127 | public: 128 | // local node attributes 129 | 130 | static MObject blurSculptMatrix; // blurSculpt center and axis 131 | 132 | static MTypeId id; 133 | 134 | static MObject inTime; // the inTime 135 | static MObject uvSet; // the uv set 136 | static MObject smoothNormals; // the uv set 137 | /* 138 | static MObject vertexFaceIndices; // store the vertex face relationship 139 | static MObject vertexVertexIndices; // store the vertex vertex relationship 140 | 141 | // structure to save relationShips 142 | static MObject vertexTriangleIndices; // store the vertex face relationship 143 | static MObject triangleFaceValues; // array of all the triangles 144 | static MObject vertex1; // 145 | static MObject vertex2; // 146 | static MObject vertex3; // 147 | static MObject uValue; // 148 | static MObject vValue; // 149 | 150 | */ 151 | 152 | static MObject poses; // array of all the poses 153 | 154 | static MObject poseName; 155 | static MObject poseGain; // mult of the pose position 156 | static MObject poseOffset; // add of the pose position 157 | static MObject poseEnabled; // boolean for enable/disable Pose 158 | static MObject poseMatrix; // a matrix to calculate deformation from 159 | static MObject deformationType; // type of deformation (world, local, uv) 160 | 161 | static MObject deformations; // array of the deformations containing 162 | static MObject frame; // float for the frame 163 | static MObject frameEnabled; // float for the frame 164 | static MObject gain; // multier 165 | static MObject offset; // added 166 | static MObject vectorMovements; // the vectors of movements 167 | 168 | 169 | private : 170 | // cached attributes 171 | int init =0; 172 | std::vector connectedVertices;// use by MItMeshVertex getConnectedVertices 173 | std::vector connectedFaces; // use by MItMeshVertex getConnectedFaces 174 | /* 175 | private: 176 | bool getConnectedVerts(MItMeshVertex& meshIter, MIntArray& connVerts, int currVertIndex); 177 | static MVector getCurrNormal(MPointArray& inputPts, MIntArray& connVerts); 178 | bool inited; 179 | protected: 180 | */ 181 | }; 182 | 183 | 184 | // the GPU override implementation of the blurSculptNode 185 | // 186 | 187 | 188 | 189 | #endif -------------------------------------------------------------------------------- /argileCppCode/blurPostDeformPlugin.cpp: -------------------------------------------------------------------------------- 1 | #include "blurPostDeformNode.h" 2 | #include "blurPostDeformCmd.h" 3 | 4 | #include 5 | 6 | //- 7 | // ========================================================================== 8 | // Copyright 2015 Autodesk, Inc. All rights reserved. 9 | // 10 | // Use of this software is subject to the terms of the Autodesk 11 | // license agreement provided at the time of installation or download, 12 | // or which otherwise accompanies this software in either electronic 13 | // or hard copy form. 14 | // ========================================================================== 15 | //+ 16 | 17 | //////////////////////////////////////////////////////////////////////// 18 | // 19 | // DESCRIPTION: 20 | // 21 | // Produces the dependency graph node "blurSculptNode". 22 | // 23 | // This plug-in demonstrates how to create a user-defined weighted deformer 24 | // with an associated shape. A deformer is a node which takes any number of 25 | // input geometries, deforms them, and places the output into the output 26 | // geometry attribute. This example plug-in defines a new deformer node 27 | // that blurSculpts vertices according to their CV's weights. The weights are set 28 | // using the set editor or the percent command. 29 | // 30 | // To use this node: 31 | // - create a plane or some other object 32 | // - type: "deformer -type blurSculpt" 33 | // - a locator is created by the command, and you can use this locator 34 | // to control the direction of the blurSculpt. The object's CV's will be blurSculpt 35 | // by the value of the weights of the CV's (the default will be the weight * some constant) 36 | // in the direction of the y-vector of the locator 37 | // - you can edit the weights using either the component editor or by using 38 | // the percent command (eg. percent -v .5 blurSculpt1;) 39 | // 40 | // Use this script to create a simple example with the blurSculpt node: 41 | // 42 | // loadPlugin blurSculptNode; 43 | // polyTorus -r 1 -sr 0.5 -tw 0 -sx 50 -sy 50 -ax 0 1 0 -cuv 1 -ch 1; 44 | // deformer -type "blurSculpt"; 45 | // setKeyframe -v 0 -at rotateZ -t 1 transform1; 46 | // setKeyframe -v 180 -at rotateZ -t 60 transform1; 47 | // select -cl; 48 | 49 | MStatus initializePlugin(MObject obj) 50 | { 51 | MStatus result; 52 | MFnPlugin plugin(obj, "blur studios", "1.0", "Any"); 53 | result = plugin.registerNode("blurSculpt", blurSculpt::id, blurSculpt::creator, 54 | blurSculpt::initialize, MPxNode::kDeformerNode); 55 | 56 | result = plugin.registerCommand(blurSculptCmd::kName, blurSculptCmd::creator, blurSculptCmd::newSyntax); 57 | CHECK_MSTATUS_AND_RETURN_IT(result); 58 | 59 | MString nodeClassName("blurSculpt"); 60 | MString registrantId("blurPlugin"); 61 | 62 | return result; 63 | } 64 | 65 | MStatus uninitializePlugin( MObject obj) 66 | { 67 | 68 | MStatus status; 69 | MFnPlugin plugin( obj ); 70 | status = plugin.deregisterNode(blurSculpt::id ); 71 | 72 | MString nodeClassName("blurSculpt"); 73 | MString registrantId("blurPlugin"); 74 | status = plugin.deregisterCommand(blurSculptCmd::kName); 75 | CHECK_MSTATUS_AND_RETURN_IT(status); 76 | 77 | return status; 78 | 79 | } 80 | -------------------------------------------------------------------------------- /argileCppCode/common.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | from chad vernon 3 | */ 4 | 5 | #include "common.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #define NORMALIZATION_INDEX -1 19 | 20 | 21 | MVector getVertexTangent(MFnMesh& fnInputMesh, MItMeshVertex& meshVertIt, int indVtx) 22 | { 23 | int oldInd; 24 | meshVertIt.setIndex(indVtx, oldInd); 25 | MIntArray connectedFaces; 26 | meshVertIt.getConnectedFaces(connectedFaces); 27 | 28 | return getVertexTangentFromFace(fnInputMesh, connectedFaces, indVtx) ; 29 | } 30 | 31 | MVector getVertexTangentFromFace(MFnMesh& fnInputMesh, MIntArray& connectedFaces, int indVtx) 32 | { 33 | MVector tangent; 34 | int oldInd; 35 | MVector theTangent; 36 | for (unsigned int i = 0; i < connectedFaces.length(); i += 3) { 37 | int theFace = connectedFaces[i]; 38 | fnInputMesh.getFaceVertexTangent(theFace, indVtx, theTangent, MSpace::kWorld); 39 | if (i == 0) { 40 | tangent = theTangent; 41 | } 42 | else { 43 | tangent += theTangent; 44 | } 45 | } 46 | tangent.normalize(); 47 | return tangent; 48 | } 49 | 50 | unsigned int getMStringIndex (MStringArray& myArray, MString &searching) 51 | { 52 | unsigned int toReturn = -1; 53 | for (unsigned int element = 0; element < myArray.length(); ++element) 54 | { 55 | if (myArray[element] == searching) { 56 | toReturn = element; 57 | break; 58 | } 59 | 60 | } 61 | return toReturn; 62 | } 63 | unsigned int GetFreeIndex(MPlug& plug) 64 | { 65 | MStatus stat; 66 | unsigned int numElements = plug.numElements(&stat); 67 | 68 | // Look for an empty slot 69 | unsigned int freeIndex = numElements; 70 | for (unsigned int element = 0; element < numElements; ++element) 71 | { 72 | MPlug newTargetPlug = plug.elementByPhysicalIndex(element, &stat); 73 | unsigned int logicalIndex = newTargetPlug.logicalIndex(&stat); 74 | if (logicalIndex > element) 75 | { 76 | return element; 77 | } 78 | } 79 | return freeIndex; 80 | } 81 | 82 | void StartProgress(const MString& title, unsigned int count) { 83 | if (MGlobal::mayaState() == MGlobal::kInteractive) { 84 | MString message = "progressBar -e -bp -ii true -st \""; 85 | message += title; 86 | message += "\" -max "; 87 | message += count; 88 | message += " $gMainProgressBar;"; 89 | MGlobal::executeCommand(message); 90 | } 91 | } 92 | 93 | 94 | void StepProgress(int step) { 95 | if (MGlobal::mayaState() == MGlobal::kInteractive) { 96 | MString message = "progressBar -e -s "; 97 | message += step; 98 | message += " $gMainProgressBar;"; 99 | MGlobal::executeCommand(message); 100 | } 101 | } 102 | 103 | 104 | bool ProgressCancelled() { 105 | if (MGlobal::mayaState() == MGlobal::kInteractive) { 106 | int cmdResult = 0; 107 | MGlobal::executeCommand("progressBar -query -isCancelled $gMainProgressBar", cmdResult); 108 | return cmdResult != 0; 109 | } 110 | return false; 111 | } 112 | 113 | 114 | void EndProgress() { 115 | if (MGlobal::mayaState() == MGlobal::kInteractive) { 116 | MGlobal::executeCommand("progressBar -e -ep $gMainProgressBar;"); 117 | } 118 | } 119 | 120 | 121 | bool IsShapeNode(MDagPath& path) { 122 | return path.node().hasFn(MFn::kMesh) || 123 | path.node().hasFn(MFn::kNurbsCurve) || 124 | path.node().hasFn(MFn::kNurbsSurface); 125 | } 126 | 127 | 128 | MStatus GetShapeNode(MDagPath& path, bool intermediate) { 129 | MStatus status; 130 | 131 | if (IsShapeNode(path)) { 132 | // Start at the transform so we can honor the intermediate flag. 133 | path.pop(); 134 | } 135 | 136 | if (path.hasFn(MFn::kTransform)) { 137 | unsigned int shapeCount = path.childCount(); 138 | 139 | for (unsigned int i = 0; i < shapeCount; ++i) { 140 | status = path.push(path.child(i)); 141 | CHECK_MSTATUS_AND_RETURN_IT(status); 142 | if (!IsShapeNode(path)) { 143 | path.pop(); 144 | continue; 145 | } 146 | 147 | MFnDagNode fnNode(path, &status); 148 | CHECK_MSTATUS_AND_RETURN_IT(status); 149 | if ((!fnNode.isIntermediateObject() && !intermediate) || 150 | (fnNode.isIntermediateObject() && intermediate)) { 151 | return MS::kSuccess; 152 | } 153 | // Go to the next shape 154 | path.pop(); 155 | } 156 | } 157 | 158 | // No valid shape node found. 159 | return MS::kFailure; 160 | } 161 | 162 | 163 | MStatus GetDagPath(MString& name, MDagPath& path) { 164 | MStatus status; 165 | MSelectionList list; 166 | status = MGlobal::getSelectionListByName(name, list); 167 | CHECK_MSTATUS_AND_RETURN_IT(status); 168 | status = list.getDagPath(0, path); 169 | CHECK_MSTATUS_AND_RETURN_IT(status); 170 | return MS::kSuccess; 171 | } 172 | 173 | MStatus DeleteIntermediateObjects(MDagPath& path) { 174 | MStatus status; 175 | MDagPath pathMesh(path); 176 | while (GetShapeNode(pathMesh, true) == MS::kSuccess) { 177 | status = MGlobal::executeCommand("delete " + pathMesh.partialPathName()); 178 | CHECK_MSTATUS_AND_RETURN_IT(status); 179 | pathMesh = MDagPath(path); 180 | } 181 | return MS::kSuccess; 182 | } 183 | 184 | void GetBarycentricCoordinates(const MPoint& P, const MPoint& A, const MPoint& B, const MPoint& C, 185 | BaryCoords& coords) { 186 | // Compute the normal of the triangle 187 | MVector N = (B - A) ^ (C - A); 188 | MVector unitN = N.normal(); 189 | 190 | // Compute twice area of triangle ABC 191 | double areaABC = unitN * N; 192 | 193 | if (areaABC == 0.0) { 194 | // If the triangle is degenerate, just use one of the points. 195 | coords[0] = 1.0f; 196 | coords[1] = 0.0f; 197 | coords[2] = 0.0f; 198 | return; 199 | } 200 | 201 | // Compute a 202 | double areaPBC = unitN * ((B - P) ^ (C - P)); 203 | coords[0] = (float)(areaPBC / areaABC); 204 | 205 | // Compute b 206 | double areaPCA = unitN * ((C - P) ^ (A - P)); 207 | coords[1] = (float)(areaPCA / areaABC); 208 | 209 | // Compute c 210 | coords[2] = 1.0f - coords[0] - coords[1]; 211 | } 212 | 213 | 214 | MStatus GetAdjacency(MDagPath& pathMesh, std::vector >& adjacency) { 215 | MStatus status; 216 | // Get mesh adjacency. The adjacency will be all vertex ids on the connected faces. 217 | MItMeshVertex itVert(pathMesh, MObject::kNullObj, &status); 218 | CHECK_MSTATUS_AND_RETURN_IT(status); 219 | MFnMesh fnMesh(pathMesh, &status); 220 | CHECK_MSTATUS_AND_RETURN_IT(status); 221 | adjacency.resize(itVert.count()); 222 | for (; !itVert.isDone(); itVert.next()) { 223 | MIntArray faces; 224 | status = itVert.getConnectedFaces(faces); 225 | CHECK_MSTATUS_AND_RETURN_IT(status); 226 | adjacency[itVert.index()].clear(); 227 | // Put the vertex ids in a set to avoid duplicates 228 | for (unsigned int j = 0; j < faces.length(); ++j) { 229 | MIntArray vertices; 230 | fnMesh.getPolygonVertices(faces[j], vertices); 231 | for (unsigned int k = 0; k < vertices.length(); ++k) { 232 | if (vertices[k] != itVert.index()) { 233 | adjacency[itVert.index()].insert(vertices[k]); 234 | } 235 | } 236 | } 237 | } 238 | return MS::kSuccess; 239 | } 240 | 241 | 242 | /** 243 | Used in the CrawlSurface function to keep track of where we are crawling. 244 | */ 245 | struct CrawlData { 246 | MPoint sourcePosition; /**< Where the crawl iteration came from. */ 247 | double crawlDistance; /**< How far this crawl iteration has traveled. */ 248 | int nextVertex; /**< Where this crawl iteration should go next. */ 249 | }; 250 | 251 | 252 | MStatus CrawlSurface(const MPoint& startPoint, const MIntArray& vertexIndices, MPointArray& points, double maxDistance, 253 | std::vector >& adjacency, std::map& distances) { 254 | MStatus status; 255 | distances[NORMALIZATION_INDEX] = 0.0; // -1 will represent our hit point. 256 | double minStartDistance = 999999.0; 257 | unsigned int minStartIndex = 0; 258 | 259 | // Instead of a recursive function, which can get pretty slow, we'll use a queue to keep 260 | // track of where we are going and where we are coming from. 261 | std::queue verticesToVisit; 262 | // Add the initial crawl paths to the queue. 263 | for (unsigned int i = 0; i < vertexIndices.length(); ++i) { 264 | double distance = startPoint.distanceTo(points[vertexIndices[i]]); 265 | // Only crawl to the starting vertices if they are within the radius. 266 | if (distance <= maxDistance) { 267 | CrawlData root = {startPoint, distance, vertexIndices[i]}; 268 | verticesToVisit.push(root); 269 | } 270 | // Track the minimum start distance in case we need to add the closest vertex below. 271 | // The minimum must be greater than 0 to make sure we do not use the vertex that is the 272 | // same as the startPoint which would create an invalid up vector. 273 | if (distance < minStartDistance && distance > 0.000001) { 274 | minStartDistance = distance; 275 | minStartIndex = vertexIndices[i]; 276 | } 277 | } 278 | // If we didn't even reach a vertex in the hit face, or the startPoint is equal to a vertex 279 | // on the face, add the closest vertex so we can calculate a proper up vector 280 | if (verticesToVisit.size() <= 1) { 281 | CrawlData root = {startPoint, maxDistance - 0.001, minStartIndex}; 282 | verticesToVisit.push(root); 283 | distances[minStartIndex] = maxDistance - 0.001; 284 | } 285 | while (verticesToVisit.size()) { 286 | CrawlData next = verticesToVisit.front(); 287 | verticesToVisit.pop(); 288 | 289 | // Extract the data out of the crawl struct 290 | int idx = next.nextVertex; 291 | MPoint& pt = points[idx]; 292 | MPoint sourcePoint = next.sourcePosition; 293 | double currentCrawlDistance = next.crawlDistance; 294 | 295 | currentCrawlDistance += sourcePoint.distanceTo(pt); 296 | if (currentCrawlDistance >= maxDistance) { 297 | // If this vertex is outside the radius, no need to crawl anymore from that vertex. 298 | continue; 299 | } 300 | double& savedDistance = distances[idx]; 301 | if (currentCrawlDistance <= savedDistance || savedDistance == 0.0) { 302 | // If this current crawl distance is less then the distance we have saved for this 303 | // vertex, use this new crawl distance instead. 304 | savedDistance = currentCrawlDistance; 305 | } else { 306 | // A smaller distance is already stored so we don't want to crawl 307 | // from this vertex any further. 308 | continue; 309 | } 310 | // Crawl the adjacent vertices 311 | std::set::iterator iter; 312 | for (iter = adjacency[idx].begin(); iter != adjacency[idx].end(); ++iter) { 313 | CrawlData data = {pt, currentCrawlDistance, *iter}; 314 | verticesToVisit.push(data); 315 | } 316 | } 317 | assert(distances.size() > 0); 318 | 319 | return MS::kSuccess; 320 | } 321 | 322 | bool SampleSort(std::pair lhs, std::pair rhs) { 323 | // Ensure that the normalization sample comes last. 324 | return (lhs.second < rhs.second) || rhs.first == NORMALIZATION_INDEX; 325 | } 326 | 327 | void CalculateSampleWeights(const std::map& distances, double radius, 328 | MIntArray& vertexIds, MDoubleArray& weights) { 329 | 330 | std::map::const_iterator itDistance; 331 | std::vector > samples; 332 | for (itDistance = distances.begin(); 333 | itDistance != distances.end(); 334 | itDistance++) { 335 | double x = itDistance->second; 336 | double w = 1.0 - (x/radius); 337 | samples.push_back(std::pair(itDistance->first, w)); 338 | } 339 | 340 | // Make the samples a multiple of 4 so we can use fast intrinsics! 341 | int remainder = 4 - ((samples.size()-1) % 4); 342 | if (remainder != 4) { 343 | for (int i = 0; i < remainder; ++i) { 344 | samples.push_back(std::pair(0, 0.0)); 345 | } 346 | } 347 | 348 | unsigned int length = (unsigned int)samples.size(); 349 | weights.setLength(length); 350 | vertexIds.setLength(length); 351 | std::sort(samples.begin(), samples.end(), SampleSort); 352 | std::vector >::iterator iter; 353 | int ii = 0; 354 | double sum = 0.0; 355 | for (iter = samples.begin(); iter != samples.end(); ++iter, ++ii) { 356 | vertexIds[ii] = (*iter).first; 357 | weights[ii] = (*iter).second; 358 | sum += (*iter).second; 359 | } 360 | assert(sum > 0.0); 361 | // Normalize the weights 362 | for (unsigned int i = 0; i < weights.length(); ++i) { 363 | weights[i] /= sum; 364 | } 365 | } 366 | 367 | 368 | void CreateMatrix(const MPoint& origin, const MVector& normal, const MVector& up, 369 | MMatrix& matrix) { 370 | const MPoint& t = origin; 371 | const MVector& y = normal; 372 | MVector x = y ^ up; 373 | MVector z = x ^ y; 374 | // Renormalize vectors 375 | x.normalize(); 376 | z.normalize(); 377 | matrix[0][0] = x.x; matrix[0][1] = x.y; matrix[0][2] = x.z; matrix[0][3] = 0.0; 378 | matrix[1][0] = y.x; matrix[1][1] = y.y; matrix[1][2] = y.z; matrix[1][3] = 0.0; 379 | matrix[2][0] = z.x; matrix[2][1] = z.y; matrix[2][2] = z.z; matrix[2][3] = 0.0; 380 | matrix[3][0] = t.x; matrix[3][1] = t.y; matrix[3][2] = t.z; matrix[3][3] = 1.0; 381 | } 382 | 383 | 384 | void CalculateBasisComponents(const MDoubleArray& weights, const BaryCoords& coords, 385 | const MIntArray& triangleVertices, const MPointArray& points, 386 | const MFloatVectorArray& normals, const MIntArray& sampleIds, 387 | double* alignedStorage, 388 | MPoint& origin, MVector& up, MVector& normal) { 389 | // Start with the recreated point and normal using the barycentric coordinates of the hit point. 390 | unsigned int hitIndex = weights.length()-1; 391 | #ifdef __AVX__ 392 | __m256d originV = Dot4(coords[0], coords[1], coords[2], 0.0, 393 | points[triangleVertices[0]], points[triangleVertices[1]], 394 | points[triangleVertices[2]], MPoint::origin); 395 | __m256d hitNormalV = Dot4(coords[0], coords[1], coords[2], 0.0, 396 | normals[triangleVertices[0]], normals[triangleVertices[1]], 397 | normals[triangleVertices[2]], MVector::zero); 398 | __m256d hitWeightV = _mm256_set1_pd(weights[hitIndex]); 399 | // Create the barycentric point and normal. 400 | __m256d normalV = _mm256_mul_pd(hitNormalV, hitWeightV); 401 | // Then use the weighted adjacent data. 402 | for (unsigned int j = 0; j < hitIndex; j += 4) { 403 | __m256d tempNormal = Dot4(weights[j], weights[j+1], weights[j+2], weights[j+3], 404 | normals[sampleIds[j]], normals[sampleIds[j+1]], 405 | normals[sampleIds[j+2]], normals[sampleIds[j+3]]); 406 | normalV = _mm256_add_pd(tempNormal, normalV); 407 | } 408 | 409 | _mm256_store_pd(alignedStorage, originV); 410 | origin.x = alignedStorage[0]; 411 | origin.y = alignedStorage[1]; 412 | origin.z = alignedStorage[2]; 413 | _mm256_store_pd(alignedStorage, normalV); 414 | normal.x = alignedStorage[0]; 415 | normal.y = alignedStorage[1]; 416 | normal.z = alignedStorage[2]; 417 | 418 | // Calculate the up vector 419 | const MPoint& pt1 = points[triangleVertices[0]]; 420 | const MPoint& pt2 = points[triangleVertices[1]]; 421 | __m256d p1 = _mm256_set_pd(pt1.w, pt1.z, pt1.y, pt1.x); 422 | __m256d p2 = _mm256_set_pd(pt2.w, pt2.z, pt2.y, pt2.x); 423 | p1 = _mm256_add_pd(p1, p2); 424 | __m256d half = _mm256_set_pd(0.5, 0.5, 0.5, 0.5); 425 | p1 = _mm256_mul_pd(p1, half); 426 | __m256d upV = _mm256_sub_pd(p1, originV); 427 | _mm256_store_pd(alignedStorage, upV); 428 | up.x = alignedStorage[0]; 429 | up.y = alignedStorage[1]; 430 | up.z = alignedStorage[2]; 431 | #else 432 | MVector hitNormal; 433 | // Create the barycentric point and normal. 434 | for (int i = 0; i < 3; ++i) { 435 | origin += points[triangleVertices[i]] * coords[i]; 436 | hitNormal += MVector(normals[triangleVertices[i]]) * coords[i]; 437 | } 438 | // Use crawl data to calculate normal 439 | normal = hitNormal * weights[hitIndex]; 440 | for (unsigned int j = 0; j < hitIndex; j++) { 441 | normal += MVector(normals[sampleIds[j]]) * weights[j]; 442 | } 443 | 444 | // Calculate the up vector 445 | // The triangle vertices are sorted by decreasing barycentric coordinates so the first two are 446 | // the two closest vertices in the triangle. 447 | up = ((points[triangleVertices[0]] + points[triangleVertices[1]]) * 0.5) - origin; 448 | #endif 449 | normal.normalize(); 450 | GetValidUp(weights, points, sampleIds, origin, normal, up); 451 | } 452 | 453 | 454 | void GetValidUp(const MDoubleArray& weights, const MPointArray& points, 455 | const MIntArray& sampleIds, const MPoint& origin, const MVector& normal, 456 | MVector& up) { 457 | MVector unitUp = up.normal(); 458 | // Adjust up if it's parallel to normal or if it's zero length 459 | if (abs((unitUp * normal) - 1.0) < 0.001 || up.length() < 0.0001) { 460 | for (unsigned int j = 0; j < weights.length()-1; ++j) { 461 | up -= (points[sampleIds[j]] - origin) * weights[j]; 462 | unitUp = up.normal(); 463 | if (abs((unitUp * normal) - 1.0) > 0.001 && up.length() > 0.0001) { 464 | // If the up and normal vectors are no longer parallel and the up vector has a length, 465 | // then we are good to go. 466 | break; 467 | } 468 | } 469 | up.normalize(); 470 | } else { 471 | up = unitUp; 472 | } 473 | } 474 | 475 | 476 | 477 | -------------------------------------------------------------------------------- /argileCppCode/common.h: -------------------------------------------------------------------------------- 1 | /** 2 | Contains various helper functions. 3 | from chad vernon 4 | */ 5 | 6 | #ifndef CVWRAP_COMMON_H 7 | #define CVWRAP_COMMON_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #ifdef __AVX__ 26 | #include 27 | #include 28 | #endif 29 | 30 | MVector getVertexTangent(MFnMesh& fnInputMesh, MItMeshVertex& meshVertIt, int theVertexNumber); 31 | MVector getVertexTangentFromFace(MFnMesh& fnInputMesh, MIntArray& connectedFaces, int theVertexNumber); 32 | 33 | // get a free index of a channel 34 | unsigned int GetFreeIndex(MPlug& plug); 35 | 36 | unsigned int getMStringIndex(MStringArray& myArray, MString &searching); 37 | 38 | /** 39 | Helper function to start a new progress bar. 40 | @param[in] title Status title. 41 | @param[in] count Progress bar maximum count. 42 | */ 43 | void StartProgress(const MString& title, unsigned int count); 44 | 45 | 46 | /** 47 | Helper function to increase the progress bar by the specified amount. 48 | @param[in] step Step amount. 49 | */ 50 | void StepProgress(int step); 51 | 52 | 53 | /** 54 | Check if the progress has been cancelled. 55 | @return true if the progress has been cancelled. 56 | */ 57 | bool ProgressCancelled(); 58 | 59 | 60 | /** 61 | Ends any running progress bar. 62 | */ 63 | void EndProgress(); 64 | 65 | 66 | /** 67 | Checks if the path points to a shape node. 68 | @param[in] path A dag path. 69 | @return true if the path points to a shape node. 70 | */ 71 | bool IsShapeNode(MDagPath& path); 72 | 73 | 74 | /** 75 | Ensures that the given dag path points to a non-intermediate shape node. 76 | @param[in,out] path Path to a dag node that could be a transform or a shape. 77 | On return, the path will be to a shape node if one exists. 78 | @param[in] intermediate true to get the intermediate shape. 79 | @return MStatus. 80 | */ 81 | MStatus GetShapeNode(MDagPath& path, bool intermediate=false); 82 | 83 | 84 | /** 85 | Get the MDagPath of an object. 86 | @param[in] name Name of a dag node. 87 | @param[out] path Storage for the dag path. 88 | */ 89 | MStatus GetDagPath(MString& name, MDagPath& path); 90 | 91 | 92 | /** 93 | Delete all intermediate shapes of the given dag path. 94 | @param[in] path MDagPath. 95 | */ 96 | MStatus DeleteIntermediateObjects(MDagPath& path); 97 | 98 | 99 | /** 100 | Helper struct to hold the 3 barycentric coordinates. 101 | */ 102 | struct BaryCoords { 103 | float coords[3]; 104 | float operator[](int index) const { return coords[index]; } 105 | float& operator[](int index) { return coords[index]; } 106 | }; 107 | 108 | 109 | /** 110 | Get the barycentric coordinates of point P in the triangle specified by points A,B,C. 111 | @param[in] P The sample point. 112 | @param[in] A Triangle point. 113 | @param[in] B Triangle point. 114 | @param[in] C Triangle point. 115 | @param[out] coords Barycentric coordinates storage. 116 | */ 117 | void GetBarycentricCoordinates(const MPoint& P, const MPoint& A, const MPoint& B, const MPoint& C, 118 | BaryCoords& coords); 119 | 120 | 121 | /** 122 | Get the vertex adjacency of the specified mesh. The vertex adjacency are the vertex ids 123 | of the connected faces of each vertex. 124 | @param[in] pathMesh Path to a mesh. 125 | @param[out] adjacency Ajdancency storage of the adjancency per vertex id. 126 | */ 127 | MStatus GetAdjacency(MDagPath& pathMesh, std::vector >& adjacency); 128 | 129 | 130 | /** 131 | Crawls the surface to find all the points within the sampleradius. 132 | @param[in] startPoint The position from which to start the crawl. 133 | @param[in] vertexIndices The starting vertex indices we want to crawl to. 134 | @param[in] points The array of all the mesh points. 135 | @param[in] maxDistance The maximum crawl distance. 136 | @param[in] adjacency Vertex adjacency data from the GetAdjacency function. 137 | @param[out] distances Storage for the distances to the crawled points. 138 | @return MStatus 139 | */ 140 | MStatus CrawlSurface(const MPoint& startPoint, const MIntArray& vertexIndices, MPointArray& points, double maxDistance, 141 | std::vector >& adjacency, std::map& distances); 142 | 143 | 144 | /** 145 | Calculates a weight for each vertex within the crawl sample radius. Vertices that are further 146 | away from the origin should have a lesser effect than vertices closer to the origin. 147 | @param[in] distances Crawl distances calculated from CrawlSurface. 148 | @param[in] radius Sample radius. 149 | @param[out] vertexIds Storage for the vertex ids sampled during the crawl. 150 | @param[out] weights Storage for the calculated weights of each sampled vertex. 151 | */ 152 | void CalculateSampleWeights(const std::map& distances, double radius, 153 | MIntArray& vertexIds, MDoubleArray& weights); 154 | 155 | /** 156 | Creates an orthonormal basis using the given point and two axes. 157 | @param[in] origin Position. 158 | @param[in] normal Normal vector. 159 | @param[in] up Up vector. 160 | @param[out] matrix Generated matrix. 161 | */ 162 | void CreateMatrix(const MPoint& origin, const MVector& normal, const MVector& up, 163 | MMatrix& matrix); 164 | 165 | /** 166 | Calculates the components necessary to create a wrap basis matrix. 167 | @param[in] weights The sample weights array from the wrap binding. 168 | @param[in] coords The barycentric coordinates of the closest point. 169 | @param[in] triangleVertices The vertex ids forming the triangle of the closest point. 170 | @param[in] points The driver point array. 171 | @param[in] normals The driver per-vertex normal array. 172 | @param[in] sampleIds The vertex ids on the driver of the current sample. 173 | @param[in] alignedStorage double array that is 32 byte aligned for AVX. 174 | @param[out] origin The origin of the coordinate system. 175 | @param[out] up The up vector of the coordinate system. 176 | @param[out] normal The normal vector of the coordinate system. 177 | */ 178 | void CalculateBasisComponents(const MDoubleArray& weights, const BaryCoords& coords, 179 | const MIntArray& triangleVertices, const MPointArray& points, 180 | const MFloatVectorArray& normals, const MIntArray& sampleIds, 181 | double* alignedStorage, 182 | MPoint& origin, MVector& up, MVector& normal); 183 | 184 | /** 185 | Ensures that the up and normal vectors are perpendicular to each other. 186 | @param[in] weights The sample weights array from the wrap binding. 187 | @param[in] points The driver point array. 188 | @param[in] sampleIds The vertex ids on the driver of the current sample. 189 | @param[in] origin The origin of the coordinate system. 190 | @param[in] up The up vector of the coordinate system. 191 | @param[out] normal The normal vector of the coordinate system. 192 | */ 193 | void GetValidUp(const MDoubleArray& weights, const MPointArray& points, 194 | const MIntArray& sampleIds, const MPoint& origin, const MVector& normal, 195 | MVector& up); 196 | 197 | 198 | template 199 | struct ThreadData { 200 | unsigned int start; 201 | unsigned int end; 202 | unsigned int numTasks; 203 | double* alignedStorage; 204 | T* pData; 205 | 206 | ThreadData() { 207 | alignedStorage = (double*) _mm_malloc(4*sizeof(double), 256); 208 | } 209 | ~ThreadData() { 210 | _mm_free(alignedStorage); 211 | } 212 | }; 213 | 214 | 215 | /** 216 | Creates the data stuctures that will be sent to each thread. Divides the vertices into 217 | discrete chunks to be evaluated in the threads. 218 | @param[in] taskCount The number of individual tasks we want to divide the calculation into. 219 | @param[in] elementCount The number of vertices or elements to be divided up. 220 | @param[in] taskData The TaskData or BindData object. 221 | @param[out] threadData The array of ThreadData objects. It is assumed the array is of size taskCount. 222 | */ 223 | template 224 | void CreateThreadData(int taskCount, unsigned int elementCount, T* taskData, ThreadData* threadData) { 225 | unsigned int taskLength = (elementCount + taskCount - 1) / taskCount; 226 | unsigned int start = 0; 227 | unsigned int end = taskLength; 228 | int lastTask = taskCount - 1; 229 | for(int i = 0; i < taskCount; i++) { 230 | if (i == lastTask) { 231 | end = elementCount; 232 | } 233 | threadData[i].start = start; 234 | threadData[i].end = end; 235 | threadData[i].numTasks = taskCount; 236 | threadData[i].pData = taskData; 237 | 238 | start += taskLength; 239 | end += taskLength; 240 | } 241 | } 242 | 243 | #ifdef __AVX__ 244 | /** 245 | Calculates 4 dot products at once. 246 | @param[in] w1 Weight vector x element. 247 | @param[in] w2 Weight vector y element. 248 | @param[in] w3 Weight vector z element. 249 | @param[in] w4 Weight vector w element. 250 | @param[in] p1 First vector. 251 | @param[in] p2 Second vector. 252 | @param[in] p3 Third vector. 253 | @param[in] p4 Fourth vector. 254 | @return A __m256d vector where each element is the corresponding p vector dot product with w. 255 | */ 256 | template 257 | __m256d Dot4(double w1, double w2, double w3, double w4, 258 | const T& p1, const T& p2, const T& p3, const T& p4) { 259 | __m256d xxx = _mm256_set_pd(p1.x, p2.x, p3.x, p4.x); 260 | __m256d yyy = _mm256_set_pd(p1.y, p2.y, p3.y, p4.y); 261 | __m256d zzz = _mm256_set_pd(p1.z, p2.z, p3.z, p4.z); 262 | __m256d www = _mm256_set_pd(w1, w2, w3, w4); 263 | __m256d xw = _mm256_mul_pd(xxx, www); 264 | __m256d yw = _mm256_mul_pd(yyy, www); 265 | __m256d zw = _mm256_mul_pd(zzz, www); 266 | __m256d ww = _mm256_mul_pd(www, www); // Dummy 267 | // low to high: xw0+xw1 yw0+yw1 xw2+xw3 yw2+yw3 268 | __m256d temp01 = _mm256_hadd_pd(xw, yw); 269 | // low to high: zw0+zw1 ww0+ww1 zw2+zw3 ww2+ww3 270 | __m256d temp23 = _mm256_hadd_pd(zw, ww); 271 | // low to high: xw2+xw3 yw2+yw3 zw0+zw1 ww0+ww1 272 | __m256d swapped = _mm256_permute2f128_pd(temp01, temp23, 0x21); 273 | // low to high: xw0+xw1 yw0+yw1 zw2+zw3 ww2+ww3 274 | __m256d blended = _mm256_blend_pd(temp01, temp23, 0xC); 275 | // low to high: xw0+xw1+xw2+xw3 yw0+yw1+yw2+yw3 zw0+zw1+zw2+zw3 ww0+ww1+ww2+ww3 276 | __m256d dotproduct = _mm256_add_pd(swapped, blended); 277 | return dotproduct; 278 | } 279 | #endif 280 | 281 | #endif -------------------------------------------------------------------------------- /argileCppCode/example.py: -------------------------------------------------------------------------------- 1 | ''' 2 | file -f -new; 3 | unloadPlugin blurPostDeform; 4 | 5 | 6 | 7 | 8 | 9 | loadPlugin blurPostDeform; 10 | 11 | polyTorus -r 1 -sr 0.5 -tw 0 -sx 50 -sy 50 -ax 0 1 0 -cuv 1 -ch 1; 12 | blurSculpt; 13 | 14 | polyTorus -r 1 -sr 0.5 -tw 0 -sx 50 -sy 50 -ax 0 1 0 -cuv 1 -ch 1; 15 | select -r pTorus2.vtx[886] ; 16 | move -r -0.710423 1.410621 0.0330195 ; 17 | select -r pTorus2.vtx[845] ; 18 | move -r 0.101498 1.7977 -1.052875 ; 19 | select -cl ; 20 | move -r 1.398724 0.770505 -2.010649 pTorus2; 21 | 22 | select transform1; 23 | 24 | ''' 25 | 26 | 27 | 28 | 29 | 30 | 31 | from maya import cmds 32 | BSN = "blurSculpt1" 33 | 34 | 35 | cmds.getAttr (BSN+".poses[0].deformations", mi=True) 36 | cmds.removeMultiInstance(BSN+".poses[0].deformations[1]", b=True) 37 | cmds.blurSculpt (BSN,query = True, listFrames=True, poseName= "testPose") 38 | cmds.blurSculpt (help = True) 39 | 40 | 41 | cmds.connectAttr ("locator2.worldMatrix", BSN +".poses[0].poseMatrix",f=True) 42 | ############################################################################ 43 | ############################################################################ 44 | 45 | cmds.blurSculpt ("pTorus1",addAtTime="pTorus2", poseName = "testPose") 46 | BSN = "blurSculpt1" 47 | cmds.listAttr (BSN) 48 | cmds.getAttr (BSN +".poses", mi=True) 49 | cmds.setAttr (BSN +".poses[0].poseOffset",0) 50 | 51 | 52 | cmds.getAttr (BSN +".poses[0].poseName") 53 | cmds.getAttr (BSN +".poses[0].poseGain") 54 | cmds.getAttr (BSN +".poses[0].poseOffset") 55 | 56 | cmds.getAttr (BSN +".poses[0].deformations[0].frame") 57 | cmds.getAttr (BSN +".poses[0].deformations[0].vectorMovements", mi=True) 58 | cmds.getAttr (BSN +".poses[0].deformations[0].vectorMovements[845]") 59 | 60 | cmds.blurSculpt (help = True) 61 | cmds.blurSculpt ("pTorus1",query = True, listPoses=True) 62 | cmds.blurSculpt ("pTorus1",query = True, listFrames=True, poseName= "testPose") 63 | 64 | cmds.listAttr ("blurSculpt1") 65 | 66 | 67 | from maya import cmds 68 | BSN = "blurSculpt1" 69 | 70 | cmds.currentTime (30) 71 | cmds.blurSculpt ("pTorus1",addAtTime="pTorus2", poseName = "testPose", offset = .002) 72 | cmds.currentTime (70) 73 | 74 | cmds.setAttr (BSN +".poses[0].poseGain",0) 75 | cmds.blurSculpt ("pTorus1",addAtTime="pTorus3", poseName = "testPose") 76 | cmds.setAttr (BSN +".poses[0].poseGain",1) 77 | 78 | cmds.setAttr (BSN +".poses[0].poseGain",0) 79 | cmds.currentTime (100) 80 | cmds.blurSculpt ("pTorus1",addAtTime="pTorus4", poseName = "testPose") 81 | cmds.setAttr (BSN +".poses[0].poseGain",1) 82 | 83 | cmds.blurSculpt (help = True) 84 | cmds.blurSculpt ("pTorus1",query = True, listPoses=True) 85 | cmds.blurSculpt ("pTorus1",query = True, listFrames=True, poseName= "testPose") 86 | 87 | 88 | cmds.setAttr (BSN +".poses[0].deformations[3].frame",50) 89 | 90 | 91 | --------------------------------------------------------------------------------