initial commit mesh tissue tesselation: T51508 by @Alessandro Zomparelli (alessandrozompa)
This commit is contained in:
parent
05fa3eed2b
commit
cfc5f45776
|
@ -0,0 +1,14 @@
|
|||
# Tissue
|
||||
Tissue - Blender's add-on for computational design by Co-de-iT
|
||||
http://www.co-de-it.com/wordpress/code/blender-tissue
|
||||
|
||||
Latest version (master): https://github.com/alessandro-zomparelli/tissue/archive/master.zip
|
||||
|
||||
Releases: https://github.com/alessandro-zomparelli/tissue/releases
|
||||
|
||||
Installation:
|
||||
|
||||
1. Start Blender. Open User Preferences, the addons tab
|
||||
2. Click "install from file" and point Blender at the downloaded zip
|
||||
3. Activate Tissue add-on from user preferences
|
||||
3. Save user preferences if you want to have it on at startup.
|
|
@ -0,0 +1,77 @@
|
|||
# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# --------------------------------- TISSUE ------------------------------------#
|
||||
#-------------------------------- version 0.29 --------------------------------#
|
||||
# #
|
||||
# Creates duplicates of selected mesh to active morphing the shape according #
|
||||
# to target faces. #
|
||||
# #
|
||||
# Alessandro Zomparelli #
|
||||
# (2017) #
|
||||
# #
|
||||
# http://www.co-de-it.com/ #
|
||||
# http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Mesh/Tissue #
|
||||
# #
|
||||
################################################################################
|
||||
|
||||
|
||||
if "bpy" in locals():
|
||||
import importlib
|
||||
importlib.reload(tessellate_numpy)
|
||||
importlib.reload(colors_groups_exchanger)
|
||||
importlib.reload(dual_mesh)
|
||||
|
||||
else:
|
||||
from . import tessellate_numpy
|
||||
from . import colors_groups_exchanger
|
||||
from . import dual_mesh
|
||||
|
||||
import bpy
|
||||
from mathutils import Vector
|
||||
#bpy.types.Object.vertexgroup = bpy.props.StringProperty()
|
||||
#bpy.types.Panel.vertexgroup = bpy.props.StringProperty()
|
||||
|
||||
bl_info = {
|
||||
"name": "Tissue",
|
||||
"author": "Alessandro Zomparelli (Co-de-iT)",
|
||||
"version": (0, 2, 9),
|
||||
"blender": (2, 7, 8),
|
||||
"location": "",
|
||||
"description": "Tools for Computational Design",
|
||||
"warning": "",
|
||||
"wiki_url": ("http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/M"
|
||||
"esh/Tissue"),
|
||||
"tracker_url": "https://plus.google.com/u/0/+AlessandroZomparelli/",
|
||||
"category": "Mesh"}
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_module(__name__)
|
||||
bpy.types.Object.tissue_tessellate = bpy.props.PointerProperty(
|
||||
type=tessellate_numpy.tissue_tessellate_prop)
|
||||
|
||||
|
||||
def unregister():
|
||||
tessellate_numpy.unregister()
|
||||
colors_groups_exchanger.unregister()
|
||||
dual_mesh.unregister()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
|
@ -0,0 +1,300 @@
|
|||
# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
#-------------------------- COLORS / GROUPS EXCHANGER -------------------------#
|
||||
# #
|
||||
# Vertex Color to Vertex Group allow you to convert colors channles to weight #
|
||||
# maps. #
|
||||
# The main purpose is to use vertex colors to store information when importing #
|
||||
# files from other softwares. The script works with the active vertex color #
|
||||
# slot. #
|
||||
# For use the command "Vertex Clors to Vertex Groups" use the search bar #
|
||||
# (space bar). #
|
||||
# #
|
||||
# (c) Alessandro Zomparelli #
|
||||
# (2017) #
|
||||
# #
|
||||
# http://www.co-de-it.com/ #
|
||||
# #
|
||||
################################################################################
|
||||
|
||||
import bpy
|
||||
import math
|
||||
|
||||
bl_info = {
|
||||
"name": "Colors/Groups Exchanger",
|
||||
"author": "Alessandro Zomparelli (Co-de-iT)",
|
||||
"version": (0, 2),
|
||||
"blender": (2, 7, 8),
|
||||
"location": "",
|
||||
"description": ("Convert vertex colors channels to vertex groups and vertex"
|
||||
" groups to colors"),
|
||||
"warning": "",
|
||||
"wiki_url": "",
|
||||
"tracker_url": "",
|
||||
"category": "Mesh"}
|
||||
|
||||
class vertex_colors_to_vertex_groups(bpy.types.Operator):
|
||||
bl_idname = "object.vertex_colors_to_vertex_groups"
|
||||
bl_label = "Weight from Colors"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
red = bpy.props.BoolProperty(
|
||||
name="red channel", default=False, description="convert red channel")
|
||||
green = bpy.props.BoolProperty(
|
||||
name="green channel", default=False,
|
||||
description="convert green channel")
|
||||
blue = bpy.props.BoolProperty(
|
||||
name="blue channel", default=False, description="convert blue channel")
|
||||
value = bpy.props.BoolProperty(
|
||||
name="value channel", default=True, description="convert value channel")
|
||||
invert = bpy.props.BoolProperty(
|
||||
name="invert", default=False, description="invert all color channels")
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.context.active_object
|
||||
id = len(obj.vertex_groups)
|
||||
id_red = id
|
||||
id_green = id
|
||||
id_blue = id
|
||||
id_value = id
|
||||
|
||||
boolCol = len(obj.data.vertex_colors)
|
||||
if(boolCol): col_name = obj.data.vertex_colors.active.name
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
|
||||
if(self.red and boolCol):
|
||||
bpy.ops.object.vertex_group_add()
|
||||
bpy.ops.object.vertex_group_assign()
|
||||
id_red = id
|
||||
obj.vertex_groups[id_red].name = col_name + '_red'
|
||||
id+=1
|
||||
if(self.green and boolCol):
|
||||
bpy.ops.object.vertex_group_add()
|
||||
bpy.ops.object.vertex_group_assign()
|
||||
id_green = id
|
||||
obj.vertex_groups[id_green].name = col_name + '_green'
|
||||
id+=1
|
||||
if(self.blue and boolCol):
|
||||
bpy.ops.object.vertex_group_add()
|
||||
bpy.ops.object.vertex_group_assign()
|
||||
id_blue = id
|
||||
obj.vertex_groups[id_blue].name = col_name + '_blue'
|
||||
id+=1
|
||||
if(self.value and boolCol):
|
||||
bpy.ops.object.vertex_group_add()
|
||||
bpy.ops.object.vertex_group_assign()
|
||||
id_value = id
|
||||
obj.vertex_groups[id_value].name = col_name + '_value'
|
||||
id+=1
|
||||
|
||||
mult = 1
|
||||
if(self.invert): mult = -1
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
sub_red = 1 + self.value + self.blue + self.green
|
||||
sub_green = 1 + self.value + self.blue
|
||||
sub_blue = 1 + self.value
|
||||
sub_value = 1
|
||||
|
||||
id = len(obj.vertex_groups)
|
||||
if(id_red <= id and id_green <= id and id_blue <= id and id_value <= \
|
||||
id and boolCol):
|
||||
v_colors = obj.data.vertex_colors.active.data
|
||||
i = 0
|
||||
for f in obj.data.polygons:
|
||||
for v in f.vertices:
|
||||
gr = obj.data.vertices[v].groups
|
||||
if(self.red): gr[min(len(gr)-sub_red, id_red)].weight = \
|
||||
self.invert + mult * v_colors[i].color.r
|
||||
if(self.green): gr[min(len(gr)-sub_green, id_green)].weight\
|
||||
= self.invert + mult * v_colors[i].color.g
|
||||
if(self.blue): gr[min(len(gr)-sub_blue, id_blue)].weight = \
|
||||
self.invert + mult * v_colors[i].color.b
|
||||
if(self.value): gr[min(len(gr)-sub_value, id_value)].weight\
|
||||
= self.invert + mult * v_colors[i].color.v
|
||||
i+=1
|
||||
bpy.ops.paint.weight_paint_toggle()
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class vertex_group_to_vertex_colors(bpy.types.Operator):
|
||||
bl_idname = "object.vertex_group_to_vertex_colors"
|
||||
bl_label = "Colors from Weight"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
channel = bpy.props.EnumProperty(
|
||||
items=[('Blue', 'Blue Channel', 'Convert to Blue Channel'),
|
||||
('Green', 'Green Channel', 'Convert to Green Channel'),
|
||||
('Red', 'Red Channel', 'Convert to Red Channel'),
|
||||
('Value', 'Value Channel', 'Convert to Grayscale'),
|
||||
('False Colors', 'False Colors', 'Convert to False Colors')],
|
||||
name="Convert to", description="Choose how to convert vertex group",
|
||||
default="Value", options={'LIBRARY_EDITABLE'})
|
||||
|
||||
invert = bpy.props.BoolProperty(
|
||||
name="invert", default=False, description="invert color channel")
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.context.active_object
|
||||
group_id = obj.vertex_groups.active_index
|
||||
if (group_id == -1):
|
||||
return {'FINISHED'}
|
||||
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
group_name = obj.vertex_groups[group_id].name
|
||||
bpy.ops.mesh.vertex_color_add()
|
||||
colors_id = obj.data.vertex_colors.active_index
|
||||
|
||||
colors_name = group_name
|
||||
if(self.channel == 'False Colors'): colors_name += "_false_colors"
|
||||
elif(self.channel == 'Value'): colors_name += "_value"
|
||||
elif(self.channel == 'Red'): colors_name += "_red"
|
||||
elif(self.channel == 'Green'): colors_name += "_green"
|
||||
elif(self.channel == 'Blue'): colors_name += "_blue"
|
||||
bpy.context.object.data.vertex_colors[colors_id].name = colors_name
|
||||
|
||||
v_colors = obj.data.vertex_colors.active.data
|
||||
|
||||
mult = 1
|
||||
if(self.invert): mult = -1
|
||||
|
||||
i = 0
|
||||
for f in obj.data.polygons:
|
||||
for v in f.vertices:
|
||||
gr = obj.data.vertices[v].groups
|
||||
|
||||
if(self.channel == 'False Colors'): v_colors[i].color = (0,0,1)
|
||||
else: v_colors[i].color = (0,0,0)
|
||||
|
||||
for g in gr:
|
||||
if g.group == group_id:
|
||||
if(self.channel == 'False Colors'):
|
||||
if g.weight < 0.25:
|
||||
v_colors[i].color = (0, g.weight*4, 1)
|
||||
elif g.weight < 0.5:
|
||||
v_colors[i].color = (0, 1, 1-(g.weight-0.25)*4)
|
||||
elif g.weight < 0.75:
|
||||
v_colors[i].color = ((g.weight-0.5)*4,1,0)
|
||||
else:
|
||||
v_colors[i].color = (1,1-(g.weight-0.75)*4,0)
|
||||
elif(self.channel == 'Value'):
|
||||
v_colors[i].color = (
|
||||
self.invert + mult * g.weight,
|
||||
self.invert + mult * g.weight,
|
||||
self.invert + mult * g.weight)
|
||||
elif(self.channel == 'Red'):
|
||||
v_colors[i].color = (
|
||||
self.invert + mult * g.weight,0,0)
|
||||
elif(self.channel == 'Green'):
|
||||
v_colors[i].color = (
|
||||
0, self.invert + mult * g.weight,0)
|
||||
elif(self.channel == 'Blue'):
|
||||
v_colors[i].color = (
|
||||
0,0, self.invert + mult * g.weight)
|
||||
i+=1
|
||||
bpy.ops.paint.vertex_paint_toggle()
|
||||
bpy.context.object.data.vertex_colors[colors_id].active_render = True
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class face_area_to_vertex_groups(bpy.types.Operator):
|
||||
bl_idname = "object.face_area_to_vertex_groups"
|
||||
bl_label = "Weight from Faces Area"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
invert = bpy.props.BoolProperty(
|
||||
name="invert", default=False, description="invert values")
|
||||
|
||||
def execute(self, context):
|
||||
obj = bpy.context.active_object
|
||||
id_value = len(obj.vertex_groups)
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
bpy.ops.mesh.select_all(action='SELECT')
|
||||
bpy.ops.object.vertex_group_add()
|
||||
bpy.ops.object.vertex_group_assign()
|
||||
obj.vertex_groups[id_value].name = 'faces_area'
|
||||
mult = 1
|
||||
|
||||
if self.invert: mult = -1
|
||||
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
min_area = False
|
||||
max_area = False
|
||||
n_values = [0]*len(obj.data.vertices)
|
||||
values = [0]*len(obj.data.vertices)
|
||||
print(len(values))
|
||||
for p in obj.data.polygons:
|
||||
for v in p.vertices:
|
||||
n_values[v] += 1
|
||||
values[v] += p.area
|
||||
|
||||
if min_area:
|
||||
if min_area > p.area: min_area = p.area
|
||||
else: min_area = p.area
|
||||
|
||||
if max_area:
|
||||
if max_area < p.area: max_area = p.area
|
||||
else: max_area = p.area
|
||||
|
||||
id = len(obj.vertex_groups)
|
||||
for v in obj.data.vertices:
|
||||
gr = v.groups
|
||||
index = v.index
|
||||
try:
|
||||
gr[min(len(gr)-1, id_value)].weight = self.invert + mult * \
|
||||
((values[index] / n_values[index] - min_area)/(max_area - \
|
||||
min_area))
|
||||
except:
|
||||
gr[min(len(gr)-1, id_value)].weight = 0.5
|
||||
bpy.ops.paint.weight_paint_toggle()
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class colors_groups_exchanger_panel(bpy.types.Panel):
|
||||
bl_label = "Colors-Weight Exchanger"
|
||||
bl_category = "Create"
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "TOOLS"
|
||||
#bl_context = "objectmode"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
col = layout.column(align=True)
|
||||
col.operator("object.vertex_group_to_vertex_colors", icon="GROUP_VCOL")
|
||||
col.operator(
|
||||
"object.vertex_colors_to_vertex_groups", icon="GROUP_VERTEX")
|
||||
col.separator()
|
||||
col.operator("object.face_area_to_vertex_groups", icon="SNAP_FACE")
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(vertex_colors_to_vertex_groups)
|
||||
bpy.utils.register_class(vertex_group_to_vertex_colors)
|
||||
bpy.utils.register_class(face_area_to_vertex_groups)
|
||||
bpy.utils.register_class(colors_groups_exchanger_panel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(vertex_colors_to_vertex_groups)
|
||||
bpy.utils.unregister_class(vertex_group_to_vertex_colors)
|
||||
bpy.utils.unregister_class(face_area_to_vertex_groups)
|
||||
bpy.utils.unregister_class(colors_groups_exchanger_panel)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
|
@ -0,0 +1,243 @@
|
|||
# ##### 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
#---------------------------------- DUAL MESH ---------------------------------#
|
||||
#--------------------------------- version 0.3 --------------------------------#
|
||||
# #
|
||||
# Convert a generic mesh to its dual. With open meshes it can get some wired #
|
||||
# effect on the borders. #
|
||||
# #
|
||||
# (c) Alessandro Zomparelli #
|
||||
# (2017) #
|
||||
# #
|
||||
# http://www.co-de-it.com/ #
|
||||
# #
|
||||
################################################################################
|
||||
|
||||
import bpy
|
||||
import bmesh
|
||||
|
||||
bl_info = {
|
||||
"name": "Dual Mesh",
|
||||
"author": "Alessandro Zomparelli (Co-de-iT)",
|
||||
"version": (0, 3),
|
||||
"blender": (2, 7, 8),
|
||||
"location": "",
|
||||
"description": "Convert a generic mesh to its dual",
|
||||
"warning": "",
|
||||
"wiki_url": "",
|
||||
"tracker_url": "",
|
||||
"category": "Mesh"}
|
||||
|
||||
class dual_mesh(bpy.types.Operator):
|
||||
bl_idname = "object.dual_mesh"
|
||||
bl_label = "Dual Mesh"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
quad_method = bpy.props.EnumProperty(
|
||||
items=[('BEAUTY', 'Beauty',
|
||||
'Split the quads in nice triangles, slower method'),
|
||||
('FIXED', 'Fixed', 'Split the quads on the 1st and 3rd vertices'),
|
||||
('FIXED_ALTERNATE', 'Fixed Alternate', ('Split the quads on the 2nd and'
|
||||
' 4th vertices')),
|
||||
('SHORTEST_DIAGONAL', 'Shortest Diagonal', ('Split the quads based on '
|
||||
'the distance between the vertices'))],
|
||||
name="Quad Method",
|
||||
description="Method for splitting the quads into triangles",
|
||||
default="FIXED", options={'LIBRARY_EDITABLE'})
|
||||
|
||||
polygon_method = bpy.props.EnumProperty(
|
||||
items=[
|
||||
('BEAUTY', 'Beauty', 'Arrange the new triangles evenly'),
|
||||
('CLIP', 'Clip',
|
||||
'Split the polygons with an ear clipping algorithm')],
|
||||
name="Polygon Method",
|
||||
description="Method for splitting the polygons into triangles",
|
||||
default="BEAUTY", options={'LIBRARY_EDITABLE'})
|
||||
|
||||
preserve_borders = bpy.props.BoolProperty(
|
||||
name="Preserve Borders", default=True,
|
||||
description="Preserve original borders")
|
||||
|
||||
def execute(self, context):
|
||||
act = bpy.context.active_object
|
||||
sel = bpy.context.selected_objects
|
||||
doneMeshes = []
|
||||
for ob0 in sel:
|
||||
if ob0.type != 'MESH': continue
|
||||
if ob0.data.name in doneMeshes: continue
|
||||
##ob = bpy.data.objects.new("dual_mesh_wip", ob0.data.copy())
|
||||
ob = ob0
|
||||
mesh_name = ob0.data.name
|
||||
|
||||
# store linked objects
|
||||
clones = []
|
||||
n_users = ob0.data.users
|
||||
count = 0
|
||||
for o in bpy.data.objects:
|
||||
if o.type != 'MESH': continue
|
||||
if o.data.name == mesh_name:
|
||||
count+=1
|
||||
clones.append(o)
|
||||
if count == n_users: break
|
||||
ob.data = ob.data.copy()
|
||||
bpy.ops.object.select_all(action='DESELECT')
|
||||
ob.select = True
|
||||
bpy.context.scene.objects.active = ob0
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
|
||||
if self.preserve_borders:
|
||||
bpy.ops.mesh.select_mode(
|
||||
use_extend=False, use_expand=False, type='EDGE')
|
||||
bpy.ops.mesh.select_non_manifold(
|
||||
extend=False, use_wire=False, use_boundary=True,
|
||||
use_multi_face=False, use_non_contiguous=False,
|
||||
use_verts=False)
|
||||
bpy.ops.mesh.extrude_region_move(
|
||||
MESH_OT_extrude_region={"mirror":False},
|
||||
TRANSFORM_OT_translate={"value":(0, 0, 0),
|
||||
"constraint_axis":(False, False, False),
|
||||
"constraint_orientation":'GLOBAL', "mirror":False,
|
||||
"proportional":'DISABLED',
|
||||
"proportional_edit_falloff":'SMOOTH', "proportional_size":1,
|
||||
"snap":False, "snap_target":'CLOSEST',
|
||||
"snap_point":(0, 0, 0), "snap_align":False,
|
||||
"snap_normal":(0, 0, 0), "gpencil_strokes":False,
|
||||
"texture_space":False, "remove_on_cancel":False,
|
||||
"release_confirm":False})
|
||||
|
||||
bpy.ops.mesh.select_mode(
|
||||
use_extend=False, use_expand=False, type='VERT',
|
||||
action='TOGGLE')
|
||||
bpy.ops.mesh.select_all(action = 'SELECT')
|
||||
bpy.ops.mesh.quads_convert_to_tris(
|
||||
quad_method=self.quad_method, ngon_method=self.polygon_method)
|
||||
bpy.ops.mesh.select_all(action = 'DESELECT')
|
||||
bpy.ops.object.mode_set(mode = 'OBJECT')
|
||||
bpy.ops.object.modifier_add(type='SUBSURF')
|
||||
ob.modifiers[-1].name = "dual_mesh_subsurf"
|
||||
bpy.ops.object.modifier_apply(
|
||||
apply_as='DATA', modifier='dual_mesh_subsurf')
|
||||
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
bpy.ops.mesh.select_all(action = 'DESELECT')
|
||||
|
||||
verts = ob.data.vertices
|
||||
|
||||
bpy.ops.object.mode_set(mode = 'OBJECT')
|
||||
verts[0].select = True
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
bpy.ops.mesh.select_more(use_face_step=False)
|
||||
|
||||
bpy.ops.mesh.select_similar(
|
||||
type='EDGE', compare='EQUAL', threshold=0.01)
|
||||
bpy.ops.mesh.select_all(action='INVERT')
|
||||
bpy.ops.mesh.dissolve_verts()
|
||||
bpy.ops.mesh.select_all(action = 'DESELECT')
|
||||
|
||||
bpy.ops.mesh.select_non_manifold(
|
||||
extend=False, use_wire=False, use_boundary=True,
|
||||
use_multi_face=False, use_non_contiguous=False, use_verts=False)
|
||||
bpy.ops.mesh.select_more()
|
||||
|
||||
# find boundaries
|
||||
bpy.ops.object.mode_set(mode = 'OBJECT')
|
||||
bound_v = [v.index for v in ob.data.vertices if v.select]
|
||||
bound_e = [e.index for e in ob.data.edges if e.select]
|
||||
bound_p = [p.index for p in ob.data.polygons if p.select]
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
|
||||
# select quad faces
|
||||
bpy.context.tool_settings.mesh_select_mode = (False, False, True)
|
||||
bpy.ops.mesh.select_face_by_sides(number=4, extend=False)
|
||||
|
||||
# deselect boundaries
|
||||
bpy.ops.object.mode_set(mode = 'OBJECT')
|
||||
for i in bound_v:
|
||||
bpy.context.active_object.data.vertices[i].select = False
|
||||
for i in bound_e:
|
||||
bpy.context.active_object.data.edges[i].select = False
|
||||
for i in bound_p:
|
||||
bpy.context.active_object.data.polygons[i].select = False
|
||||
|
||||
bpy.ops.object.mode_set(mode = 'EDIT')
|
||||
|
||||
bpy.context.tool_settings.mesh_select_mode = (False, False, True)
|
||||
bpy.ops.mesh.edge_face_add()
|
||||
bpy.context.tool_settings.mesh_select_mode = (True, False, False)
|
||||
bpy.ops.mesh.select_all(action = 'DESELECT')
|
||||
|
||||
# delete boundaries
|
||||
bpy.ops.mesh.select_non_manifold(
|
||||
extend=False, use_wire=True, use_boundary=True,
|
||||
use_multi_face=False, use_non_contiguous=False, use_verts=True)
|
||||
bpy.ops.mesh.delete(type='VERT')
|
||||
|
||||
# remove middle vertices
|
||||
bm = bmesh.from_edit_mesh(ob.data)
|
||||
for v in bm.verts:
|
||||
if len(v.link_edges) == 2 and len(v.link_faces) < 3:
|
||||
v.select_set(True)
|
||||
|
||||
# dissolve
|
||||
bpy.ops.mesh.dissolve_verts()
|
||||
bpy.ops.mesh.select_all(action = 'DESELECT')
|
||||
|
||||
# clean wires
|
||||
bpy.ops.mesh.select_non_manifold(
|
||||
extend=False, use_wire=True, use_boundary=False,
|
||||
use_multi_face=False, use_non_contiguous=False, use_verts=False)
|
||||
bpy.ops.mesh.delete(type='EDGE')
|
||||
|
||||
bpy.ops.object.mode_set(mode = 'OBJECT')
|
||||
ob0.data.name = mesh_name
|
||||
doneMeshes.append(mesh_name)
|
||||
for o in clones: o.data = ob.data
|
||||
for o in sel: o.select = True
|
||||
bpy.context.scene.objects.active = act
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class dual_mesh_panel(bpy.types.Panel):
|
||||
bl_label = "Dual Mesh"
|
||||
bl_category = "Create"
|
||||
bl_space_type = "VIEW_3D"
|
||||
bl_region_type = "TOOLS"
|
||||
bl_context = (("objectmode"))
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
col = layout.column(align=True)
|
||||
try:
|
||||
if bpy.context.active_object.type == 'MESH':
|
||||
col.operator("object.dual_mesh")
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(dual_mesh)
|
||||
bpy.utils.register_class(dual_mesh_panel)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_class(dual_mesh)
|
||||
bpy.utils.unregister_class(dual_mesh_panel)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue