├── 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