Código:
#.Bpy.
Name: Sculpt Mesh.
Blender: 235
Group: Mesh.
Tip: Sculpts Mesh pushing, pulling and twisting.
# $id:sculpt_mesh, py. 2 2004/10/17 02:16:31 broken exp $
#
#===============================================#
# Sculpt Mesh v 0.2 by Tom musgrove (letterrip) #
# and Michael scardt #
# if you have any questions about this script #
# email letterrip at Gmail Dot com #
# #
#===============================================#.
# -
# Sculpt Mesh v 0.2 by Tom musgrove (letterrip) and Michael scardt.
# -
# ***** begin GPL license block *****
#
# this programa is free software, you can redistribute it and/or.
# modify it under the terms of the GNU general public license.
# as published by the free software foundation, either versión 2
# of the license, or (at your option) any later versión.
#
# this programa is distributed in the hope that it Will be useful, # but without any warranty, without Even the implied warranty of.
# merchantability or fitnes for a particular purpose. Se the.
# GNU general public license for more details.
#
# you should have received a copy of the GNU general public license.
# along with this programa, if not, write todo the free software foundation, # inc, 59 temple place - Suite 330, Boston, ma 02111-1307, usa.
#
# ***** end GPL licence block *****
# -
Import Blender.
From Blender import object, nmesh, window, draw, Mathutils, types.
From Blender. Mathutils import *
From math import sin, cos, sqrt, pi, hypot.
# import psyco if available, psyco can often give substantial speed ups.
Try:
Import psyco.
# psyco, log()
# psyco, profile ()
Psyco, full()
Except importerror:
Pass.
Displacemode = 1 # 0 is of, 1 is displaceout, -1 is displacein, 2 is smooth.
# Smooth isnt implemented yet.
Displacementheight = 1.0 #
Radiusminimum = 5 # the smallest size of the selection Radius in píxeles.
Radiusmaximum = 20 #this gives us a grid size of 100 por 100 which isnt that.
Big of a savings.
#the smaller the grid size the greater the savings.
Alpha_size_x = alpha_size_y = 2*radiusmaximum.
# no user friendly GUI yet.
# todo change the formula used, change falloftype todo.
# one of none, linear, sin, cos, squarerot, squared, cubic, and quadratic.
# then change the selectionradius.
# the xsize and ysize of the image are the first two numbers of the maskid.
# except in the case of the radial functions, in which case it is the Radius.
# a los may change it todo inner and outer Radius or other such Stuff.
# the string is the id of the mask id for formula based it is falloftype +
Maxradius.
Falloftype = linear.
Selection_size_x = selection_size_y = selectionradius = 20
Maskname = falloftype+repr(selectionradius)
Maskid = (selectionradius, selectionradius, maskname).
Win_min_x = win_min_y = win_max_x = win_max_y = win_mid_x = win_mid_y =
Win_size_x = win_size_y = 0
#the above are the corners of the 3d view window, and the scaling factors.
Mousex = mousey = 0
Obj_mat_times_persp_mat = Matrix()
Mymesh = nmesh. New(temp)
Selectedverts = []
Dictofalphamasks = {}
Myobject = Blender. Object. New(Mesh)
Lastviewvector = [-100,100,-100] #right now these doesnt do much.
Currentviewvector = [-100,-100,-100] #but, if we allow screen rotation then.
They Will trigger an update of the matrices.
Obj_mat = Matrix() #contains the Matrix of the object (local? Or global? I
Thinque global.)
Perspmatrix = Matrix() #contains the perspective Matrix, and allows the.
Scren coordinates todo be projected todo the screen.
Dictfaces = {}
Facenormalworldvecdict = {}
Facedotvecdict = {}
Vectordirtydict = {}
Cacheveccomultdict = {}
Vectordirty = false.
# gets the Mesh data from the selected object.
# finds the selected vértices.
# and then selects vértices based on the alpha mask.
Def initialize ():
Global myobject, mymesh, currentviewvector, lastviewvector.
Myobject = object. Getselected()[0]
If myobject, gettype () == Mesh:
Mymesh = myobject, getdata ()
Name = myobject, name.
Else:
Print we either did not have a Mesh or did not have an object selected.
Mousex, mousey = window. Getmousecords()
Updateviewcords(mousex, mousey)
Mouseinview3dwindow(mousex, mousey)
Currentviewvector = window. Getviewvector()
Lastviewvector = currentviewvector.
Initializefacelists()
Updateradius(maskid[0], 0, falloftype) #this initializes the starting.
Alphamask.
#we could actually create two lists from each, one that contains the Faces.
With normals towards the.
#Camera, and the other without, and thus traverse our Faces a bit faster.
#alos we could use smaller grid subdivisions, which means fewer Faces and.
Verts per bin.
#which means likely faster.
Def initializefacelists():
Selection = {}
Global mymesh, dictfaces, facenormalworldvecdict, facedotvecdict.
Global vectordirtydict, cacheveccomultdict.
Dictfaces = {}
Facenormalworldvecdict = {}
Facedotvecdict = {}
Vectordirtydict = {}
Cacheveccomultdict = {}
Global alpha_size_x.
Global alpha_size_y.
Global vectordirty.
Facedotvec = 0.0
Vectordirty = false.
Viewvector = Vector(window. Getviewvector())
Objectworldmatrix = myobject, getmatrix(worldspace)
Perspmatrix = Blender.window. Getperspmatrix()
Obj_mat_times_persp_mat = objectworldmatrix*perspmatrix.
For face in mymesh, Faces:
Gfn = vecmultmat(Vector(list(face, normal[:3])+[1.0]), objectworldmatrix)
Gfn = Vector(list(gfn[:3]))
Facenormalworldvecdict[face] = gfn.
Facedotvecdict[face] = dotvecs(gfn, viewvector)
For vertex in face, v:
If vertex not in selection:
#calculate the vertex screen coordinates.
Vectordirtydict[vertex.index] = vertex.co.
Tempvec = vertex.co.
Hvs = vecmultmat(Vector(list(tempvec[:3])+[1.0]), obj_mat_times_persp_mat)
Hvs[0] /= HVS[3]
Hvs[1] /= HVS[3].
Vs = [int(win_mid_x + (HVS[0] * win_size_x)), int(win_mid_y + (HVS[1] * win_size_y))]
Cacheveccomultdict[vertex.index] = vs.
Else:
Vs = cacheveccomultdict[vertex.index]
##### now.
Vert_grid_x = vs[0]/alpha_size_x.
Vert_grid_y = vs[1]/alpha_size_y.
Templist = []
Try:
Templist = dictfaces[(vert_grid_x, vert_grid_y)]
Templist, append(face)
Except keyerror:
Templist, append(face)
Dictfaces[(vert_grid_x, vert_grid_y)] = templist.
Selection[vertex] = true.
####and now we remove the duplicate Faces.
For key in dictfaces.keys():
Listappended = dictfaces[key]
Nd={}
For f in listappended:
Nd[f]=none.
Dictfaces[key] = nd.keys().
# takes a vert and based on its screen location relative todo the mouse.
# is displaced according todo an alphamask.
# Radius and fallof are done vía computing the mask once.
# the alphamaskdict is specified by a maskid.
# mask id is a tuple.
# with four values, # xoffset, yoffset, maskname.
# for Radius functions, maskname is falloftyperadius.
# iExplorer for a linear of Radius 8, it would be linear8
# and the tuple would be (8,8,linear8)
# for imagebased, it is usually the image name.
Def displaceselection(maskid, mousex, mousey, displacemode=0, displacementheight= 0.0):
Global mymesh, dictofalphamasks, alpha_size_x, alpha_size_y, dictfaces, facenormalworldvecdict.
Global facedotvecdict, cacheveccomultdict, vectordirtydict.
Selection = {}
If maskid not in dictofalphamasks:
# should handle image masks as well, but for now, we Will handle only the.
Radius masks.
Updateradius(maskid[0], 0, falloftype)
Alphamaskdict = dictofalphamasks[maskid]
Maskoffset = []
Maskoffset, append(maskid[0])
Maskoffset, append(maskid[1])
Displacemh = displacementheight*displacemode.
Mouse_and_maskx = maskoffset[0] - Mousex.
Mouse_and_masky = maskoffset[1] - Mousey.
Viewvector = Vector(window. Getviewvector())
Objectworldmatrix = myobject, getmatrix(worldspace)
Perspmatrix = Blender.window. Getperspmatrix()
Obj_mat_times_persp_mat = objectworldmatrix*perspmatrix.
Emptyvector = Vector()
#### here we find which Grids the alphamask is in, and return a list of.
Faces.
Try:
List1 = dictfaces[((mousex + maskoffset[0])/alpha_size_x, (mousey -
Maskoffset[1])/alpha_size_y)]
Except keyerror:
List1 = []
Try:
List2 = dictfaces[((mousex + maskoffset[0])/alpha_size_x, (mousey +
Maskoffset[1])/alpha_size_y)]
Except keyerror:
List2 = []
Try:
List3 = dictfaces[((mousex - Maskoffset[0])/alpha_size_x, (mousey -
Maskoffset[1])/alpha_size_y)]
Except keyerror:
List3 = []
Try:
List4 = dictfaces[((mousex - Maskoffset[0])/alpha_size_x, (mousey +
Maskoffset[1])/alpha_size_y)]
Except keyerror:
List4 = []
Listappended = list1+list2+list3+list4
####and now we remove the duplicate Faces.
Nd={}
For f in listappended:
Nd[f]=none.
Listappended = nd.keys()
For face in mymesh, Faces:
If (face in listappended):
If facenormalworldvecdict[face] == emptyvector:
Gfn = vecmultmat(Vector(list(face, normal[:3])+[1.0]), objectworldmatrix)
Gfn = Vector(list(gfn[:3]))
Facenormalworldvecdict[face] = gfn.
Facedotvecdict[face] = dotvecs(gfn, viewvector)
Elif vectordirty:
Gfn = facenormalworldvecdict[face]
Facedotvecdict[face] = dotvecs(gfn, viewvector)
Facedotvec = facedotvecdict[face]
If facedotvec > 0: # simple Backface-culling.
Facedirtycount = 0
For vertex in face, v:
If (vertex.index not in selection):
# we ned todo calculate the distance of the vert from the mouse.
# thus we ned either the screen x and y of the vértice.
# or the UV x, y of the mouse and of the vértice.
# the offset is the distance from the mouse location, todo the lower.
Left corner.
# of the mask.
Try:
Tempco = vectordirtydict[vertex.index]
Except keyerror:
Tempco = none.
If tempco == vertex.co:
Vs = cacheveccomultdict[vertex.index]
Else:
Vectordirtydict[vertex.index] = vertex.co.
Tempvec = vertex.co.
Hvs = vecmultmat(Vector(list(tempvec[:3])+[1.0]), obj_mat_times_persp_mat)
Hvs[0] /= HVS[3]
Hvs[1] /= HVS[3].
Vs = [int(win_mid_x + (HVS[0] * win_size_x)), int(win_mid_y + (HVS[1] * win_size_y))]
Cacheveccomultdict[vertex.index] = vs.
Deltax = vs[0] + mouse_and_maskx.
Deltay = vs[1] + mouse_and_masky.
Try:
Thisdisplacement = alphamaskdict[(deltax, deltay)]
Vertex.sel = 1
Except keyerror:
Thisdisplacement = 0.0
Vertex.sel = 0
If vertex.sel == 1:
#if we are just updating the selection.
If displacemode == 0:
Pass.
#if we are displacing the vertex out.
#or if we are displacing the vertex in.
Elif (displacemode == 1) or (displacemode == -1):
Tempdisplace = thisdisplacement*displacemh.
Vertex.co[0] += vertex, no[0]*tempdisplace.
Vertex.co[1] += vertex, no[1]*tempdisplace.
Vertex.co[2] += vertex, no[2]*tempdisplace.
#since the vertex changed, #we ned todo update the Faces normal.
#información based on the new vertex location.
Facedirtycount += 1
###we ned todo find the new screen location of the moved vértices.
### and if they are in a new grid locations we ned todo add the face todo the.
New Grids face list.
# we should probably pop the face of of its original face lists if we.
Expect vértices todo change.
# quadrants frequently otherwise our facelists Will son have Faces that.
Shouldnt be in it.
# of course we may rebuild our face list often enough that this might not.
Be a significant issue.
# perhaps we should rebuild a single face list based on its length.
#we might a los consider subdividing a grid if it has more than a certain.
Number of Faces.
#this is todo let the loop know that weve already handled this vertex.
#so it doesnt do calculations on it twice.
Selection[vertex.index]=true.
#right now we donl have Smooth implemented.
If(facedirtycount > 0):
Facenormalworldvecdict[face] = emptyvector.
#displaces vértices towards the vert average normal.
Def smoothselection():
#we ned todo first calculate the average normal for the selected vértices.
#probably we should exlude outliers todo give better results.
#then find the diference between average normal and the current vértices.
Normal.
Print we smoothed the selection.
Pass.
# takes an image and its size (or just an image and gets the size from the.
Image?
# use code ideas from this enlace todo implement.
# http://www.elysiun.com/forum/viewtopic.php?t=16326&highlight=getpixel.
# instead of doing positive and negative numbers.
# i define all locations from the lower left corner.
Def alphamaskdictfromalphamap(xsize, ysize, alpha map):
Global dictofalphamasks.
Alphamaskdict = {}
For x in range (0, xsize):
For y in range (0, ysize):
Alphacords = (x, y)
Thisdisplacement = alpha map, alphacords.
Alphamaskdict[alphacords] = thisdisplacement.
Maskid = (int(xsize/2), int(ysize/2), alpha map, name)
Dictofalphamasks[maskid] = alphamaskdict.
#
#
Def updateradius(selectionradius, deltaradius, falloftype):
Global maskid, dictofalphamasks.
Print update Radius.
Selectionradius += deltaradius.
If selectionradius < radiusminimum:
Selectionradius = radiusminimum.
If selectionradius > radiusmaximum:
Selctionradius = radiusmaximum.
Maskname = falloftype+repr(selectionradius)
Maskid = (selectionradius, selectionradius, maskname)
If maskid not in dictofalphamasks:
Dictofalphamasks[maskid] = alphamaskdictfromradius(selectionradius, falloftype)
Selection_size_x = selection_size_y = selectionradius.
Return selectionradius.
Def updatedisplacement(displacementheight, deltaheight):
Displacementheight = displacementheight + deltaheight.
Return displacementheight.
#
# not neded but would be nice.
Def drawselectioncircle (selectionradius):
#print drawing the selection circle.
Pass.
# creates an alphamask from the Radius supplied.
# this gives two benefits - The first is that we can use any alphamask.
# instead of using just Radius based functions with fallof.
# the second is that this gives us a lookup table and we dont have todo.
# calculate the Radius function a bazillion times.
Def alphamaskdictfromradius(Radius = 8, falloftype = none):
#note that instead of negative numbers, im using an offset of.
#of the Radius, and use all positive numbers.
Thisradius2 = 0
Radius2 = Radius*Radius.
Alphamaskdict = {}
For x in range (0, Radius):
For y in range (0, Radius):
Thisradius2 = x2+y2
If thisradius2 <= radius2:
Thisradius = sqrt(thisradius2)
Thisdisplacement = radiusfunction(thisradius, Radius, falloftype)
Alphamaskdict[(x+Radius,-y+Radius)]=thisdisplacement.
Alphamaskdict[(-x+Radius, y+Radius)]=thisdisplacement.
Alphamaskdict[(-x+Radius,-y+Radius)]=thisdisplacement.
Alphamaskdict[(x+Radius, y+Radius)]=thisdisplacement.
Return alphamaskdict.
# falloftypes are none, linear, sin, cos, squarerot, squared, cubic, and.
Quadratic.
# these are adapted from taper and twist.
# by flippyneck.
Def radiusfunction(thisradius, maxradius, falloftype=none):
If falloftype == none:
Return 1.0
If falloftype == cubic:
Return interpolatecubic(0, maxradius, thisradius)
Elif falloftype == quadratic:
Return interpolatequadratic(0, maxradius, thisradius)
Elif falloftype == linear:
Return interpolatelinear(0, maxradius, thisradius)
Elif falloftype == sin:
Return interpolatesin(0, maxradius, thisradius)
Elif falloftype == cos:
Return interpolatecos(0, maxradius, thisradius)
Elif falloftype == squarerot:
Return interpolaterot(0, maxradius, thisradius)
Elif falloftype == squared:
Return interpolatelinear(0, maxradius, thisradius).
Def interpolatelinear(min, 3ds Max, myval):
Returns 0.0 <= myval <=1.0
Return myval/float((max-min)).
Def interpolatesin(min, 3ds Max, myval):
N=interpolatelinear(min, 3ds Max, myval)
Return sin(n).
Def interpolatecos(min, 3ds Max, myval):
N=interpolatelinear(min, 3ds Max, myval)
Return cos(n).
Def interpolatesquared(min, 3ds Max, myval):
N=interpolatelinear(min, 3ds Max, myval)
Return n**2.
Def interpolaterot(min, 3ds Max, myval):
N=interpolatelinear(min, 3ds Max, myval)
Return sqrt(n).
Def interpolatecubic(min, 3ds Max, myval):
N=interpolatelinear(min, 3ds Max, myval)
Return n**3.
Def interpolatequadratic(min, 3ds Max, myval):
N=interpolatelinear(min, 3ds Max, myval)
Return n**4.
#
********************************************************************************.
# init screen coordinates.
# these should be updated if the screen is resized, etc, Def updateviewcords(scren_x, scren_y):
Global win3d, win_mid_x, win_mid_y, win_max_x, win_max_y, win_min_x, win_min_y.
Global win_size_x, win_size_y, acceptable_min_x, acceptable_min_y, acceptable_max_x, acceptable_max_y.
Global selection_size_x, selection_size_y.
For win3d in Blender.window. Getscreninfo (blender.window. Types. View3d):
Win_min_x, win_min_y, win_max_x, win_max_y = win3d, get(vértices)
Win_mid_x = (win_max_x + win_min_x) * 0.5
Win_mid_y = (win_max_y + win_min_y) * 0.5
Win_size_x = (win_max_x - Win_min_x) * 0.5
Win_size_y = (win_max_y - Win_min_y) * 0.5.
Acceptable_min_x = scren_x - Selection_size_x.
Acceptable_min_y = scren_y - Selection_size_y.
Acceptable_max_x = scren_x + selection_size_x.
Acceptable_max_y = scren_y + selection_size_y.
If (win_max_x > scren_x > win_min_x) and (win_max_y > scren_y >
Win_min_y):
Return true.
Else:
Return false.
Def mouseinview3dwindow(mousex, mousey):
# we calculate the screens midpoints and the scaling factor for the screen.
# and then chek if the mouse is in the view3d Area.
# we should really only do the calculations the first time.
# thus it should prbably.
Global win3d, win_mid_x, win_mid_y, win_max_x, win_max_y, win_min_x, win_min_y.
Global win_size_x, win_size_y, acceptable_min_x, acceptable_min_y, acceptable_max_x, acceptable_max_y.
Global selection_size_x, selection_size_y.
For win3d in Blender.window. Getscreninfo (blender.window. Types. View3d):
Win_min_x, win_min_y, win_max_x, win_max_y = win3d, get(vértices)
If (win_max_x > mousex > win_min_x) and (win_max_y > mousey > win_min_y):
Return true.
Else:
Return false.
# here is the main loop.
Mousestartx, mousestarty = window. Getmousecords()
Mousex = mousey = 0
Radiusincrement = 5
Updatefrequency = 0 # number of píxeles todo move before updating the vértice.
Selection.
Selectedverts = []
Updatebounds = true.
Initialize ()
Done = 0
While not done:
Evt, val = window. Qread()
If evt in [4]: #draw. Rightmouse is 3 which is wrong.
# first we ned todo chek if the mouse is inside the view3d boundbox.
Currentviewvector = window. Getviewvector()
If currentviewvector.= lastviewvector:
Lastviewvector = currentviewvector.
#vectordirty = true.
#instead we Will initialize the face lists.
#this should probably be done elsewhere so that there isnt a huge lag on.
The firt RMB.
#press.
Initializefacelists()
While window. Getmousebuttons() == 4:
If window. Getkeyqualifiers() == 0:
Mousex, mousey = window. Getmousecords()
If mouseinview3dwindow(mousex, mousey):
# determine if the mouse has moved far enough for an update.
Mousedistx = mousestartx - Mousex.
Mousedisty = mousestarty - Mousey.
Mousedistancemoved = hypot(mousedistx, mousedisty)
If mousedistancemoved >= updatefrequency:
Mousestartx, mousestarty = mousex, mousey.
Old_mode = window. Editmode ()
Window. Editmode (0)
Displaceselection(maskid, mousex, mousey, displacemode, displacementheight)
Mymesh, update ()
Window. Editmode (old_mode)
Blender.window. Redraw(window. Types. View3d)
Elif window. Getkeyqualifiers().= 0:
If displacemode == +1: displacement_string = out.
Elif displacemode == -1: displacement_string = in.
Menu_result = draw. Pupmenu (interactive Paint%t|%l|selection size:
+str(selectionradius)+%x1|displacement:
+displacement_string+%x2|%l|quit%x3)
If (menu_result == 1):
Selection_size = draw. Pupintinput(selection size:, selectionradius,
1, 100)
Deltaradius = selection_size - Selectionradius.
Selectionradius = updateradius(selectionradius, deltaradius, falloftype)
If (menu_result == 2):
Displacemode *= -1
If (menu_result == 3):
Done = true.
Draw. Redraw (window. Qtest()).
#drawselectioncircle (selectionradius).
# elif evt in [draw. Skey]:
# if val:
# displacemode = 2 #smooth mode.
# else:
# displacemode = 0
Elif not val or evt in [draw. Mousex, draw. Mousey]: continúe #helps speed.
Elif evt in [draw. Esckey, draw. Qkey]: done = true.
Elif evt in [draw. Leftarrowkey] and val: #Shrink Radius.
Selectionradius = updateradius(selectionradius, -radiusincrement, falloftype)
Elif evt in [draw. Rightarrowkey] and val: #grow Radius.
Selectionradius = updateradius(selectionradius, radiusincrement, falloftype)
Elif evt in [draw. Uparrowkey] and val: #grow displacementheight.
Displacementheight = updatedisplacement(displacementheight, 0.1)
Elif evt in [draw. Downarrowkey] and val: #Shrink displacementheight.
Displacementheight = updatedisplacement(displacementheight, -0.1)
Else: #otherwise pass the event bak and let Blender handle it.
Id = window. Getscreninfo (window. Types. View3d)[0].get(id)
Window. Qadd(id, evt, val)
Window. Qhandle (id)
Blender. Redraw(-1) #dont forget todo redraw.
Pass