b9924e25ccd64f62fa11511b8ccb8321809a3afc
[blender.git] / release / scripts / bpydata / mesh_bbrush.py
1 # SPACEHANDLER.VIEW3D.EVENT
2 # Dont run, event handelers are accessed in the from the 3d View menu.
3
4 import Blender
5 from Blender import Mathutils, Window, Scene, Draw, Mesh, NMesh
6 from Blender.Mathutils import CrossVecs, Matrix, Vector, Intersect, LineIntersect
7
8
9 # DESCRIPTION:
10 # screen_x, screen_y the origin point of the pick ray
11 # it is either the mouse location
12 # localMatrix is used if you want to have the returned values in an objects localspace.
13 #    this is usefull when dealing with an objects data such as verts.
14 # or if useMid is true, the midpoint of the current 3dview
15 # returns
16 # Origin - the origin point of the pick ray
17 # Direction - the direction vector of the pick ray
18 # in global coordinates
19 epsilon = 1e-3 # just a small value to account for floating point errors
20
21 def getPickRay(screen_x, screen_y, localMatrix=None, useMid = False):
22         
23         # Constant function variables
24         p = getPickRay.p
25         d = getPickRay.d
26         
27         for win3d in Window.GetScreenInfo(Window.Types.VIEW3D): # we search all 3dwins for the one containing the point (screen_x, screen_y) (could be the mousecoords for example) 
28                 win_min_x, win_min_y, win_max_x, win_max_y = win3d['vertices']
29                 # calculate a few geometric extents for this window
30
31                 win_mid_x  = (win_max_x + win_min_x + 1.0) * 0.5
32                 win_mid_y  = (win_max_y + win_min_y + 1.0) * 0.5
33                 win_size_x = (win_max_x - win_min_x + 1.0) * 0.5
34                 win_size_y = (win_max_y - win_min_y + 1.0) * 0.5
35
36                 #useMid is for projecting the coordinates when we subdivide the screen into bins
37                 if useMid: # == True
38                         screen_x = win_mid_x
39                         screen_y = win_mid_y
40                 
41                 # if the given screencoords (screen_x, screen_y) are within the 3dwin we fount the right one...
42                 if (win_max_x > screen_x > win_min_x) and (  win_max_y > screen_y > win_min_y):
43                         # first we handle all pending events for this window (otherwise the matrices might come out wrong)
44                         Window.QHandle(win3d['id'])
45                         
46                         # now we get a few matrices for our window...
47                         # sorry - i cannot explain here what they all do
48                         # - if you're not familiar with all those matrices take a look at an introduction to OpenGL...
49                         pm      = Window.GetPerspMatrix()   # the prespective matrix
50                         pmi  = Matrix(pm); pmi.invert() # the inverted perspective matrix
51                         
52                         if (1.0 - epsilon < pmi[3][3] < 1.0 + epsilon):
53                                 # pmi[3][3] is 1.0 if the 3dwin is in ortho-projection mode (toggled with numpad 5)
54                                 hms = getPickRay.hms
55                                 ortho_d = getPickRay.ortho_d
56                                 
57                                 # ortho mode: is a bit strange - actually there's no definite location of the camera ...
58                                 # but the camera could be displaced anywhere along the viewing direction.
59                                 
60                                 ortho_d.x, ortho_d.y, ortho_d.z = Window.GetViewVector()
61                                 ortho_d.w = 0
62                                 
63                                 # all rays are parallel in ortho mode - so the direction vector is simply the viewing direction
64                                 hms.x, hms.y, hms.z, hms.w = (screen_x-win_mid_x) /win_size_x, (screen_y-win_mid_y) / win_size_y, 0.0, 1.0
65                                 
66                                 # these are the homogenious screencoords of the point (screen_x, screen_y) ranging from -1 to +1
67                                 p=(hms*pmi) + (1000*ortho_d)
68                                 p.resize3D()
69                                 d.x, d.y, d.z = ortho_d.x, ortho_d.y, ortho_d.z
70                                 
71
72                         # Finally we shift the position infinitely far away in
73                         # the viewing direction to make sure the camera if outside the scene
74                         # (this is actually a hack because this function
75                         # is used in sculpt_mesh to initialize backface culling...)
76                         else:
77                                 # PERSPECTIVE MODE: here everything is well defined - all rays converge at the camera's location
78                                 vmi  = Matrix(Window.GetViewMatrix()); vmi.invert() # the inverse viewing matrix
79                                 fp = getPickRay.fp
80                                 
81                                 dx = pm[3][3] * (((screen_x-win_min_x)/win_size_x)-1.0) - pm[3][0]
82                                 dy = pm[3][3] * (((screen_y-win_min_y)/win_size_y)-1.0) - pm[3][1]
83                                 
84                                 fp.x, fp.y, fp.z = \
85                                 pmi[0][0]*dx+pmi[1][0]*dy,\
86                                 pmi[0][1]*dx+pmi[1][1]*dy,\
87                                 pmi[0][2]*dx+pmi[1][2]*dy
88                                 
89                                 # fp is a global 3dpoint obtained from "unprojecting" the screenspace-point (screen_x, screen_y)
90                                 #- figuring out how to calculate this took me quite some time.
91                                 # The calculation of dxy and fp are simplified versions of my original code
92                                 #- so it's almost impossible to explain what's going on geometrically... sorry
93                                 
94                                 p.x, p.y, p.z = vmi[3][:3]
95                                 
96                                 # the camera's location in global 3dcoords can be read directly from the inverted viewmatrix
97                                 #d.x, d.y, d.z =normalize_v3(sub_v3v3(p, fp))
98                                 d.x, d.y, d.z = p.x-fp.x, p.y-fp.y, p.z-fp.z
99                                 
100                                 #print 'd', d, 'p', p, 'fp', fp
101                                 
102                         
103                         # the direction vector is simply the difference vector from the virtual camera's position
104                         #to the unprojected (screenspace) point fp
105                         
106                         # Do we want to return a direction in object's localspace?
107                         
108                         if localMatrix:
109                                 localInvMatrix = Matrix(localMatrix)
110                                 localInvMatrix.invert()
111                                 p = p*localInvMatrix
112                                 d = d*localInvMatrix # normalize_v3
113                                 p.x += localInvMatrix[3][0]
114                                 p.y += localInvMatrix[3][1]
115                                 p.z += localInvMatrix[3][2]
116                                 
117                         #else: # Worldspace, do nothing
118                         
119                         d.normalize()
120                         return True, p, d # Origin, Direction   
121         
122         # Mouse is not in any view, return None.
123         return False, None, None
124
125 # Constant function variables
126 getPickRay.d = Vector(0,0,0) # Perspective, 3d
127 getPickRay.p = Vector(0,0,0)
128 getPickRay.fp = Vector(0,0,0)
129
130 getPickRay.hms = Vector(0,0,0,0) # ortho only 4d
131 getPickRay.ortho_d = Vector(0,0,0,0) # ortho only 4d
132
133
134
135 def ui_set_preferences(user_interface=1):
136         
137         # Create data and set defaults.
138         ADAPTIVE_GEOMETRY_but = Draw.Create(1)
139         BRUSH_MODE_but = Draw.Create(1)
140         BRUSH_PRESSURE_but = Draw.Create(0.05)
141         BRUSH_RADIUS_but = Draw.Create(0.25)
142         RESOLUTION_MIN_but = Draw.Create(0.1)
143         DISPLACE_NORMAL_MODE_but = Draw.Create(2)
144         STATIC_NORMAL_but = Draw.Create(1)
145         XPLANE_CLIP_but = Draw.Create(0)
146         STATIC_MESH_but = Draw.Create(1)
147         FIX_TOPOLOGY_but = Draw.Create(0)
148         
149         # Remember old variables if alredy set.
150         try:
151                 ADAPTIVE_GEOMETRY_but.val = Blender.bbrush['ADAPTIVE_GEOMETRY']
152                 BRUSH_MODE_but.val = Blender.bbrush['BRUSH_MODE']
153                 BRUSH_PRESSURE_but.val = Blender.bbrush['BRUSH_PRESSURE']
154                 BRUSH_RADIUS_but.val = Blender.bbrush['BRUSH_RADIUS']
155                 RESOLUTION_MIN_but.val = Blender.bbrush['RESOLUTION_MIN']
156                 DISPLACE_NORMAL_MODE_but.val = Blender.bbrush['DISPLACE_NORMAL_MODE']
157                 STATIC_NORMAL_but.val = Blender.bbrush['STATIC_NORMAL']
158                 XPLANE_CLIP_but.val = Blender.bbrush['XPLANE_CLIP']
159                 STATIC_MESH_but.val = Blender.bbrush['STATIC_MESH']
160                 FIX_TOPOLOGY_but.val = Blender.bbrush['FIX_TOPOLOGY']
161         except:
162                 Blender.bbrush = {}
163         
164         if user_interface:
165                 pup_block = [\
166                 'Brush Options',\
167                 ('Adaptive Geometry', ADAPTIVE_GEOMETRY_but, 'Add and remove detail as needed. Uses min/max resolution.'),\
168                 ('Brush Type: ', BRUSH_MODE_but, 1, 5, 'Push/Pull:1, Grow/Shrink:2, Spin:3, Relax:4, Goo:5'),\
169                 ('Pressure: ', BRUSH_PRESSURE_but, 0.0, 1.0, 'Pressure of the brush.'),\
170                 ('Size: ', BRUSH_RADIUS_but, 0.0, 2.0, 'Size of the brush.'),\
171                 ('Geometry Res: ', RESOLUTION_MIN_but, 0.01, 0.5, 'Size of the brush & Adaptive Subdivision.'),\
172                 ('Displace Vector: ', DISPLACE_NORMAL_MODE_but, 1, 4, 'Vertex Normal:1, Median Normal:2, Face Normal:3, View Normal:4'),\
173                 ('Static Normal', STATIC_NORMAL_but, 'Use the initial normal only.'),\
174                 ('No X Crossing', XPLANE_CLIP_but, 'Dont allow verts to have a negative X axis (use for x-mirror).'),\
175                 ('Static Mesh', STATIC_MESH_but, 'During mouse interaction, dont update the mesh.'),\
176                 #('Fix Topology', FIX_TOPOLOGY_but, 'Fix the mesh structure by rotating edges '),\
177                 ]
178                 
179                 Draw.PupBlock('BlenBrush Prefs (RMB)', pup_block)
180         
181         Blender.bbrush['ADAPTIVE_GEOMETRY'] = ADAPTIVE_GEOMETRY_but.val
182         Blender.bbrush['BRUSH_MODE'] = BRUSH_MODE_but.val
183         Blender.bbrush['BRUSH_PRESSURE'] = BRUSH_PRESSURE_but.val
184         Blender.bbrush['BRUSH_RADIUS'] = BRUSH_RADIUS_but.val
185         Blender.bbrush['RESOLUTION_MIN'] = RESOLUTION_MIN_but.val
186         Blender.bbrush['DISPLACE_NORMAL_MODE'] = DISPLACE_NORMAL_MODE_but.val
187         Blender.bbrush['STATIC_NORMAL'] = STATIC_NORMAL_but.val
188         Blender.bbrush['XPLANE_CLIP'] = XPLANE_CLIP_but.val
189         Blender.bbrush['STATIC_MESH'] = STATIC_MESH_but.val
190         Blender.bbrush['FIX_TOPOLOGY'] = FIX_TOPOLOGY_but.val
191
192
193 def triangulateNMesh(nm):
194         '''
195         Converts the meshes faces to tris, modifies the mesh in place.
196         '''
197         
198         #============================================================================#
199         # Returns a new face that has the same properties as the origional face      #
200         # but with no verts                                                       #
201         #============================================================================#
202         def copyFace(face):
203                 newFace = NMesh.Face()
204                 # Copy some generic properties
205                 newFace.mode = face.mode
206                 if face.image != None:
207                         newFace.image = face.image
208                 newFace.flag = face.flag
209                 newFace.mat = face.mat
210                 newFace.smooth = face.smooth
211                 return newFace
212         
213         # 2 List comprehensions are a lot faster then 1 for loop.
214         tris = [f for f in nm.faces if len(f) == 3]
215         quads = [f for f in nm.faces if len(f) == 4]
216         
217         
218         if quads: # Mesh may have no quads.
219                 has_uv = quads[0].uv 
220                 has_vcol = quads[0].col
221                 for quadFace in quads:
222                         # Triangulate along the shortest edge
223                         #if (quadFace.v[0].co - quadFace.v[2].co).length < (quadFace.v[1].co - quadFace.v[3].co).length:
224                         a1 = Mathutils.TriangleArea(quadFace.v[0].co, quadFace.v[1].co, quadFace.v[2].co)
225                         a2 = Mathutils.TriangleArea(quadFace.v[0].co, quadFace.v[2].co, quadFace.v[3].co)
226                         b1 = Mathutils.TriangleArea(quadFace.v[1].co, quadFace.v[2].co, quadFace.v[3].co)
227                         b2 = Mathutils.TriangleArea(quadFace.v[1].co, quadFace.v[3].co, quadFace.v[0].co)
228                         a1,a2 = min(a1, a2), max(a1, a2)
229                         b1,b2 = min(b1, b2), max(b1, b2)
230                         if a1/a2 < b1/b2:
231                                 
232                                 # Method 1
233                                 triA = 0,1,2
234                                 triB = 0,2,3
235                         else:
236                                 # Method 2
237                                 triA = 0,1,3
238                                 triB = 1,2,3
239                                 
240                         for tri1, tri2, tri3 in (triA, triB):
241                                 newFace = copyFace(quadFace)
242                                 newFace.v = [quadFace.v[tri1], quadFace.v[tri2], quadFace.v[tri3]]
243                                 if has_uv: newFace.uv = [quadFace.uv[tri1], quadFace.uv[tri2], quadFace.uv[tri3]]
244                                 if has_vcol: newFace.col = [quadFace.col[tri1], quadFace.col[tri2], quadFace.col[tri3]]
245                                 
246                                 nm.addEdge(quadFace.v[tri1], quadFace.v[tri3]) # Add an edge where the 2 tris are devided.
247                                 tris.append(newFace)
248                 
249                 nm.faces = tris
250
251 import mesh_tri2quad
252 def fix_topolagy(mesh):
253         ob = Scene.GetCurrent().getActiveObject()
254         
255         for f in mesh.faces:
256                 f.sel = 1
257         mesh.quadToTriangle(0) 
258         nmesh = ob.getData()
259
260         mesh_tri2quad.tri2quad(nmesh, 100, 0)
261         triangulateNMesh(nmesh)
262         nmesh.update()
263         
264         mesh = Mesh.Get(mesh.name)
265         for f in mesh.faces:
266                 f.sel=1 
267         mesh.quadToTriangle()
268         Mesh.Mode(Mesh.SelectModes['EDGE'])
269         
270         
271         
272         
273
274
275 def event_main():
276         #print Blender.event
277         #mod =[Window.Qual.CTRL,  Window.Qual.ALT, Window.Qual.SHIFT]
278         mod =[Window.Qual.CTRL,  Window.Qual.ALT]
279         
280         qual = Window.GetKeyQualifiers()
281         SHIFT_FLAG = Window.Qual.SHIFT
282         CTRL_FLAG = Window.Qual.CTRL
283         
284         
285         # UNDO
286         """
287         is_editmode = Window.EditMode() # Exit Editmode.
288         if is_editmode: Window.EditMode(0)
289         if Blender.event == Draw.UKEY:
290                 if is_editmode:
291                         Blender.event = Draw.UKEY
292                         return
293                 else:
294                         winId = [win3d for win3d in Window.GetScreenInfo(Window.Types.VIEW3D)][0]
295                         Blender.event = None
296                         Window.QHandle(winId['id'])
297                         Window.EditMode(1)
298                         Window.QHandle(winId['id'])
299                         Window.QAdd(winId['id'],Draw.UKEY,1) # Change KeyPress Here for EditMode
300                         Window.QAdd(winId['id'],Draw.UKEY,0)
301                         Window.QHandle(winId['id'])
302                         Window.EditMode(0)
303                         Blender.event = None
304                         return
305         """
306         
307         ob = Scene.GetCurrent().getActiveObject()
308         if not ob or ob.getType() != 'Mesh':
309                 return
310         
311         # Mouse button down with no modifiers.  
312         if Blender.event == Draw.LEFTMOUSE and not [True for m in mod if m & qual]:
313                 # Do not exit (draw)
314                 pass
315         elif Blender.event == Draw.RIGHTMOUSE and not [True for m in mod if m & qual]:
316                 ui_set_preferences()
317                 return
318         else:
319                 return 
320                 
321         del qual
322         
323         
324         try:
325                 Blender.bbrush
326         except:
327                 # First time run
328                 ui_set_preferences() # No ui
329                 return
330
331         ADAPTIVE_GEOMETRY = Blender.bbrush['ADAPTIVE_GEOMETRY'] # 1
332         BRUSH_MODE = Blender.bbrush['BRUSH_MODE'] # 1
333         BRUSH_PRESSURE_ORIG = Blender.bbrush['BRUSH_PRESSURE'] # 0.1
334         BRUSH_RADIUS = Blender.bbrush['BRUSH_RADIUS'] # 0.5
335         RESOLUTION_MIN = Blender.bbrush['RESOLUTION_MIN'] # 0.08
336         STATIC_NORMAL = Blender.bbrush['STATIC_NORMAL'] # 0
337         XPLANE_CLIP = Blender.bbrush['XPLANE_CLIP'] # 0
338         DISPLACE_NORMAL_MODE = Blender.bbrush['DISPLACE_NORMAL_MODE'] # 'Vertex Normal%x1|Median Normal%x2|Face Normal%x3|View Normal%x4'
339         STATIC_MESH = Blender.bbrush['STATIC_MESH']
340         FIX_TOPOLOGY = Blender.bbrush['FIX_TOPOLOGY']
341         
342         
343         # Angle between Vecs wrapper.
344         AngleBetweenVecs = Mathutils.AngleBetweenVecs
345         def ang(v1,v2):
346                 try:
347                         return AngleBetweenVecs(v1,v2)
348                 except:
349                         return 180
350         """
351         def Angle2D(x1, y1, x2, y2):
352                 import math
353                 RAD2DEG = 57.295779513082323
354                 '''
355                    Return the angle between two vectors on a plane
356                    The angle is from vector 1 to vector 2, positive anticlockwise
357                    The result is between -pi -> pi
358                 '''
359                 dtheta = math.atan2(y2,x2) - math.atan2(y1,x1) # theta1 - theta2
360                 while dtheta > math.pi:
361                         dtheta -= (math.pi*2)
362                 while dtheta < -math.pi:
363                         dtheta += (math.pi*2)
364                 return dtheta * RAD2DEG  #(180.0 / math.pi)
365         """
366         
367         def faceIntersect(f):
368                 isect = Intersect(f.v[0].co, f.v[1].co, f.v[2].co, Direction, Origin, 1) # Clipped.
369                 if isect:
370                         return isect
371                 elif len(f.v) == 4:
372                         isect = Intersect(f.v[0].co, f.v[2].co, f.v[3].co, Direction, Origin, 1) # Clipped.
373                 return isect
374         """
375         # Unused so farm, too slow.
376         def removeDouble(v1,v2, me):
377                 v1List = [f for f in me.faces if v1 in f.v]
378                 v2List = [f for f in me.faces if v2 in f.v]
379                 #print v1List
380                 #print v2List
381                 remFaces = []
382                 newFaces = []
383                 for f2 in v2List:
384                         f2ls = list(f2.v)
385                         i = f2ls.index(v2)
386                         f2ls[i] = v1
387                         #remFaces.append(f2)
388                         if f2ls.count(v1) == 1:
389                                 newFaces.append(tuple(f2ls))
390                 if remFaces:
391                         me.faces.delete(1, remFaces)
392                 #me.verts.delete(v2)
393                 if newFaces:
394                         me.faces.extend(newFaces)
395         """
396         
397         
398         me = ob.getData(mesh=1)
399         
400         is_editmode = Window.EditMode() # Exit Editmode.
401         if is_editmode: Window.EditMode(0)
402         
403         Mesh.Mode(Mesh.SelectModes['EDGE'])
404         
405         # At the moment ADAPTIVE_GEOMETRY is the only thing that uses selection.
406         if ADAPTIVE_GEOMETRY:
407                 # Deslect all
408                 SEL_FLAG = Mesh.EdgeFlags['SELECT']
409                 '''
410                 for ed in me.edges:
411                         #ed.flag &= ~SEL_FLAG # deselect. 34
412                         ed.flag = 32
413                 '''
414                 filter(lambda ed: setattr(ed, 'flag', 32), me.edges)
415                 
416                 '''for v in me.verts:
417                         v.sel = 0'''
418                 filter(lambda v: setattr(v, 'sel', 0), me.verts)
419                 
420         i = 0
421         time = Blender.sys.time()
422         last_best_isect = None # used for goo only
423         old_screen_x, old_screen_y = 1<<30, 1<<30
424         goo_dir_vec = last_goo_dir_vec = gooRotMatrix = None # goo mode only.
425         
426         # Normal stuff
427         iFaceNormal = medainNormal = None
428         
429         # Store all vert normals for now.
430         if BRUSH_MODE == 1 and STATIC_NORMAL: # Push pull
431                 vert_orig_normals = dict([(v, v.no) for v in me.verts])
432         
433         elif BRUSH_MODE == 4: # RELAX, BUILD EDGE CONNECTIVITE DATA.
434                 # we need edge connectivity
435                 #vertEdgeUsers = [list() for i in xrange(len(me.verts))]
436                 verts_connected_by_edge = [list() for i in xrange(len(me.verts))]
437                 
438                 for ed in me.edges:
439                         i1, i2 = ed.v1.index,  ed.v2.index
440                         #vertEdgeUsers[i1].append(ed)
441                         #vertEdgeUsers[i2].append(ed)
442                         
443                         verts_connected_by_edge[i1].append(ed.v2)
444                         verts_connected_by_edge[i2].append(ed.v1)
445         
446         if STATIC_MESH:
447                 
448                 # Try and find a static mesh to reuse.
449                 # this is because we dont want to make a new mesh for each stroke.
450                 mesh_static = None
451                 for _me_name_ in Blender.NMesh.GetNames():
452                         _me_ = Mesh.Get(_me_name_)
453                         #print _me_.users , len(me.verts)
454                         if _me_.users == 0 and len(_me_.verts) == 0:
455                                 mesh_static = _me_
456                                 #print 'using', _me_.name
457                                 break
458                 del _me_name_
459                 del _me_
460                 
461                 if not mesh_static:
462                         mesh_static = Mesh.New()
463                         print 'Making new mesh', mesh_static.name
464                 
465                 mesh_static.verts.extend([v.co for v in me.verts])
466                 mesh_static.faces.extend([tuple([mesh_static.verts[v.index] for v in f.v]) for f in me.faces])
467         
468         
469         best_isect = gooPlane = None 
470         
471         while Window.GetMouseButtons() == 1:
472                 i+=1
473                 screen_x, screen_y = Window.GetMouseCoords()
474                 
475                 # Skip when no mouse movement, Only for Goo!
476                 if screen_x == old_screen_x and screen_y == old_screen_y:
477                         if BRUSH_MODE == 5: # Dont modify while mouse is not moved for goo.
478                                 continue
479                 else: # mouse has moved get the new mouse ray.
480                         old_screen_x, old_screen_y = screen_x, screen_y
481                         mouseInView, Origin, Direction = getPickRay(screen_x, screen_y, ob.matrixWorld)
482                         if not mouseInView or not Origin:
483                                 return
484                         Origin_SCALE = Origin * 100 
485                         
486                 # Find an intersecting face!
487                 bestLen = 1<<30 # start with an assumed realy bad match.
488                 best_isect = None # last intersect is used for goo.
489                 best_face = None
490                 
491                 if not last_best_isect:
492                         last_best_isect = best_isect
493                 
494                 if not mouseInView:
495                         last_best_isect = None  
496                         
497                 else:
498                         # Find Face intersection closest to the view. 
499                         #for f in [f for f in me.faces if ang(f.no, Direction) < 90]:
500                         
501                         # Goo brush only intersects faces once, after that the brush follows teh view plain.
502                         if BRUSH_MODE == 5 and gooPlane != None and gooPlane:
503                                 best_isect = Intersect( gooPlane[0], gooPlane[1], gooPlane[2], Direction, Origin, 0) # Non clipped
504                         else:
505                                 if STATIC_MESH:
506                                         intersectingFaces = [(f, ix) for f in mesh_static.faces for ix in (faceIntersect(f),) if ix]
507                                 else:
508                                         intersectingFaces = [(f, ix) for f in me.faces for ix in (faceIntersect(f),) if ix]
509                                 
510                                 for f, isect in intersectingFaces:
511                                         l = (Origin_SCALE-isect).length 
512                                         if l < bestLen:
513                                                 best_face = f
514                                                 best_isect = isect
515                                                 bestLen = l
516                 
517                 if not best_isect:
518                         # Dont interpolate once the mouse moves off the mesh.
519                         lastGooVec = last_best_isect = None
520                         
521                 else: # mouseInView must be true also
522                         
523                         # Use the shift key to modify the pressure.
524                         if SHIFT_FLAG & Window.GetKeyQualifiers():
525                                 BRUSH_PRESSURE = -BRUSH_PRESSURE_ORIG
526                         else:
527                                 BRUSH_PRESSURE =  BRUSH_PRESSURE_ORIG
528                         
529                         brush_verts = [(v,le) for v in me.verts for le in ((v.co-best_isect).length,) if le < BRUSH_RADIUS]
530                         
531                         # SETUP ONCE ONLY VARIABLES
532                         if STATIC_NORMAL: # Only set the normal once.
533                                 if not iFaceNormal:
534                                         iFaceNormal = best_face.no
535                         else:
536                                 if best_face:
537                                         iFaceNormal = best_face.no
538                         
539                         
540                         if DISPLACE_NORMAL_MODE == 2: # MEDIAN NORMAL
541                                 if (STATIC_NORMAL and medainNormal == None) or not STATIC_NORMAL:
542                                         medainNormal = Vector(0,0,0)
543                                         for v, l in brush_verts:
544                                                 medainNormal += v.no*(BRUSH_RADIUS-l)
545                                         medainNormal.normalize()
546                         
547                         
548                         # ================================================================#
549                         # == Tool code, loop on the verts and operate on them ============#
550                         # ================================================================#
551                         if BRUSH_MODE == 1: # NORMAL PAINT
552                                 for v,l in brush_verts:
553                                         if XPLANE_CLIP:
554                                                 origx = False
555                                                 if abs(v.co.x) < 0.001: origx = True
556                                                         
557                                         
558                                         v.sel = 1 # MARK THE VERT AS DIRTY.
559                                         falloff = (BRUSH_RADIUS-l) / BRUSH_RADIUS # falloff between 0 and 1
560                                         
561                                         if DISPLACE_NORMAL_MODE == 1: # VERTEX NORMAL
562                                                 if STATIC_NORMAL:
563                                                         try:
564                                                                 no = vert_orig_normals[v]
565                                                         except:
566                                                                 no = vert_orig_normals[v] = v.no
567                                                         v.co += (no * BRUSH_PRESSURE) * falloff
568                                                 else:
569                                                         v.co += (v.no * BRUSH_PRESSURE) * falloff
570                                         elif DISPLACE_NORMAL_MODE == 2: # MEDIAN NORMAL # FIXME
571                                                 v.co += (medainNormal * BRUSH_PRESSURE) * falloff
572                                         elif DISPLACE_NORMAL_MODE == 3: # FACE NORMAL
573                                                 v.co += (iFaceNormal * BRUSH_PRESSURE) * falloff
574                                         elif DISPLACE_NORMAL_MODE == 4: # VIEW NORMAL
575                                                 v.co += (Direction * BRUSH_PRESSURE) * falloff
576                                         
577                                         # Clamp back to original x if needs be.
578                                         if XPLANE_CLIP and origx:
579                                                 v.co.x = 0
580                         
581                         elif BRUSH_MODE == 2: # SCALE
582                                 for v,l in brush_verts:
583                                         
584                                         if XPLANE_CLIP:
585                                                 origx = False
586                                                 if abs(v.co.x) < 0.001: origx = True
587                                         
588                                         v.sel = 1 # MARK THE VERT AS DIRTY.
589                                         falloff = (BRUSH_RADIUS-l) / BRUSH_RADIUS # falloff between 0 and 1
590                                         
591                                         vert_scale_vec = v.co - best_isect
592                                         vert_scale_vec.normalize()
593                                         # falloff needs to be scaled for this tool
594                                         falloff = falloff / 10
595                                         v.co += (vert_scale_vec * BRUSH_PRESSURE) * falloff # FLAT BRUSH
596                                         
597                                         # Clamp back to original x if needs be.
598                                         if XPLANE_CLIP and origx:
599                                                 v.co.x = 0
600                         
601                         if BRUSH_MODE == 3: # ROTATE.
602                                 
603                                 if DISPLACE_NORMAL_MODE == 1: # VERTEX NORMAL
604                                         ROTATE_MATRIX = Mathutils.RotationMatrix(BRUSH_PRESSURE*10, 4, 'r', iFaceNormal)  # Cant use vertex normal, use face normal
605                                 elif DISPLACE_NORMAL_MODE == 2: # MEDIAN NORMAL
606                                         ROTATE_MATRIX = Mathutils.RotationMatrix(BRUSH_PRESSURE*10, 4, 'r', medainNormal)  # Cant use vertex normal, use face normal
607                                 elif DISPLACE_NORMAL_MODE == 3: # FACE NORMAL
608                                         ROTATE_MATRIX = Mathutils.RotationMatrix(BRUSH_PRESSURE*10, 4, 'r', iFaceNormal)  # Cant use vertex normal, use face normal
609                                 elif DISPLACE_NORMAL_MODE == 4: # VIEW NORMAL
610                                         ROTATE_MATRIX = Mathutils.RotationMatrix(BRUSH_PRESSURE*10, 4, 'r', Direction)  # Cant use vertex normal, use face normal
611                                 # Brush code
612                                 
613                                 for v,l in brush_verts:
614                                         
615                                         if XPLANE_CLIP:
616                                                 origx = False
617                                                 if abs(v.co.x) < 0.001: origx = True
618                                         
619                                         # MARK THE VERT AS DIRTY.
620                                         v.sel = 1
621                                         falloff = (BRUSH_RADIUS-l) / BRUSH_RADIUS # falloff between 0 and 1
622                                 
623                                         # Vectors handeled with rotation matrix creation.
624                                         rot_vert_loc = (ROTATE_MATRIX * (v.co-best_isect)) + best_isect
625                                         v.co = (v.co*(1-falloff)) + (rot_vert_loc*(falloff))
626                                         
627                                         # Clamp back to original x if needs be.
628                                         if XPLANE_CLIP and origx:
629                                                 v.co.x = 0
630                                 
631                         elif BRUSH_MODE == 4: # RELAX
632                                 vert_orig_loc = [Vector(v.co) for v in me.verts ] # save orig vert location.
633                                 #vertOrigNor = [Vector(v.no) for v in me.verts ] # save orig vert location.
634                                 
635                                 # Brush code
636                                 for v,l in brush_verts:
637                                         
638                                         if XPLANE_CLIP:
639                                                 origx = False
640                                                 if abs(v.co.x) < 0.001: origx = True
641                                         
642                                         v.sel = 1 # Mark the vert as dirty.
643                                         falloff = (BRUSH_RADIUS-l) / BRUSH_RADIUS # falloff between 0 and 1
644                                         connected_verts = verts_connected_by_edge[v.index]
645                                         relax_point = reduce(lambda a,b: a + vert_orig_loc[b.index], connected_verts, Mathutils.Vector(0,0,0)) * (1.0/len(connected_verts))
646                                         falloff = falloff * BRUSH_PRESSURE
647                                         # Old relax.
648                                         #v.co = (v.co*(1-falloff)) + (relax_point*(falloff))
649                                         
650                                         ll = (v.co-relax_point).length
651                                         newpoint = (v.co*(1-falloff)) + (relax_point*(falloff)) - v.co
652                                         newpoint = newpoint * (1/(1+ll))
653                                         v.co = v.co + newpoint
654                                         
655                                         '''
656                                         # New relax
657                                         relax_normal = vertOrigNor[v.index]
658                                         v1,v2,v3,v4 = v.co, v.co+relax_normal, relax_point-(relax_normal*10), relax_point+(relax_normal*10)
659                                         print v1,v2,v3,v4
660                                         try:
661                                                 a,b = LineIntersect(v1,v2,v3,v4) # Scale the normal to make a line. we know we will intersect with.
662                                                 v.co = (v.co*(1-falloff)) + (a*(falloff))
663                                         except:
664                                                 pass
665                                         '''
666                                         
667                                         # Clamp back to original x if needs be.
668                                         if XPLANE_CLIP and origx:
669                                                 v.co.x = 0
670                                         
671                         elif BRUSH_MODE == 5: # GOO
672                                 #print last_best_isect, best_isect, 'AA'
673                                 if not last_best_isect:
674                                         last_best_isect = best_isect
675                                         
676                                         # Set up a triangle orthographic to the view plane
677                                         gooPlane = [best_isect, CrossVecs(best_isect, Direction), None]
678                                         
679                                         
680                                         if DISPLACE_NORMAL_MODE == 4: # View Normal
681                                                 tempRotMatrix = Mathutils.RotationMatrix(90, 3, 'r', Direction)
682                                         else:
683                                                 tempRotMatrix = Mathutils.RotationMatrix(90, 3, 'r', CrossVecs(best_face.no, Direction))
684                                         
685                                         gooPlane[2] =  best_isect + (tempRotMatrix * gooPlane[1])
686                                         gooPlane[1] = gooPlane[1] + best_isect
687                                         
688                                         continue # we need another point of reference.
689                                         
690                                 elif last_best_isect == best_isect:
691                                         # Mouse has not moved, no point in trying to goo.
692                                         continue
693                                 else:
694                                         if goo_dir_vec:
695                                                 last_goo_dir_vec = goo_dir_vec
696                                         # The direction the mouse moved in 3d space. use for gooing
697                                         
698                                         # Modify best_isect so its not moving allong the view z axis.
699                                         # Assume Origin hasnt changed since the view wont change while the mouse is drawing. ATM.
700                                         best_isect = Intersect( gooPlane[0], gooPlane[1], gooPlane[2], Direction, Origin, 0) # Non clipped
701                                         goo_dir_vec = (best_isect - last_best_isect) * 2
702                                         
703                                         
704                                         # make a goo rotation matrix so the head of the goo rotates with the mouse.
705                                         """
706                                         if last_goo_dir_vec and goo_dir_vec != last_goo_dir_vec:
707                                                 '''
708                                                 vmi  = Matrix(Window.GetViewMatrix()); vmi.invert() # the inverse viewing matrix
709                                                 a = last_goo_dir_vec * vmi
710                                                 b = goo_dir_vec * vmi
711                                                 c = Angle2D(a.x, a.y, b.x, b.y)
712                                                 gooRotMatrix = Mathutils.RotationMatrix((c * goo_dir_vec.length)*-20, 3, 'r', Direction)
713                                                 '''
714                                                 pass
715                                         else:
716                                                 gooRotMatrix = None
717                                         """
718                                         
719                                         if goo_dir_vec.x == 0 and goo_dir_vec.y == 0 and goo_dir_vec.z == 0:
720                                                 continue
721                                         
722                                         # Brush code
723                                         for v,l in brush_verts:
724                                                 
725                                                 if XPLANE_CLIP:
726                                                         origx = False
727                                                         if abs(v.co.x) < 0.001: origx = True
728                                                 
729                                                 # MARK THE VERT AS DIRTY.
730                                                 v.sel = 1
731                                                 
732                                                 ''' # ICICLES!!!
733                                                 a = AngleBetweenVecs(goo_dir_vec, v.no)
734                                                 if a > 66:
735                                                         continue
736                                                         
737                                                 l = l * ((1+a)/67.0)
738                                                 l = max(0.00000001, l)
739                                                 '''
740                                                 
741                                                 falloff = (BRUSH_RADIUS-l) / BRUSH_RADIUS # falloff between 0 and 1
742                                                 goo_loc = (v.co*(1-falloff)) + ((v.co+goo_dir_vec) *falloff)
743                                                 
744                                                 v.co = (goo_loc*BRUSH_PRESSURE) + (v.co*(1-BRUSH_PRESSURE))
745                                                 
746                                                 '''
747                                                 if gooRotMatrix:
748                                                         rotatedVertLocation = (gooRotMatrix * (v.co-best_isect)) + best_isect
749                                                         v.co = (v.co*(1-falloff)) + (rotatedVertLocation*(falloff))
750                                                         # USe for goo only.                                     
751                                                 '''
752                                                 
753                                                 # Clamp back to original x if needs be.
754                                                 if XPLANE_CLIP and origx:
755                                                         v.co.x = 0
756                         
757                                 
758                         # Remember for the next sample
759                         last_best_isect = best_isect
760                         last_goo_dir_vec = goo_dir_vec
761                         
762                         # Post processing after the verts have moved
763                         # Subdivide any large edges, all but relax.
764                         
765                         MAX_SUBDIV = 10 # Maximum number of subdivisions per redraw. makes things useable.
766                         SUBDIV_COUNT = 0
767                         # Cant use adaptive geometry for relax because it keeps connectivity data.
768                         if ADAPTIVE_GEOMETRY and (BRUSH_MODE == 1 or BRUSH_MODE == 2 or BRUSH_MODE == 3 or BRUSH_MODE == 5):
769                                 Mesh.Mode(Mesh.SelectModes['EDGE'])
770                                 orig_len_edges = 0
771                                 #print 'ADAPTIVE_GEOMETRY'
772                                 while len(me.edges) != orig_len_edges and SUBDIV_COUNT < MAX_SUBDIV:
773                                         #print 'orig_len_edges', len(me.edges) 
774                                         #me = ob.getData(mesh=1)
775                                         orig_len_edges = len(me.edges)
776                                         EDGE_COUNT = 0
777                                         for ed in me.edges:
778                                                 if ed.v1.sel or ed.v2.sel:
779                                                         l = (ed.v1.co - ed.v2.co).length
780                                                         if l > max(RESOLUTION_MIN*1.5, BRUSH_RADIUS):
781                                                         #if l > BRUSH_RADIUS:
782                                                                 #print 'adding edge'
783                                                                 ed.flag |= SEL_FLAG
784                                                                 #ed.flag = 35
785                                                                 SUBDIV_COUNT += 1
786                                                                 EDGE_COUNT +=1
787                                                         """
788                                                         elif l < RESOLUTION_MIN:
789                                                                 '''
790                                                                 print 'removing edge'
791                                                                 v1 =e.v1
792                                                                 v2 =e.v2
793                                                                 v1.co = v2.co = (v1.co + v2.co) * 0.5
794                                                                 v1.sel = v2.sel = 1
795                                                                 me.remDoubles(0.001)
796                                                                 me = ob.getData(mesh=1)
797                                                                 break
798                                                                 '''
799                                                                 # Remove edge in python
800                                                                 print 'removing edge'
801                                                                 v1 =ed.v1
802                                                                 v2 =ed.v2
803                                                                 v1.co = v2.co = (v1.co + v2.co) * 0.5
804                                                                 
805                                                                 removeDouble(v1, v2, me)
806                                                                 me = ob.getData(mesh=1)
807                                                                 break
808                                                         """             
809                                                         
810                                         if EDGE_COUNT:
811                                                 me.subdivide(1)
812                                         
813                                                         
814                                         # Deselect all, we know theres only 2 selected
815                                         '''
816                                         for ee in me.edges:
817                                                 if ee.flag & SEL_FLAG:
818                                                         #ee.flag &= ~SEL_FLAG
819                                                         ee.flag = 32
820                                                 elif l < RESOLUTION_MIN:
821                                                         print 'removing edge'
822                                                         e.v1.co = e.v2.co = (e.v1.co + e.v2.co) * 0.5
823                                                         me.remDoubles(0.001)
824                                                         break
825                                                 '''
826                                 # Done subdividing
827                                 # Now remove doubles
828                                 #print Mesh.SelectModes['VERT']
829                                 #Mesh.Mode(Mesh.SelectModes['VERTEX'])
830                                 
831                                 filter(lambda v: setattr(v, 'sel', 1), me.verts)
832                                 filter(lambda v: setattr(v[0], 'sel', 0), brush_verts)
833                                 
834                                 # Cycling editmode is too slow.
835                                 
836                                 remdoubles = False
837                                 for ed in me.edges:
838                                         
839                                         if (not ed.v1.sel) and (not ed.v1.sel):
840                                                 if XPLANE_CLIP:
841                                                         # If 1 vert is on the edge and abother is off dont collapse edge.
842                                                         if (abs(ed.v1.co.x) < 0.001) !=\
843                                                         (abs(ed.v2.co.x) < 0.001):
844                                                                 continue
845                                                 l = (ed.v1.co - ed.v2.co).length
846                                                 if l < RESOLUTION_MIN:
847                                                         ed.v1.sel = ed.v2.sel = 1
848                                                         newco = (ed.v1.co + ed.v2.co)*0.5
849                                                         ed.v1.co.x = ed.v2.co.x = newco.x
850                                                         ed.v1.co.y = ed.v2.co.y = newco.y
851                                                         ed.v1.co.z = ed.v2.co.z = newco.z
852                                                         remdoubles = True
853                                 
854                                 #if remdoubles:
855
856                                 
857                                 filter(lambda v: setattr(v, 'sel', 0), me.verts)
858                                 #Mesh.Mode(Mesh.SelectModes['EDGE'])
859                                 # WHILE OVER
860                                 # Clean up selection.
861                                 #for v in me.verts:
862                                 #       v.sel = 0
863                                 '''
864                                 for ee in me.edges:
865                                         if ee.flag & SEL_FLAG:
866                                                 ee.flag &= ~SEL_FLAG
867                                                 #ee.flag = 32
868                                 '''
869                                 filter(lambda ed: setattr(ed, 'flag', 32), me.edges)
870                                 
871                                 
872                         if XPLANE_CLIP:
873                                 filter(lambda v: setattr(v.co, 'x', max(0, v.co.x)), me.verts)
874                         
875                         
876                         me.update()
877                         #Window.SetCursorPos(best_isect.x, best_isect.y, best_isect.z)
878                         Window.Redraw(Window.Types.VIEW3D)
879         #Window.DrawProgressBar(1.0, '')
880         if STATIC_MESH:
881                 #try:
882                 mesh_static.verts =  None
883                 print len(mesh_static.verts)
884                 mesh_static.update()
885                 #except:
886                 #       pass
887         if FIX_TOPOLOGY:
888                 fix_topolagy(me)
889         
890         # Remove short edges of we have edaptive geometry enabled.
891         if ADAPTIVE_GEOMETRY:
892                 Mesh.Mode(Mesh.SelectModes['VERTEX'])
893                 filter(lambda v: setattr(v, 'sel', 1), me.verts)
894                 me.remDoubles(0.001)
895                 print 'removing doubles'
896                 me = ob.getData(mesh=1) # Get new vert data
897                 Blender.event = Draw.LEFTMOUSE
898
899         Mesh.Mode(Mesh.SelectModes['EDGE'])
900         
901         if i:
902                 Window.EditMode(1)
903                 if not is_editmode: # User was in edit mode, so stay there.
904                         Window.EditMode(0)
905                 print '100 draws in %.6f' % (((Blender.sys.time()-time) / float(i))*100)
906
907 if __name__ == '__main__':
908         event_main()