Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'mesh_tissue/tessellate_numpy.py')
-rw-r--r--mesh_tissue/tessellate_numpy.py1322
1 files changed, 1322 insertions, 0 deletions
diff --git a/mesh_tissue/tessellate_numpy.py b/mesh_tissue/tessellate_numpy.py
new file mode 100644
index 00000000..65afeee7
--- /dev/null
+++ b/mesh_tissue/tessellate_numpy.py
@@ -0,0 +1,1322 @@
+# ##### 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 #####
+
+# ---------------------------- ADAPTIVE DUPLIFACES ----------------------------#
+#-------------------------------- version 0.83 --------------------------------#
+# #
+# Creates duplicates of selected mesh to active morphing the shape according #
+# to target faces. #
+# #
+# (c) Alessandro Zomparelli #
+# (2017) #
+# #
+# http://www.co-de-it.com/ #
+# #
+################################################################################
+
+
+import bpy
+from mathutils import Vector
+import numpy as np
+from math import sqrt
+import random
+
+
+def lerp(a,b,t):
+ return a + (b-a)*t
+
+
+def lerp2(v1, v2, v3, v4, v):
+ v12 = v1 + (v2-v1)*v.x
+ v43 = v4 + (v3-v4)*v.x
+ return v12 + (v43-v12)*v.y
+
+
+def lerp3(v1, v2, v3, v4, v):
+ loc = lerp2(v1.co, v2.co, v3.co, v4.co, v)
+ nor = lerp2(v1.normal, v2.normal, v3.normal, v4.normal, v)
+ nor.normalize()
+ return loc + nor*v.z
+
+
+def tassellate(ob0, ob1, offset, zscale, gen_modifiers, com_modifiers, mode,
+ scale_mode, rotation_mode, rand_seed, fill_mode,
+ bool_vertex_group, bool_selection, bool_shapekeys):
+ random.seed(rand_seed)
+ old_me0 = ob0.data # Store generator mesh
+ if gen_modifiers: # Apply generator modifiers
+ me0 = ob0.to_mesh(bpy.context.scene, apply_modifiers=True,
+ settings = 'PREVIEW')
+ else: me0 = ob0.data
+ ob0.data = me0
+ base_polygons = []
+
+ # Check if zero faces are selected
+ if bool_selection:
+ for p in ob0.data.polygons:
+ if p.select: base_polygons.append(p)
+ else:
+ base_polygons = ob0.data.polygons
+ if len(base_polygons) == 0: return 0
+
+ # Apply component modifiers
+ if com_modifiers:
+ me1 = ob1.to_mesh(bpy.context.scene, apply_modifiers=True,
+ settings = 'PREVIEW')
+ else: me1 = ob1.data
+
+ verts0 = me0.vertices # Collect generator vertices
+
+ # Component statistics
+ n_verts = len(me1.vertices)
+ n_edges = len(me1.edges)
+ n_faces = len(me1.polygons)
+
+ # Component transformations
+ loc = ob1.location
+ dim = ob1.dimensions
+ scale = ob1.scale
+
+ # Create empty lists
+ new_verts = []
+ new_edges = []
+ new_faces = []
+ new_verts_np = np.array(())
+
+ # Component bounding box
+ min = Vector((0,0,0))
+ max = Vector((0,0,0))
+ first = True
+ for v in me1.vertices:
+ vert = v.co
+ if vert[0] < min[0] or first:
+ min[0] = vert[0]
+ if vert[1] < min[1] or first:
+ min[1] = vert[1]
+ if vert[2] < min[2] or first:
+ min[2] = vert[2]
+ if vert[0] > max[0] or first:
+ max[0] = vert[0]
+ if vert[1] > max[1] or first:
+ max[1] = vert[1]
+ if vert[2] > max[2] or first:
+ max[2] = vert[2]
+ first = False
+ bb = max-min
+
+ # adaptive XY
+ verts1 = []
+ for v in me1.vertices:
+ if mode=="ADAPTIVE":
+ vert = v.co - min#( ob1.matrix_world * v.co ) - min
+ vert[0] = (vert[0] / bb[0] if bb[0] != 0 else 0.5)
+ vert[1] = (vert[1] / bb[1] if bb[1] != 0 else 0.5)
+ vert[2] = (vert[2] + (-0.5 + offset*0.5)*bb[2])*zscale
+ else:
+ vert = v.co.xyz
+ vert[2] = (vert[2] - min[2] + (-0.5 + offset*0.5)*bb[2])*zscale
+ verts1.append(vert)
+
+ # component vertices
+ vs1 = np.array([v for v in verts1]).reshape(len(verts1),3,1)
+ vx = vs1[:,0]
+ vy = vs1[:,1]
+ vz = vs1[:,2]
+
+ # Component polygons
+ fs1 = [[i for i in p.vertices] for p in me1.polygons]
+ new_faces = fs1[:]
+
+ # Component edges
+ es1 = [[i for i in e.vertices] for e in me1.edges if e.is_loose]
+ new_edges = es1[:]
+
+ # SHAPE KEYS
+ shapekeys = []
+ do_shapekeys = False
+ if me1.shape_keys is not None and bool_shapekeys:
+ if len(me1.shape_keys.key_blocks) > 1:
+ do_shapekeys = True
+
+ # Read active key
+ active_key = ob1.active_shape_key_index
+ if active_key == 0: active_key = 1
+
+ for v in me1.shape_keys.key_blocks[active_key].data:
+ if mode=="ADAPTIVE":
+ vert = v.co - min
+ #vert = ( ob1.matrix_world * v.co ) - min
+ vert[0] = vert[0] / bb[0]
+ vert[1] = vert[1] / bb[1]
+ vert[2] = (vert[2] + (-0.5 + offset*0.5)*bb[2]) * zscale
+ else:
+ vert = v.co.xyz
+ vert[2] = (vert[2] - min[2] + (-0.5 + offset*0.5)*bb[2]) * \
+ zscale
+ shapekeys.append(vert)
+
+ # Component vertices
+ key1 = np.array([v for v in shapekeys]).reshape(len(shapekeys),3,1)
+ vx_key = key1[:,0]
+ vy_key = key1[:,1]
+ vz_key = key1[:,2]
+
+ # Active vertex group
+ if bool_vertex_group:
+ try:
+ weight = []
+ group_index = ob0.vertex_groups.active_index
+ active_vertex_group = ob0.vertex_groups[group_index]
+ for v in me0.vertices:
+ try:
+ weight.append(active_vertex_group.weight(v.index))
+ except:
+ weight.append(0)
+ except:
+ bool_vertex_group = False
+
+ # FAN tessellation mode
+ if fill_mode == 'FAN':
+ fan_verts = [v.co.to_tuple() for v in me0.vertices]
+ fan_polygons = []
+ selected_faces = []
+ for p in base_polygons:
+ #if bool_selection and not p.select: continue
+ fan_center = Vector((0,0,0))
+ for v in p.vertices:
+ fan_center += me0.vertices[v].co
+ fan_center /= len(p.vertices)
+ last_vert = len(fan_verts)
+ fan_verts.append(fan_center.to_tuple())
+
+ # Vertex Group
+ if bool_vertex_group:
+ center_weight = sum([weight[i] for i in p.vertices])/ \
+ len(p.vertices)
+ weight.append(center_weight)
+
+ for i in range(len(p.vertices)):
+ fan_polygons.append((p.vertices[i],
+ p.vertices[(i+1)%len(p.vertices)],
+ last_vert, last_vert))
+ #if bool_selection: selected_faces.append(p.select)
+ fan_me = bpy.data.meshes.new('Fan.Mesh')
+ fan_me.from_pydata(tuple(fan_verts), [], tuple(fan_polygons))
+ me0 = fan_me
+ verts0 = me0.vertices
+ base_polygons = me0.polygons
+ #for i in range(len(selected_faces)):
+ # fan_me.polygons[i].select = selected_faces[i]
+ count = 0 # necessary for UV calculation
+
+ # TESSELLATION
+ j = 0
+ for p in base_polygons:
+ # Random rotation
+ if rotation_mode == 'RANDOM':
+ shifted_vertices = []
+ n_poly_verts = len(p.vertices)
+ rand = random.randint(0,n_poly_verts)
+ for i in range(n_poly_verts):
+ shifted_vertices.append(p.vertices[(i+rand)%n_poly_verts])
+ vs0 = np.array([verts0[i].co for i in shifted_vertices])
+ nvs0 = np.array([verts0[i].normal for i in shifted_vertices])
+ # vertex weight
+ if bool_vertex_group:
+ ws0 = []
+ for i in shifted_vertices:
+ try: ws0.append(weight[i])
+ except: ws0.append(0)
+ ws0 = np.array(ws0)
+
+ # UV rotation
+ elif rotation_mode == 'UV' and len(ob0.data.uv_layers) > 0 \
+ and fill_mode != 'FAN':
+ i = p.index
+ v01 = (me0.uv_layers.active.data[count].uv + \
+ me0.uv_layers.active.data[count+1].uv)
+ if len(p.vertices) > 3:
+ v32 = (me0.uv_layers.active.data[count+3].uv + \
+ me0.uv_layers.active.data[count+2].uv)
+ else:
+ v32 = (me0.uv_layers.active.data[count].uv + \
+ me0.uv_layers.active.data[count+2].uv)
+ v0132 = v32-v01
+ v0132.normalize()
+
+ v12 = (me0.uv_layers.active.data[count+1].uv + \
+ me0.uv_layers.active.data[count+2].uv)
+ if len(p.vertices) > 3:
+ v03 = (me0.uv_layers.active.data[count].uv + \
+ me0.uv_layers.active.data[count+3].uv)
+ else:
+ v03 = (me0.uv_layers.active.data[count].uv + \
+ me0.uv_layers.active.data[count].uv)
+ v1203 = v03 - v12
+ v1203.normalize()
+
+ vertUV = []
+ dot1203 = v1203.x
+ dot0132 = v0132.x
+ if(abs(dot1203) < abs(dot0132)):
+ if(dot0132 > 0): vertUV = p.vertices[1:] + p.vertices[:1]
+ else: vertUV = p.vertices[3:] + p.vertices[:3]
+ else:
+ if(dot1203 < 0): vertUV = p.vertices[:]
+ else: vertUV = p.vertices[2:] + p.vertices[:2]
+ vs0 = np.array([verts0[i].co for i in vertUV])
+ nvs0 = np.array([verts0[i].normal for i in vertUV])
+
+ # Vertex weight
+ if bool_vertex_group:
+ ws0 = []
+ for i in vertUV:
+ try: ws0.append(weight[i])
+ except: ws0.append(0)
+ ws0 = np.array(ws0)
+
+ count += len(p.vertices)
+
+ # Default rotation
+ else:
+ vs0 = np.array([verts0[i].co for i in p.vertices])
+ nvs0 = np.array([verts0[i].normal for i in p.vertices])
+ # Vertex weight
+ if bool_vertex_group:
+ ws0 = []
+ for i in p.vertices:
+ try: ws0.append(weight[i])
+ except: ws0.append(0)
+ ws0 = np.array(ws0)
+
+ # considering only 4 vertices
+ vs0 = np.array((vs0[0], vs0[1], vs0[2], vs0[-1]))
+ nvs0 = np.array((nvs0[0], nvs0[1], nvs0[2], nvs0[-1]))
+
+ # remapped vertex coordinates
+ v0 = vs0[0] + (vs0[1] -vs0[0])*vx
+ v1 = vs0[3] + (vs0[2] -vs0[3])*vx
+ v2 = v0 + (v1 - v0)*vy
+
+ # remapped vertex normal
+ nv0 = nvs0[0] + (nvs0[1] -nvs0[0])*vx
+ nv1 = nvs0[3] + (nvs0[2] -nvs0[3])*vx
+ nv2 = nv0 + (nv1 - nv0)*vy
+
+ # vertex z to normal
+ v3 = v2 + nv2*vz*(sqrt(p.area) if scale_mode == "ADAPTIVE" else 1)
+
+ if bool_vertex_group:
+ ws0 = np.array((ws0[0], ws0[1], ws0[2], ws0[-1]))
+ # Interpolate vertex weight
+ w0 = ws0[0] + (ws0[1] -ws0[0])*vx
+ w1 = ws0[3] + (ws0[2] -ws0[3])*vx
+ w2 = w0 + (w1 - w0)*vy
+
+ # Shapekeys
+ if do_shapekeys:
+ # remapped vertex coordinates
+ v0 = vs0[0] + (vs0[1] -vs0[0])*vx_key
+ v1 = vs0[3] + (vs0[2] -vs0[3])*vx_key
+ v2 = v0 + (v1 - v0)*vy_key
+ # remapped vertex normal
+ nv0 = nvs0[0] + (nvs0[1] -nvs0[0])*vx_key
+ nv1 = nvs0[3] + (nvs0[2] -nvs0[3])*vx_key
+ nv2 = nv0 + (nv1 - nv0)*vy_key
+ # vertex z to normal
+ v3_key = v2 + nv2*vz_key*(sqrt(p.area) \
+ if scale_mode == "ADAPTIVE" else 1)
+ v3 = v3 + (v3_key - v3) * w2
+
+ if j == 0:
+ new_verts_np = v3
+ if bool_vertex_group: new_vertex_group_np = w2
+ else:
+ # Appending vertices
+ new_verts_np = np.concatenate((new_verts_np, v3), axis=0)
+ # Appending vertex group
+ if bool_vertex_group:
+ new_vertex_group_np = np.concatenate((new_vertex_group_np, w2),
+ axis=0)
+ # Appending faces
+ for p in fs1: new_faces.append([i+n_verts*j for i in p])
+ # Appending edges
+ for e in es1: new_edges.append([i+n_verts*j for i in e])
+
+ j += 1
+
+ new_verts = new_verts_np.tolist()
+ new_name = ob0.name + "_" + ob1.name
+ new_me = bpy.data.meshes.new(new_name)
+ new_me.from_pydata(new_verts, new_edges, new_faces)
+ #new_me.from_pydata(new_verts, new_edges, [])
+ new_me.update(calc_edges=True)
+ new_ob = bpy.data.objects.new("tessellate_temp", new_me)
+
+ # vertex group
+ if bool_vertex_group:
+ new_ob.vertex_groups.new("generator_group")
+ for i in range(len(new_vertex_group_np)):
+ new_ob.vertex_groups["generator_group"].add([i],
+ new_vertex_group_np[i],
+ "ADD")
+ ob0.data = old_me0
+ return new_ob
+
+
+def store_parameters(operator, ob):
+ ob.tissue_tessellate.generator = operator.generator
+ ob.tissue_tessellate.component = operator.component
+ ob.tissue_tessellate.zscale = operator.zscale
+ ob.tissue_tessellate.offset = operator.offset
+ ob.tissue_tessellate.gen_modifiers = operator.gen_modifiers
+ ob.tissue_tessellate.com_modifiers = operator.com_modifiers
+ ob.tissue_tessellate.mode = operator.mode
+ ob.tissue_tessellate.rotation_mode = operator.rotation_mode
+ ob.tissue_tessellate.merge = operator.merge
+ ob.tissue_tessellate.merge_thres = operator.merge_thres
+ ob.tissue_tessellate.scale_mode = operator.scale_mode
+ ob.tissue_tessellate.bool_random = operator.bool_random
+ ob.tissue_tessellate.random_seed = operator.random_seed
+ ob.tissue_tessellate.fill_mode = operator.fill_mode
+ ob.tissue_tessellate.bool_vertex_group = operator.bool_vertex_group
+ ob.tissue_tessellate.bool_selection = operator.bool_selection
+ ob.tissue_tessellate.bool_shapekeys = operator.bool_shapekeys
+ return ob
+
+
+class tissue_tessellate_prop(bpy.types.PropertyGroup):
+ generator = bpy.props.StringProperty()
+ component = bpy.props.StringProperty()
+ offset = bpy.props.FloatProperty()
+ zscale = bpy.props.FloatProperty(default=1)
+ merge = bpy.props.BoolProperty()
+ merge_thres = bpy.props.FloatProperty()
+ gen_modifiers = bpy.props.BoolProperty()
+ com_modifiers = bpy.props.BoolProperty()
+ mode = bpy.props.StringProperty()
+ rotation_mode = bpy.props.StringProperty()
+ scale_mode = bpy.props.StringProperty()
+ fill_mode = bpy.props.StringProperty()
+ bool_random = bpy.props.BoolProperty()
+ random_seed = bpy.props.IntProperty()
+ vertexgroup = bpy.props.StringProperty()
+ bool_vertex_group = bpy.props.BoolProperty()
+ bool_selection = bpy.props.BoolProperty()
+ bool_shapekeys = bpy.props.BoolProperty()
+
+
+class tessellate(bpy.types.Operator):
+ bl_idname = "object.tessellate"
+ bl_label = "Tessellate"
+ bl_description = ("Create a copy of selected object on the active object's "
+ "faces, adapting the shape to the different faces.")
+ bl_options = {'REGISTER', 'UNDO'}
+
+ object_name = bpy.props.StringProperty(
+ name="", description="Name of the generated object")
+ zscale = bpy.props.FloatProperty(
+ name="Scale", default=1, soft_min=0, soft_max=10,
+ description="Scale factor for the component thickness")
+ scale_mode = bpy.props.EnumProperty(
+ items=(('CONSTANT', "Constant", ""), ('ADAPTIVE', "Proportional", "")),
+ default='CONSTANT', name="Z-Scale according to faces size")
+ offset = bpy.props.FloatProperty(
+ name="Surface Offset", default=0, min=-1, max=1, soft_min=-1,
+ soft_max=1, description="Surface offset")
+ mode = bpy.props.EnumProperty(
+ items=(('CONSTANT', "Constant", ""), ('ADAPTIVE', "Adaptive", "")),
+ default='ADAPTIVE', name="Component Mode")
+ rotation_mode = bpy.props.EnumProperty(
+ items=(('RANDOM', "Random", ""), ('UV', "Active UV", ""),
+ ('DEFAULT', "Default", "")), default='DEFAULT',
+ name="Component Rotation")
+ fill_mode = bpy.props.EnumProperty(
+ items=(('QUAD', "Quad", ""), ('FAN', "Fan", "")), default='QUAD',
+ name="Fill Mode")
+ gen_modifiers = bpy.props.BoolProperty(
+ name="Generator Modifiers", default=False,
+ description="Apply modifiers to base object")
+ com_modifiers = bpy.props.BoolProperty(
+ name="Component Modifiers", default=False,
+ description="Apply modifiers to component object")
+ merge = bpy.props.BoolProperty(
+ name="Merge", default=False,
+ description="Merge vertices in adjacent duplicates")
+ merge_thres = bpy.props.FloatProperty(
+ name="Distance", default=0.001, soft_min=0, soft_max=10,
+ description="Limit below which to merge vertices")
+ generator = bpy.props.StringProperty(
+ name="", description="Base object for the tessellation")
+ component = bpy.props.StringProperty(
+ name="", description="Component object for the tessellation")
+ bool_random = bpy.props.BoolProperty(
+ name="Randomize", default=False,
+ description="Randomize component rotation")
+ random_seed = bpy.props.IntProperty(
+ name="Seed", default=0, soft_min=0, soft_max=10,
+ description="Random seed")
+ bool_vertex_group = bpy.props.BoolProperty(
+ name="Map Vertex Group", default=False, description=("Map the active "
+ "Vertex Group from the Base object to generated geometry"))
+ bool_selection = bpy.props.BoolProperty(
+ name="On selected Faces", default=False,
+ description="Create Tessellation only on selected faces")
+ bool_shapekeys = bpy.props.BoolProperty(
+ name="Use Shape Keys", default=False, description=("Use component's "
+ "active Shape Key according to active Vertex Group of the base object"))
+ working_on = ""
+
+ def draw(self, context):
+ try:
+ bool_working = self.working_on == self.object_name and \
+ self.working_on != ""
+ except:
+ bool_working = False
+
+ sel = bpy.context.selected_objects
+
+ bool_meshes = False
+ if len(sel) == 2:
+ bool_meshes = True
+ for o in sel:
+ if o.type != 'MESH': bool_meshes = False
+
+ if len(sel) != 2 and not bool_working:
+ layout = self.layout
+ layout.label(icon='INFO')
+ layout.label(text="Please, select two different objects")
+ layout.label(text="Select first the Component object, then select")
+ layout.label(text="the Base mesh.")
+ elif not bool_meshes and not bool_working:
+ layout = self.layout
+ layout.label(icon='INFO')
+ layout.label(text="Please, select two Mesh objects")
+ else:
+ try:
+ ob0 = bpy.data.Objects[self.generator]
+ except:
+ ob0 = bpy.context.active_object
+ self.generator = ob0.name
+
+ for o in sel:
+ if(o.name == ob0.name or o.type != 'MESH'): continue
+ else:
+ ob1 = o
+ self.component = o.name
+ no_component = False
+ break
+
+ # Checks for Tool Shelf panel, it loose the original Selection
+ if bpy.context.active_object.name == self.object_name:
+ ob1 = bpy.data.objects[
+ bpy.context.active_object.tissue_tessellate.component]
+ self.component = ob1.name
+ ob0 = bpy.data.objects[
+ bpy.context.active_object.tissue_tessellate.generator]
+ self.generator = ob0.name
+ no_component = False
+
+ # new object name
+ if self.object_name == "":
+ if self.generator == "": self.object_name = "Tessellation"
+ else: self.object_name = self.generator + "_Tessellation"
+
+ layout = self.layout
+ # Base and Component
+ col = layout.column(align=True)
+ row = col.row(align=True)
+ row.label(text="BASE : " + self.generator)
+ row.label(text="COMPONENT : " + self.component)
+ row = col.row(align=True)
+ col2 = row.column(align=True)
+ col2.prop(self, "gen_modifiers", text="Use Modifiers")
+ if len(bpy.data.objects[self.generator].modifiers) == 0:
+ col2.enabled = False
+ self.gen_modifiers = False
+ col2 = row.column(align=True)
+ col2.prop(self, "com_modifiers", text="Use Modifiers")
+ if len(bpy.data.objects[self.component].modifiers) == 0:
+ col2.enabled = False
+ self.com_modifiers = False
+
+ # On selected faces
+ row = col.row(align=True)
+ row.prop(self, "bool_selection", text="On selected Faces")
+ col.separator()
+
+ # General
+ col = layout.column(align=True)
+ col.label(text="New Object Name:")
+ col.prop(self, "object_name")
+
+ # Count number of faces
+ try:
+ polygons = 0
+ if self.gen_modifiers: me_temp = ob0.to_mesh(bpy.context.scene,
+ apply_modifiers=True, settings = 'PREVIEW')
+ else: me_temp = ob0.data
+ for p in me_temp.polygons:
+ if not self.bool_selection or p.select:
+ if self.fill_mode == "FAN": polygons += len(p.vertices)
+ else: polygons += 1
+
+ if self.com_modifiers: me_temp = bpy.data.objects[
+ self.component].to_mesh(bpy.context.scene,
+ apply_modifiers=True, settings = 'PREVIEW')
+ else: me_temp = bpy.data.objects[self.component].data
+ polygons *= len(me_temp.polygons)
+
+ str_polygons = '{:0,.0f}'.format(polygons)
+ if polygons > 200000:
+ col.label(text=str_polygons + " polygons will be created!",
+ icon='ERROR')
+ else:
+ col.label(text=str_polygons + " faces will be created!",
+ icon='INFO')
+ except:
+ pass
+ col.separator()
+
+ # Fill and Rotation
+ row = col.row(align=True)
+ row.label(text="Fill Mode:")
+ row.separator()
+ row.label(text="Rotation:")
+ row = col.row(align=True)
+
+ # Fill
+ row.prop(
+ self, "fill_mode", text="", icon='NONE', expand=False,
+ slider=True, toggle=False, icon_only=False, event=False,
+ full_event=False, emboss=True, index=-1)
+ row.separator()
+
+ # Rotation
+ row.prop(
+ self, "rotation_mode", text="", icon='NONE', expand=False,
+ slider=True, toggle=False, icon_only=False, event=False,
+ full_event=False, emboss=True, index=-1)
+ if self.rotation_mode == 'RANDOM':
+ row = col.row(align=True)
+ row.prop(self, "random_seed")
+ if self.rotation_mode == 'UV':
+ uv_error = False
+ if self.fill_mode == 'FAN':
+ row = col.row(align=True)
+ row.label(text="UV rotation doesn't work in FAN mode",
+ icon='ERROR')
+ uv_error = True
+ if len(bpy.data.objects[self.generator].data.uv_layers) == 0:
+ row = col.row(align=True)
+ row.label(text="'" + bpy.data.objects[self.generator].name \
+ + "' doesn't have UV Maps", icon='ERROR')
+ uv_error = True
+ if uv_error:
+ row = col.row(align=True)
+ row.label(text="Default rotation will be used instead",
+ icon='INFO')
+
+ # Component XY
+ row = col.row(align=True)
+ row.label(text="Component XY:")
+ row = col.row(align=True)
+ row.prop(
+ self, "mode", text="Component XY", icon='NONE', expand=True,
+ slider=False, toggle=False, icon_only=False, event=False,
+ full_event=False, emboss=True, index=-1)
+
+ # Component Z
+ col.label(text="Component Z:")
+ row = col.row(align=True)
+ row.prop(
+ self, "scale_mode", text="Scale Mode", icon='NONE', expand=True,
+ slider=False, toggle=False, icon_only=False, event=False,
+ full_event=False, emboss=True, index=-1)
+ col.prop(
+ self, "zscale", text="Scale", icon='NONE', expand=False,
+ slider=True, toggle=False, icon_only=False, event=False,
+ full_event=False, emboss=True, index=-1)
+ col.prop(
+ self, "offset", text="Offset", icon='NONE', expand=False,
+ slider=True, toggle=False, icon_only=False, event=False,
+ full_event=False, emboss=True, index=-1)
+
+ # Merge
+ col = layout.column(align=True)
+ row = col.row(align=True)
+ row.prop(self, "merge")
+ if self.merge: row.prop(self, "merge_thres")
+ row = col.row(align=True)
+
+ # ADVANCED
+ col = layout.column(align=True)
+ col.label(text="Advanced Settings:")
+ # vertex group + shape keys
+ row = col.row(align=True)
+ col2 = row.column(align=True)
+ col2.prop(self, "bool_vertex_group")
+ if len(bpy.data.objects[self.generator].vertex_groups) == 0:
+ col2.enabled = False
+ bool_vertex_group = False
+ col2 = row.column(align=True)
+ col2.prop(self, "bool_shapekeys", text="Use Shape Keys")
+ if len(bpy.data.objects[self.generator].vertex_groups) == 0 or \
+ bpy.data.objects[self.component].data.shape_keys == None:
+ col2.enabled = False
+ bool_shapekeys = False
+ elif len(bpy.data.objects[self.generator].vertex_groups) == 0 or \
+ bpy.data.objects[self.component].data.shape_keys != None:
+ if len(bpy.data.objects[
+ self.component].data.shape_keys.key_blocks) < 2:
+ col2.enabled = False
+ bool_shapekeys = False
+
+ def execute(self, context):
+ try:
+ ob0 = bpy.context.active_object
+ self.generator = ob0.name
+ except:
+ self.report({'ERROR'}, "A Generator mesh object must be selected")
+
+ # component object
+ sel = bpy.context.selected_objects
+ no_component = True
+ for o in sel:
+ if(o.name == ob0.name or o.type != 'MESH'): continue
+ else:
+ ob1 = o
+ self.component = o.name
+ no_component = False
+ break
+
+ # Checks for Tool Shelf panel, it loose the original Selection
+ if bpy.context.active_object == self.object_name:
+ ob1 = bpy.data.objects[
+ bpy.context.active_object.tissue_tessellate.component]
+ self.component = ob1.name
+ ob0 = bpy.data.objects[
+ bpy.context.active_object.tissue_tessellate.generator]
+ self.generator = ob0.name
+ no_component = False
+
+ if(no_component):
+ #self.report({'ERROR'}, "A component mesh object must be selected")
+ return {'CANCELLED'}
+
+ # new object name
+ if self.object_name == "":
+ if self.generator == "": self.object_name = "Tessellation"
+ else: self.object_name = self.generator + "_Tessellation"
+
+ if bpy.data.objects[self.component].type != 'MESH':
+ message = "Component must be Mesh Objects!"
+ self.report({'ERROR'}, message)
+ self.component = ""
+ if bpy.data.objects[self.generator].type != 'MESH':
+ message = "Generator must be Mesh Objects!"
+ self.report({'ERROR'}, message)
+ self.generator = ""
+ if self.component != "" and self.generator != "":
+ bpy.ops.object.select_all(action='TOGGLE')
+
+ new_ob = tassellate(
+ ob0, ob1, self.offset, self.zscale, self.gen_modifiers,
+ self.com_modifiers, self.mode, self.scale_mode,
+ self.rotation_mode, self.random_seed, self.fill_mode,
+ self.bool_vertex_group, self.bool_selection,
+ self.bool_shapekeys)
+
+ if new_ob == 0:
+ message = "Zero faces selected in the Base mesh!"
+ self.report({'ERROR'}, message)
+ return {'CANCELLED'}
+
+ new_ob.name = self.object_name
+ #new_ob = bpy.data.objects.new(self.object_name, new_me)
+
+ new_ob.location = ob0.location
+ new_ob.matrix_world = ob0.matrix_world
+
+ scene = bpy.context.scene
+ scene.objects.link(new_ob)
+ new_ob.select = True
+ bpy.context.scene.objects.active = new_ob
+ if self.merge:
+ bpy.ops.object.mode_set(mode = 'EDIT')
+ bpy.ops.mesh.select_mode(
+ use_extend=False, use_expand=False, type='VERT')
+ 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.remove_doubles(
+ threshold=self.merge_thres, use_unselected=False)
+ bpy.ops.object.mode_set(mode = 'OBJECT')
+ new_ob = store_parameters(self, new_ob)
+ self.object_name = new_ob.name
+ self.working_on = self.object_name
+
+ bpy.ops.object.mode_set(mode='EDIT')
+ bpy.ops.object.mode_set(mode='OBJECT')
+
+ # MATERIALS
+ # create materials list
+ polygon_materials = [p.material_index for p in ob1.data.polygons]*int(
+ len(new_ob.data.polygons) / len(ob1.data.polygons))
+ # assign old material
+ component_materials = [slot.material for slot in ob1.material_slots]
+ for i in range(len(component_materials)):
+ bpy.ops.object.material_slot_add()
+ bpy.context.object.material_slots[i].material = \
+ component_materials[i]
+ for i in range(len(new_ob.data.polygons)):
+ new_ob.data.polygons[i].material_index = polygon_materials[i]
+
+ return {'FINISHED'}
+
+ def check(self, context):
+ return True
+
+ def invoke(self, context, event):
+ return context.window_manager.invoke_props_dialog(self)
+
+
+class update_tessellate(bpy.types.Operator):
+#class adaptive_duplifaces(bpy.types.Panel):
+ bl_idname = "object.update_tessellate"
+ bl_label = "Refresh"
+ bl_description = ("Fast update the tessellated mesh according to base and "
+ "component changes")
+ bl_options = {'REGISTER', 'UNDO'}
+ go = False
+ ob = bpy.types.Object
+
+ @classmethod
+ def poll(cls, context):
+ try:
+ return context.active_object.tissue_tessellate.generator != "" and \
+ context.active_object.tissue_tessellate.component != ""
+ except: return False
+
+ def execute(self, context):
+ ob = bpy.context.active_object
+ if not self.go:
+ generator = ob.tissue_tessellate.generator
+ component = ob.tissue_tessellate.component
+ zscale = ob.tissue_tessellate.zscale
+ scale_mode = ob.tissue_tessellate.scale_mode
+ rotation_mode = ob.tissue_tessellate.rotation_mode
+ offset = ob.tissue_tessellate.offset
+ merge = ob.tissue_tessellate.merge
+ merge_thres = ob.tissue_tessellate.merge_thres
+ gen_modifiers = ob.tissue_tessellate.gen_modifiers
+ com_modifiers = ob.tissue_tessellate.com_modifiers
+ bool_random = ob.tissue_tessellate.bool_random
+ random_seed = ob.tissue_tessellate.random_seed
+ fill_mode = ob.tissue_tessellate.fill_mode
+ bool_vertex_group = ob.tissue_tessellate.bool_vertex_group
+ bool_selection = ob.tissue_tessellate.bool_selection
+ bool_shapekeys = ob.tissue_tessellate.bool_shapekeys
+ mode = ob.tissue_tessellate.mode
+
+ if(generator == "" or component == ""):
+ self.report({'ERROR'},
+ "Active object must be Tessellate before Update")
+ return {'CANCELLED'}
+
+ ob0 = bpy.data.objects[generator]
+ ob1 = bpy.data.objects[component]
+ me0 = ob0.data
+ verts = me0.vertices
+
+ temp_ob = tassellate(
+ ob0, ob1, offset, zscale, gen_modifiers, com_modifiers,
+ mode, scale_mode, rotation_mode, random_seed, fill_mode,
+ bool_vertex_group, bool_selection, bool_shapekeys)
+
+ if temp_ob == 0:
+ message = "Zero faces selected in the Base mesh!"
+ self.report({'ERROR'}, message)
+ return {'CANCELLED'}
+
+ ob.data = temp_ob.data
+ bpy.data.objects.remove(temp_ob)
+ if merge:
+ bpy.ops.object.mode_set(mode = 'EDIT')
+ bpy.ops.mesh.select_mode(
+ use_extend=False, use_expand=False, type='VERT')
+ 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.remove_doubles(
+ threshold=merge_thres, use_unselected=False)
+ bpy.ops.object.mode_set(mode = 'OBJECT')
+ bpy.ops.object.mode_set(mode='EDIT')
+ bpy.ops.object.mode_set(mode='OBJECT')
+
+ # MATERIALS
+ # create materials list
+ polygon_materials = [p.material_index for p in ob1.data.polygons]*int(
+ len(ob.data.polygons) / len(ob1.data.polygons))
+ # assign old material
+ component_materials = [slot.material for slot in ob1.material_slots]
+ for i in range(len(component_materials)):
+ bpy.ops.object.material_slot_add()
+ bpy.context.object.material_slots[i].material = \
+ component_materials[i]
+ for i in range(len(ob.data.polygons)):
+ ob.data.polygons[i].material_index = polygon_materials[i]
+
+ return {'FINISHED'}
+
+ def check(self, context):
+ return True
+
+
+class settings_tessellate(bpy.types.Operator):
+#class adaptive_duplifaces(bpy.types.Panel):
+ bl_idname = "object.settings_tessellate"
+ bl_label = "Settings"
+ bl_description = ("Update the tessellated mesh according to base and "
+ "component changes. Allow also to change tessellation's parameters")
+ bl_options = {'REGISTER', 'UNDO'}
+
+ object_name = bpy.props.StringProperty(
+ name="", description="Name of the generated object")
+ zscale = bpy.props.FloatProperty(
+ name="Scale", default=1, soft_min=0, soft_max=10,
+ description="Scale factor for the component thickness")
+ scale_mode = bpy.props.EnumProperty(
+ items=(('CONSTANT', "Constant", ""), ('ADAPTIVE', "Proportional", "")),
+ default='ADAPTIVE', name="Scale variation")
+ offset = bpy.props.FloatProperty(
+ name="Surface Offset", default=0, min=-1, max=1, soft_min=-1,
+ soft_max=1, description="Surface offset")
+ mode = bpy.props.EnumProperty(
+ items=(('CONSTANT', "Constant", ""), ('ADAPTIVE', "Adaptive", "")),
+ default='ADAPTIVE', name="Component Mode")
+ rotation_mode = bpy.props.EnumProperty(
+ items=(('RANDOM', "Random", ""), ('UV', "Active UV", ""),
+ ('DEFAULT', "Default", "")), default='DEFAULT',
+ name="Component Rotation")
+ fill_mode = bpy.props.EnumProperty(
+ items=(('QUAD', "Quad", ""), ('FAN', "Fan", "")), default='QUAD',
+ name="Fill Mode")
+ gen_modifiers = bpy.props.BoolProperty(
+ name="Generator Modifiers", default=False,
+ description="Apply modifiers to base object")
+ com_modifiers = bpy.props.BoolProperty(
+ name="Component Modifiers", default=False,
+ description="Apply modifiers to component object")
+ merge = bpy.props.BoolProperty(
+ name="Merge", default=False,
+ description="Merge vertices in adjacent duplicates")
+ merge_thres = bpy.props.FloatProperty(
+ name="Distance", default=0.001, soft_min=0, soft_max=10,
+ description="Limit below which to merge vertices")
+ generator = bpy.props.StringProperty(
+ name="", description="Base object for the tessellation")
+ component = bpy.props.StringProperty(
+ name="", description="Component object for the tessellation")
+ bool_random = bpy.props.BoolProperty(
+ name="Randomize", default=False,
+ description="Randomize component rotation")
+ random_seed = bpy.props.IntProperty(
+ name="Seed", default=0, soft_min=0, soft_max=10,
+ description="Random seed")
+ bool_vertex_group = bpy.props.BoolProperty(
+ name="Map Vertex Group", default=False, description=("Map on generated "
+ "geometry the active Vertex Group from the base object"))
+ bool_selection = bpy.props.BoolProperty(
+ name="On selected Faces", default=False,
+ description="Create Tessellation only on select faces")
+ bool_shapekeys = bpy.props.BoolProperty(
+ name="Use Shape Keys", default=False, description=("Use component's "
+ "active Shape Key according to active Vertex Group of the base object"))
+ go = False
+ ob = bpy.types.Object
+
+ @classmethod
+ def poll(cls, context):
+ try:
+ return context.active_object.tissue_tessellate.generator != "" and \
+ context.active_object.tissue_tessellate.component != ""
+ except: return False
+
+ def draw(self, context):
+ layout = self.layout
+ ob0 = bpy.context.active_object
+
+ if not self.go:
+ self.generator = ob0.tissue_tessellate.generator
+ self.component = ob0.tissue_tessellate.component
+ self.zscale = ob0.tissue_tessellate.zscale
+ self.scale_mode = ob0.tissue_tessellate.scale_mode
+ self.rotation_mode = ob0.tissue_tessellate.rotation_mode
+ self.offset = ob0.tissue_tessellate.offset
+ self.merge = ob0.tissue_tessellate.merge
+ self.merge_thres = ob0.tissue_tessellate.merge_thres
+ self.gen_modifiers = ob0.tissue_tessellate.gen_modifiers
+ self.com_modifiers = ob0.tissue_tessellate.com_modifiers
+ self.bool_random = ob0.tissue_tessellate.bool_random
+ self.random_seed = ob0.tissue_tessellate.random_seed
+ self.fill_mode = ob0.tissue_tessellate.fill_mode
+ self.bool_vertex_group = ob0.tissue_tessellate.bool_vertex_group
+ self.bool_selection = ob0.tissue_tessellate.bool_selection
+ self.bool_shapekeys = ob0.tissue_tessellate.bool_shapekeys
+ self.mode = ob0.tissue_tessellate.mode
+
+ # start drawing
+ layout = self.layout
+ #ob0 = bpy.context.active_object
+ # Base and Component
+ col = layout.column(align=True)
+ row = col.row(align=True)
+ row.label(text="BASE :")
+ row.label(text="COMPONENT :")
+ row = col.row(align=True)
+
+ col2 = row.column(align=True)
+ col2.prop_search(self, "generator", bpy.data, "objects")
+ row.separator()
+ col2 = row.column(align=True)
+ col2.prop_search(self, "component", bpy.data, "objects")
+
+ row = col.row(align=True)
+ col2 = row.column(align=True)
+ col2.prop(self, "gen_modifiers", text="Use Modifiers")
+ if len(bpy.data.objects[self.generator].modifiers) == 0:
+ col2.enabled = False
+ self.gen_modifiers = False
+ col2 = row.column(align=True)
+ col2.prop(self, "com_modifiers", text="Use Modifiers")
+ if len(bpy.data.objects[self.component].modifiers) == 0:
+ col2.enabled = False
+ self.com_modifiers = False
+
+ # On selected faces
+ row = col.row(align=True)
+ row.prop(self, "bool_selection", text="On selected Faces")
+ col.separator()
+
+ # Count number of faces
+ try:
+ polygons = 0
+ if self.gen_modifiers:
+ me_temp = bpy.data.objects[self.generator].to_mesh(
+ bpy.context.scene, apply_modifiers=True,
+ settings = 'PREVIEW')
+ else: me_temp = bpy.data.objects[self.generator].data
+ for p in me_temp.polygons:
+ if not self.bool_selection or p.select:
+ if self.fill_mode == "FAN": polygons += len(p.vertices)
+ else: polygons += 1
+
+ if self.com_modifiers:
+ me_temp = bpy.data.objects[self.component].to_mesh(
+ bpy.context.scene, apply_modifiers=True,
+ settings = 'PREVIEW')
+ else: me_temp = bpy.data.objects[self.component].data
+ polygons *= len(me_temp.polygons)
+
+ str_polygons = '{:0,.0f}'.format(polygons)
+ if polygons > 200000:
+ col.label(text=str_polygons + " polygons will be created!",
+ icon='ERROR')
+ else:
+ col.label(text=str_polygons + " faces will be created!",
+ icon='INFO')
+ except:
+ pass
+ col.separator()
+
+ # Fill and Rotation
+ row = col.row(align=True)
+ row.label(text="Fill Mode:")
+ row.separator()
+ row.label(text="Rotation:")
+ row = col.row(align=True)
+
+ # fill
+ row.prop(self, "fill_mode", text="", icon='NONE', expand=False,
+ slider=True, toggle=False, icon_only=False, event=False,
+ full_event=False, emboss=True, index=-1)
+ row.separator()
+
+ # rotation
+ row.prop(self, "rotation_mode", text="", icon='NONE', expand=False,
+ slider=True, toggle=False, icon_only=False, event=False,
+ full_event=False, emboss=True, index=-1)
+ if self.rotation_mode == 'RANDOM':
+ row = col.row(align=True)
+ row.prop(self, "random_seed")
+ if self.rotation_mode == 'UV':
+ uv_error = False
+ if self.fill_mode == 'FAN':
+ row = col.row(align=True)
+ row.label(text="UV rotation doesn't work in FAN mode",
+ icon='ERROR')
+ uv_error = True
+ if len(bpy.data.objects[self.generator].data.uv_layers) == 0:
+ row = col.row(align=True)
+ row.label(text="'" + bpy.data.objects[self.generator].name + \
+ " doesn't have UV Maps", icon='ERROR')
+ uv_error = True
+ if uv_error:
+ row = col.row(align=True)
+ row.label(text="Default rotation will be used instead",
+ icon='INFO')
+
+ # component XY
+ row = col.row(align=True)
+ row.label(text="Component XY:")
+ row = col.row(align=True)
+ row.prop(self, "mode", text="Component XY", icon='NONE', expand=True,
+ slider=False, toggle=False, icon_only=False, event=False,
+ full_event=False, emboss=True, index=-1)
+
+ # component Z
+ col.label(text="Component Z:")
+ row = col.row(align=True)
+ row.prop(self, "scale_mode", text="Scale Mode", icon='NONE',
+ expand=True, slider=False, toggle=False, icon_only=False,
+ event=False, full_event=False, emboss=True, index=-1)
+ col.prop(self, "zscale", text="Scale", icon='NONE', expand=False,
+ slider=True, toggle=False, icon_only=False, event=False,
+ full_event=False, emboss=True, index=-1)
+ col.prop(self, "offset", text="Offset", icon='NONE', expand=False,
+ slider=True, toggle=False, icon_only=False, event=False,
+ full_event=False, emboss=True, index=-1)
+
+ # merge
+ col = layout.column(align=True)
+ row = col.row(align=True)
+ row.prop(self, "merge")
+ if self.merge: row.prop(self, "merge_thres")
+ row = col.row(align=True)
+
+ ### ADVANCED ###
+ col = layout.column(align=True)
+ tessellate.rotation_mode
+
+ col.label(text="Advanced Settings:")
+ # vertex group + shape keys
+ row = col.row(align=True)
+ col2 = row.column(align=True)
+ col2.prop(self, "bool_vertex_group")
+ if len(bpy.data.objects[self.generator].vertex_groups) == 0:
+ col2.enabled = False
+ bool_vertex_group = False
+ col2 = row.column(align=True)
+ col2.prop(self, "bool_shapekeys", text="Use Shape Keys")
+ if len(bpy.data.objects[self.generator].vertex_groups) == 0 or \
+ bpy.data.objects[self.component].data.shape_keys == None:
+ col2.enabled = False
+ bool_shapekeys = False
+ elif len(bpy.data.objects[self.generator].vertex_groups) == 0 or \
+ bpy.data.objects[self.component].data.shape_keys != None:
+ if len(bpy.data.objects[self.component].data.shape_keys.key_blocks) < 2:
+ col2.enabled = False
+ bool_shapekeys = False
+ self.go = True
+
+ def execute(self, context):
+ self.ob = bpy.context.active_object
+ old_material = None
+ if(len(self.ob.material_slots) > 0):
+ old_material = self.ob.material_slots[0].material
+ if not self.go:
+ self.generator = self.ob.tissue_tessellate.generator
+ self.component = self.ob.tissue_tessellate.component
+ self.zscale = self.ob.tissue_tessellate.zscale
+ self.scale_mode = self.ob.tissue_tessellate.scale_mode
+ self.rotation_mode = self.ob.tissue_tessellate.rotation_mode
+ self.offset = self.ob.tissue_tessellate.offset
+ self.merge = self.ob.tissue_tessellate.merge
+ self.merge_thres = self.ob.tissue_tessellate.merge_thres
+ self.gen_modifiers = self.ob.tissue_tessellate.gen_modifiers
+ self.com_modifiers = self.ob.tissue_tessellate.com_modifiers
+ self.bool_random = self.ob.tissue_tessellate.bool_random
+ self.random_seed = self.ob.tissue_tessellate.random_seed
+ self.fill_mode = self.ob.tissue_tessellate.fill_mode
+ self.bool_vertex_group = self.ob.tissue_tessellate.bool_vertex_group
+ self.bool_selection = self.ob.tissue_tessellate.bool_selection
+ self.bool_shapekeys = self.ob.tissue_tessellate.bool_shapekeys
+
+ if(self.generator == "" or self.component == ""):
+ self.report({'ERROR'},
+ "Active object must be Tessellate before Update")
+ return {'CANCELLED'}
+ if(bpy.data.objects[self.generator].type != 'MESH'):
+ self.report({'ERROR'}, "Base object must be a Mesh")
+ return {'CANCELLED'}
+ if(bpy.data.objects[self.component].type != 'MESH'):
+ self.report({'ERROR'}, "Component object must be a Mesh")
+ return {'CANCELLED'}
+
+ ob0 = bpy.data.objects[self.generator]
+ ob1 = bpy.data.objects[self.component]
+ me0 = ob0.data
+ verts = me0.vertices
+
+ temp_ob = tassellate(
+ ob0, ob1, self.offset, self.zscale, self.gen_modifiers,
+ self.com_modifiers, self.mode, self.scale_mode, self.rotation_mode,
+ self.random_seed, self.fill_mode, self.bool_vertex_group,
+ self.bool_selection, self.bool_shapekeys)
+
+ if temp_ob == 0:
+ message = "Zero faces selected in the Base mesh!"
+ self.report({'ERROR'}, message)
+ return {'CANCELLED'}
+
+ # Transfer mesh data
+ self.ob.data = temp_ob.data
+
+ # Create object in order to transfer vertex group
+ scene = bpy.context.scene
+ scene.objects.link(temp_ob)
+ temp_ob.select = True
+ bpy.context.scene.objects.active = temp_ob
+ try:
+ bpy.ops.object.vertex_group_copy_to_linked()
+ except:
+ pass
+ scene.objects.unlink(temp_ob)
+ bpy.data.objects.remove(temp_ob)
+ bpy.context.scene.objects.active = self.ob
+
+ if self.merge:
+ bpy.ops.object.mode_set(mode = 'EDIT')
+ bpy.ops.mesh.select_mode(
+ use_extend=False, use_expand=False, type='VERT')
+ 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.remove_doubles(
+ threshold=self.merge_thres, use_unselected=False)
+ bpy.ops.object.mode_set(mode = 'OBJECT')
+ self.ob = store_parameters(self, self.ob)
+ bpy.ops.object.mode_set(mode='EDIT')
+ bpy.ops.object.mode_set(mode='OBJECT')
+
+ # MATERIALS
+ # create materials list
+ polygon_materials = [p.material_index for p in ob1.data.polygons] * \
+ int(len(self.ob.data.polygons) / len(ob1.data.polygons))
+ # assign old material
+ component_materials = [slot.material for slot in ob1.material_slots]
+ for i in range(len(component_materials)):
+ bpy.ops.object.material_slot_add()
+ bpy.context.object.material_slots[i].material = \
+ component_materials[i]
+ for i in range(len(self.ob.data.polygons)):
+ self.ob.data.polygons[i].material_index = polygon_materials[i]
+ return {'FINISHED'}
+
+ def check(self, context):
+ return True
+
+ def invoke(self, context, event):
+ return context.window_manager.invoke_props_dialog(self)
+
+
+class tessellate_panel(bpy.types.Panel):
+ bl_label = "Tessellate"
+ bl_category = "Create"
+ bl_space_type = "VIEW_3D"
+ bl_region_type = "TOOLS"
+ #bl_context = "objectmode", "editmode"
+
+ def draw(self, context):
+ layout = self.layout
+ col = layout.column(align=True)
+ col.label(text="Add:")
+ col.operator("object.tessellate")#, icon="STRANDS")
+ #col.enable = False
+ #col.operator("object.adaptive_duplifaces", icon="MESH_CUBE")
+ col = layout.column(align=True)
+ col.label(text="Edit:")
+ col.operator("object.settings_tessellate")
+ col.operator("object.update_tessellate")
+ col = layout.column(align=True)
+ col.operator("mesh.rotate_face")
+ act = context.active_object
+ sel = act #context.selected_objects[0]
+
+ for ob1 in context.selected_objects:
+ if(ob1.name == act.name or ob1.type != 'MESH'): continue
+ sel = ob1
+
+
+class rotate_face(bpy.types.Operator):
+ bl_idname = "mesh.rotate_face"
+ bl_label = "Rotate Faces"
+ bl_description = "Rotate selected faces and update tessellated meshes."
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ return context.mode == 'EDIT_MESH'
+
+ def execute(self, context):
+ ob = bpy.context.active_object
+ me = ob.data
+ bpy.ops.object.mode_set(mode='OBJECT')
+
+ for p in [f for f in me.polygons if f.select]:
+ p.vertices = p.vertices[1:] + p.vertices[:1]
+
+ bpy.ops.object.mode_set(mode='EDIT')
+ bpy.ops.mesh.flip_normals()
+ bpy.ops.mesh.flip_normals()
+ #me.vertices[0].co[0] = 10
+ me.update(calc_edges=True)
+
+ #update tessellated meshes
+ bpy.ops.object.mode_set(mode='OBJECT')
+ for o in [object for object in bpy.data.objects if \
+ object.tissue_tessellate.generator == ob.name]:
+ bpy.context.scene.objects.active = o
+ bpy.ops.object.update_tessellate()
+ bpy.context.scene.objects.active = ob
+ bpy.ops.object.mode_set(mode='EDIT')
+ return {'FINISHED'}
+
+
+def register():
+ bpy.utils.register_class(tissue_tessellate_prop)
+ bpy.utils.register_class(tessellate)
+ bpy.utils.register_class(update_tessellate)
+ bpy.utils.register_class(settings_tessellate)
+ bpy.utils.register_class(tessellate_panel)
+ bpy.utils.register_class(rotate_face)
+
+
+def unregister():
+ bpy.utils.unregister_class(tissue_tessellate_prop)
+ bpy.utils.unregister_class(tessellate)
+ bpy.utils.unregister_class(update_tessellate)
+ bpy.utils.unregister_class(settings_tessellate)
+ bpy.utils.unregister_class(tessellate_panel)
+ bpy.utils.unregister_class(rotate_face)
+
+
+if __name__ == "__main__":
+ register()