Welcome to mirror list, hosted at ThFree Co, Russian Federation.

mesh_bbrush.py « scripts « release - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 6f110f3617614ca9dc18cb8add23bc3e358f5f1a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
# SPACEHANDLER.VIEW3D.EVENT
# Dont run, event handelers are accessed in the from the 3d View menu.

import Blender
from Blender import Mathutils, Window, Scene, Draw, Mesh, NMesh
from Blender.Mathutils import CrossVecs, Matrix, Vector, Intersect, LineIntersect


# DESCRIPTION:
# screen_x, screen_y the origin point of the pick ray
# it is either the mouse location
# localMatrix is used if you want to have the returned values in an objects localspace.
#    this is usefull when dealing with an objects data such as verts.
# or if useMid is true, the midpoint of the current 3dview
# returns
# Origin - the origin point of the pick ray
# Direction - the direction vector of the pick ray
# in global coordinates
epsilon = 1e-3 # just a small value to account for floating point errors

def getPickRay(screen_x, screen_y, localMatrix=None, useMid = False):
	
	# Constant function variables
	p = getPickRay.p
	d = getPickRay.d
	
	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) 
		win_min_x, win_min_y, win_max_x, win_max_y = win3d['vertices']
		# calculate a few geometric extents for this window

		win_mid_x  = (win_max_x + win_min_x + 1.0) * 0.5
		win_mid_y  = (win_max_y + win_min_y + 1.0) * 0.5
		win_size_x = (win_max_x - win_min_x + 1.0) * 0.5
		win_size_y = (win_max_y - win_min_y + 1.0) * 0.5

		#useMid is for projecting the coordinates when we subdivide the screen into bins
		if useMid: # == True
			screen_x = win_mid_x
			screen_y = win_mid_y
		
		# if the given screencoords (screen_x, screen_y) are within the 3dwin we fount the right one...
		if (win_max_x > screen_x > win_min_x) and (  win_max_y > screen_y > win_min_y):
			# first we handle all pending events for this window (otherwise the matrices might come out wrong)
			Window.QHandle(win3d['id'])
			
			# now we get a few matrices for our window...
			# sorry - i cannot explain here what they all do
			# - if you're not familiar with all those matrices take a look at an introduction to OpenGL...
			pm	= Window.GetPerspMatrix()   # the prespective matrix
			pmi  = Matrix(pm); pmi.invert() # the inverted perspective matrix
			
			if (1.0 - epsilon < pmi[3][3] < 1.0 + epsilon):
				# pmi[3][3] is 1.0 if the 3dwin is in ortho-projection mode (toggled with numpad 5)
				hms = getPickRay.hms
				ortho_d = getPickRay.ortho_d
				
				# ortho mode: is a bit strange - actually there's no definite location of the camera ...
				# but the camera could be displaced anywhere along the viewing direction.
				
				ortho_d.x, ortho_d.y, ortho_d.z = Window.GetViewVector()
				ortho_d.w = 0
				
				# all rays are parallel in ortho mode - so the direction vector is simply the viewing direction
				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
				
				# these are the homogenious screencoords of the point (screen_x, screen_y) ranging from -1 to +1
				p=(hms*pmi) + (1000*ortho_d)
				p.resize3D()
				d.x, d.y, d.z = ortho_d.x, ortho_d.y, ortho_d.z
				

			# Finally we shift the position infinitely far away in
			# the viewing direction to make sure the camera if outside the scene
			# (this is actually a hack because this function
			# is used in sculpt_mesh to initialize backface culling...)
			else:
				# PERSPECTIVE MODE: here everything is well defined - all rays converge at the camera's location
				vmi  = Matrix(Window.GetViewMatrix()); vmi.invert() # the inverse viewing matrix
				fp = getPickRay.fp
				
				dx = pm[3][3] * (((screen_x-win_min_x)/win_size_x)-1.0) - pm[3][0]
				dy = pm[3][3] * (((screen_y-win_min_y)/win_size_y)-1.0) - pm[3][1]
				
				fp.x, fp.y, fp.z = \
				pmi[0][0]*dx+pmi[1][0]*dy,\
				pmi[0][1]*dx+pmi[1][1]*dy,\
				pmi[0][2]*dx+pmi[1][2]*dy
				
				# fp is a global 3dpoint obtained from "unprojecting" the screenspace-point (screen_x, screen_y)
				#- figuring out how to calculate this took me quite some time.
				# The calculation of dxy and fp are simplified versions of my original code
				#- so it's almost impossible to explain what's going on geometrically... sorry
				
				p.x, p.y, p.z = vmi[3][:3]
				
				# the camera's location in global 3dcoords can be read directly from the inverted viewmatrix
				#d.x, d.y, d.z =normalize_v3(sub_v3v3(p, fp))
				d.x, d.y, d.z = p.x-fp.x, p.y-fp.y, p.z-fp.z
				
				#print 'd', d, 'p', p, 'fp', fp
				
			
			# the direction vector is simply the difference vector from the virtual camera's position
			#to the unprojected (screenspace) point fp
			
			# Do we want to return a direction in object's localspace?
			
			if localMatrix:
				localInvMatrix = Matrix(localMatrix)
				localInvMatrix.invert()
				p = p*localInvMatrix
				d = d*localInvMatrix # normalize_v3
				p.x += localInvMatrix[3][0]
				p.y += localInvMatrix[3][1]
				p.z += localInvMatrix[3][2]
				
			#else: # Worldspace, do nothing
			
			d.normalize()
			return True, p, d # Origin, Direction	
	
	# Mouse is not in any view, return None.
	return False, None, None

