PageRenderTime 60ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/mayaTools/cgm/core/classes/DraggerContextFactory.py

https://bitbucket.org/jjburton/cgmtools
Python | 1040 lines | 963 code | 29 blank | 48 comment | 16 complexity | 8935ee105aa8b8ed511ed449bcecf12f MD5 | raw file
Possible License(s): CC-BY-4.0
  1. #=================================================================================================================================================
  2. #=================================================================================================================================================
  3. # DraggerContextFactory - a part of cgmTools
  4. #=================================================================================================================================================
  5. #=================================================================================================================================================
  6. #
  7. # DESCRIPTION:
  8. # Classes and functions for DraggerContext
  9. #
  10. # AUTHOR:
  11. # Josh Burton
  12. # http://www.cgmonks.com
  13. # Copyright 2012 CG Monks - All Rights Reserved.
  14. #
  15. # ACKNOWLEDGEMENTS:
  16. # Morgan Loomis
  17. # http://forums.cgsociety.org/archive/index.php/t-983068.html
  18. # http://forums.cgsociety.org/archive/index.php/t-1002257.html
  19. # https://groups.google.com/forum/?fromgroups#!topic/python_inside_maya/n6aJq27fg0o%5B1-25%5D
  20. #======================================================================================================================
  21. import time
  22. import maya.cmds as mc
  23. import copy
  24. import maya.OpenMayaUI as OpenMayaUI
  25. import maya.OpenMaya as om
  26. #CANNOT IMPORT: LOC
  27. #from cgm.core import cgm_Meta as cgmMeta
  28. import Red9.core.Red9_Meta as r9Meta
  29. from cgm.core.rigger.lib import joint_Utils as jntUtils
  30. from cgm.core.lib import rayCaster as RayCast
  31. from cgm.core import cgm_General as cgmGen
  32. from cgm.core.cgmPy import validateArgs as cgmValid
  33. from cgm.core.lib import search_utils as SEARCH
  34. from cgm.core.lib import distance_utils as DIST
  35. from cgm.core.lib import position_utils as POS
  36. from cgm.core.lib import node_utils as NODES
  37. from cgm.core.lib import name_utils as NAMES
  38. from cgm.core.lib import snap_utils as SNAP
  39. from cgm.core.lib import shape_utils as SHAPE
  40. from cgm.core.lib import attribute_utils as ATTR
  41. import cgm.core.lib.transform_utils as TRANS
  42. reload(ATTR)
  43. reload(POS)
  44. reload(NODES)
  45. from cgm.core.lib import math_utils as MATHUTILS
  46. reload(RayCast)
  47. from cgm.lib import (locators,
  48. geo,
  49. curves,
  50. search,
  51. attributes,
  52. lists,
  53. nodes,
  54. rigging,
  55. distance,
  56. guiFactory)
  57. reload(distance)
  58. reload(curves)
  59. import os
  60. #========================================================================
  61. import logging
  62. logging.basicConfig()
  63. log = logging.getLogger(__name__)
  64. log.setLevel(logging.INFO)
  65. #========================================================================
  66. class ContextualPick(object):
  67. def __init__(self,name = 'cgmDraggerContext', *a,**kws):
  68. """
  69. Initializes a draggerContext object for use with other tools
  70. Arguments:
  71. undoMode --
  72. projection --
  73. plane --
  74. space --
  75. cursor
  76. drag -- Whether to enable drag mode
  77. """
  78. self.undoMode = kws.pop('undoMode','step')
  79. self.projection = kws.pop('projection','plane')
  80. self.plane = kws.pop('plane',[1,0,0])
  81. self.space = kws.pop('space','world')
  82. self.cursor = kws.pop('cursor','crossHair')
  83. self.dragOption = kws.pop('drag',False)
  84. self.name = name
  85. self.l_return = []
  86. self.build(*a,**kws )
  87. self.setTool()
  88. def build(self,*a,**kws ):
  89. if mc.draggerContext(self.name,query=True,exists=True): # if it exists, delete it
  90. mc.setToolTo('selectSuperContext')
  91. mc.selectMode(object=True)
  92. mc.deleteUI(self.name)
  93. imageFilePath = ('cgmDefault.png')
  94. mc.draggerContext( self.name, image1 = imageFilePath,
  95. undoMode = self.undoMode, projection = self.projection, space = self.space,
  96. initialize = self.initialPress,
  97. pressCommand = self.press,
  98. releaseCommand = self.release,
  99. finalize = self.finalize,
  100. *a,**kws )
  101. if self.projection == 'plane': mc.draggerContext(self.name,e=True, plane = self.plane)# if our projection is 'plane', set the plane
  102. if self.dragOption:mc.draggerContext(self.name,e=True, dragCommand = self.drag)# If drag mode, turn it on
  103. def finalize(self):pass
  104. def press(self):
  105. if not mc.draggerContext(self.name,query=True,exists=True): # if it exists, delete it
  106. self.build
  107. self.setTool()
  108. self.anchorPoint = mc.draggerContext(self.name, query=True, anchorPoint=True)
  109. self.modifier = mc.draggerContext(self.name, query=True, modifier=True)
  110. self.button = mc.draggerContext(self.name, query=True, button=True)
  111. self.x = self.anchorPoint[0]
  112. self.y = self.anchorPoint[1]
  113. self.z = self.anchorPoint[2]
  114. #log.info("Position is %s in '%s' space"%(self.anchorPoint,self.space))
  115. def release(self):pass
  116. def drag(self):
  117. self.dragPoint = mc.draggerContext(self.name, query=True, dragPoint=True)
  118. self.button = mc.draggerContext( self.name, query=True, button=True)
  119. self.modifier = mc.draggerContext( self.name, query=True, modifier=True)
  120. self.x = self.dragPoint[0]
  121. self.y = self.dragPoint[1]
  122. self.z = self.dragPoint[2]
  123. def initialPress(self):pass
  124. def dropTool(self):
  125. mc.setToolTo('selectSuperContext')
  126. mc.selectMode(object=True)
  127. def setTool(self):
  128. mc.setToolTo(self.name)
  129. #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  130. # Subclasses
  131. #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  132. _clickMesh_modes = ['surface','intersections','midPoint']
  133. def clickMesh_func(*a,**kws):
  134. return clickMesh(*a,**kws).finalize()
  135. class clickMesh(ContextualPick):
  136. """
  137. Find positional data from clicking on a surface from a ContextualPick instance.
  138. And other things...:)
  139. :parameters:
  140. mesh(list) -- Mesh/nurbsSurfaces to cast to. If None, will use all visible mesh.
  141. mode(string) -- options:('surface' is default)
  142. 'surface' -- Single return
  143. 'intersections' -- all hits on the vector ray
  144. 'midPoint' -- mid point of intersections
  145. 'planeX' -- cast to x plane
  146. 'planeY' -- cast to y plane
  147. 'planeZ' -- cast to z plane
  148. create(False/option) -- options:('locator' is default)
  149. 'locator' -- makes a locator
  150. 'joint' -- creates joints per click
  151. 'jointChain' -- rebuilds all positions as a chain at tool exit
  152. 'curve' -- builds curve from positions at tool exit
  153. 'group' -- build groups at positions
  154. 'follicle' --creates a folicle
  155. 'vectorLine' -- visualize cast data
  156. 'data' -- just return data
  157. closestOnly(bool) -- only return the closest hit from all mesh hits (default - True)
  158. clampIntersections(False/int) -- clamp number of interactions
  159. dragStore(bool) -- whether to force store data during drag
  160. maxStore(int) -- Maximum number of hits to accept before finalizing
  161. posOffset(vector) -- x,y,z offset from given point
  162. offsetDistance(float) -- amount to offset for distance offsetMode
  163. toDuplicate(list) -- objects to duplicate
  164. offsetMode(str) -- 'vector','normal' (default -- 'vector')
  165. distance -- offset along the ray with the end - start being the base distance
  166. snapCast -- An auto guessing post cast mode to attempt to keep things off the surface when casting
  167. clampValues(vector) -- clamp given values to provided ones. Useful for a spine cast for example
  168. orientSnap(bool) -- orient the created object to surface
  169. orientMode(str) -- how to orient our created or duplicated objects
  170. None
  171. normal
  172. timeDelay(float) -- Wait to start the tool after a given delay. Useful for marking menu calling
  173. objAimAxis(str/vector) --
  174. objUpAxis(str/vector) --
  175. objOutAxis(str/vector) --
  176. aimMode(str):
  177. 'local'-- use Bokser's fancy method
  178. 'world' -- use standard maya aiming
  179. dragInterval(float) -- Distance inverval for drag mode
  180. tagAndName(dict) -- I don't remember...:)
  181. toCreate(list) -- list of items names to make, sets's max as well. When it's through the list, it shops
  182. toSnap(list) -- objects to snap to a final pos value
  183. maxDistance(float) -- maximum cast distance
  184. :Stores
  185. self.l_pos(list) -- points on the surface selected in world space
  186. TODO:
  187. Add depth option
  188. """
  189. def __init__(self,
  190. mode = 'surface',
  191. mesh = None,
  192. create = False,
  193. closestOnly = True,
  194. clampIntersections = False,
  195. dragStore = False,
  196. maxStore = False,
  197. posOffset = None,
  198. offsetMode = None,
  199. offsetDistance = 1.0,
  200. clampValues = [None,None,None],
  201. orientSnap = True,
  202. orientMode = None,
  203. timeDelay = None,
  204. objAimAxis = 'z+',
  205. objUpAxis = 'y+',
  206. objOutAxis = 'x+',
  207. aimMode = 'local',
  208. dragInterval = .2,
  209. tagAndName = {},
  210. toCreate = [],
  211. toDuplicate = [],
  212. toSnap = [],#...objects to snap on release
  213. toAim = [],#...objects to aim on update
  214. maxDistance = 100000,
  215. *a,**kws):
  216. _str_funcName = 'clickMesh.__init__'
  217. log.debug(">>> %s >> "%_str_funcName + "="*75)
  218. #>>> Store our info ====================================================================
  219. self._createModes = ['locator','joint','jointChain','curve','follicle','group','vectorLine','data',False]
  220. self._l_modes = _clickMesh_modes
  221. self.l_mesh = []
  222. self.d_meshPos = {} #creating this pretty much just for follicle mode so we can attache to a specific mesh
  223. self.d_meshUV = {}
  224. self.d_meshNormals = {}
  225. self.f_meshArea = 1
  226. self.l_toSnap = cgmValid.listArg(toSnap)
  227. self.l_toAim = cgmValid.listArg(toAim)
  228. self._getUV = False
  229. self._time_start = time.clock()
  230. self._time_delayCheck = timeDelay
  231. self._l_folliclesToMake = []
  232. self._l_folliclesBuffer = []
  233. self._orientMode = orientMode
  234. self._l_toDuplicate = toDuplicate
  235. self.str_aimMode = aimMode
  236. self._f_maxDistance = maxDistance
  237. self.b_closestOnly = closestOnly
  238. self.l_created = []
  239. self.b_clampSetting = clampIntersections
  240. self.b_dragStoreMode = dragStore
  241. self.l_return = []
  242. self.l_returnRaw = []
  243. self.d_tagAndName = tagAndName
  244. self._posBuffer = False
  245. self._prevBuffer = False
  246. self.v_posOffset = posOffset or False
  247. self.str_offsetMode = offsetMode
  248. self.f_offsetDistance = offsetDistance
  249. self.f_dragInterval = dragInterval
  250. self.b_orientSnap = orientSnap
  251. self._createModeBuffer = False
  252. self.int_maxStore = maxStore
  253. self.l_toCreate = toCreate
  254. self.v_clampValues = clampValues
  255. self._int_runningTally = 0
  256. self._str_castPlane = None
  257. if toCreate:
  258. self.int_maxStore = len(toCreate)
  259. self.mAxis_aim = cgmValid.simpleAxis(objAimAxis)
  260. self.mAxis_out = cgmValid.simpleAxis(objOutAxis)
  261. self.mAxis_up = cgmValid.simpleAxis(objUpAxis)
  262. if mode in ['planeX','planeY','planeZ']:
  263. d_plane_axis = {'planeX':[1,0,0],
  264. 'planeY':[0,1,0],
  265. 'planeZ':[0,0,1]}
  266. self._str_castPlane = mc.polyPlane(n=mode, axis = d_plane_axis[mode], width = self._f_maxDistance, height = self._f_maxDistance, cuv = 1)[0]
  267. attributes.doSetAttr(self._str_castPlane,'v',False)
  268. self.addTargetMesh( self._str_castPlane )
  269. mode = 'surface'#...change for casting
  270. self.setMode(mode)
  271. self.setCreate(create)
  272. ContextualPick.__init__(self, drag = True, space = 'screen',projection = 'viewPlane', *a,**kws )
  273. if mesh is None and not self._str_castPlane:
  274. log.debug("|clickMesh| >> Using all visible mesh!")
  275. for l in mc.ls(type='mesh',visible = True, long=True), mc.ls(type='nurbsSurface',long=True, visible = True):
  276. for o in l:
  277. self.addTargetMesh( o )#
  278. if mesh:
  279. assert type(mesh) is list,"Mesh call must be in list form when called"
  280. for m in mesh:
  281. self.addTargetMesh(m)
  282. #self.updateMeshArea()
  283. def updateMeshArea(self):
  284. """
  285. Updates the mesh area of the added meshes (for sizing purposes)
  286. """
  287. self.f_meshArea = 1
  288. buffer = []
  289. for m in self.l_mesh:
  290. buffer.append(distance.returnObjectSize(m))
  291. if buffer:
  292. self.f_meshArea = sum(buffer)/len(buffer)
  293. def add_toDuplicate(self,objs):
  294. _str_func = 'add_toDuplicate'
  295. for obj in objs:
  296. _transform = SEARCH.get_transform(obj)
  297. if _transform and _transform not in self._l_toDuplicate:
  298. log.info("|{0}| >> Added to toDuplicate: {1}".format(_str_func,obj))
  299. self._l_toDuplicate.append(obj)
  300. def addTargetMesh(self,mesh):
  301. """
  302. Add target mesh to the tool
  303. """
  304. if not mc.objExists(mesh):
  305. log.warning("'%s' doesn't exist. Ignoring..."%mesh)
  306. return False
  307. _mesh = SHAPE.get_nonintermediate(mesh)
  308. if _mesh not in self.l_mesh:
  309. buffer = cgmValid.get_mayaType(_mesh)
  310. if buffer in ['mesh','nurbsSurface']:
  311. self.l_mesh.append( _mesh )#...this accounts for deformed mesh
  312. #self.updateMeshArea()
  313. return True
  314. else:
  315. log.warning("'%s' is not a mesh. It is returning as '%s'"%(_mesh,buffer))
  316. return False
  317. return False
  318. def removeTargetMesh(self,mesh):
  319. """
  320. Remove target mesh to the tool
  321. """
  322. if mesh in self.l_mesh:
  323. self.l_mesh.remove(mesh)
  324. log.info("'%s' removed from '%s'"%(mesh,self.name))
  325. def setMode(self,mode):
  326. """
  327. Set tool mode
  328. """
  329. #assert mode in ['surface','intersections','midPoint'],"'%s' not a defined mode"%mode
  330. self.mode = cgmValid.kw_fromList(mode, ['surface','intersections','midPoint','far'],indexCallable=True)
  331. def setClamp(self,setting):
  332. """
  333. Set clamp setting
  334. """
  335. assert type(setting) is int or setting is False,"'%s' is not a valid setting"%setting
  336. self.b_clampSetting = setting
  337. def setCreate(self,create):
  338. """
  339. Set tool mode
  340. """
  341. #assert create in self._createModes,"'%s' is not a valid create mode"%create
  342. #if create == 'vectorLine' and self.mode == 'midPoint':
  343. #raise ValueError,"|setCreate| >> May not have midPoint mode with vectorLine creation"
  344. log.debug("|setCreate| >> create mode {0}".format(create))
  345. self._createMode = create
  346. if self._createMode == 'follicle':#Only get uv intersection for follicles
  347. self._getUV = True
  348. def setDragStoreMode(self,mode):
  349. """
  350. Set drag update mode
  351. """
  352. assert bool(mode) in [True,False],"'%s' should be a bool"%mode
  353. self.b_dragStoreMode = mode
  354. log.warning("Drag store is %s!"%mode)
  355. def reset(self):
  356. """
  357. Reset data
  358. """
  359. self.l_created = []
  360. self.l_return = []
  361. log.debug("'%s' reset."%self.name)
  362. def setTool(self):
  363. ContextualPick.setTool(self)
  364. def dropTool(self):
  365. log.info("|clickMesh| >> tool dropped...")
  366. mc.setToolTo('selectSuperContext')
  367. mc.selectMode(object=True)
  368. mc.refresh()
  369. def press(self):
  370. """
  371. Press action. Clears buffers.
  372. """
  373. ContextualPick.press(self)
  374. self._createModeBuffer = []
  375. self.updatePos()
  376. def release_post_insert(self):pass
  377. def release_pre_insert(self):pass
  378. def finalize(self):
  379. """
  380. Press action. Clears buffers.
  381. """
  382. #Clean our lists...
  383. _str_funcName = 'finalize'
  384. self.l_created = lists.returnListNoDuplicates(self.l_created)
  385. self.l_return = lists.returnListNoDuplicates(self.l_return)
  386. if self._createMode in ['curve','jointChain','group','follicle'] and self.l_return:
  387. if self._createMode == 'group':
  388. bufferList = []
  389. for i,o in enumerate(self.l_created):
  390. buffer = rigging.groupMeObject(o,False)
  391. bufferList.append(buffer)
  392. try:mc.delete(o)
  393. except:pass
  394. self.l_created = bufferList
  395. elif self._createMode =='follicle':
  396. #...Get our follicle uvs here.
  397. log.debug("|{0}| follicle uv search...".format(_str_funcName))
  398. for pos in self.l_returnRaw:
  399. log.debug("|{0}|...pos {1}".format(_str_funcName,pos))
  400. for i,m in enumerate(self.d_meshPos.keys()):
  401. log.debug("|{0}|...mesh: {1}".format(_str_funcName,m))
  402. for i2,h in enumerate(self.d_meshPos[m]):
  403. if h == pos:
  404. log.debug("Found follicle match!")
  405. try:
  406. _set = [m, self.d_meshUV[m][i2], "{0}_u{1}s_v{2}".format(NAMES.get_short(m),"{0:.4f}".format(self.d_meshUV[m][i2][0]),"{0:.4f}".format(self.d_meshUV[m][i2][1]))]
  407. self._l_folliclesToMake.append(_set)
  408. log.debug("|{0}|...uv {1}".format(_str_funcName,_set))
  409. except Exception,err:
  410. log.error("|{0}| >> Failed to query uv for hit {2} on shape {2} | err:{1}".format(_str_funcName,err,pos,m))
  411. if self._l_folliclesToMake:
  412. for f_dat in self._l_folliclesToMake:
  413. _follicle = NODES.add_follicle(f_dat[0],f_dat[2])
  414. log.info("|finalize| >> Follicle created: {0}".format(_follicle))
  415. attributes.doSetAttr(_follicle[0],'parameterU',f_dat[1][0])
  416. attributes.doSetAttr(_follicle[0],'parameterV',f_dat[1][1])
  417. """bufferList = []
  418. for o in self.l_created:
  419. mesh = attributes.doGetAttr(o,'cgmHitTarget')
  420. if mc.objExists(mesh):
  421. uv = distance.returnClosestUVToPos(mesh,distance.returnWorldSpacePosition(o))
  422. log.info("uv: {0}".format(uv))
  423. follicle = nodes.createFollicleOnMesh(mesh)
  424. log.info("follicle: {0}".format(follicle))
  425. attributes.doSetAttr(follicle[0],'parameterU',uv[0])
  426. attributes.doSetAttr(follicle[0],'parameterV',uv[1])
  427. try:mc.delete(o)
  428. except:pass """
  429. try:mc.delete(self.l_created)
  430. except:pass
  431. elif self._createMode == 'jointChain':
  432. mc.ls(sl=1)
  433. ml_joints = []
  434. for i,o in enumerate(self.l_created):
  435. mi_jnt = r9Meta.MetaClass(mc.joint(n="cast_{0}_jnt".format(i)))
  436. ml_joints.append(mi_jnt)
  437. #Position
  438. POS.set(mi_jnt.mNode, POS.get(o))
  439. #Get our orientation data
  440. _mi_loc = r9Meta.MetaClass(self.l_created[i])
  441. if self.mode not in ['midPoint']:
  442. _d = _mi_loc.cgmLocDat
  443. _m_normal = _d['normal']
  444. _m = ATTR.get_message(_mi_loc.mNode,'meshTarget')[0]
  445. _aim = MATHUTILS.Vector3(self.mAxis_aim.p_vector[0], self.mAxis_aim.p_vector[1], self.mAxis_aim.p_vector[2])
  446. _up = MATHUTILS.Vector3(self.mAxis_up.p_vector[0], self.mAxis_up.p_vector[1], self.mAxis_up.p_vector[2])
  447. _right = _aim.cross(_up)
  448. if o != self.l_created[-1]:
  449. _constraint = mc.aimConstraint(self.l_created[i+1],mi_jnt.mNode,
  450. aimVector=self.mAxis_aim.p_vector,
  451. upVector=[_right.x, _right.y, _right.z],
  452. worldUpType = 3,#)
  453. worldUpVector = _m_normal)
  454. else:
  455. _constraint = mc.aimConstraint(self.l_created[-2],mi_jnt.mNode,
  456. aimVector=self.mAxis_aim.inverse.p_vector,
  457. upVector=[_right.x, _right.y, _right.z],
  458. worldUpType = 3, #)
  459. worldUpVector = _m_normal)
  460. else:#...midPoint aim setup...
  461. if o != self.l_created[-1]:
  462. _constraint = mc.aimConstraint(self.l_created[i+1],mi_jnt.mNode,
  463. maintainOffset = False,
  464. aimVector = self.mAxis_aim.p_vector,
  465. upVector = self.mAxis_up.p_vector,
  466. worldUpType = 'scene')
  467. else:
  468. _constraint = mc.aimConstraint(self.l_created[-2],mi_jnt.mNode,
  469. maintainOffset = False,
  470. aimVector = self.mAxis_aim.inverse.p_vector,
  471. upVector = self.mAxis_up.p_vector,
  472. worldUpType = 'scene')
  473. mc.delete(_constraint)
  474. if ml_joints:#parent to the last
  475. TRANS.parent_set(mi_jnt.mNode, ml_joints[-1].mNode)
  476. #mi_jnt.parent = ml_joints[-1]
  477. log.info("|finalize| >> Created: {0}".format(mi_jnt))
  478. mc.delete(self.l_created)
  479. else:
  480. for o in self.l_created:
  481. try:mc.delete(o)
  482. except:pass
  483. if self._createMode == 'curve' and len(self.l_return)>1:
  484. if len(self.l_return) > 1:
  485. #self.l_created = [curves.curveFromPosList(self.l_return)]
  486. #self.l_created = [ mc.curve (d=3, ep = self.l_return , ws=True, )]
  487. knot_len = len(self.l_return)+3-1
  488. curveBuffer = mc.curve (d=3, ep = self.l_return, k = [i for i in range(0,knot_len)], os=True)
  489. self.l_created = [curveBuffer]
  490. else:
  491. log.warning("Need at least 2 points for a curve")
  492. if self._orientMode == 'normal' and self._createMode not in ['vectorLine','curve']:
  493. for o in nameBuffer:
  494. if _m:
  495. if self._createMode == 'joint':
  496. constBuffer = mc.normalConstraint(_m,o,
  497. aimVector=[0,0,1],
  498. upVector=[0,1,0],
  499. #worldUpVector = _m_normal)
  500. worldUpVector = _m_normal)
  501. else:
  502. constBuffer = mc.normalConstraint(_m,o,
  503. aimVector=[0,0,1],
  504. upVector=[0,1,0],
  505. worldUpVector = _m_normal)
  506. #worldUpType = 'scene')
  507. log.debug( self.l_created)
  508. if self.d_tagAndName:
  509. for o in self.l_created:
  510. try:
  511. #i_o = cgmMeta.cgmNode(o)
  512. for tag in self.d_tagAndName.keys():
  513. ATTR.store_info(o, tag, self.d_tagAndName[tag])
  514. #i_o.doStore(tag,self.d_tagAndName[tag])
  515. #i_o.doName()
  516. except Exception,error:
  517. log.error(">>> clickMesh >> Failed to tag and name: %s | error: %s"%(i_o.p_nameShort,error))
  518. if self._str_castPlane:
  519. mc.delete(self._str_castPlane)
  520. self.reset()
  521. def release(self):
  522. """
  523. Store current data to return buffers
  524. """
  525. _str_funcName = 'release'
  526. self.release_pre_insert()
  527. self.l_created = lists.returnListNoDuplicates(self.l_created)
  528. #Only store return values on release
  529. if not self.b_dragStoreMode:#If not on drag, do it here. Otherwise do it on update
  530. if self._posBuffer:
  531. self.l_return.extend(self._posBuffer)
  532. if self._posBufferRaw:
  533. self.l_returnRaw.extend(self._posBufferRaw)
  534. else:
  535. self.l_returnRaw.extend(self._posBuffer)
  536. if self._createModeBuffer:
  537. self.l_created.extend(self._createModeBuffer)
  538. if self._posBuffer:
  539. log.debug("Position data : %s "%(self.l_return))
  540. if self._createModeBuffer:
  541. log.debug("Created : %s "%(self._createModeBuffer))
  542. mc.select(cl=True)
  543. log.debug("|{0}| >> create mode: {1}".format(_str_funcName,self._createMode))
  544. #if 'joint' in self._createMode:
  545. #jntUtils.metaFreezeJointOrientation(self._createModeBuffer)
  546. l_cull = []
  547. for o in self.l_created:
  548. if not mc.objExists(o):
  549. log.debug("|{0}| >> missing created: {1}".format(_str_funcName,o))
  550. else:
  551. l_cull.append(o)
  552. self.l_created = l_cull
  553. if self._createMode == 'data' and self.l_created:
  554. log.debug("Data!")
  555. for i,o in enumerate(self.l_created):
  556. try:_mi_loc = r9Meta.MetaClass(o)
  557. except:continue
  558. _dBuffer = _mi_loc.cgmLocDat
  559. _m_normal = _dBuffer['normal']
  560. _d = {'startPoint':self.clickPos,
  561. 'hit':POS.get(o,pivot='rp',space='w'),
  562. 'normal':_m_normal,
  563. 'vector':self.clickVector,
  564. 'meshHit':_dBuffer['shape'],
  565. 'uv':_dBuffer['uv']}
  566. cgmGen.log_info_dict(_d,"Cast {0} | Hit {1} Data".format(self._int_runningTally, i))
  567. try:_mi_loc.delete()
  568. except:pass
  569. self.l_created = []
  570. if self.l_created:
  571. if self._l_toDuplicate:
  572. for o in self.l_created:
  573. self.addTargetMesh( o )#...append our new geo to make it updatable
  574. elif self.l_toSnap:
  575. log.debug("Snap Mode!")
  576. """
  577. #TESTING OFFSET
  578. _dist = DIST.get_distance_between_points(p,self.clickPos)
  579. log.info("BaseDistance: {0}".format(_dist))
  580. _dist = _dist - 1
  581. _newP = DIST.get_pos_by_vec_dist(self.clickPos,self.clickVector,_dist)
  582. log.info("Old point: {0} | new point: {1}".format(p,_newP))
  583. self._posBuffer[i] = _newP"""
  584. """#_pos_base = POS.get(self.l_created[-1],pivot='rp',space='w')
  585. _pos_base = self._posBuffer[-1]
  586. #Find our mesh...
  587. _base_idex = self._posBuffer.index(_pos_base)
  588. _rawPos = self._posBufferRaw[_base_idex]
  589. _m = False
  590. _m_hit_idx = None
  591. if _rawPos:
  592. for i,m in enumerate(self.d_meshPos.keys()):
  593. log.debug("|{0}|...mesh: {1}".format(_str_funcName,m))
  594. for i2,h in enumerate(self.d_meshPos[m]):
  595. if h == _rawPos:
  596. log.debug("Found mesh match!")
  597. _m = m
  598. _m_hit_idx = self.d_meshPos[_m].index(h)
  599. _m_normal = self.d_meshNormals[_m][_m_hit_idx]
  600. log.info("|{0}| >> mesh normal: {1}".format(_str_funcName,_m_normal))
  601. break """
  602. _mi_loc = r9Meta.MetaClass(self.l_created[-1])
  603. if self.mode not in ['midPoint']:
  604. _d = _mi_loc.cgmLocDat
  605. _m_normal = _d['normal']
  606. _m = ATTR.get_message(_mi_loc.mNode,'meshTarget')[0]
  607. _pos_base = POS.get(self.l_created[-1],pivot='rp',space='w')
  608. _pos = _pos_base
  609. #Use that distance to subtract along our original ray's hit distance to get our new point
  610. for o in self.l_toSnap:
  611. if self.str_offsetMode == 'snapCast' and self.mode not in ['midPoint']:
  612. try:
  613. log.debug("snapCast: {0}".format(o))
  614. _pos_obj = POS.get(o,pivot='rp',space='w')#...Get the point of the object to snap
  615. log.debug("startPoint: {0}".format(_pos_obj))
  616. log.debug("posBuffer: {0}".format(_pos_base))
  617. _vec_obj = MATHUTILS.get_vector_of_two_points( _pos_obj,_pos_base)#...Get the vector from there to our hit
  618. _dist_base = DIST.get_distance_between_points(_pos_base, _pos_obj)#...get our base distance
  619. _cast = RayCast.cast(self.l_mesh, startPoint=_pos_obj,vector=_vec_obj)
  620. _nearHit = _cast['near']
  621. _dist_firstHit = DIST.get_distance_between_points(_pos_obj,_nearHit)
  622. log.debug("baseDist: {0}".format(_dist_base))
  623. log.debug("firstHit: {0}".format(_dist_firstHit))
  624. if not _m_normal:
  625. if self.mode == 'far':
  626. _dist_new = _dist_base + _dist_firstHit
  627. else:
  628. _dist_new = _dist_base - _dist_firstHit
  629. _offsetPos = DIST.get_pos_by_vec_dist(_pos_obj,_vec_obj,(_dist_new))
  630. else:
  631. log.debug("|{0}| >> mesh normal offset!".format(_str_funcName))
  632. _offsetPos = DIST.get_pos_by_vec_dist(_pos_base,_m_normal,(_dist_firstHit))
  633. _pos = _offsetPos
  634. #Failsafe for casting to self...
  635. if DIST.get_distance_between_points(_pos, _pos_obj) < _dist_firstHit:
  636. log.warning("Close proximity cast, using default")
  637. _pos = self._posBuffer[-1]
  638. except Exception,err:
  639. _pos = _pos_base
  640. log.error("SnapCast fail. Using original pos... | err: {0}".format(err))
  641. try:
  642. POS.set(o,_pos)
  643. if self._orientMode == 'normal':
  644. mc.xform(o, ro= mc.xform(self.l_created[-1],q=True,ro=True))
  645. except Exception,err:
  646. log.error("{0} failed to snap. err: {1}".format(o,err))
  647. mc.delete(self.l_created)
  648. if self.l_toSnap:
  649. mc.select(self.l_toSnap)
  650. self.dropTool()
  651. elif self.l_toAim:
  652. mc.delete(self.l_created)
  653. mc.select(self.l_toAim)
  654. self.dropTool()
  655. self._int_runningTally+=1
  656. if self.int_maxStore and len(self.l_return) == self.int_maxStore:
  657. log.debug("Max hit, finalizing")
  658. log.info("|{0}| >> created: {1}".format(_str_funcName,self.l_created))
  659. self.dropTool()
  660. self.release_post_insert()
  661. def drag(self):
  662. """
  663. update positions
  664. """
  665. ContextualPick.drag(self)
  666. self.updatePos()
  667. #print len(self._createModeBuffer)
  668. #self._int_runningTally+=1
  669. if self._createModeBuffer:
  670. self.l_created.extend(self._createModeBuffer)
  671. def getDistanceToCheck(self,m):
  672. assert mc.objExists(m), "'%s' doesn't exist. Couldn't check distance!"%m
  673. baseDistance = distance.returnDistanceBetweenPoints(self.clickPos, distance.returnWorldSpacePosition(m))
  674. baseSize = distance.returnBoundingBoxSize(m)
  675. return distance.returnWorldSpaceFromMayaSpace( baseDistance + sum(baseSize) )
  676. def updatePos(self):
  677. """
  678. Get updated position data via shooting rays
  679. """
  680. _str_funcName = 'clickMesh.updatePos'
  681. #log.debug(">>> %s >> "%_str_funcName + "="*75)
  682. if not self.l_mesh:
  683. return log.warning("No mesh objects have been added to '%s'"%(self.name))
  684. #_time = "%0.3f" %(time.clock() - self._time_start)
  685. if self._time_delayCheck is not None:
  686. _time = float( "%0.3f" %(time.clock() - self._time_start) )
  687. #log.info("time {0} | delay: {1}".format(_time,self._time_delayCheck))
  688. if _time <= self._time_delayCheck:
  689. log.warning("Time delay, not starting...")
  690. if self.l_created:
  691. mc.delete(self.l_created)
  692. return
  693. # = MATHUTILS.get_screenspace_value_from_api_space([int(self.x),int(self.y)])
  694. buffer = screenToWorld(int(self.x),int(self.y))#get world point and vector!
  695. #buffer = [int(self.x),int(self.y)]
  696. #self.clickPos = buffer[0] #Our world space click point
  697. self.clickPos = MATHUTILS.get_space_value( buffer[0],'mayaSpace' )
  698. self.clickVector = buffer[1] #Camera vector
  699. #self.clickVector = MATHUTILS.get_screenspace_value_from_api_space( buffer[1] )
  700. self._posBuffer = []#Clear our pos buffer
  701. self._posBufferRaw = []
  702. #checkDistance = self.getDistanceToCheck(m)
  703. #MATHUTILS.get_space_value( self.clickPos,'apiSpace' )
  704. kws = {'mesh':self.l_mesh,'startPoint':self.clickPos,'vector':self.clickVector,'maxDistance':self._f_maxDistance}
  705. if self.mode != 'surface' or not self.b_closestOnly:
  706. kws['firstHit'] = False
  707. try:
  708. #buffer = RayCast.findMeshIntersection(m, self.clickPos , self.clickVector, checkDistance)
  709. _res = RayCast.cast(**kws)
  710. except Exception,error:
  711. _res = None
  712. log.error("{0} >>> surface cast fail. More than likely, the offending face lacks uv's. Error:{1}".format(_str_funcName,error))
  713. _d_hit_mesh_queried = {}
  714. if _res:
  715. try:
  716. for i,m in enumerate(_res['meshHits'].keys()):
  717. #Buffer our data for processing on release....
  718. if self.d_meshPos.has_key(m):
  719. self.d_meshPos[m].extend(_res['meshHits'][m])
  720. else:
  721. self.d_meshPos[m] = _res['meshHits'][m]
  722. if self.d_meshNormals.has_key(m):
  723. self.d_meshNormals[m].extend(_res['meshNormals'][m])
  724. else:
  725. self.d_meshNormals[m] = _res['meshNormals'][m]
  726. _d_UVS = _res.get('uvs',{})
  727. if self.d_meshUV.has_key(m):
  728. self.d_meshUV[m].extend(_d_UVS[m])
  729. else:
  730. self.d_meshUV[m] = _d_UVS[m]
  731. #self.d_meshUV[m] = _d.get(m,[])
  732. if self.mode == 'surface':
  733. if self.b_closestOnly:
  734. self._posBuffer = [_res['near']]
  735. else:
  736. self._posBuffer = _res['hits']
  737. elif self.mode == 'far':
  738. self._posBuffer = [_res['far']]
  739. elif self.mode == 'midPoint':
  740. if len(_res['hits']) < 2:
  741. log.error("Must have two hits for midpoint mode")
  742. return
  743. _near = _res['near']
  744. _far = _res['far']
  745. self._posBuffer = [ DIST.get_average_position([_near,_far]) ]
  746. except Exception,err:
  747. cgmGen.log_info_dict(_res,'Result')
  748. log.error("|{0}| >> Processing fail. err:{1}".format(_str_funcName,err))
  749. return
  750. else:
  751. log.debug('No hits detected!')
  752. return
  753. if not self._posBuffer:
  754. log.debug('No hits detected!')
  755. return
  756. if self.b_clampSetting and self.b_clampSetting < len(self._posBuffer):
  757. log.warning("Position buffer was clamped. Check settings if this was not desired.")
  758. self._posBuffer = distance.returnPositionDataDistanceSortedList(self.startPoint,self._posBuffer)
  759. self._posBuffer = self._posBuffer[:self.b_clampSetting]
  760. #...Gonna do our offsets now
  761. self._posBufferRaw = copy.copy(self._posBuffer)
  762. if self.mode not in ['midPoint']:
  763. if self.str_offsetMode == 'distance':
  764. log.debug("|{0}| >> offset by distance".format(_str_funcName))
  765. self._posBufferRaw = copy.copy(self._posBuffer)
  766. _l = copy.copy(self._posBuffer)
  767. for i,pos in enumerate(self._posBuffer):
  768. _m_normal = False
  769. if str(pos) in _d_hit_mesh_queried.keys():
  770. log.debug("|{0}| >> Using queryied data for hit {1}".format(_str_funcName,pos))
  771. _d = _d_hit_mesh_queried[str(pos)]
  772. _m = _d['m']
  773. _m_hit_idx = _d['m_hit_idx']
  774. _m_normal = _d['m_normal']
  775. _m_uv = _d['m_uv']
  776. else:
  777. for i2,m in enumerate(self.d_meshPos.keys()):
  778. #log.debug("|{0}|...mesh: {1}".format(_str_funcName,m))
  779. for i3,h in enumerate(self.d_meshPos[m]):
  780. if h == pos:
  781. log.debug("Found mesh match!")
  782. _m = m
  783. _m_hit_idx = _res['meshHits'][_m].index(h)
  784. _m_normal = _res['meshNormals'][_m][_m_hit_idx]
  785. _m_uv = _res['uvs'][_m][_m_hit_idx]
  786. if str(pos) not in _d_hit_mesh_queried.keys():
  787. _d_hit_mesh_queried[str(pos)] = {'m':m,'m_hit_idx':_m_hit_idx,'m_normal':_m_normal,'m_uv':_m_uv}
  788. log.debug("|{0}| >> mesh normal: {1}".format(_str_funcName,_m_normal))
  789. break
  790. if not _m_normal:
  791. cgmGen.log_info_dict(self.d_meshPos,"Mesh hit dict")
  792. raise ValueError,"|{0}| >> Missing normal for hit: {1}".format(_str_funcName,pos)
  793. #_p = RayCast.offset_hits_by_distance(pos,self.clickPos,_m_normal,self.f_offsetDistance)
  794. try:_p = DIST.get_pos_by_vec_dist(pos,_m_normal,self.f_offsetDistance)
  795. except Exception,err:
  796. for item in pos,_m_normal,self.f_offsetDistance:
  797. log.debug("|{0}| >> {1}".format(_str_funcName,item))
  798. raise Exception,"|{0}| >> offset fail!".format(_str_funcName)
  799. #_p = RayCast.offset_hit_by_distance(pos,self.clickPos,self.clickVector,-self.f_offsetDistance)
  800. self._posBuffer[i] = _p
  801. if self.b_dragStoreMode:#If not on drag, do it here. Otherwise do it on update
  802. if self._posBuffer:
  803. if self._prevBuffer:
  804. for pos in self._posBuffer:
  805. for prev_pos in self._prevBuffer:
  806. # exit out if point is not within tolerance
  807. pos_vector = MATHUTILS.Vector3(pos[0], pos[1], pos[2])
  808. prev_pos_vector = MATHUTILS.Vector3( prev_pos[0], prev_pos[1], prev_pos[2] )
  809. mag = (prev_pos_vector - pos_vector).magnitude()
  810. if mag < self.f_dragInterval:
  811. return
  812. self._prevBuffer = copy.copy(self._posBuffer)
  813. self.l_return.extend(self._posBuffer)
  814. if self._posBufferRaw:
  815. self.l_returnRaw.extend(self._posBufferRaw)
  816. else:
  817. self.l_returnRaw.extend(self._posBuffer)
  818. self._int_runningTally+=1
  819. #>>> Make our stuff ====================================================================================
  820. if self._posBuffer: # Make our stuff
  821. mc.select(cl=1)
  822. # if self._createMode and
  823. #Delete the old stuff
  824. if self._createModeBuffer and not self.b_dragStoreMode:
  825. for o in self._createModeBuffer:
  826. try:mc.delete(o)
  827. except:pass
  828. self._createModeBuffer = []
  829. for i,pos in enumerate(self._posBuffer):
  830. if self.mode not in ['midPoint']:
  831. for i2,v in enumerate(self.v_clampValues):
  832. if v is not None:
  833. pos[i2] = v
  834. #Find our mesh...
  835. _rawPos = self._posBufferRaw[i]
  836. _m = False
  837. _m_hit_idx = None
  838. _m_normal = False
  839. _m_uv = False
  840. if _rawPos:
  841. if str(_rawPos) in _d_hit_mesh_queried.keys():
  842. log.debug("|{0}| >> Using queryied data for hit {1}".format(_str_funcName,_rawPos))
  843. _d = _d_hit_mesh_queried[str(_rawPos)]
  844. _m = _d['m']
  845. _m_hit_idx = _d['m_hit_idx']
  846. _m_normal = _d['m_normal']
  847. _m_uv = _d['m_uv']
  848. else:
  849. for i2,m in enumerate(self.d_meshPos.keys()):
  850. #log.debug("|{0}|...mesh: {1}".format(_str_funcName,m))
  851. for i3,h in enumerate(self.d_meshPos[m]):
  852. if h == _rawPos:
  853. log.debug("Found mesh match!")
  854. _m = m
  855. _m_hit_idx = _res['meshHits'][_m].index(h)
  856. _m_normal = _res['meshNormals'][_m][_m_hit_idx]
  857. _m_uv = _res['uvs'][_m][_m_hit_idx]
  858. log.debug("|{0}| >> mesh normal: {1}".format(_str_funcName,_m_normal))
  859. break
  860. else:
  861. log.debug("no raw pos match")
  862. _jsonDict = {'hitIndex':_m_hit_idx,"normal":_m_normal,"uv":_m_uv,"shape":NAMES.get_base(_m)}
  863. if self.str_offsetMode == 'distance':
  864. _jsonDict['offsetDist'] = self.f_offsetDistance
  865. #Let's make our stuff
  866. #baseScale = distance.returnMayaSpaceFromWorldSpace(10)
  867. if self._l_toDuplicate:
  868. nameBuffer = []
  869. for o in self._l_toDuplicate:
  870. _dup = mc.duplicate(o)[0]
  871. _dup = mc.rename(_dup,"cast_{1}_hit_{2}_{0}_DUPLICATE".format(NAMES.get_base(o),self._int_runningTally,i))
  872. _pos = pos
  873. _oType = cgmValid.get_mayaType(o)
  874. if self.str_offsetMode == 'snapCast':
  875. try:
  876. log.debug("snapCast: {0}".format(_dup))
  877. _pos_base = _pos
  878. _pos_obj = POS.get(_dup,pivot='rp',space='w')#...Get the point of the object to snap
  879. log.debug("startPoint: {0}".format(_pos_obj))
  880. log.debug("posBuffer: {0}".format(_pos_base))
  881. _dist_base = DIST.get_distance_between_points(_pos_base, _pos_obj)#...get our base distance
  882. #_vec_obj = MATHUTILS.get_vector_of_two_points( _pos_obj,_pos_base)#...Get the vector from there to our hit
  883. #_cast = RayCast.cast(self.l_mesh, startPoint=_pos_obj,vector=_vec_obj)
  884. _cast = RayCast.cast(self.l_mesh, startPoint=_pos_obj,vector=self.mAxis_up.inverse.p_vector)
  885. _nearHit = _cast['near']
  886. _dist_firstHit = DIST.get_distance_between_points(_pos_obj,_nearHit)
  887. log.debug("baseDist: {0}".format(_dist_base))
  888. log.debug("firstHit: {0}".format(_dist_firstHit))
  889. if not _m_normal:
  890. if self.mode == 'far':
  891. _dist_new = _dist_base + _dist_firstHit
  892. else:
  893. _dist_new = _d