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
285 lines
7.8 KiB
Python
285 lines
7.8 KiB
Python
#!BPY
|
|
"""
|
|
Name: 'Solid Wireframe'
|
|
Blender: 243
|
|
Group: 'Mesh'
|
|
Tooltip: 'Make a solid wireframe copy of this mesh'
|
|
"""
|
|
|
|
# --------------------------------------------------------------------------
|
|
# Solid Wireframe1.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 *****
|
|
# --------------------------------------------------------------------------
|
|
import Blender
|
|
from Blender import Scene, Mesh, Window, sys
|
|
from Blender.Mathutils import AngleBetweenVecs, TriangleNormal
|
|
from BPyMesh import faceAngles # get angles for face cornders
|
|
#import BPyMesh
|
|
#reload(BPyMesh)
|
|
#faceAngles = BPyMesh.faceAngles
|
|
|
|
# works out the distanbce to inset the corners based on angles
|
|
from BPyMathutils import angleToLength
|
|
#import BPyMathutils
|
|
#reload(BPyMathutils)
|
|
#angleToLength = BPyMathutils.angleToLength
|
|
|
|
import mesh_solidify
|
|
|
|
import BPyMessages
|
|
import bpy
|
|
|
|
|
|
def solid_wire(ob_orig, me_orig, sce, PREF_THICKNESS, PREF_SOLID, PREF_SHARP, PREF_XSHARP):
|
|
if not PREF_SHARP and PREF_XSHARP:
|
|
PREF_XSHARP = False
|
|
|
|
# This function runs out of editmode with a mesh
|
|
# error cases are alredy checked for
|
|
|
|
inset_half = PREF_THICKNESS / 2
|
|
del PREF_THICKNESS
|
|
|
|
ob = ob_orig.copy()
|
|
me = me_orig.copy()
|
|
ob.link(me)
|
|
sce.objects.selected = []
|
|
sce.objects.link(ob)
|
|
ob.sel = True
|
|
sce.objects.active = ob
|
|
|
|
# Modify the object, should be a set
|
|
FGON= Mesh.EdgeFlags.FGON
|
|
edges_fgon = dict([(ed.key,None) for ed in me.edges if ed.flag & FGON])
|
|
# edges_fgon.fromkeys([ed.key for ed in me.edges if ed.flag & FGON])
|
|
|
|
del FGON
|
|
|
|
|
|
|
|
# each face needs its own verts
|
|
# orig_vert_count =len(me.verts)
|
|
new_vert_count = len(me.faces) * 4
|
|
for f in me.faces:
|
|
if len(f) == 3:
|
|
new_vert_count -= 1
|
|
|
|
if PREF_SHARP == 0:
|
|
new_faces_edge= {}
|
|
|
|
def add_edge(i1,i2, ni1, ni2):
|
|
|
|
if i1>i2:
|
|
i1,i2 = i2,i1
|
|
flip = True
|
|
else:
|
|
flip = False
|
|
new_faces_edge.setdefault((i1,i2), []).append((ni1, ni2, flip))
|
|
|
|
|
|
new_verts = []
|
|
new_faces = []
|
|
vert_index = len(me.verts)
|
|
|
|
for f in me.faces:
|
|
f_v_co = [v.co for v in f]
|
|
angles = faceAngles(f_v_co)
|
|
f_v_idx = [v.index for v in f]
|
|
|
|
def new_vert(fi):
|
|
co = f_v_co[fi]
|
|
a = angles[fi]
|
|
if a > 180:
|
|
vert_inset = 1 * inset_half
|
|
else:
|
|
vert_inset = inset_half * angleToLength( abs((180-a) / 2) )
|
|
|
|
# Calculate the inset direction
|
|
co1 = f_v_co[fi-1]
|
|
co2 = fi+1 # Wrap this index back to the start
|
|
if co2 == len(f_v_co): co2 = 0
|
|
co2 = f_v_co[co2]
|
|
|
|
co1 = co1 - co
|
|
co2 = co2 - co
|
|
co1.normalize()
|
|
co2.normalize()
|
|
d = co1+co2
|
|
# Done with inset direction
|
|
|
|
d.length = vert_inset
|
|
return co+d
|
|
|
|
new_verts.extend([new_vert(i) for i in xrange(len(f_v_co))])
|
|
|
|
if len(f_v_idx) == 4:
|
|
faces = [\
|
|
(f_v_idx[1], f_v_idx[0], vert_index, vert_index+1),\
|
|
(f_v_idx[2], f_v_idx[1], vert_index+1, vert_index+2),\
|
|
(f_v_idx[3], f_v_idx[2], vert_index+2, vert_index+3),\
|
|
(f_v_idx[0], f_v_idx[3], vert_index+3, vert_index),\
|
|
]
|
|
else:
|
|
faces = [\
|
|
(f_v_idx[1], f_v_idx[0], vert_index, vert_index+1),\
|
|
(f_v_idx[2], f_v_idx[1], vert_index+1, vert_index+2),\
|
|
(f_v_idx[0], f_v_idx[2], vert_index+2, vert_index),\
|
|
]
|
|
|
|
|
|
if PREF_SHARP == 1:
|
|
if not edges_fgon:
|
|
new_faces.extend(faces)
|
|
else:
|
|
for nf in faces:
|
|
i1,i2 = nf[0], nf[1]
|
|
if i1>i2: i1,i2 = i2,i1
|
|
|
|
if edges_fgon and (i1,i2) not in edges_fgon:
|
|
new_faces.append(nf)
|
|
|
|
|
|
|
|
elif PREF_SHARP == 0:
|
|
for nf in faces:
|
|
add_edge(*nf)
|
|
|
|
vert_index += len(f_v_co)
|
|
|
|
me.verts.extend(new_verts)
|
|
|
|
if PREF_SHARP == 0:
|
|
def add_tri_flipped(i1,i2,i3):
|
|
try:
|
|
if AngleBetweenVecs(me.verts[i1].no, TriangleNormal(me.verts[i1].co, me.verts[i2].co, me.verts[i3].co)) < 90:
|
|
return i3,i2,i1
|
|
else:
|
|
return i1,i2,i3
|
|
except:
|
|
return i1,i2,i3
|
|
|
|
# This stores new verts that use this vert
|
|
# used for re-averaging this verts location
|
|
# based on surrounding verts. looks better but not needed.
|
|
vert_users = [set() for i in xrange(vert_index)]
|
|
|
|
for (i1,i2), nf in new_faces_edge.iteritems():
|
|
|
|
if len(nf) == 2:
|
|
# Add the main face
|
|
if edges_fgon and (i1,i2) not in edges_fgon:
|
|
new_faces.append((nf[0][0], nf[0][1], nf[1][0], nf[1][1]))
|
|
|
|
|
|
if nf[0][2]: key1 = nf[0][1],nf[0][0]
|
|
else: key1 = nf[0][0],nf[0][1]
|
|
if nf[1][2]: key2 = nf[1][1],nf[1][0]
|
|
else: key2 = nf[1][0],nf[1][1]
|
|
|
|
# CRAP, cont work out which way to flip so make it oppisite the verts normal.
|
|
|
|
###new_faces.append((i2, key1[0], key2[0])) # NO FLIPPING, WORKS THOUGH
|
|
###new_faces.append((i1, key1[1], key2[1]))
|
|
new_faces.append(add_tri_flipped(i2, key1[0], key2[0]))
|
|
new_faces.append(add_tri_flipped(i1, key1[1], key2[1]))
|
|
|
|
# Average vert loction so its not tooo pointy
|
|
# not realy needed but looks better
|
|
vert_users[i2].update((key1[0], key2[0]))
|
|
vert_users[i1].update((key1[1], key2[1]))
|
|
|
|
if len(nf) == 1:
|
|
if nf[0][2]: new_faces.append((nf[0][0], nf[0][1], i2, i1)) # flipped
|
|
else: new_faces.append((i1,i2, nf[0][0], nf[0][1]))
|
|
|
|
|
|
# average points now.
|
|
for i, vusers in enumerate(vert_users):
|
|
if vusers:
|
|
co = me.verts[i].co
|
|
co.zero()
|
|
|
|
for ii in vusers:
|
|
co += me.verts[ii].co
|
|
co /= len(vusers)
|
|
|
|
|
|
|
|
me.faces.delete(1, range(len(me.faces)))
|
|
|
|
me.faces.extend(new_faces)
|
|
|
|
# External function, solidify
|
|
me.sel = True
|
|
if PREF_SOLID:
|
|
mesh_solidify.solidify(me, -inset_half*2, True, False, PREF_XSHARP)
|
|
|
|
|
|
def main():
|
|
|
|
# Gets the current scene, there can be many scenes in 1 blend file.
|
|
sce = bpy.data.scenes.active
|
|
|
|
# Get the active object, there can only ever be 1
|
|
# and the active object is always the editmode object.
|
|
ob_act = sce.objects.active
|
|
|
|
if not ob_act or ob_act.type != 'Mesh':
|
|
BPyMessages.Error_NoMeshActive()
|
|
return
|
|
|
|
# Create the variables.
|
|
PREF_THICK = Blender.Draw.Create(0.005)
|
|
PREF_SOLID = Blender.Draw.Create(1)
|
|
PREF_SHARP = Blender.Draw.Create(1)
|
|
PREF_XSHARP = Blender.Draw.Create(0)
|
|
|
|
pup_block = [\
|
|
('Thick:', PREF_THICK, 0.0001, 2.0, 'Skin thickness in mesh space.'),\
|
|
('Solid Wire', PREF_SOLID, 'If Disabled, will use 6 sided wire segments'),\
|
|
('Sharp Wire', PREF_SHARP, 'Use the original mesh topology for more accurate sharp wire.'),\
|
|
('Extra Sharp', PREF_XSHARP, 'Use less geometry to create a sharper looking wire'),\
|
|
]
|
|
|
|
if not Blender.Draw.PupBlock('Solid Wireframe', pup_block):
|
|
return
|
|
|
|
# Saves the editmode state and go's out of
|
|
# editmode if its enabled, we cant make
|
|
# changes to the mesh data while in editmode.
|
|
is_editmode = Window.EditMode()
|
|
Window.EditMode(0)
|
|
|
|
Window.WaitCursor(1)
|
|
me = ob_act.getData(mesh=1) # old NMesh api is default
|
|
t = sys.time()
|
|
|
|
# Run the mesh editing function
|
|
solid_wire(ob_act, me, sce, PREF_THICK.val, PREF_SOLID.val, PREF_SHARP.val, PREF_XSHARP.val)
|
|
|
|
# Timing the script is a good way to be aware on any speed hits when scripting
|
|
print 'Solid Wireframe finished in %.2f seconds' % (sys.time()-t)
|
|
Window.WaitCursor(0)
|
|
if is_editmode: Window.EditMode(1)
|
|
|
|
|
|
# This lets you can import the script without running it
|
|
if __name__ == '__main__':
|
|
main() |