# Constant function variables
getPickRay.d = Vector(0,0,0) # Perspective, 3d
getPickRay.p = Vector(0,0,0)
getPickRay.fp = Vector(0,0,0)

getPickRay.hms = Vector(0,0,0,0) # ortho only 4d
getPickRay.ortho_d = Vector(0,0,0,0) # ortho only 4d



def ui_set_preferences(user_interface=1):
	
	# Create data and set defaults.
	ADAPTIVE_GEOMETRY_but = Draw.Create(1)
	BRUSH_MODE_but = Draw.Create(1)
	BRUSH_PRESSURE_but = Draw.Create(0.05)
	BRUSH_RADIUS_but = Draw.Create(0.25)
	RESOLUTION_MIN_but = Draw.Create(0.1)
	DISPLACE_NORMAL_MODE_but = Draw.Create(2)
	STATIC_NORMAL_but = Draw.Create(1)
	XPLANE_CLIP_but = Draw.Create(0)
	STATIC_MESH_but = Draw.Create(1)
	FIX_TOPOLOGY_but = Draw.Create(0)
	
	# Remember old variables if alredy set.
	try:
		ADAPTIVE_GEOMETRY_but.val = Blender.bbrush['ADAPTIVE_GEOMETRY']
		BRUSH_MODE_but.val = Blender.bbrush['BRUSH_MODE']
		BRUSH_PRESSURE_but.val = Blender.bbrush['BRUSH_PRESSURE']
		BRUSH_RADIUS_but.val = Blender.bbrush['BRUSH_RADIUS']
		RESOLUTION_MIN_but.val = Blender.bbrush['RESOLUTION_MIN']
		DISPLACE_NORMAL_MODE_but.val = Blender.bbrush['DISPLACE_NORMAL_MODE']
		STATIC_NORMAL_but.val = Blender.bbrush['STATIC_NORMAL']
		XPLANE_CLIP_but.val = Blender.bbrush['XPLANE_CLIP']
		STATIC_MESH_but.val = Blender.bbrush['STATIC_MESH']
		FIX_TOPOLOGY_but.val = Blender.bbrush['FIX_TOPOLOGY']
	except:
		Blender.bbrush = {}
	
	if user_interface:
		pup_block = [\
		'Brush Options',\
		('Adaptive Geometry', ADAPTIVE_GEOMETRY_but, 'Add and remove detail as needed. Uses min/max resolution.'),\
		('Brush Type: ', BRUSH_MODE_but, 1, 5, 'Push/Pull:1, Grow/Shrink:2, Spin:3, Relax:4, Goo:5'),\
		('Pressure: ', BRUSH_PRESSURE_but, 0.0, 1.0, 'Pressure of the brush.'),\
		('Size: ', BRUSH_RADIUS_but, 0.0, 2.0, 'Size of the brush.'),\
		('Geometry Res: ', RESOLUTION_MIN_but, 0.01, 0.5, 'Size of the brush & Adaptive Subdivision.'),\
		('Displace Vector: ', DISPLACE_NORMAL_MODE_but, 1, 4, 'Vertex Normal:1, Median Normal:2, Face Normal:3, View Normal:4'),\
		('Static Normal', STATIC_NORMAL_but, 'Use the initial normal only.'),\
		('No X Crossing', XPLANE_CLIP_but, 'Dont allow verts to have a negative X axis (use for x-mirror).'),\
		('Static Mesh', STATIC_MESH_but, 'During mouse interaction, dont update the mesh.'),\
		#('Fix Topology', FIX_TOPOLOGY_but, 'Fix the mesh structure by rotating edges '),\
		]
		
		Draw.PupBlock('BlenBrush Prefs (RMB)', pup_block)
	
	Blender.bbrush['ADAPTIVE_GEOMETRY'] = ADAPTIVE_GEOMETRY_but.val
	Blender.bbrush['BRUSH_MODE'] = BRUSH_MODE_but.val
	Blender.bbrush['BRUSH_PRESSURE'] = BRUSH_PRESSURE_but.val
	Blender.bbrush['BRUSH_RADIUS'] = BRUSH_RADIUS_but.val
	Blender.bbrush['RESOLUTION_MIN'] = RESOLUTION_MIN_but.val
	Blender.bbrush['DISPLACE_NORMAL_MODE'] = DISPLACE_NORMAL_MODE_but.val
	Blender.bbrush['STATIC_NORMAL'] = STATIC_NORMAL_but.val
	Blender.bbrush['XPLANE_CLIP'] = XPLANE_CLIP_but.val
	Blender.bbrush['STATIC_MESH'] = STATIC_MESH_but.val
	Blender.bbrush['FIX_TOPOLOGY'] = FIX_TOPOLOGY_but.val


