forked from bartvdbraak/blender
3e1a5ce7a2
moved bpy into bpy.data and bpy will be eventually replace the root level 'Blender' module. currently we have bpy.library bpy.config and bpy.data
345 lines
9.2 KiB
Python
345 lines
9.2 KiB
Python
#!BPY
|
|
"""
|
|
Name: 'Solidify Selection'
|
|
Blender: 243
|
|
Group: 'Mesh'
|
|
Tooltip: 'Makes the mesh solid by creating a second skin.'
|
|
"""
|
|
|
|
__author__ = "Campbell Barton"
|
|
__url__ = ("www.blender.org", "blenderartists.org")
|
|
__version__ = "1.1"
|
|
|
|
__bpydoc__ = """\
|
|
This script makes a skin from the selected faces.
|
|
Optionaly you can skin between the original and new faces to make a watertight solid object
|
|
"""
|
|
|
|
# --------------------------------------------------------------------------
|
|
# Solidify Selection 1.0 by Campbell Barton (AKA Ideasman42)
|
|
# --------------------------------------------------------------------------
|
|
# ***** 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 *****
|
|
# --------------------------------------------------------------------------
|
|
|
|
from Blender import *
|
|
import bpy
|
|
import BPyMesh
|
|
# reload(BPyMesh)
|
|
import BPyMessages
|
|
# reload(BPyMessages)
|
|
|
|
from BPyMathutils import angleToLength
|
|
|
|
# python 2.3 has no reversed() iterator. this will only work on lists and tuples
|
|
try:
|
|
reversed
|
|
except:
|
|
def reversed(l): return l[::-1]
|
|
|
|
def copy_facedata_multilayer(me, from_faces, to_faces):
|
|
'''
|
|
Tkes 2 lists of faces and copies multilayer data from 1 to another
|
|
make sure they are aligned, cant copy from a quad to a tri, used for solidify selection.
|
|
'''
|
|
|
|
def copy_default_face(data):
|
|
face_from, face_to = data
|
|
face_to.mat = face_from.mat
|
|
face_to.smooth = face_from.smooth
|
|
face_to.sel = True
|
|
face_from.sel = False
|
|
|
|
def copy_tex_face(data):
|
|
face_from, face_to = data
|
|
face_to.uv = [c for c in reversed(face_from.uv)]
|
|
face_to.mode = face_from.mode
|
|
face_to.flag = face_from.flag
|
|
face_to.image = face_from.image
|
|
|
|
def copy_col_face(data):
|
|
face_from, face_to = data
|
|
face_to.col = [c for c in reversed(face_from.col)]
|
|
|
|
# make a list of face_from, face_to pairs
|
|
#face_pairs = zip(faces_sel, [me_faces[len_faces + i] for i in xrange(len(faces_sel))])
|
|
face_pairs = zip(from_faces, to_faces)
|
|
|
|
# Copy properties from 1 set of faces to another.
|
|
map(copy_default_face, face_pairs)
|
|
|
|
for uvlayer in me.getUVLayerNames():
|
|
me.activeUVLayer = uvlayer
|
|
map(copy_tex_face, face_pairs)
|
|
|
|
for collayer in me.getColorLayerNames():
|
|
me.activeColorLayer = collayer
|
|
map(copy_col_face, face_pairs)
|
|
|
|
# Now add quads between if we wants
|
|
|
|
|
|
Ang= Mathutils.AngleBetweenVecs
|
|
SMALL_NUM=0.00001
|
|
|
|
def solidify(me, PREF_THICK, PREF_SKIN_SIDES=True, PREF_REM_ORIG=False, PREF_COLLAPSE_SIDES=False):
|
|
|
|
# Main code function
|
|
me_faces = me.faces
|
|
faces_sel= [f for f in me_faces if f.sel]
|
|
|
|
BPyMesh.meshCalcNormals(me)
|
|
normals= [v.no for v in me.verts]
|
|
vertFaces= [[] for i in xrange(len(me.verts))]
|
|
for f in me_faces:
|
|
no=f.no
|
|
for v in f:
|
|
vertFaces[v.index].append(no)
|
|
|
|
# Scale the normals by the face angles from the vertex Normals.
|
|
for i in xrange(len(me.verts)):
|
|
length=0.0
|
|
if vertFaces[i]:
|
|
for fno in vertFaces[i]:
|
|
try:
|
|
a= Ang(fno, normals[i])
|
|
except:
|
|
a= 0
|
|
if a>=90:
|
|
length+=1
|
|
elif a < SMALL_NUM:
|
|
length+= 1
|
|
else:
|
|
length+= angleToLength(a)
|
|
|
|
length= length/len(vertFaces[i])
|
|
#print 'LENGTH %.6f' % length
|
|
# normals[i]= (normals[i] * length) * PREF_THICK
|
|
normals[i] *= length * PREF_THICK
|
|
|
|
|
|
|
|
len_verts = len( me.verts )
|
|
len_faces = len( me_faces )
|
|
|
|
vert_mapping= [-1] * len(me.verts)
|
|
verts= []
|
|
for f in faces_sel:
|
|
for v in f:
|
|
i= v.index
|
|
if vert_mapping[i]==-1:
|
|
vert_mapping[i]= len_verts + len(verts)
|
|
verts.append(v.co + normals[i])
|
|
|
|
#verts= [v.co + normals[v.index] for v in me.verts]
|
|
|
|
me.verts.extend( verts )
|
|
#faces= [tuple([ me.verts[v.index+len_verts] for v in reversed(f.v)]) for f in me_faces ]
|
|
faces= [ tuple([vert_mapping[v.index] for v in reversed(f.v)]) for f in faces_sel ]
|
|
me_faces.extend( faces )
|
|
|
|
|
|
|
|
|
|
# Old method before multi UVs
|
|
"""
|
|
has_uv = me.faceUV
|
|
has_vcol = me.vertexColors
|
|
for i, orig_f in enumerate(faces_sel):
|
|
new_f= me_faces[len_faces + i]
|
|
new_f.mat = orig_f.mat
|
|
new_f.smooth = orig_f.smooth
|
|
orig_f.sel=False
|
|
new_f.sel= True
|
|
new_f = me_faces[i+len_faces]
|
|
if has_uv:
|
|
new_f.uv = [c for c in reversed(orig_f.uv)]
|
|
new_f.mode = orig_f.mode
|
|
new_f.flag = orig_f.flag
|
|
if orig_f.image:
|
|
new_f.image = orig_f.image
|
|
if has_vcol:
|
|
new_f.col = [c for c in reversed(orig_f.col)]
|
|
"""
|
|
copy_facedata_multilayer(me, faces_sel, [me_faces[len_faces + i] for i in xrange(len(faces_sel))])
|
|
|
|
if PREF_SKIN_SIDES or PREF_COLLAPSE_SIDES:
|
|
skin_side_faces= []
|
|
skin_side_faces_orig= []
|
|
# Get edges of faces that only have 1 user - so we can make walls
|
|
edges = {}
|
|
|
|
# So we can reference indicies that wrap back to the start.
|
|
ROT_TRI_INDEX = 0,1,2,0
|
|
ROT_QUAD_INDEX = 0,1,2,3,0
|
|
|
|
for f in faces_sel:
|
|
f_v= f.v
|
|
for i, edgekey in enumerate(f.edge_keys):
|
|
if edges.has_key(edgekey):
|
|
edges[edgekey]= None
|
|
else:
|
|
if len(f_v) == 3:
|
|
edges[edgekey] = f, f_v, i, ROT_TRI_INDEX[i+1]
|
|
else:
|
|
edges[edgekey] = f, f_v, i, ROT_QUAD_INDEX[i+1]
|
|
del ROT_QUAD_INDEX, ROT_TRI_INDEX
|
|
|
|
# So we can remove doubles with edges only.
|
|
if PREF_COLLAPSE_SIDES:
|
|
me.sel = False
|
|
|
|
# Edges are done. extrude the single user edges.
|
|
for edge_face_data in edges.itervalues():
|
|
if edge_face_data: # != None
|
|
f, f_v, i1, i2 = edge_face_data
|
|
v1i,v2i= f_v[i1].index, f_v[i2].index
|
|
|
|
if PREF_COLLAPSE_SIDES:
|
|
# Collapse
|
|
cv1 = me.verts[v1i]
|
|
cv2 = me.verts[vert_mapping[v1i]]
|
|
|
|
cv3 = me.verts[v2i]
|
|
cv4 = me.verts[vert_mapping[v2i]]
|
|
|
|
cv1.co = cv2.co = (cv1.co+cv2.co)/2
|
|
cv3.co = cv4.co = (cv3.co+cv4.co)/2
|
|
|
|
cv1.sel=cv2.sel=cv3.sel=cv4.sel=True
|
|
|
|
|
|
|
|
else:
|
|
# Now make a new Face
|
|
# skin_side_faces.append( (v1i, v2i, vert_mapping[v2i], vert_mapping[v1i]) )
|
|
skin_side_faces.append( (v2i, v1i, vert_mapping[v1i], vert_mapping[v2i]) )
|
|
skin_side_faces_orig.append((f, len(me_faces) + len(skin_side_faces_orig), i1, i2))
|
|
|
|
if PREF_COLLAPSE_SIDES:
|
|
me.remDoubles(0.0001)
|
|
else:
|
|
me_faces.extend(skin_side_faces)
|
|
# Now assign properties.
|
|
"""
|
|
# Before MultiUVs
|
|
for i, origfData in enumerate(skin_side_faces_orig):
|
|
orig_f, new_f_idx, i1, i2 = origfData
|
|
new_f= me_faces[new_f_idx]
|
|
|
|
new_f.mat= orig_f.mat
|
|
new_f.smooth= orig_f.smooth
|
|
if has_uv:
|
|
new_f.mode= orig_f.mode
|
|
new_f.flag= orig_f.flag
|
|
if orig_f.image:
|
|
new_f.image= orig_f.image
|
|
|
|
uv1= orig_f.uv[i1]
|
|
uv2= orig_f.uv[i2]
|
|
new_f.uv= (uv1, uv2, uv2, uv1)
|
|
|
|
if has_vcol:
|
|
col1= orig_f.col[i1]
|
|
col2= orig_f.col[i2]
|
|
new_f.col= (col1, col2, col2, col1)
|
|
"""
|
|
|
|
for i, origfData in enumerate(skin_side_faces_orig):
|
|
orig_f, new_f_idx, i2, i1 = origfData
|
|
new_f= me_faces[new_f_idx]
|
|
|
|
new_f.mat= orig_f.mat
|
|
new_f.smooth= orig_f.smooth
|
|
|
|
for uvlayer in me.getUVLayerNames():
|
|
me.activeUVLayer = uvlayer
|
|
for i, origfData in enumerate(skin_side_faces_orig):
|
|
orig_f, new_f_idx, i2, i1 = origfData
|
|
new_f= me_faces[new_f_idx]
|
|
|
|
new_f.mode= orig_f.mode
|
|
new_f.flag= orig_f.flag
|
|
new_f.image= orig_f.image
|
|
|
|
uv1= orig_f.uv[i1]
|
|
uv2= orig_f.uv[i2]
|
|
new_f.uv= (uv1, uv2, uv2, uv1)
|
|
|
|
for collayer in me.getColorLayerNames():
|
|
me.activeColorLayer = collayer
|
|
for i, origfData in enumerate(skin_side_faces_orig):
|
|
orig_f, new_f_idx, i2, i1 = origfData
|
|
new_f= me_faces[new_f_idx]
|
|
|
|
col1= orig_f.col[i1]
|
|
col2= orig_f.col[i2]
|
|
new_f.col= (col1, col2, col2, col1)
|
|
|
|
|
|
if PREF_REM_ORIG:
|
|
me_faces.delete(0, faces_sel)
|
|
|
|
|
|
|
|
|
|
def main():
|
|
scn = bpy.data.scenes.active
|
|
ob = scn.objects.active
|
|
|
|
if not ob or ob.type != 'Mesh':
|
|
BPyMessages.Error_NoMeshActive()
|
|
return
|
|
|
|
me = ob.getData(mesh=1)
|
|
if me.multires:
|
|
BPyMessages.Error_NoMeshMultiresEdit()
|
|
return
|
|
|
|
# Create the variables.
|
|
PREF_THICK = Draw.Create(-0.1)
|
|
PREF_SKIN_SIDES= Draw.Create(1)
|
|
PREF_COLLAPSE_SIDES= Draw.Create(0)
|
|
PREF_REM_ORIG= Draw.Create(0)
|
|
|
|
pup_block = [\
|
|
('Thick:', PREF_THICK, -10, 10, 'Skin thickness in mesh space.'),\
|
|
('Skin Sides', PREF_SKIN_SIDES, 'Skin between the original and new faces.'),\
|
|
('Collapse Sides', PREF_COLLAPSE_SIDES, 'Skin between the original and new faces.'),\
|
|
('Remove Original', PREF_REM_ORIG, 'Remove the selected faces after skinning.'),\
|
|
]
|
|
|
|
if not Draw.PupBlock('Solid Skin Selection', pup_block):
|
|
return
|
|
|
|
is_editmode = Window.EditMode()
|
|
if is_editmode: Window.EditMode(0)
|
|
|
|
Window.WaitCursor(1)
|
|
|
|
me = ob.getData(mesh=1)
|
|
solidify(me, PREF_THICK.val, PREF_SKIN_SIDES.val, PREF_REM_ORIG.val, PREF_COLLAPSE_SIDES.val)
|
|
|
|
|
|
Window.WaitCursor(0)
|
|
if is_editmode: Window.EditMode(1)
|
|
|
|
Window.RedrawAll()
|
|
|
|
if __name__ == '__main__':
|
|
main() |