diff --git a/release/scripts/mesh_bbrush.py b/release/scripts/mesh_bbrush.py new file mode 100644 index 00000000000..ba9e6454d39 --- /dev/null +++ b/release/scripts/mesh_bbrush.py @@ -0,0 +1,798 @@ +# 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 +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(1) + + # 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.1, 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 + print '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. + ''' + NMesh = Blender.NMesh + + #============================================================================# + # 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: + # 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): + return + ob = Scene.GetCurrent().getActiveObject() + nmesh = ob.getData() + #nmesh = Blender.NMesh.GetRaw(mesh.name) + triangulateNMesh(nmesh) + 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() + ''' + + + +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 + """ + + # 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) + """ + + ob = Scene.GetCurrent().getActiveObject() + if not ob or ob.getType() != 'Mesh': + return + + me = ob.getData(mesh=1) + + is_editmode = Window.EditMode() # Exit Editmode. + if is_editmode: Window.EditMode(0) + + # 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: + 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 + + elif BRUSH_MODE == 2: # SCALE + for v,l in brush_verts: + 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 + + + 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: + # 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)) + + + 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: + 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 + ''' + + 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: + # 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. + ''' + + + # 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 + ''' + + # WHILE OVER + # Clean up selection. + #for v in me.verts: + # v.sel = 0 + filter(lambda v: setattr(v, 'sel', 0), me.verts) + + 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() \ No newline at end of file