def triangulateNMesh(nm):
	'''
	Converts the meshes faces to tris, modifies the mesh in place.
	'''
	
	#============================================================================#
	# Returns a new face that has the same properties as the origional face      #
	# but with no verts							  #
	#============================================================================#
	def copyFace(face):
		newFace = NMesh.Face()
		# Copy some generic properties
		newFace.mode = face.mode
		if face.image != None:
			newFace.image = face.image
		newFace.flag = face.flag
		newFace.mat = face.mat
		newFace.smooth = face.smooth
		return newFace
	
	# 2 List comprehensions are a lot faster then 1 for loop.
	tris = [f for f in nm.faces if len(f) == 3]
	quads = [f for f in nm.faces if len(f) == 4]
	
	
	if quads: # Mesh may have no quads.
		has_uv = quads[0].uv 
		has_vcol = quads[0].col
		for quadFace in quads:
			# Triangulate along the shortest edge
			#if (quadFace.v[0].co - quadFace.v[2].co).length < (quadFace.v[1].co - quadFace.v[3].co).length:
			a1 = Mathutils.TriangleArea(quadFace.v[0].co, quadFace.v[1].co, quadFace.v[2].co)
			a2 = Mathutils.TriangleArea(quadFace.v[0].co, quadFace.v[2].co, quadFace.v[3].co)
			b1 = Mathutils.TriangleArea(quadFace.v[1].co, quadFace.v[2].co, quadFace.v[3].co)
			b2 = Mathutils.TriangleArea(quadFace.v[1].co, quadFace.v[3].co, quadFace.v[0].co)
			a1,a2 = min(a1, a2), max(a1, a2)
			b1,b2 = min(b1, b2), max(b1, b2)
			if a1/a2 < b1/b2:
				
				# Method 1
				triA = 0,1,2
				triB = 0,2,3
			else:
				# Method 2
				triA = 0,1,3
				triB = 1,2,3
				
			for tri1, tri2, tri3 in (triA, triB):
				newFace = copyFace(quadFace)
				newFace.v = [quadFace.v[tri1], quadFace.v[tri2], quadFace.v[tri3]]
				if has_uv: newFace.uv = [quadFace.uv[tri1], quadFace.uv[tri2], quadFace.uv[tri3]]
				if has_vcol: newFace.col = [quadFace.col[tri1], quadFace.col[tri2], quadFace.col[tri3]]
				
				nm.addEdge(quadFace.v[tri1], quadFace.v[tri3]) # Add an edge where the 2 tris are devided.
				tris.append(newFace)
		
		nm.faces = tris

import mesh_tri2quad
def fix_topolagy(mesh):
	ob = Scene.GetCurrent().getActiveObject()
	
	for f in mesh.faces:
		f.sel = 1
	mesh.quadToTriangle(0) 
	nmesh = ob.getData()

	mesh_tri2quad.tri2quad(nmesh, 100, 0)
	triangulateNMesh(nmesh)
	nmesh.update()
	
	mesh = Mesh.Get(mesh.name)
	for f in mesh.faces:
		f.sel=1	
	mesh.quadToTriangle()
	Mesh.Mode(Mesh.SelectModes['EDGE'])
	
	
	
	


