initial commit mesh tissue tesselation: T51508 by @Alessandro Zomparelli (alessandrozompa)

This commit is contained in:
Brendon Murphy 2017-05-22 10:01:22 +10:00
parent 05fa3eed2b
commit cfc5f45776
5 changed files with 1956 additions and 0 deletions

14
mesh_tissue/README.md Normal file
View File

@ -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.

77
mesh_tissue/__init__.py Normal file
View File

@ -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()

View File

@ -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()

243
mesh_tissue/dual_mesh.py Normal file
View File

@ -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