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