def event_main():
	#print Blender.event
	#mod =[Window.Qual.CTRL,  Window.Qual.ALT, Window.Qual.SHIFT]
	mod =[Window.Qual.CTRL,  Window.Qual.ALT]
	
	qual = Window.GetKeyQualifiers()
	SHIFT_FLAG = Window.Qual.SHIFT
	CTRL_FLAG = Window.Qual.CTRL
	
	
	# UNDO
	"""
	is_editmode = Window.EditMode() # Exit Editmode.
	if is_editmode: Window.EditMode(0)
	if Blender.event == Draw.UKEY:
		if is_editmode:
			Blender.event = Draw.UKEY
			return
		else:
			winId = [win3d for win3d in Window.GetScreenInfo(Window.Types.VIEW3D)][0]
			Blender.event = None
			Window.QHandle(winId['id'])
			Window.EditMode(1)
			Window.QHandle(winId['id'])
			Window.QAdd(winId['id'],Draw.UKEY,1) # Change KeyPress Here for EditMode
			Window.QAdd(winId['id'],Draw.UKEY,0)
			Window.QHandle(winId['id'])
			Window.EditMode(0)
			Blender.event = None
			return
	"""
	
	ob = Scene.GetCurrent().getActiveObject()
	if not ob or ob.getType() != 'Mesh':
		return
	
	# Mouse button down with no modifiers.	
	if Blender.event == Draw.LEFTMOUSE and not [True for m in mod if m & qual]:
		# Do not exit (draw)
		pass
	elif Blender.event == Draw.RIGHTMOUSE and not [True for m in mod if m & qual]:
		ui_set_preferences()
		return
	else:
		return 
		
	del qual
	
	
	try:
		Blender.bbrush
	except:
		# First time run
		ui_set_preferences() # No ui
		return

	ADAPTIVE_GEOMETRY = Blender.bbrush['ADAPTIVE_GEOMETRY'] # 1
	BRUSH_MODE = Blender.bbrush['BRUSH_MODE'] # 1
	BRUSH_PRESSURE_ORIG = Blender.bbrush['BRUSH_PRESSURE'] # 0.1
	BRUSH_RADIUS = Blender.bbrush['BRUSH_RADIUS'] # 0.5
	RESOLUTION_MIN = Blender.bbrush['RESOLUTION_MIN'] # 0.08
	STATIC_NORMAL = Blender.bbrush['STATIC_NORMAL'] # 0
	XPLANE_CLIP = Blender.bbrush['XPLANE_CLIP'] # 0
	DISPLACE_NORMAL_MODE = Blender.bbrush['DISPLACE_NORMAL_MODE'] # 'Vertex Normal%x1|Median Normal%x2|Face Normal%x3|View Normal%x4'
	STATIC_MESH = Blender.bbrush['STATIC_MESH']
	FIX_TOPOLOGY = Blender.bbrush['FIX_TOPOLOGY']
	
	
	# Angle between Vecs wrapper.
	AngleBetweenVecs = Mathutils.AngleBetweenVecs
	def ang(v1,v2):
		try:
			return AngleBetweenVecs(v1,v2)
		except:
			return 180
	"""
	def Angle2D(x1, y1, x2, y2):
		import math
		RAD2DEG = 57.295779513082323
		'''
		   Return the angle between two vectors on a plane
		   The angle is from vector 1 to vector 2, positive anticlockwise
		   The result is between -pi -> pi
		'''
		dtheta = math.atan2(y2,x2) - math.atan2(y1,x1) # theta1 - theta2
		while dtheta > math.pi:
			dtheta -= (math.pi*2)
		while dtheta < -math.pi:
			dtheta += (math.pi*2)
		return dtheta * RAD2DEG  #(180.0 / math.pi)
	"""
	
	def faceIntersect(f):
		isect = Intersect(f.v[0].co, f.v[1].co, f.v[2].co, Direction, Origin, 1) # Clipped.
		if isect:
			return isect
		elif len(f.v) == 4:
			isect = Intersect(f.v[0].co, f.v[2].co, f.v[3].co, Direction, Origin, 1) # Clipped.
		return isect
	"""
	# Unused so farm, too slow.
	def removeDouble(v1,v2, me):
		v1List = [f for f in me.faces if v1 in f.v]
		v2List = [f for f in me.faces if v2 in f.v]
		#print v1List
		#print v2List
		remFaces = []
		newFaces = []
		for f2 in v2List:
			f2ls = list(f2.v)
			i = f2ls.index(v2)
			f2ls[i] = v1
			#remFaces.append(f2)
			if f2ls.count(v1) == 1:
				newFaces.append(tuple(f2ls))
		if remFaces:
			me.faces.delete(1, remFaces)
		#me.verts.delete(v2)
		if newFaces:
			me.faces.extend(newFaces)
	"""
	
	
	me = ob.getData(mesh=1)
	
	is_editmode = Window.EditMode() # Exit Editmode.
	if is_editmode: Window.EditMode(0)
	
	Mesh.Mode(Mesh.SelectModes['EDGE'])
	
	# At the moment ADAPTIVE_GEOMETRY is the only thing that uses selection.
	if ADAPTIVE_GEOMETRY:
		# Deslect all
		SEL_FLAG = Mesh.EdgeFlags['SELECT']
		for ed in me.edges:
			#ed.flag &= ~SEL_FLAG # deselect. 34
			ed.flag = 32
		
		'''for v in me.verts:
			v.sel = 0'''
		filter(lambda v: setattr(v, 'sel', 0), me.verts)
		
	i = 0
	time = Blender.sys.time()
	last_best_isect = None # used for goo only
	old_screen_x, old_screen_y = 1<<30, 1<<30
	goo_dir_vec = last_goo_dir_vec = gooRotMatrix = None # goo mode only.
	
	# Normal stuff
	iFaceNormal = medainNormal = None
	
	# Store all vert normals for now.
	if BRUSH_MODE == 1 and STATIC_NORMAL: # Push pull
		vert_orig_normals = dict([(v, v.no) for v in me.verts])
	
	elif BRUSH_MODE == 4: # RELAX, BUILD EDGE CONNECTIVITE DATA.
		# we need edge connectivity
		#vertEdgeUsers = [list() for i in xrange(len(me.verts))]
		verts_connected_by_edge = [list() for i in xrange(len(me.verts))]
		
		for ed in me.edges:
			i1, i2 = ed.v1.index,  ed.v2.index
			#vertEdgeUsers[i1].append(ed)
			#vertEdgeUsers[i2].append(ed)
			
			verts_connected_by_edge[i1].append(ed.v2)
			verts_connected_by_edge[i2].append(ed.v1)
	
	if STATIC_MESH:
		
		# Try and find a static mesh to reuse.
		# this is because we dont want to make a new mesh for each stroke.
		mesh_static = None
		for _me_name_ in Blender.NMesh.GetNames():
			_me_ = Mesh.Get(_me_name_)
			#print _me_.users , len(me.verts)
			if _me_.users == 0 and len(_me_.verts) == 0:
				mesh_static = _me_
				#print 'using', _me_.name
				break
		del _me_name_
		del _me_
		
		if not mesh_static:
			mesh_static = Mesh.New()
			print 'Making new mesh', mesh_static.name
		
		mesh_static.verts.extend([v.co for v in me.verts])
		mesh_static.faces.extend([tuple([mesh_static.verts[v.index] for v in f.v]) for f in me.faces])
	
	
	best_isect = gooPlane = None 
	
	while Window.GetMouseButtons() == 1:
		i+=1
		screen_x, screen_y = Window.GetMouseCoords()
		
		# Skip when no mouse movement, Only for Goo!
		if screen_x == old_screen_x and screen_y == old_screen_y:
			if BRUSH_MODE == 5: # Dont modify while mouse is not moved for goo.
				continue
		else: # mouse has moved get the new mouse ray.
			old_screen_x, old_screen_y = screen_x, screen_y
			mouseInView, Origin, Direction = getPickRay(screen_x, screen_y, ob.matrixWorld)
			if not mouseInView or not Origin:
				return
			Origin_SCALE = Origin * 100 
			
		# Find an intersecting face!
		bestLen = 1<<30 # start with an assumed realy bad match.
		best_isect = None # last intersect is used for goo.
		best_face = None
		
		if not last_best_isect:
			last_best_isect = best_isect
		
		if not mouseInView:
			last_best_isect = None	
			
		else:
			# Find Face intersection closest to the view. 
			#for f in [f for f in me.faces if ang(f.no, Direction) < 90]:
			
			# Goo brush only intersects faces once, after that the brush follows teh view plain.
			if BRUSH_MODE == 5 and gooPlane != None and gooPlane:
				best_isect = Intersect( gooPlane[0], gooPlane[1], gooPlane[2], Direction, Origin, 0) # Non clipped
			else:
				if STATIC_MESH:
					intersectingFaces = [(f, ix) for f in mesh_static.faces for ix in (faceIntersect(f),) if ix]
				else:
					intersectingFaces = [(f, ix) for f in me.faces for ix in (faceIntersect(f),) if ix]
				
				for f, isect in intersectingFaces:
					l = (Origin_SCALE-isect).length 
					if l < bestLen:
						best_face = f
						best_isect = isect
						bestLen = l
		
		if not best_isect:
			# Dont interpolate once the mouse moves off the mesh.
			lastGooVec = last_best_isect = None
			
		else: # mouseInView must be true also
			
			# Use the shift key to modify the pressure.
			if SHIFT_FLAG & Window.GetKeyQualifiers():
				BRUSH_PRESSURE = -BRUSH_PRESSURE_ORIG
			else:
				BRUSH_PRESSURE =  BRUSH_PRESSURE_ORIG
			
			brush_verts = [(v,le) for v in me.verts for le in ((v.co-best_isect).length,) if le < BRUSH_RADIUS]
			
			# SETUP ONCE ONLY VARIABLES
			if STATIC_NORMAL: # Only set the normal once.
				if not iFaceNormal:
					iFaceNormal = best_face.no
			else:
				if best_face:
					iFaceNormal = best_face.no
			
			
			if DISPLACE_NORMAL_MODE == 2: # MEDIAN NORMAL
				if (STATIC_NORMAL and medainNormal == None) or not STATIC_NORMAL:
					medainNormal = Vector(0,0,0)
					for v, l in brush_verts:
						medainNormal += v.no*(BRUSH_RADIUS-l)
					medainNormal.normalize()
			
			
			# ================================================================#
			# == Tool code, loop on the verts and operate on them ============#
			# ================================================================#
			if BRUSH_MODE == 1: # NORMAL PAINT
				for v,l in brush_verts:
					if XPLANE_CLIP:
						origx = False
						if abs(v.co.x) < 0.001: origx = True
							
					
					v.sel = 1 # MARK THE VERT AS DIRTY.
					falloff = (BRUSH_RADIUS-l) / BRUSH_RADIUS # falloff between 0 and 1
					
					if DISPLACE_NORMAL_MODE == 1: # VERTEX NORMAL
						if STATIC_NORMAL:
							try:
								no = vert_orig_normals[v]
							except:
								no = vert_orig_normals[v] = v.no
							v.co += (no * BRUSH_PRESSURE) * falloff
						else:
							v.co += (v.no * BRUSH_PRESSURE) * falloff
					elif DISPLACE_NORMAL_MODE == 2: # MEDIAN NORMAL # FIXME
						v.co += (medainNormal * BRUSH_PRESSURE) * falloff
					elif DISPLACE_NORMAL_MODE == 3: # FACE NORMAL
						v.co += (iFaceNormal * BRUSH_PRESSURE) * falloff
					elif DISPLACE_NORMAL_MODE == 4: # VIEW NORMAL
						v.co += (Direction * BRUSH_PRESSURE) * falloff
					
					# Clamp back to original x if needs be.
					if XPLANE_CLIP and origx:
						v.co.x = 0
			
			elif BRUSH_MODE == 2: # SCALE
				for v,l in brush_verts:
					
					if XPLANE_CLIP:
						origx = False
						if abs(v.co.x) < 0.001: origx = True
					
					v.sel = 1 # MARK THE VERT AS DIRTY.
					falloff = (BRUSH_RADIUS-l) / BRUSH_RADIUS # falloff between 0 and 1
					
					vert_scale_vec = v.co - best_isect
					vert_scale_vec.normalize()
					# falloff needs to be scaled for this tool
					falloff = falloff / 10
					v.co += (vert_scale_vec * BRUSH_PRESSURE) * falloff # FLAT BRUSH
					
					# Clamp back to original x if needs be.
					if XPLANE_CLIP and origx:
						v.co.x = 0
			
			if BRUSH_MODE == 3: # ROTATE.
				
				if DISPLACE_NORMAL_MODE == 1: # VERTEX NORMAL
					ROTATE_MATRIX = Mathutils.RotationMatrix(BRUSH_PRESSURE*10, 4, 'r', iFaceNormal)  # Cant use vertex normal, use face normal
				elif DISPLACE_NORMAL_MODE == 2: # MEDIAN NORMAL
					ROTATE_MATRIX = Mathutils.RotationMatrix(BRUSH_PRESSURE*10, 4, 'r', medainNormal)  # Cant use vertex normal, use face normal
				elif DISPLACE_NORMAL_MODE == 3: # FACE NORMAL
					ROTATE_MATRIX = Mathutils.RotationMatrix(BRUSH_PRESSURE*10, 4, 'r', iFaceNormal)  # Cant use vertex normal, use face normal
				elif DISPLACE_NORMAL_MODE == 4: # VIEW NORMAL
					ROTATE_MATRIX = Mathutils.RotationMatrix(BRUSH_PRESSURE*10, 4, 'r', Direction)  # Cant use vertex normal, use face normal
				# Brush code
				
				for v,l in brush_verts:
					
					if XPLANE_CLIP:
						origx = False
						if abs(v.co.x) < 0.001: origx = True
					
					# MARK THE VERT AS DIRTY.
					v.sel = 1
					falloff = (BRUSH_RADIUS-l) / BRUSH_RADIUS # falloff between 0 and 1
				
					# Vectors handeled with rotation matrix creation.
					rot_vert_loc = (ROTATE_MATRIX * (v.co-best_isect)) + best_isect
					v.co = (v.co*(1-falloff)) + (rot_vert_loc*(falloff))
					
					# Clamp back to original x if needs be.
					if XPLANE_CLIP and origx:
						v.co.x = 0
				
			elif BRUSH_MODE == 4: # RELAX
				vert_orig_loc = [Vector(v.co) for v in me.verts ] # save orig vert location.
				#vertOrigNor = [Vector(v.no) for v in me.verts ] # save orig vert location.
				
				# Brush code
				for v,l in brush_verts:
					
					if XPLANE_CLIP:
						origx = False
						if abs(v.co.x) < 0.001: origx = True
					
					v.sel = 1 # Mark the vert as dirty.
					falloff = (BRUSH_RADIUS-l) / BRUSH_RADIUS # falloff between 0 and 1
					connected_verts = verts_connected_by_edge[v.index]
					relax_point = reduce(lambda a,b: a + vert_orig_loc[b.index], connected_verts, Mathutils.Vector(0,0,0)) * (1.0/len(connected_verts))
					falloff = falloff * BRUSH_PRESSURE
					# Old relax.
					#v.co = (v.co*(1-falloff)) + (relax_point*(falloff))
					
					ll = (v.co-relax_point).length
					newpoint = (v.co*(1-falloff)) + (relax_point*(falloff)) - v.co
					newpoint = newpoint * (1/(1+ll))
					v.co = v.co + newpoint
					
					'''
					# New relax
					relax_normal = vertOrigNor[v.index]
					v1,v2,v3,v4 = v.co, v.co+relax_normal, relax_point-(relax_normal*10), relax_point+(relax_normal*10)
					print v1,v2,v3,v4
					try:
						a,b = LineIntersect(v1,v2,v3,v4) # Scale the normal to make a line. we know we will intersect with.
						v.co = (v.co*(1-falloff)) + (a*(falloff))
					except:
						pass
					'''
					
					# Clamp back to original x if needs be.
					if XPLANE_CLIP and origx:
						v.co.x = 0
					
			elif BRUSH_MODE == 5: # GOO
				#print last_best_isect, best_isect, 'AA'
				if not last_best_isect:
					last_best_isect = best_isect
					
					# Set up a triangle orthographic to the view plane
					gooPlane = [best_isect, CrossVecs(best_isect, Direction), None]
					
					
					if DISPLACE_NORMAL_MODE == 4: # View Normal
						tempRotMatrix = Mathutils.RotationMatrix(90, 3, 'r', Direction)
					else:
						tempRotMatrix = Mathutils.RotationMatrix(90, 3, 'r', CrossVecs(best_face.no, Direction))
					
					gooPlane[2] =  best_isect + (tempRotMatrix * gooPlane[1])
					gooPlane[1] = gooPlane[1] + best_isect
					
					continue # we need another point of reference.
					
				elif last_best_isect == best_isect:
					# Mouse has not moved, no point in trying to goo.
					continue
				else:
					if goo_dir_vec:
						last_goo_dir_vec = goo_dir_vec
					# The direction the mouse moved in 3d space. use for gooing
					
					# Modify best_isect so its not moving allong the view z axis.
					# Assume Origin hasnt changed since the view wont change while the mouse is drawing. ATM.
					best_isect = Intersect( gooPlane[0], gooPlane[1], gooPlane[2], Direction, Origin, 0) # Non clipped
					goo_dir_vec = (best_isect - last_best_isect) * 2
					
					
					# make a goo rotation matrix so the head of the goo rotates with the mouse.
					"""
					if last_goo_dir_vec and goo_dir_vec != last_goo_dir_vec:
						'''
						vmi  = Matrix(Window.GetViewMatrix()); vmi.invert() # the inverse viewing matrix
						a = last_goo_dir_vec * vmi
						b = goo_dir_vec * vmi
						c = Angle2D(a.x, a.y, b.x, b.y)
						gooRotMatrix = Mathutils.RotationMatrix((c * goo_dir_vec.length)*-20, 3, 'r', Direction)
						'''
						pass
					else:
						gooRotMatrix = None
					"""
					
					if goo_dir_vec.x == 0 and goo_dir_vec.y == 0 and goo_dir_vec.z == 0:
						continue
					
					# Brush code
					for v,l in brush_verts:
						
						if XPLANE_CLIP:
							origx = False
							if abs(v.co.x) < 0.001: origx = True
						
						# MARK THE VERT AS DIRTY.
						v.sel = 1
						
						''' # ICICLES!!!
						a = AngleBetweenVecs(goo_dir_vec, v.no)
						if a > 66:
							continue
							
						l = l * ((1+a)/67.0)
						l = max(0.00000001, l)
						'''
						
						falloff = (BRUSH_RADIUS-l) / BRUSH_RADIUS # falloff between 0 and 1
						goo_loc = (v.co*(1-falloff)) + ((v.co+goo_dir_vec) *falloff)
						
						v.co = (goo_loc*BRUSH_PRESSURE) + (v.co*(1-BRUSH_PRESSURE))
						
						'''
						if gooRotMatrix:
							rotatedVertLocation = (gooRotMatrix * (v.co-best_isect)) + best_isect
							v.co = (v.co*(1-falloff)) + (rotatedVertLocation*(falloff))
							# USe for goo only.					
						'''
						
						# Clamp back to original x if needs be.
						if XPLANE_CLIP and origx:
							v.co.x = 0
			
				
			# Remember for the next sample
			last_best_isect = best_isect
			last_goo_dir_vec = goo_dir_vec
			
			# Post processing after the verts have moved
			# Subdivide any large edges, all but relax.
			
			MAX_SUBDIV = 10 # Maximum number of subdivisions per redraw. makes things useable.
			SUBDIV_COUNT = 0
			if ADAPTIVE_GEOMETRY and (BRUSH_MODE == 1 or BRUSH_MODE == 2 or BRUSH_MODE == 3 or BRUSH_MODE == 5):
				orig_len_edges = 0
				#print 'ADAPTIVE_GEOMETRY'
				while len(me.edges) != orig_len_edges and SUBDIV_COUNT < MAX_SUBDIV:
					#print 'orig_len_edges', len(me.edges) 
					#me = ob.getData(mesh=1)
					orig_len_edges = len(me.edges)
					EDGE_COUNT = 0
					for ed in me.edges:
						if ed.v1.sel or ed.v2.sel:
							l = (ed.v1.co - ed.v2.co).length
							#if l > RESOLUTION_MAX:
							if l > BRUSH_RADIUS:
								#print 'adding edge'
								#ed.flag |= SEL_FLAG
								ed.flag = 35
								SUBDIV_COUNT += 1
								EDGE_COUNT +=1
							"""
							elif l < RESOLUTION_MIN:
								'''
								print 'removing edge'
								v1 =e.v1
								v2 =e.v2
								v1.co = v2.co = (v1.co + v2.co) * 0.5
								v1.sel = v2.sel = 1
								me.remDoubles(0.001)
								me = ob.getData(mesh=1)
								break
								'''
								# Remove edge in python
								print 'removing edge'
								v1 =ed.v1
								v2 =ed.v2
								v1.co = v2.co = (v1.co + v2.co) * 0.5
								
								removeDouble(v1, v2, me)
								me = ob.getData(mesh=1)
								break
							"""		
							
					if EDGE_COUNT:
						me.subdivide(1)
					
							
					# Deselect all, we know theres only 2 selected
					
					for ee in me.edges:
						if ee.flag & SEL_FLAG:
							#ee.flag &= ~SEL_FLAG
							ee.flag = 32
				
						'''
						elif l < RESOLUTION_MIN:
							print 'removing edge'
							e.v1.co = e.v2.co = (e.v1.co + e.v2.co) * 0.5
							me.remDoubles(0.001)
							break
						'''
				# Done subdividing
				# Now remove doubles
				#print Mesh.SelectModes['VERT']
				Mesh.Mode(Mesh.SelectModes['VERTEX'])
				
				filter(lambda v: setattr(v, 'sel', 1), me.verts)
				filter(lambda v: setattr(v[0], 'sel', 0), brush_verts)
				
				
				remdoubles = False
				for ed in me.edges:
					
					if (not ed.v1.sel) and (not ed.v1.sel):
						l = (ed.v1.co - ed.v2.co).length
						if l < RESOLUTION_MIN:
							ed.v1.sel = ed.v2.sel = 1
							newco = (ed.v1.co + ed.v2.co)*0.5
							ed.v1.co.x = ed.v2.co.x = newco.x
							ed.v1.co.y = ed.v2.co.y = newco.y
							ed.v1.co.z = ed.v2.co.z = newco.z
							remdoubles = True
				
				if remdoubles:
					me.remDoubles(0.001)
					me = ob.getData(mesh=1) # Get new vert data
				filter(lambda v: setattr(v, 'sel', 0), me.verts)
				Mesh.Mode(Mesh.SelectModes['EDGE'])
				# WHILE OVER
				# Clean up selection.
				#for v in me.verts:
				#	v.sel = 0
				for ee in me.edges:
					if ee.flag & SEL_FLAG:
						#ee.flag &= ~SEL_FLAG
						ee.flag = 32
				
				
				
			if XPLANE_CLIP:
				filter(lambda v: setattr(v.co, 'x', max(0, v.co.x)), me.verts)
			
			me.update()
			#Window.SetCursorPos(best_isect.x, best_isect.y, best_isect.z)
			Window.Redraw(Window.Types.VIEW3D)
	if i:
		Window.EditMode(1)
		if not is_editmode: # User was in edit mode, so stay there.
			Window.EditMode(0)
		print '100 draws in %.6f' % (((Blender.sys.time()-time) / float(i))*100)
	#Window.DrawProgressBar(1.0, '')
	if STATIC_MESH:
		#try:
		mesh_static.verts =  None
		print len(mesh_static.verts)
		mesh_static.update()
		#except:
		#	pass
	if FIX_TOPOLOGY:
		fix_topolagy(me)
	
	Blender.event = Draw.LEFTMOUSE

if __name__ == '__main__':
	event_main()