「Dev:Source/Modeling/Sculpt Mesh」の版間の差分
細 |
細 (1版 をインポートしました) |
(相違点なし)
|
2018年6月29日 (金) 02:45時点における最新版
This is the latest version of Sculpt Mesh - unzip it and place it in your scripts folder, it now has Pen Table Support (using Michael Schardts plugin, Windows only...), which can be downloaded here http://members.fortunecity.de/pytablet , MirrorMode and it can maintain the subsurf level (although this is turned off by default).
MirrorMode is turned off if the mesh is too complex to give accurate mirroring. Also I've just added an 'undoMode' which is toggled with the UKey. It reverts the vert back to its position at the start of the script.
Use this version with 2.37, 2.36 and 2.35
or this version with current CVS
Install Procedure
1) Get release 2.37a (or 2.40alpha1 or CVS)
it can be downloaded from here
2) Download the script unzip it and then place it in your .scripts folder
the script can be downloaded from here
media:Sculpt_mesh_tablet23.zip
(media:Sculpt_mesh_tablet25.zip for CVS)
3) start up blender
4) select a mesh object and and make sure you are in object mode, also make
sure you only have ONE view3d window open (An error message will be given if you have more than one open)
5) goto a scripts window (changing the buttons window would be a good
choice) and then goto Scripts > Mesh > sculpt_mesh_tablet23...
6) LMB on the object and start sculpting
7) Use LMB + CTRLKEY to change the properties (selection radius, and
displacement height) these are also available via hotkeys (see below)
Also you can change whether you are in mirror mode via the properties popup menu
8) FKEY to flip/toggle displacement direction (OUT/IN)
9) UKEY to toggle in and out of undoMode
10) MKEY to toggle in and out of mirrorMode
11) Up and Down Arrowkeys to change the displacement height
12) Left and Right Arrowkeys to change the selection radius
13) ESCKEY to exit
Notes
The material will change to the default while you are sculpting.
Also 'big' meshes can go really slow. Mirror mode only works on extremely symetric meshes basically a cube subdivided to 1500 faces gives good results, but more than that it can give poor results.
Sculpt Mesh utilizes bining so that far fewer faces are examined each time through the loop.
This newest version also only updates the mesh part that is being worked on, which has given another substantial speed up (I can comfortably work on a 120,000 face mesh that displays about 1/4 of the object being sculpted). I'm using projection to local space right now, which is a bit faster, however I'm not sure how to project the alphamaps so instead it does it via a formula.
Demos & Movies
See these demos for an idea of what can be done - note that the frame rate is 3 frames per second so it is much faster than it looks. Also with the binning method, you can zoom in on the mesh and it will speed up the script.
- http://www.letwory.net/tutes/displace_mesh_test.htm
- http://www.letwory.net/tutes/displace_mesh_makehuman.htm
To-Do
Here is the todo list with brief descriptions of how to code them.
- Mirroring - I currently have mirroring enabled for the x axis only. Right now I base it on the vertex position relative to the objects center. Thus it is based on the expectation that the mesh is completely symmetrical. Ultimately I would like to base mirroring on converting the mouse coordinates to radial coordinates based on the objects center. This would allow mirroring on any axis (x,y,z or arbitrary) and radial mirroring as well. However, before I can do this, I need to change the way I do binning so that it is based on local space of the object, instead of based on the screen space (the current method).
- undo sculpt - this is currently implemented but undos back to the starting position of the vert.
- base alphamask on an image http://www.elysiun.com/forum/viewtopic.php?t=16326&highlight=getpixel The alphamask code was based purely on viewspace coordinates. Since I now use localspace for speed reasons, I need to figure out how to project the alphamap into screenspace as well.
- move the mesh in a direction other than along the normal - easy, some possibilities are along global coordinates or local coordinates, towards the screen, or constain to a coordinate direction (x,y,z), or constrain to some specified geometry
- smooth mode - calculate average face area, and per face area, as well as the selection average area, move the verts towards the center of the large faces
- pinch mode - add to the coordinate displacement that is perpendicular to normal at the mouse location
- add an inner radius where falloff doesn't happen
TomMusgrove - 12 Nov 2004
Code
#!BPY
""" Registration info for Blender menus:
Name: 'Sculpt Mesh Tablet29'
Blender: 237
Group: 'Mesh'
Tip: 'Sculpts mesh as if the mesh were clay'
"""
__author__ = ("Tom Musgrove", "Michael Schardt")
__url__ = ("blender", "elysiun", "http://mediawiki.blender.org/index.php/BlenderDev/SculpMesh")
__version__ = "2.38 31/10/05"
__bpydoc__ = """\
This script sculpts a mesh as if it were clay.
sculpt_mesh allows you to push and pull mesh vertices just by dragging the mouse over it.
Allowing easy and fast intuitive modeling.
Supported:<br>
a basic sculpt tool that allows you to specify a radius of influence and a displacement height. We now can use a pen tablet (MS Windows (TM)) such as Wacom (TM).
All displacement is along the vertex normals.
Usage:<br>
To use the script, select a mesh, run the script, then use the LMB to displace mesh,
and the LMB with the CTRL_KEY for a menu to change properties such as the selection radius
and displacement height, and mirroring Mode. For simple meshes Mirroring on the XAxis is
turned on by default. For complex meshes it is turned off by default if a vert could not
be matched with its mirror image. The FKEY toggles displacement in/out. The UKEY toggles
undoMode - which returns the verts that are selected back to their original position.
To exit the script press the ESCKEY.
It is recommended that the mesh have subsurface smoothing turned on, that the mesh be in object
mode, and that the shading method is Solid.
The tool can be speed up by zooming in on the object.
Missing:<br>
Future versions will allow other directions of displacement to be specified
(such as towards the screen, or only along a single local or global axis),
mirroring of the displacement (in the x,y,z locally or globally, as well as radial mirroring),
allow directional displacement, and allow the usage of alphamaps to specify displacment height and pattern.
Known issues:<br>
To improve speed the screen is subdivided into grids and then only faces within
the same grid as the mouse are evaluated. If displacement happens on a grid edge
then odd results may occur (ie some faces not getting displaced). Also if a face
is displaced beyond a grid boundary it may also give strange results. Lastly,
for faces that are steeply sloping away from the screen view the quandrant evaluation
can skip over faces that should be included, thus the user should try and limit
using the script to areas where most of the faces are mostly alighed with the camera.
Notes:<br>
"""
# $Id: sculpt_mesh_tablet22.py,v 1.9 2005/11/21 22:53:43 fstmm Exp $
#
#=======================================================#
# Sculpt Mesh v 0.32 by Tom Musgrove (LetterRip) #
# and Michael Schardt #
# if you have any questions about this script #
# email LetterRip at gmail dot com #
# #
#=======================================================#
# --------------------------------------------------------------------------
# Sculpt Mesh v 0.38 by Tom Musgrove (LetterRip) and Michael Schardt
# --------------------------------------------------------------------------
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program 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 version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------
import Blender; from Blender import *
from Blender.Mathutils import *
import math
try:
import psyco
psyco.full()
except:
pass
try:
import PyTablet
print (PyTablet.GetVersion()+" found\n")
if PyTablet.GetVersion() == "PyTablet v1.0b":
Tablet = PyTablet.New()
Tablet.SetRange("Pressure", [[0.01, 1.0], [None, None]])
Tablet.SetFallback("Pressure", [1.0, None])
else:
Tablet = None
print "PyTablet: wrong version number! (need PyTablet v1.0b)"
print "You can get the latest version of PyTablet at\n http://members.fortunecity.de/pytablet\n"
except:
Tablet = None
print "If you would like to use a Wacom or other tablet you can download PyTablet to enable using"
print "the pen pressure to control the sculpting radius and displacement"
print "You can get the latest version of PyTablet at\n http://members.fortunecity.de/pytablet\n"
# ********************************************************************************
# some math functions:
# --------------------
def cross_v3v3 (vector1, vector2):
return [vector1[1]*vector2[2] - vector1[2]*vector2[1],
vector1[2]*vector2[0] - vector1[0]*vector2[2],
vector1[0]*vector2[1] - vector1[1]*vector2[0]]
def dot_v2v2 (vector1, vector2):
return vector1[0]*vector2[0] + vector1[1]*vector2[1]
def dot_v3v3 (vector1, vector2):
return vector1[0]*vector2[0] + vector1[1]*vector2[1] + vector1[2]*vector2[2]
def dot_v4v4 (vector1, vector2):
return vector1[0]*vector2[0] + vector1[1]*vector2[1] + vector1[2]*vector2[2] + vector1[3]*vector2[3]
def add_v2v2 (vector1, vector2):
return [vector1[0]+vector2[0], vector1[1]+vector2[1]]
def add_v3v3 (vector1, vector2):
return [vector1[0]+vector2[0], vector1[1]+vector2[1], vector1[2]+vector2[2]]
def add_v4v4 (vector1, vector2):
return [vector1[0]+vector2[0], vector1[1]+vector2[1], vector1[2]+vector2[2], vector1[3]+vector2[3]]
def sub_v2v2 (vector1, vector2):
return [vector1[0]-vector2[0], vector1[1]-vector2[1]]
def sub_v3v3 (vector1, vector2):
return [vector1[0]-vector2[0], vector1[1]-vector2[1], vector1[2]-vector2[2]]
def sub_v4v4 (vector1, vector2):
return [vector1[0]-vector2[0], vector1[1]-vector2[1], vector1[2]-vector2[2], vector1[3]-vector2[3]]
def scale_v2s (vector, scalar):
return [vector[0]*scalar, vector[1]*scalar]
def scale_v3s (vector, scalar):
return [vector[0]*scalar, vector[1]*scalar, vector[2]*scalar]
def scale_v4s (vector, scalar):
return [vector[0]*scalar, vector[1]*scalar, vector[2]*scalar, vector[3]*scalar]
def normalize_v2 (vector):
length = math.sqrt (dot_v2v2 (vector, vector))
if length != 0.0: return scale_v2s (vector, 1.0/length)
return [0.0, 0.0]
def normalize_v3 (vector):
length = math.sqrt (dot_v3v3 (vector, vector))
if length != 0.0: return scale_v3s (vector, 1.0/length)
return [0.0, 0.0, 0.0]
def normalize_v4 (vector):
length = math.sqrt (dot_v4v4 (vector, vector))
if length != 0.0: return scale_v4s (vector, 1.0/length)
return [0.0, 0.0, 0.0, 0.0]
# ********************************************************************************
#returns the list of faces to check based on the four corners of the brush
def getFaceList(mouseX, mouseY):
global dictFaces
global alpha_size_x
global alpha_size_y
global maskOffset
#right now the maskOffset and the alpha_size_x and alpha_size_y
#are constants, they really should be calculated from the selection_size
#and current zoom level
#but they should be 'good enough' as long as we aren't really closely zoomed in
#or really far zoomed out...
list1 = []
list2 = []
list3 = []
list4 = []
listAppended = []
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()
return listAppended
def getMouseLocalCoordinates(screen_x, screen_y, useMid = False):
global myObject
#
# DESCRIPTION:
#
# actually the function name is completely misleading...
#
# the function returns a point and a direction vector in global coordinates.
# The point is the location of the virtual camera of the examined 3dwin (not a blender camera object!)
# The direction vector is the direction of a ray originating from the (vitual)screen at screencoords (screen_x, screen_y)
# and shooting towards this vitual camera position.
#
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.get('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:
screen_x = win_mid_x
screen_y = win_mid_y
if win_max_x > screen_x > win_min_x and win_max_y > screen_y > win_min_y:
# if the given screencoords (screen_x, screen_y) are within the 3dwin we fount the right one...
Window.QHandle(win3d.get('id'))
# first we handle all pending events for this window (otherwise the matrices might come out wrong)
# 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...
vmi = Matrix(Window.GetViewMatrix())
vmi.invert() # the inverse viewing matrix
pm = Matrix(Window.GetPerspMatrix()) # the prespective matrix
pmi = Matrix(Window.GetPerspMatrix())
pmi.invert() # the inverted perspective matrix
epsilon = 1e-3 # just a small value to account for floating point errors
if abs(pmi[3][3] - 1.0) < epsilon:
# pmi[3][3] is 1.0 if the 3dwin is in ortho-projection mode (toggled with numpad 5)
# 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.
d=Vector(list(Window.GetViewVector())+[0.0])
# all rays are parallel in ortho mode - so the direction vector is simply the viewing direction
hms = Vector([(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+1e6*d
# 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
dxy=[pm[3][3]*(((screen_x-win_min_x)/win_size_x)-1.0) - pm[3][0],
pm[3][3]*(((screen_y-win_min_y)/win_size_y)-1.0) - pm[3][1]]
fp=Vector([pmi[0][0]*dxy[0]+pmi[1][0]*dxy[1],
pmi[0][1]*dxy[0]+pmi[1][1]*dxy[1],
pmi[0][2]*dxy[0]+pmi[1][2]*dxy[1]])
# 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=Vector([vmi[3][0],vmi[3][1],vmi[3][2],vmi[3][3]])
# the camera's location in global 3dcoords can be read directly from the inverted viewmatrix
d=Vector(normalize_v3(sub_v3v3(p, fp))+[0.0])
# the direction vector is simply the difference vector from the virtual camera's position
#to the unprojected (screenspace) point fp
lp = p*Matrix(myObject.getInverseMatrix())
ld = d*Matrix(myObject.getInverseMatrix()) # normalize_v3
return True, lp, ld
else:
return False,Vector(),Vector()
def displace_vertices_on_own_normal (screen_x, screen_y):
# DESCRIPTION:
# ------------
# a do-it-all function (just for speed tests...)
# needs tweaking: the backface culling is still not working correctly in all situations
# ...and is only for convev meshes so far...
# ----------------------------------------------------------------------------
global selection_size
global myObject, workingObject, workingMesh
global displacement
global mirrorMode, correspVerts
global undoDict
mouseInView, lp,ld = getMouseLocalCoordinates(screen_x, screen_y)
if not mouseInView:
return
selection = {}
if Tablet and affsize:
selection_size2 = (selection_size * Tablet.Pen_GetPressure()[0])**2
else:
selection_size2 = (selection_size)**2
if Tablet and affdisp:
cur_displacement = displacement*Tablet.Pen_GetPressure()[0]
else:
cur_displacement = displacement
displacement_const = math.pi/selection_size2
listAppended = getFaceList(screen_x, screen_y)
for face in listAppended:
for vertex in face.v:
if vertex not in selection:
l2 = cross_v3v3(ld,sub_v3v3(lp,vertex.co))
size2 = dot_v3v3(l2,l2)
if size2 <= selection_size2:
if displaceMode:
dsc=cur_displacement*(math.cos(size2*displacement_const) + 1.0)
vertex.co[0] += vertex.no[0] * dsc
vertex.co[1] += vertex.no[1] * dsc
vertex.co[2] += vertex.no[2] * dsc
selection[vertex]=True
vertex.sel=1
if mirrorMode:
try:
tempVert = correspVerts[vertex]
tempVert.co[0] += tempVert.no[0] * dsc
tempVert.co[1] += tempVert.no[1] * dsc
tempVert.co[2] += tempVert.no[2] * dsc
selection[tempVert] = True
tempVert.sel = 1
except KeyError:
pass
elif undoMode:
tempCo = undoDict[vertex]
vertex.co[0] = tempCo[0]
vertex.co[1] = tempCo[1]
vertex.co[2] = tempCo[2]
selection[vertex]=True
vertex.sel=1
if mirrorMode:
try:
tempVert = correspVerts[vertex]
tempCo = undoDict[tempVert]
tempVert.co[0] = tempCo[0]
tempVert.co[1] = tempCo[1]
tempVert.co[2] = tempCo[2]
selection[tempVert]=True
tempVert.sel=1
except KeyError:
pass
else:
selection[vertex]=False
vertex.sel=0
workingObject.makeDisplayList()
workingMesh.update(1)
# ********************************************************************************
#we could use smaller grid subdivisions, which means fewer faces and verts per bin
#which means likely faster
def initializeFaceLists():
Window.WaitCursor(1)
selection = {}
global myObject
global alpha_size_x
global alpha_size_y
global dictFaces
global workingMesh
workingMesh.faces = []
workingMesh.verts = []
dictFaces = {}
facedotVecDict = {}
vectorDirtyDict = {}
cacheVecCoMultDict = {}
win_mid_x = win_mid_y = win_size_x = win_size_y = 0
view3d_count = 0
for win3d in Window.GetScreenInfo(Window.Types.VIEW3D):
view3d_count += 1
win_min_x , win_min_y, win_max_x, win_max_y = win3d.get('vertices')
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
if view3d_count > 1:
errmsg = "too many view3d windows, only one view3d can be opened to run sculpt mesh"
Blender.Draw.PupMenu('ERROR: %s' % errmsg)
return True # we can only handle one view so we are done
#testing ...
inView, lp,ld = getMouseLocalCoordinates(0,0, True)
facedotVec = 0.0
viewVector = Vector(Window.GetViewVector())
objectWorldMatrix = Matrix(myObject.getMatrix('worldspace'))
perspMatrix = Matrix(Blender.Window.GetPerspMatrix())
obj_mat_times_persp_mat = objectWorldMatrix*perspMatrix
for face in myMesh.faces:
facedotVecDict[face] = dot_v3v3(face.normal, sub_v3v3(lp, face.v[0]))
if facedotVecDict[face] > 1e-3:
vert_in_view = False
for vertex in face.v:
if vertex not in selection:
#calculate the vertex screen coordinates...
vectorDirtyDict[vertex.index] = vertex.co
tempVec = vertex.co
hvs = 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]
vert_grid_x = vs[0]/alpha_size_x
vert_grid_y = vs[1]/alpha_size_y
selection[vertex] = True
if (win_max_x >vs[0] > win_min_x) and (win_max_y > vs[1] > win_min_y):
vert_in_view = True
if vert_in_view:
#here we sort into bins
workingMesh.faces.append(face)
for vert in face.v:
workingMesh.verts.append(vert)
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
#we aren't interested in faces with a negative normal value
####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()
####remove the dupilcate faces and verts from workingMesh
nd = {}
for f in workingMesh.faces:
nd[f] = None
workingMesh.faces = nd.keys()
nd = {}
for v in workingMesh.verts:
nd[v] = None
workingMesh.verts = nd.keys()
Window.WaitCursor(0)
return False #we only had one view3d window so we can continue
# ********************************************************************************
def sculpt_popup_menu():
global selection_size, min_sel, max_sel, sel_inc
global displacement, min_dis, max_dis, dis_inc
global mirrorMode, displaceMode, undoMode, useSubSurfMode
global affsize, affdisp
mirrorstring = undostring = affsizestring = affdispstring = "off"
if mirrorMode: mirrorstring = "on"
if undoMode: undostring = "on"
if affsize: affsizestring = "on"
if affdisp: affdispstring = "on"
menustring = "Interactive Paint%t|%l|Selection Size: "+str(round(selection_size,2))+"%x1|Displacement: "+str(round(displacement,3))+"%x2|%l|MirrorMode is: "+mirrorstring+"%x3|UndoMode is: "+undostring+"%x4"
if Tablet: menustring += "|%l|%l|Tablet pressure affects:|%l|Selection Size: "+affsizestring+"%x5|Displacement: "+affdispstring+"%x6"
menu_result = Draw.PupMenu(menustring)
if menu_result == 1:
temp = Draw.PupFloatInput("Selection Size:", selection_size, min_sel, max_sel, sel_inc, 4)
if temp: selection_size = temp
if menu_result == 2:
temp = Draw.PupFloatInput("Displacement:", displacement, min_dis, max_dis, dis_inc, 4)
if temp: displacement = temp
if menu_result == 3:
mirrorMode = not mirrorMode
if menu_result == 4:
displaceMode = not displaceMode
undoMode = not undoMode
if menu_result == 5:
affsize = not affsize
if menu_result == 6:
affdisp = not affdisp
# if menu_result == 5:
# useSubSurfMode = not useSubSurfMode
# ********************************************************************************
def prep_object ():
global myMesh, workingMesh, workingObject, myObject, scene, useSubSurfMode, old_mode
old_mode = Window.EditMode()
Window.EditMode(0)
myObject.copyAllPropertiesTo(workingObject)
workingObject.loc = myObject.loc
workingObject.drawMode = myObject.drawMode
workingObject.drawType = myObject.drawType
workingObject.dsize = myObject.dsize
workingObject.rot = myObject.rot
workingObject.size = myObject.size
workingMesh.setSubDivLevels(myMesh.getSubDivLevels())
if useSubSurfMode:
workingMesh.setMode("SubSurf")
scene.unlink(myObject)
scene.link(workingObject)
workingObject.Layer = myObject.Layer
workingObject.makeDisplayList()
workingObject.select(True)
myObject.select(False)
# ********************************************************************************
def unprep_object():
global scene, workingObject, myObject, myMesh, old_mode
scene.unlink(workingObject)
scene.link(myObject)
workingObject.select(False)
myObject.makeDisplayList()
myObject.select(True)
myMesh.update()
Window.EditMode(old_mode)
# ********************************************************************************
def viewChanged(lastPerspMat, currentPerspMat):
epsilon = .0001
tempMat = currentPerspMat - lastPerspMat
for i in range(4):
for j in range(4):
if abs(tempMat[i][j]) >= epsilon:
return True
return False
# ********************************************************************************
#finds the corresponding vertices for mirror
def FindCorrespVerts():
epsilon = .0001
global myMesh
global correspVerts
global undoDict
global mirrorMode
vertexDict = {}
correspVerts = {}
badkeys = {}
centerVert = []
FindCorrespVertsSubLoop(1000)
if len(badkeys) > 0 or len(myMesh.verts) > len(correspVerts) + len(centerVert):
#print "in second loop"
#print len(myMesh.verts)
#print len(correspVerts)
#print len(centerVert)
#print len(badkeys)
FindCorrespVertsSubLoop(1000000)
if len(badkeys) > 0 or len(myMesh.verts) > len(correspVerts) + len(centerVert):
print "mesh is not perfectly symmetrical so turning mirrorMode off"
#print len(myMesh.verts)
#print len(correspVerts)
#print len(centerVert)
#print len(badkeys)
mirrorMode = False
#print "done loading keys"
def FindCorrespVertsSubLoop(size):
epsilon = 1.0/(size*10.0)
global myMesh
global correspVerts
global undoDict
vertexDict = {}
correspVerts = {}
badkeys = {}
centerVert = []
Window.WaitCursor(1)
for vert in myMesh.verts:
x,y,z = vert.co
undoDict[vert] = [x,y,z]
tempTuple = (int(round(x*size)), int(round(y*size)), int(round(z*size)))
vertexDict[tempTuple] = vert
for key in vertexDict:
x,y,z = key
tempKey = (-x, y, z)
try:
correspVerts[vertexDict[key]] = vertexDict[tempKey]
except KeyError:
if abs(key[0]) < epsilon:
centerVert.append(key)
else:
badkeys[vertexDict[key]] = None
Window.WaitCursor(0)
# ********************************************************************************
def scaleDispAndSelection():
global selection_size, max_sel, sel_inc
global displacement, min_dis, max_dis, dis_inc
sFactor = getScaleFactor()
selection_size *= sFactor
displacement *= sFactor
if selection_size > max_sel:
selection_size = max_sel
print "warning for best results it is recommended that you scale the object down by %s and then 'apply rotation and scaling'" % int(sFactor)
elif selection_size < min_sel:
selection_size = min_sel
temp = 2
if sFactor > 0 : temp = int(1.0/sFactor)
print "warning for best results it is recommended that you scale the object up by %s and then 'apply rotation and scaling'" % temp
if displacement > max_dis:
displacement = max_dis
elif displacement < +0.005:
displacement = +0.005
# ********************************************************************************
def getScaleFactor():
global myObject
global baseSize
boundingBox = myObject.getBoundBox()
tempVert = boundingBox[0]
boundx_min = boundx_max = tempVert[0]
boundy_min = boundy_max = tempVert[1]
boundz_min = boundz_max = tempVert[2]
for v in boundingBox:
if v[0] <= boundx_min:
boundx_min = v[0]
if v[0] >= boundx_max:
boundx_max = v[0]
if v[1] <= boundy_min:
boundy_min = v[1]
if v[1] >= boundy_max:
boundy_max = v[1]
if v[2] <= boundz_min:
boundz_min = v[2]
if v[2] >= boundz_max:
boundz_max = v[2]
avg_x = boundx_max-boundx_min
avg_y = boundy_max-boundy_min
avg_z = boundz_max-boundz_min
#now we find the most representative value of the three and use that as our scale factor
thisList = [avg_x,avg_y,avg_z]
#print thisList
thisList.sort()
#print thisList
return thisList[1]/baseSize
def updateSelectionSize(direction):
global selection_size, min_sel, max_sel, sel_inc
selection_size += direction*sel_inc
if selection_size > max_sel:
selection_size = max_sel
if selection_size < min_sel:
selection_size = min_sel
def updateDisplacementHeight(direction):
global displacement, min_dis, max_dis, dis_inc
displacement += direction*dis_inc
if displacement > max_dis:
displacement = max_dis
if displacement < min_dis:
displacement = min_dis
# ********************************************************************************
def main ():
global selection_size
global myMesh
global displacement
global myObject
global undoMode, displaceMode, mirrorMode
global wasinverted
currentPerspMat = None
lastPerspMat = Matrix(Window.GetPerspMatrix())
doRedraw = False
done = False
id = None
try:
myObject = Object.GetSelected()[0]
id = Window.GetScreenInfo(Window.Types.VIEW3D)[0].get('id')
except:
print "exiting because no object was selected or no view3d window was available"
return
if myObject.getType() == 'Mesh':
myMesh = myObject.getData()
done = initializeFaceLists()
scaleDispAndSelection()
FindCorrespVerts()
else:
return
while not done:
currentPerspMat = Matrix(Window.GetPerspMatrix())
if viewChanged(lastPerspMat, currentPerspMat):
done = initializeFaceLists()
lastPerspMat = Matrix(currentPerspMat)
# lastPerspMat = currentPerspMat
evt, val = Window.QRead()
if Tablet:
if Tablet.Pen_IsInverted():
if not wasinverted:
undoMode=True
displaceMode=False
wasinverted = True
if not Tablet.Pen_IsInverted():
if wasinverted:
undoMode=False
displaceMode=True
wasinverted = False
if evt in [Draw.MOUSEX, Draw.MOUSEY]:
continue # speed-up: ignore mouse movement
elif evt == Draw.ESCKEY: done = True
elif evt == Draw.LEFTMOUSE:
if Window.GetKeyQualifiers() == 0:
prep_object()
while Window.GetMouseButtons() == 1:
mouse_x, mouse_y = Window.GetMouseCoords()
displace_vertices_on_own_normal(mouse_x, mouse_y)
Blender.Window.Redraw(Window.Types.VIEW3D)
unprep_object()
Blender.Window.Redraw(Window.Types.VIEW3D)
# elif Window.GetKeyQualifiers() != 0:
elif Window.GetKeyQualifiers() == 48 and val:
sculpt_popup_menu()
# else:
# print "we should never get here"
# print evt
# print val
else:
print Window.GetKeyQualifiers()
continue
elif evt == Draw.REDRAW: Blender.Redraw(-1)
elif evt == Draw.FKEY and val: displacement *= -1.0 #Toggle displacement In/Out
elif evt == Draw.MKEY and val: mirrorMode = not mirrorMode #Toggle mirrorMode
elif evt == Draw.UKEY and val: #Toggle Undomode
displaceMode = not displaceMode
undoMode = not undoMode
elif evt in [Draw.LEFTARROWKEY] and val: #shrink radius
updateSelectionSize(-1.0)
elif evt in [Draw.RIGHTARROWKEY] and val: #grow radius
updateSelectionSize(+1.0)
elif evt in [Draw.UPARROWKEY] and val: #grow displacementHeight
updateDisplacementHeight(+1.0)
elif evt in [Draw.DOWNARROWKEY] and val: #shrink displacementHeight
updateDisplacementHeight(-1.0)
else:
Window.QAdd(id, evt, val)
Window.QHandle(id)
if(evt not in [Draw.MOUSEX, Draw.MOUSEY]): Blender.Redraw()
# ********************************************************************************
# ********************************************************************************
# globals:
# --------
selection_size = +0.30
max_sel = +1.00
min_sel = +.01
sel_inc = +.01
displacement = +0.025
max_dis = +.200
min_dis = -.200
dis_inc = +.005
baseSize = 4.0
old_mode = 0
undoDict = {}
correspVerts = {}
undoMode = False
mirrorMode = True
useSubSurfMode = False
displaceMode = True
affsize = True
affdisp = True
wasinverted = False
maxRadiusInPixels = 15
maskOffset = [maxRadiusInPixels, maxRadiusInPixels]
alpha_size_x = alpha_size_y = 2*maxRadiusInPixels
dictFaces = {}
scene = Scene.GetCurrent()
myMesh = None
myObject = None
workingMesh = NMesh.New("temp")
workingObject = Blender.Object.New('Mesh')
workingObject.link(workingMesh)
# main:
# -----
main()
# ********************************************************************************
# ********************************************************************************
comments: I tried it and it works nice, 1 are you going to make a gui so u can see how much displacement you get and how big the brush is u use? maybe as usable sliders? I might eventually do a mouse pointer that shows radius of effect and displacement height, but not in the immediate future 2 is there a way to write the displacement to a displacement map so u dont have tokeep the new object but just keep the displacement map and the old object? Yes, you need to use an external tool though, the ATI normal mapper and the nmf exporter combined should accomplish what you want.