blender/release/scripts/bpydata/mesh_bbrush.py
Campbell Barton 2a013c1b5b Fixed 2 bugs in mesh brush. 3805 and 3806
Verts being set to NAN and cursor being placed as well as painting.
2006-04-11 02:41:11 +00:00

923 lines
30 KiB
Python

# 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
hms[:] = (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[:] = ortho_d[:3]
# 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[:] = \
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[:] = 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[:] = 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(0)
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.01, 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)
# DESELECT ABSOLUTLY ALL
Mesh.Mode(Mesh.SelectModes['FACE'])
filter(lambda f: setattr(f, 'sel', 0), me.faces)
Mesh.Mode(Mesh.SelectModes['EDGE'])
filter(lambda ed: setattr(ed, 'flag', 32), me.edges)
Mesh.Mode(Mesh.SelectModes['VERTEX'])
filter(lambda v: setattr(v, 'sel', 0), me.verts)
Mesh.Mode(Mesh.SelectModes['EDGE'])
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 or str(medainNormal.x) == 'nan':
medainNormal = Vector(0,0,0)
if brush_verts:
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_PRESSURE * ((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 * falloff
else:
v.co += no * falloff
elif DISPLACE_NORMAL_MODE == 2: # MEDIAN NORMAL # FIXME
v.co += medainNormal * falloff
elif DISPLACE_NORMAL_MODE == 3: # FACE NORMAL
v.co += iFaceNormal * falloff
elif DISPLACE_NORMAL_MODE == 4: # VIEW NORMAL
v.co += Direction * 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)
me = ob.getData(mesh=1)
filter(lambda ed: setattr(ed, 'flag', 32), me.edges)
# 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):
if XPLANE_CLIP:
# If 1 vert is on the edge and abother is off dont collapse edge.
if (abs(ed.v1.co.x) < 0.001) !=\
(abs(ed.v2.co.x) < 0.001):
continue
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
ed.v1.co[:]= ed.v2.co[:]= newco
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)
# Remove short edges of we have edaptive geometry enabled.
if ADAPTIVE_GEOMETRY:
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)
Blender.event = None
if __name__ == '__main__':
event_main()