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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2006-01-18 02:50:06 +0300
committerCampbell Barton <ideasman42@gmail.com>2006-01-18 02:50:06 +0300
commit24b5e21c6582c800bdc28f00400fca0c48c7ad14 (patch)
tree0c6f2275c09967d577ff9c7a53dece7f1c94a076 /release
parentf90b7a69065b14cb1d5bb896a52a0d0c5783b8de (diff)
Moved script. Minor updates to subdivision and adaptive meshes.
Diffstat (limited to 'release')
-rw-r--r--release/scripts/bpydata/mesh_bbrush.py899
1 files changed, 899 insertions, 0 deletions
diff --git a/release/scripts/bpydata/mesh_bbrush.py b/release/scripts/bpydata/mesh_bbrush.py
new file mode 100644
index 00000000000..88b7e00c74d
--- /dev/null
+++ b/release/scripts/bpydata/mesh_bbrush.py
@@ -0,0 +1,899 @@
+# 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
+ '''
+ filter(lambda ed: setattr(ed, 'flag', 32), me.edges)
+
+ '''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
+ # Cant use adaptive geometry for relax because it keeps connectivity data.
+ if ADAPTIVE_GEOMETRY and (BRUSH_MODE == 1 or BRUSH_MODE == 2 or BRUSH_MODE == 3 or BRUSH_MODE == 5):
+ Mesh.Mode(Mesh.SelectModes['EDGE'])
+ 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 > max(RESOLUTION_MIN*1.5, BRUSH_RADIUS):
+ #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)
+
+ # Cycling editmode is too slow.
+
+ 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:
+
+
+ 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
+ '''
+ filter(lambda ed: setattr(ed, 'flag', 32), me.edges)
+
+
+ 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)
+ #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)
+
+ Mesh.Mode(Mesh.SelectModes['VERTEX'])
+ filter(lambda v: setattr(v, 'sel', 1), me.verts)
+ me.remDoubles(0.001)
+ print 'removing doubles'
+ me = ob.getData(mesh=1) # Get new vert data
+ Blender.event = Draw.LEFTMOUSE
+ Mesh.Mode(Mesh.SelectModes['EDGE'])
+ 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)
+
+if __name__ == '__main__':
+ event_main() \ No newline at end of file