diff options
author | lijenstina <lijenstina@gmail.com> | 2017-06-25 17:01:21 +0300 |
---|---|---|
committer | lijenstina <lijenstina@gmail.com> | 2017-06-25 17:01:21 +0300 |
commit | 664ecb162c9beb5a19df67934866badb0ce2b3e4 (patch) | |
tree | 4a30024c2304560596f8887c53dec7fcc5956019 /mesh_tissue | |
parent | 58727bbb43c67b50a778a1cdb82e974b2d68c283 (diff) |
Mesh Tissue: cleanup, fix remove base object crashes
Bumped version to 0.3.3
Pep8 Cleanup
Fix crashes related to removing the base object with
Settings and Refresh
Move bl_info on top of the files
Imports as tuples
consistent prop definitions
Diffstat (limited to 'mesh_tissue')
-rw-r--r-- | mesh_tissue/__init__.py | 43 | ||||
-rw-r--r-- | mesh_tissue/colors_groups_exchanger.py | 408 | ||||
-rw-r--r-- | mesh_tissue/dual_mesh.py | 230 | ||||
-rw-r--r-- | mesh_tissue/lattice.py | 334 | ||||
-rw-r--r-- | mesh_tissue/tessellate_numpy.py | 962 | ||||
-rw-r--r-- | mesh_tissue/uv_to_mesh.py | 81 |
6 files changed, 1263 insertions, 795 deletions
diff --git a/mesh_tissue/__init__.py b/mesh_tissue/__init__.py index 7d8b0078..bef99625 100644 --- a/mesh_tissue/__init__.py +++ b/mesh_tissue/__init__.py @@ -16,8 +16,8 @@ # # ##### END GPL LICENSE BLOCK ##### -# --------------------------------- TISSUE ------------------------------------# -#-------------------------------- version 0.3 ---------------------------------# +# --------------------------------- TISSUE ----------------------------------- # +# ------------------------------- version 0.3 -------------------------------- # # # # Creates duplicates of selected mesh to active morphing the shape according # # to target faces. # @@ -28,7 +28,20 @@ # http://www.co-de-it.com/ # # http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Mesh/Tissue # # # -################################################################################ +# ############################################################################ # + +bl_info = { + "name": "Tissue", + "author": "Alessandro Zomparelli (Co-de-iT)", + "version": (0, 3, 3), + "blender": (2, 7, 9), + "location": "", + "description": "Tools for Computational Design", + "warning": "", + "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/" + "Py/Scripts/Mesh/Tissue", + "tracker_url": "https://plus.google.com/u/0/+AlessandroZomparelli/", + "category": "Mesh"} if "bpy" in locals(): @@ -47,28 +60,14 @@ else: from . import uv_to_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, 3, 2), - "blender": (2, 7, 9), - "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"} +from bpy.props import PointerProperty def register(): bpy.utils.register_module(__name__) - bpy.types.Object.tissue_tessellate = bpy.props.PointerProperty( - type=tessellate_numpy.tissue_tessellate_prop) + bpy.types.Object.tissue_tessellate = PointerProperty( + type=tessellate_numpy.tissue_tessellate_prop + ) def unregister(): @@ -78,6 +77,8 @@ def unregister(): lattice.unregister() uv_to_mesh.unregister() + del bpy.types.Object.tissue_tessellate + if __name__ == "__main__": register() diff --git a/mesh_tissue/colors_groups_exchanger.py b/mesh_tissue/colors_groups_exchanger.py index 49378c1e..b102a5b6 100644 --- a/mesh_tissue/colors_groups_exchanger.py +++ b/mesh_tissue/colors_groups_exchanger.py @@ -16,7 +16,7 @@ # # ##### END GPL LICENSE BLOCK ##### -#-------------------------- COLORS / GROUPS EXCHANGER -------------------------# +# ------------------------- COLORS / GROUPS EXCHANGER ------------------------ # # # # Vertex Color to Vertex Group allow you to convert colors channles to weight # # maps. # @@ -31,131 +31,172 @@ # # # http://www.co-de-it.com/ # # # -################################################################################ - -import bpy -import math -from math import pi, sin +# ############################################################################ # bl_info = { "name": "Colors/Groups Exchanger", "author": "Alessandro Zomparelli (Co-de-iT)", - "version": (0, 3), - "blender": (2, 7, 9), + "version": (0, 3, 1), + "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): + +import bpy +from bpy.props import ( + BoolProperty, + EnumProperty, + FloatProperty, + IntProperty, + ) +from bpy.types import Operator +from math import ( + pi, sin, + ) + + +class vertex_colors_to_vertex_groups(Operator): bl_idname = "object.vertex_colors_to_vertex_groups" bl_label = "Vertex Color" + bl_description = ("Convert the active Vertex Color into a Vertex Group") bl_options = {'REGISTER', 'UNDO'} - bl_description = ("Convert the active Vertex Color into a Vertex Group.") - - 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") + + red = BoolProperty( + name="Red Channel", + default=False, + description="Convert Red Channel" + ) + green = BoolProperty( + name="Green Channel", + default=False, + description="Convert Green Channel" + ) + blue = BoolProperty( + name="Blue Channel", + default=False, + description="Convert Blue Channel" + ) + value = BoolProperty( + name="Value Channel", + default=True, + description="Convert Value Channel" + ) + invert = 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 + ids = len(obj.vertex_groups) + id_red = ids + id_green = ids + id_blue = ids + id_value = ids boolCol = len(obj.data.vertex_colors) - if(boolCol): col_name = obj.data.vertex_colors.active.name + 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): + if self.red and boolCol: bpy.ops.object.vertex_group_add() bpy.ops.object.vertex_group_assign() - id_red = id + id_red = ids obj.vertex_groups[id_red].name = col_name + '_red' - id+=1 - if(self.green and boolCol): + ids += 1 + if self.green and boolCol: bpy.ops.object.vertex_group_add() bpy.ops.object.vertex_group_assign() - id_green = id + id_green = ids obj.vertex_groups[id_green].name = col_name + '_green' - id+=1 - if(self.blue and boolCol): + ids += 1 + if self.blue and boolCol: bpy.ops.object.vertex_group_add() bpy.ops.object.vertex_group_assign() - id_blue = id + id_blue = ids obj.vertex_groups[id_blue].name = col_name + '_blue' - id+=1 - if(self.value and boolCol): + ids += 1 + if self.value and boolCol: bpy.ops.object.vertex_group_add() bpy.ops.object.vertex_group_assign() - id_value = id + id_value = ids obj.vertex_groups[id_value].name = col_name + '_value' - id+=1 + ids += 1 mult = 1 - if(self.invert): 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): + ids = len(obj.vertex_groups) + if (id_red <= ids and id_green <= ids and id_blue <= ids and id_value <= + ids 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 + 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): +class vertex_group_to_vertex_colors(Operator): bl_idname = "object.vertex_group_to_vertex_colors" bl_label = "Vertex Group" + bl_description = ("Convert the active Vertex Group into a Vertex Color") bl_options = {'REGISTER', 'UNDO'} - bl_description = ("Convert the active Vertex Group into a Vertex Color.") - - 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") + channel = 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 = 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'} @@ -165,37 +206,45 @@ class vertex_group_to_vertex_colors(bpy.types.Operator): 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" + 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 + 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) + 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) + 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) + 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) + v_colors[i].color = ((g.weight - 0.5) * 4, 1, 0) else: - v_colors[i].color = (1,1-(g.weight-0.75)*4,0) + 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, @@ -203,46 +252,66 @@ class vertex_group_to_vertex_colors(bpy.types.Operator): self.invert + mult * g.weight) elif(self.channel == 'Red'): v_colors[i].color = ( - self.invert + mult * g.weight,0,0) + self.invert + mult * g.weight, 0, 0) elif(self.channel == 'Green'): v_colors[i].color = ( - 0, self.invert + mult * g.weight,0) + 0, self.invert + mult * g.weight, 0) elif(self.channel == 'Blue'): v_colors[i].color = ( - 0,0, self.invert + mult * g.weight) - i+=1 + 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 curvature_to_vertex_groups(bpy.types.Operator): + +class curvature_to_vertex_groups(Operator): bl_idname = "object.curvature_to_vertex_groups" bl_label = "Curvature" - bl_options = {'REGISTER', 'UNDO'} - invert = bpy.props.BoolProperty( - name="invert", default=False, description="invert values") bl_description = ("Generate a Vertex Group based on the curvature of the" - "mesh. Is based on Dirty Vertex Color.") - - blur_strength = bpy.props.FloatProperty( - name="Blur Strength", default=1, min=0.001, - max=1, description="Blur strength per iteration") - - blur_iterations = bpy.props.IntProperty( - name="Blur Iterations", default=1, min=0, - max=40, description="Number of times to blur the values") - - min_angle = bpy.props.FloatProperty( - name="Min Angle", default=0, min=0, - max=pi/2, subtype='ANGLE', description="Minimum angle") - - max_angle = bpy.props.FloatProperty( - name="Max Angle", default=pi, min=pi/2, - max=pi, subtype='ANGLE', description="Maximum angle") + "mesh. Is based on Dirty Vertex Color") + bl_options = {'REGISTER', 'UNDO'} - invert = bpy.props.BoolProperty( - name="Invert", default=False, - description="Invert the curvature map") + invert = BoolProperty( + name="Invert", + default=False, + description="Invert Values" + ) + blur_strength = FloatProperty( + name="Blur Strength", + default=1, + min=0.001, + max=1, + description="Blur strength per iteration" + ) + blur_iterations = IntProperty( + name="Blur Iterations", + default=1, + min=0, + max=40, + description="Number of times to blur the values" + ) + min_angle = FloatProperty( + name="Min Angle", + default=0, + min=0, + max=pi / 2, + subtype='ANGLE', + description="Minimum angle" + ) + max_angle = FloatProperty( + name="Max Angle", + default=pi, + min=pi / 2, + max=pi, + subtype='ANGLE', + description="Maximum angle" + ) + invert = BoolProperty( + name="Invert", + default=False, + description="Invert the curvature map" + ) def execute(self, context): bpy.ops.object.mode_set(mode='OBJECT') @@ -251,22 +320,35 @@ class curvature_to_vertex_groups(bpy.types.Operator): vertex_colors[-1].active = True vertex_colors[-1].active_render = True vertex_colors[-1].name = "Curvature" - for c in vertex_colors[-1].data: c.color.r, c.color.g, c.color.b = 1,1,1 + + for c in vertex_colors[-1].data: + c.color.r, c.color.g, c.color.b = 1, 1, 1 + bpy.ops.object.mode_set(mode='VERTEX_PAINT') - bpy.ops.paint.vertex_color_dirt(blur_strength=self.blur_strength, blur_iterations=self.blur_iterations, clean_angle=self.max_angle, dirt_angle=self.min_angle) + bpy.ops.paint.vertex_color_dirt( + blur_strength=self.blur_strength, + blur_iterations=self.blur_iterations, + clean_angle=self.max_angle, + dirt_angle=self.min_angle + ) bpy.ops.object.vertex_colors_to_vertex_groups(invert=self.invert) bpy.ops.mesh.vertex_color_remove() + return {'FINISHED'} -class face_area_to_vertex_groups(bpy.types.Operator): +class face_area_to_vertex_groups(Operator): bl_idname = "object.face_area_to_vertex_groups" bl_label = "Area" - bl_options = {'REGISTER', 'UNDO'} - invert = bpy.props.BoolProperty( - name="invert", default=False, description="invert values") bl_description = ("Generate a Vertex Group based on the area of individual" - "faces.") + "faces") + bl_options = {'REGISTER', 'UNDO'} + + invert = BoolProperty( + name="Invert", + default=False, + description="Invert Values" + ) def execute(self, context): obj = bpy.context.active_object @@ -278,65 +360,88 @@ class face_area_to_vertex_groups(bpy.types.Operator): obj.vertex_groups[id_value].name = 'faces_area' mult = 1 - if self.invert: 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) + n_values = [0] * len(obj.data.vertices) + values = [0] * len(obj.data.vertices) 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 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 + 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)) + 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 + gr[min(len(gr) - 1, id_value)].weight = 0.5 + bpy.ops.paint.weight_paint_toggle() + return {'FINISHED'} -class harmonic_weight(bpy.types.Operator): +class harmonic_weight(Operator): bl_idname = "object.harmonic_weight" bl_label = "Harmonic" + bl_description = "Create an harmonic variation of the active Vertex Group" bl_options = {'REGISTER', 'UNDO'} - bl_description = ("Create an harmonic variation of the active Vertex Group") - freq = bpy.props.FloatProperty( - name="Frequency", default=20, soft_min=0, - soft_max=100, description="Wave frequency") - - amp = bpy.props.FloatProperty( - name="Amplitude", default=1, soft_min=0, - soft_max=10, description="Wave amplitude") - - midlevel = bpy.props.FloatProperty( - name="Midlevel", default=0, min=-1, - max=1, description="Midlevel") - - add = bpy.props.FloatProperty( - name="Add", default=0, min=-1, - max=1, description="Add to the Weight") - - mult = bpy.props.FloatProperty( - name="Multiply", default=0, min=0, - max=1, description="Multiply for he Weight") + freq = FloatProperty( + name="Frequency", + default=20, + soft_min=0, + soft_max=100, + description="Wave frequency" + ) + amp = FloatProperty( + name="Amplitude", + default=1, + soft_min=0, + soft_max=10, + description="Wave amplitude" + ) + midlevel = FloatProperty( + name="Midlevel", + default=0, + min=-1, + max=1, + description="Midlevel" + ) + add = FloatProperty( + name="Add", + default=0, + min=-1, + max=1, + description="Add to the Weight" + ) + mult = FloatProperty( + name="Multiply", + default=0, + min=0, + max=1, + description="Multiply for he Weight" + ) def execute(self, context): obj = bpy.context.active_object @@ -344,13 +449,17 @@ class harmonic_weight(bpy.types.Operator): group_id = obj.vertex_groups.active_index for v in obj.data.vertices: val = v.groups[group_id].weight - v.groups[group_id].weight = (self.amp*(sin(val*self.freq) - self.midlevel)/2 + 0.5 + self.add*val)*(1-(1-val)*self.mult) + v.groups[group_id].weight = (self.amp * (sin(val * self.freq) - + self.midlevel) / 2 + 0.5 + self.add * val) * \ + (1 - (1 - val) * self.mult) except: self.report({'ERROR'}, "Active object doesn't have vertex groups") + return {'CANCELLED'} + bpy.ops.object.mode_set(mode='WEIGHT_PAINT') - return {'FINISHED'} + return {'FINISHED'} class colors_groups_exchanger_panel(bpy.types.Panel): @@ -359,7 +468,6 @@ class colors_groups_exchanger_panel(bpy.types.Panel): bl_space_type = "VIEW_3D" bl_region_type = "TOOLS" bl_options = {'DEFAULT_CLOSED'} - #bl_context = "objectmode" def draw(self, context): try: diff --git a/mesh_tissue/dual_mesh.py b/mesh_tissue/dual_mesh.py index 29926250..3b20e138 100644 --- a/mesh_tissue/dual_mesh.py +++ b/mesh_tissue/dual_mesh.py @@ -16,8 +16,8 @@ # # ##### END GPL LICENSE BLOCK ##### -#---------------------------------- DUAL MESH ---------------------------------# -#--------------------------------- version 0.3 --------------------------------# +# --------------------------------- DUAL MESH -------------------------------- # +# -------------------------------- version 0.3 ------------------------------- # # # # Convert a generic mesh to its dual. With open meshes it can get some wired # # effect on the borders. # @@ -27,10 +27,7 @@ # # # http://www.co-de-it.com/ # # # -################################################################################ - -import bpy -import bmesh +# ############################################################################ # bl_info = { "name": "Dual Mesh", @@ -41,53 +38,71 @@ bl_info = { "description": "Convert a generic mesh to its dual", "warning": "", "wiki_url": "", - "tracker_url": "", "category": "Mesh"} -class dual_mesh(bpy.types.Operator): +import bpy +from bpy.types import Operator +from bpy.props import ( + BoolProperty, + EnumProperty, + ) +import bmesh + + +class dual_mesh(Operator): bl_idname = "object.dual_mesh" bl_label = "Dual Mesh" + bl_description = ("Convert a generic mesh into a polygonal mesh") bl_options = {'REGISTER', 'UNDO'} - bl_description = ("Convert a generic mesh into a polygonal mesh.") - - 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") - - apply_modifiers = bpy.props.BoolProperty( - name="Apply Modifiers", default=True, - description="Apply object's modifiers") + + quad_method = 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 = 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 = BoolProperty( + name="Preserve Borders", + default=True, + description="Preserve original borders" + ) + apply_modifiers = BoolProperty( + name="Apply Modifiers", + default=True, + description="Apply object's modifiers" + ) def execute(self, context): mode = context.mode - if mode == 'EDIT_MESH': mode = 'EDIT' + if mode == 'EDIT_MESH': + mode = 'EDIT' act = bpy.context.active_object if mode != 'OBJECT': sel = [act] bpy.ops.object.mode_set(mode='OBJECT') - else: sel = bpy.context.selected_objects + else: + sel = bpy.context.selected_objects doneMeshes = [] + ''' if self.new_object: bpy.ops.object.duplicate_move() @@ -97,9 +112,12 @@ class dual_mesh(bpy.types.Operator): bpy.context.scene.objects.active = bpy.context.selected_objects[i] sel = bpy.context.selected_objects ''' + for ob0 in sel: - if ob0.type != 'MESH': continue - if ob0.data.name in doneMeshes: continue + if ob0.type != 'MESH': + continue + if ob0.data.name in doneMeshes: + continue ob = ob0 mesh_name = ob0.data.name @@ -108,70 +126,80 @@ class dual_mesh(bpy.types.Operator): n_users = ob0.data.users count = 0 for o in bpy.data.objects: - if o.type != 'MESH': continue + if o.type != 'MESH': + continue if o.data.name == mesh_name: - count+=1 + count += 1 clones.append(o) - if count == n_users: break - if self.apply_modifiers: bpy.ops.object.convert(target='MESH') + if count == n_users: + break + + if self.apply_modifiers: + bpy.ops.object.convert(target='MESH') 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') + bpy.ops.object.mode_set(mode='EDIT') # prevent borders erosion bpy.ops.mesh.select_mode( - use_extend=False, use_expand=False, type='EDGE') + 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) + 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}) - + 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') + 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') + 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" while True: bpy.ops.object.modifier_move_up(modifier="dual_mesh_subsurf") - if ob.modifiers[0].name == "dual_mesh_subsurf": break - bpy.ops.object.modifier_apply( - apply_as='DATA', modifier='dual_mesh_subsurf') + if ob.modifiers[0].name == "dual_mesh_subsurf": + break - bpy.ops.object.mode_set(mode = 'EDIT') - bpy.ops.mesh.select_all(action = 'DESELECT') + 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') + bpy.ops.object.mode_set(mode='OBJECT') verts[0].select = True - bpy.ops.object.mode_set(mode = 'EDIT') + 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_all(action='DESELECT') bpy.ops.mesh.select_non_manifold( extend=False, use_wire=False, use_boundary=True, @@ -179,18 +207,18 @@ class dual_mesh(bpy.types.Operator): bpy.ops.mesh.select_more() # find boundaries - bpy.ops.object.mode_set(mode = 'OBJECT') + 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') + 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') + 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: @@ -198,17 +226,18 @@ class dual_mesh(bpy.types.Operator): for i in bound_p: bpy.context.active_object.data.polygons[i].select = False - bpy.ops.object.mode_set(mode = 'EDIT') + 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') + 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) + 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 @@ -219,38 +248,47 @@ class dual_mesh(bpy.types.Operator): # dissolve bpy.ops.mesh.dissolve_verts() - bpy.ops.mesh.select_all(action = 'DESELECT') + bpy.ops.mesh.select_all(action='DESELECT') # remove border faces if not self.preserve_borders: 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) + use_multi_face=False, use_non_contiguous=False, use_verts=False + ) bpy.ops.mesh.select_more() bpy.ops.mesh.delete(type='FACE') # 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) + 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') + 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 + + for o in clones: + o.data = ob.data + + for o in sel: + o.select = True + bpy.context.scene.objects.active = act bpy.ops.object.mode_set(mode=mode) + return {'FINISHED'} -''' + +""" class dual_mesh_panel(bpy.types.Panel): bl_label = "Dual Mesh" bl_category = "Tools" bl_space_type = "VIEW_3D" bl_region_type = "TOOLS" - bl_context = (("objectmode")) + bl_context = "objectmode" def draw(self, context): layout = self.layout @@ -260,17 +298,17 @@ class dual_mesh_panel(bpy.types.Panel): col.operator("object.dual_mesh") except: pass -''' +""" def register(): bpy.utils.register_class(dual_mesh) - #bpy.utils.register_class(dual_mesh_panel) + # bpy.utils.register_class(dual_mesh_panel) def unregister(): bpy.utils.unregister_class(dual_mesh) - #bpy.utils.unregister_class(dual_mesh_panel) + # bpy.utils.unregister_class(dual_mesh_panel) if __name__ == "__main__": diff --git a/mesh_tissue/lattice.py b/mesh_tissue/lattice.py index 685d5664..737b4c87 100644 --- a/mesh_tissue/lattice.py +++ b/mesh_tissue/lattice.py @@ -15,8 +15,8 @@ # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ##### END GPL LICENSE BLOCK ##### -#---------------------------- LATTICE ALONG SURFACE ---------------------------# -#--------------------------------- version 0.3 --------------------------------# +# --------------------------- LATTICE ALONG SURFACE -------------------------- # +# -------------------------------- version 0.3 ------------------------------- # # # # Automatically generate and assign a lattice that follows the active surface. # # # @@ -25,10 +25,7 @@ # # # http://www.co-de-it.com/ # # # -################################################################################ - -import bpy, bmesh -from mathutils import Vector, Matrix +# ############################################################################ # bl_info = { "name": "Lattice", @@ -39,10 +36,19 @@ bl_info = { "description": "Generate a Lattice based on a grid mesh", "warning": "", "wiki_url": "", - "tracker_url": "", "category": "Mesh"} +import bpy +import bmesh +from bpy.types import Operator +from bpy.props import ( + BoolProperty, + FloatProperty, + ) +from mathutils import Vector + + def not_in(element, grid): output = True for loop in grid: @@ -51,6 +57,7 @@ def not_in(element, grid): break return output + def grid_from_mesh(mesh, swap_uv): bm = bmesh.new() bm.from_mesh(mesh) @@ -66,21 +73,27 @@ def grid_from_mesh(mesh, swap_uv): # storing first point verts_candidates = [] - if len(faces_grid) == 0: verts_candidates = bm.verts # for first loop check all vertices - else: verts_candidates = [v for v in bm.faces[faces_grid[-1][0]].verts if not_in(v.index, verts_grid)] # for other loops start form the vertices of the first face the last loop, skipping already used vertices + if len(faces_grid) == 0: + # for first loop check all vertices + verts_candidates = bm.verts + else: + # for other loops start form the vertices of the first face + # the last loop, skipping already used vertices + verts_candidates = [v for v in bm.faces[faces_grid[-1][0]].verts if not_in(v.index, verts_grid)] # check for last loop is_last = False for vert in verts_candidates: - if len(vert.link_faces) == 1: # check if corner vertex + if len(vert.link_faces) == 1: # check if corner vertex vert.select = True verts_loop.append(vert.index) is_last = True break + if not is_last: for vert in verts_candidates: new_link_faces = [f for f in vert.link_faces if not_in(f.index, faces_grid)] - if len(new_link_faces) < 2: # check if corner vertex + if len(new_link_faces) < 2: # check if corner vertex vert.select = True verts_loop.append(vert.index) break @@ -92,26 +105,30 @@ def grid_from_mesh(mesh, swap_uv): id = verts_loop[-1] link_edges = bm.verts[id].link_edges # storing second point - if len(verts_loop) == 1: # only one vertex stored in the loop - if len(faces_grid) == 0: ### first loop ### - edge = link_edges[swap_uv] # chose direction + if len(verts_loop) == 1: # only one vertex stored in the loop + if len(faces_grid) == 0: # first loop # + edge = link_edges[swap_uv] # chose direction for vert in edge.verts: if vert.index != id: vert.select = True - verts_loop.append(vert.index) # new vertex - edges_loop.append(edge.index) # chosen edge - faces_loop.append(edge.link_faces[0].index) # only one face - #edge.link_faces[0].select = True - else: ### other loops ### - for edge in bm.faces[faces_grid[-1][0]].edges: # start from the edges of the first face of the last loop - if bm.verts[verts_loop[0]] in edge.verts and bm.verts[verts_grid[-1][0]] not in edge.verts: # chose an edge starting from the first vertex that is not returning back + verts_loop.append(vert.index) # new vertex + edges_loop.append(edge.index) # chosen edge + faces_loop.append(edge.link_faces[0].index) # only one face + # edge.link_faces[0].select = True + else: # other loops # + # start from the edges of the first face of the last loop + for edge in bm.faces[faces_grid[-1][0]].edges: + # chose an edge starting from the first vertex that is not returning back + if bm.verts[verts_loop[0]] in edge.verts and \ + bm.verts[verts_grid[-1][0]] not in edge.verts: for vert in edge.verts: if vert.index != id: vert.select = True verts_loop.append(vert.index) edges_loop.append(edge.index) + for face in edge.link_faces: - if not_in(face.index,faces_grid): + if not_in(face.index, faces_grid): faces_loop.append(face.index) # continuing the loop else: @@ -121,7 +138,8 @@ def grid_from_mesh(mesh, swap_uv): if not_in(vert.index, verts_grid) and vert.index not in verts_loop: if len(faces_loop) > 0: bm.faces.ensure_lookup_table() - if vert not in bm.faces[faces_loop[-1]].verts: store_data = True + if vert not in bm.faces[faces_loop[-1]].verts: + store_data = True else: store_data = True if store_data: @@ -133,79 +151,108 @@ def grid_from_mesh(mesh, swap_uv): faces_loop.append(face.index) break # ending condition - if verts_loop[-1] == id or verts_loop[-1] == verts_loop[0]: running_loop = False + if verts_loop[-1] == id or verts_loop[-1] == verts_loop[0]: + running_loop = False verts_grid.append(verts_loop) edges_grid.append(edges_loop) faces_grid.append(faces_loop) - if len(faces_loop) == 0: running_grid = False + + if len(faces_loop) == 0: + running_grid = False + return verts_grid, edges_grid, faces_grid -class lattice_along_surface(bpy.types.Operator): + +class lattice_along_surface(Operator): bl_idname = "object.lattice_along_surface" bl_label = "Lattice along Surface" bl_description = ("Automatically add a Lattice modifier to the selected " "object, adapting it to the active one.\nThe active " "object must be a rectangular grid compatible with the " - "Lattice's topology.") + "Lattice's topology") bl_options = {'REGISTER', 'UNDO'} - set_parent = bpy.props.BoolProperty( - name="Set Parent", default=True, - description="Automatically set the Lattice as parent") - - flipNormals = bpy.props.BoolProperty( - name="Flip Normals", default=False, - description="Flip normals direction") - - swapUV = bpy.props.BoolProperty( - name="Swap UV", default=False, - description="Flip grid's U and V") - - flipU = bpy.props.BoolProperty( - name="Flip U", default=False, - description="Flip grid's U") - - flipV = bpy.props.BoolProperty( - name="Flip V", default=False, - description="Flip grid's V") - - flipW = bpy.props.BoolProperty( - name="Flip W", default=False, - description="Flip grid's W") - - use_groups = bpy.props.BoolProperty( - name="Vertex Group", default=False, - description="Use active Vertex Group for lattice's thickness") - - high_quality_lattice = bpy.props.BoolProperty( - name="High quality", default=True, - description="Increase the the subdivisions in normal direction for a " - "more correct result") - - hide_lattice = bpy.props.BoolProperty( - name="Hide Lattice", default=True, - description="Automatically hide the Lattice object") - - scale_x = bpy.props.FloatProperty( - name="Scale X", default=1, min=0.001, - max=1, description="Object scale") - - scale_y = bpy.props.FloatProperty( - name="Scale Y", default=1, min=0.001, - max=1, description="Object scale") - - scale_z = bpy.props.FloatProperty( - name="Scale Z", default=1, min=0.001, - max=1, description="Object scale") - - thickness = bpy.props.FloatProperty( - name="Thickness", default=1, soft_min=0, - soft_max=5, description="Lattice thickness") - - displace = bpy.props.FloatProperty( - name="Displace", default=0, soft_min=-1, - soft_max=1, description="Lattice displace") + set_parent = BoolProperty( + name="Set Parent", + default=True, + description="Automatically set the Lattice as parent" + ) + flipNormals = BoolProperty( + name="Flip Normals", + default=False, + description="Flip normals direction" + ) + swapUV = BoolProperty( + name="Swap UV", + default=False, + description="Flip grid's U and V" + ) + flipU = BoolProperty( + name="Flip U", + default=False, + description="Flip grid's U") + + flipV = BoolProperty( + name="Flip V", + default=False, + description="Flip grid's V" + ) + flipW = BoolProperty( + name="Flip W", + default=False, + description="Flip grid's W" + ) + use_groups = BoolProperty( + name="Vertex Group", + default=False, + description="Use active Vertex Group for lattice's thickness" + ) + high_quality_lattice = BoolProperty( + name="High quality", + default=True, + description="Increase the the subdivisions in normal direction for a " + "more correct result" + ) + hide_lattice = BoolProperty( + name="Hide Lattice", + default=True, + description="Automatically hide the Lattice object" + ) + scale_x = FloatProperty( + name="Scale X", + default=1, + min=0.001, + max=1, + description="Object scale" + ) + scale_y = FloatProperty( + name="Scale Y", default=1, + min=0.001, + max=1, + description="Object scale" + ) + scale_z = FloatProperty( + name="Scale Z", + default=1, + min=0.001, + max=1, + description="Object scale" + ) + thickness = FloatProperty( + name="Thickness", + default=1, + soft_min=0, + soft_max=5, + description="Lattice thickness" + ) + displace = FloatProperty( + name="Displace", + default=0, + soft_min=-1, + soft_max=1, + description="Lattice displace" + ) def draw(self, context): layout = self.layout @@ -214,11 +261,13 @@ class lattice_along_surface(bpy.types.Operator): col.prop( self, "thickness", text="Thickness", icon='NONE', expand=False, slider=True, toggle=False, icon_only=False, event=False, - full_event=False, emboss=True, index=-1) + full_event=False, emboss=True, index=-1 + ) col.prop( self, "displace", text="Offset", icon='NONE', expand=False, slider=True, toggle=False, icon_only=False, event=False, - full_event=False, emboss=True, index=-1) + full_event=False, emboss=True, index=-1 + ) row = col.row() row.prop(self, "use_groups") col.separator() @@ -226,17 +275,19 @@ class lattice_along_surface(bpy.types.Operator): col.prop( self, "scale_x", text="U", icon='NONE', expand=False, slider=True, toggle=False, icon_only=False, event=False, - full_event=False, emboss=True, index=-1) + full_event=False, emboss=True, index=-1 + ) col.prop( self, "scale_y", text="V", icon='NONE', expand=False, slider=True, toggle=False, icon_only=False, event=False, - full_event=False, emboss=True, index=-1) - ''' + full_event=False, emboss=True, index=-1 + ) + """ col.prop( self, "scale_z", text="W", icon='NONE', expand=False, slider=True, toggle=False, icon_only=False, event=False, full_event=False, emboss=True, index=-1) - ''' + """ col.separator() col.label(text="Flip:") row = col.row() @@ -262,15 +313,17 @@ class lattice_along_surface(bpy.types.Operator): return {'CANCELLED'} obj = None for o in bpy.context.selected_objects: - if o.name != grid_obj.name and o.type in ('MESH', 'CURVE', \ - 'SURFACE', 'FONT'): + if o.name != grid_obj.name and o.type in \ + ('MESH', 'CURVE', 'SURFACE', 'FONT'): obj = o o.select = False break try: obj_dim = obj.dimensions - obj_me = obj.to_mesh(bpy.context.scene, apply_modifiers=True, - settings = 'PREVIEW') + obj_me = obj.to_mesh( + bpy.context.scene, apply_modifiers=True, + settings='PREVIEW' + ) except: self.report({'ERROR'}, "The object to deform is not valid. Only " "Mesh, Curve, Surface and Font objects are allowed.") @@ -281,16 +334,18 @@ class lattice_along_surface(bpy.types.Operator): bpy.ops.object.convert(target='MESH') bpy.ops.object.transform_apply(location=True, rotation=True, scale=True) grid_mesh = grid_obj.to_mesh(bpy.context.scene, apply_modifiers=True, - settings = 'PREVIEW') - if len(grid_mesh.polygons) > 64*64: + settings='PREVIEW') + + if len(grid_mesh.polygons) > 64 * 64: bpy.ops.object.delete(use_global=False) bpy.context.scene.objects.active = obj obj.select = True self.report({'ERROR'}, "Maximum resolution allowed for Lattice is 64") return {'CANCELLED'} + # CREATING LATTICE - min = Vector((0,0,0)) - max = Vector((0,0,0)) + min = Vector((0, 0, 0)) + max = Vector((0, 0, 0)) first = True for v in obj_me.vertices: vert = obj.matrix_world * v.co @@ -307,17 +362,23 @@ class lattice_along_surface(bpy.types.Operator): if vert[2] > max[2] or first: max[2] = vert[2] first = False - bb = max-min - lattice_loc = (max+min)/2 + + bb = max - min + lattice_loc = (max + min) / 2 bpy.ops.object.add(type='LATTICE', view_align=False, enter_editmode=False) lattice = bpy.context.active_object lattice.location = lattice_loc - lattice.scale = Vector((bb.x/self.scale_x, bb.y/self.scale_y, - bb.z/self.scale_z)) - if bb.x == 0: lattice.scale.x = 1 - if bb.y == 0: lattice.scale.y = 1 - if bb.z == 0: lattice.scale.z = 1 + lattice.scale = Vector((bb.x / self.scale_x, bb.y / self.scale_y, + bb.z / self.scale_z)) + + if bb.x == 0: + lattice.scale.x = 1 + if bb.y == 0: + lattice.scale.y = 1 + if bb.z == 0: + lattice.scale.z = 1 + bpy.context.scene.objects.active = obj bpy.ops.object.modifier_add(type='LATTICE') obj.modifiers[-1].object = lattice @@ -330,8 +391,10 @@ class lattice_along_surface(bpy.types.Operator): bpy.ops.object.parent_set(type='LATTICE') # reading grid structure - verts_grid, edges_grid, faces_grid = grid_from_mesh(grid_mesh, - swap_uv=self.swapUV) + verts_grid, edges_grid, faces_grid = grid_from_mesh( + grid_mesh, + swap_uv=self.swapUV + ) nu = len(verts_grid) nv = len(verts_grid[0]) nw = 2 @@ -346,17 +409,27 @@ class lattice_along_surface(bpy.types.Operator): for w in range(nw): if self.use_groups: try: - displace = grid_obj.vertex_groups.active.weight(verts_grid[i][j])*scale_normal*bb.z + displace = grid_obj.vertex_groups.active.weight( + verts_grid[i][j]) * scale_normal * bb.z except: - displace = scale_normal*bb.z - else: displace = scale_normal*bb.z - target_point = (grid_mesh.vertices[verts_grid[i][j]].co + grid_mesh.vertices[verts_grid[i][j]].normal*(w + self.displace/2 - 0.5)*displace) - lattice.location - if self.flipW: w = 1-w - if self.flipU: i = nu-i-1 - if self.flipV: j = nv-j-1 - lattice.data.points[i + j*nu + w*nu*nv].co_deform.x = target_point.x / bpy.data.objects[lattice.name].scale.x - lattice.data.points[i + j*nu + w*nu*nv].co_deform.y = target_point.y / bpy.data.objects[lattice.name].scale.y - lattice.data.points[i + j*nu + w*nu*nv].co_deform.z = target_point.z / bpy.data.objects[lattice.name].scale.z + displace = scale_normal * bb.z + else: + displace = scale_normal * bb.z + target_point = (grid_mesh.vertices[verts_grid[i][j]].co + + grid_mesh.vertices[verts_grid[i][j]].normal * + (w + self.displace / 2 - 0.5) * displace) - lattice.location + if self.flipW: + w = 1 - w + if self.flipU: + i = nu - i - 1 + if self.flipV: + j = nv - j - 1 + lattice.data.points[i + j * nu + w * nu * nv].co_deform.x = \ + target_point.x / bpy.data.objects[lattice.name].scale.x + lattice.data.points[i + j * nu + w * nu * nv].co_deform.y = \ + target_point.y / bpy.data.objects[lattice.name].scale.y + lattice.data.points[i + j * nu + w * nu * nv].co_deform.z = \ + target_point.z / bpy.data.objects[lattice.name].scale.z except: bpy.ops.object.mode_set(mode='OBJECT') grid_obj.select = True @@ -373,9 +446,9 @@ class lattice_along_surface(bpy.types.Operator): self.report({'ERROR'}, "The grid mesh is not correct") return {'CANCELLED'} - #grid_obj.data = old_grid_data - #print(old_grid_matrix) - #grid_obj.matrix_world = old_grid_matrix + # grid_obj.data = old_grid_data + # print(old_grid_matrix) + # grid_obj.matrix_world = old_grid_matrix bpy.ops.object.mode_set(mode='OBJECT') grid_obj.select = True @@ -384,13 +457,19 @@ class lattice_along_surface(bpy.types.Operator): bpy.ops.object.delete(use_global=False) bpy.context.scene.objects.active = lattice lattice.select = True - if self.high_quality_lattice: bpy.context.object.data.points_w = 8 - else: bpy.context.object.data.use_outside = True + + if self.high_quality_lattice: + bpy.context.object.data.points_w = 8 + else: + bpy.context.object.data.use_outside = True + if self.hide_lattice: bpy.ops.object.hide_view_set(unselected=False) + bpy.context.scene.objects.active = obj obj.select = True lattice.select = False + if self.flipNormals: try: bpy.ops.object.mode_set(mode='EDIT') @@ -399,34 +478,37 @@ class lattice_along_surface(bpy.types.Operator): bpy.ops.object.mode_set(mode='OBJECT') except: pass + return {'FINISHED'} -''' + +""" class lattice_along_surface_panel(bpy.types.Panel): bl_label = "Modifiers Tools" bl_category = "Tools" bl_space_type = "VIEW_3D" bl_region_type = "TOOLS" - bl_context = (("objectmode")) + bl_context = "objectmode" def draw(self, context): layout = self.layout col = layout.column(align=True) + try: col.operator("object.lattice_along_surface", icon="MOD_LATTICE") except: pass -''' +""" def register(): bpy.utils.register_class(lattice_along_surface) - #bpy.utils.register_class(lattice_along_surface_panel) + # bpy.utils.register_class(lattice_along_surface_panel) def unregister(): bpy.utils.unregister_class(lattice_along_surface) - #bpy.utils.unregister_class(lattice_along_surface_panel) + # bpy.utils.unregister_class(lattice_along_surface_panel) if __name__ == "__main__": diff --git a/mesh_tissue/tessellate_numpy.py b/mesh_tissue/tessellate_numpy.py index 367b44f5..e80cd39b 100644 --- a/mesh_tissue/tessellate_numpy.py +++ b/mesh_tissue/tessellate_numpy.py @@ -16,8 +16,8 @@ # # ##### END GPL LICENSE BLOCK ##### -# ---------------------------- ADAPTIVE DUPLIFACES ----------------------------# -#-------------------------------- version 0.83 --------------------------------# +# ---------------------------- ADAPTIVE DUPLIFACES --------------------------- # +# ------------------------------- version 0.84 ------------------------------- # # # # Creates duplicates of selected mesh to active morphing the shape according # # to target faces. # @@ -27,31 +27,43 @@ # # # http://www.co-de-it.com/ # # # -################################################################################ +# ############################################################################ # import bpy +from bpy.types import ( + Operator, + Panel, + PropertyGroup, + ) +from bpy.props import ( + BoolProperty, + EnumProperty, + FloatProperty, + IntProperty, + StringProperty, + ) 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 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 + 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 + return loc + nor * v.z def tassellate(ob0, ob1, offset, zscale, gen_modifiers, com_modifiers, mode, @@ -59,38 +71,43 @@ def tassellate(ob0, ob1, offset, zscale, gen_modifiers, com_modifiers, 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 + 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) + if p.select: + base_polygons.append(p) else: base_polygons = ob0.data.polygons - if len(base_polygons) == 0: return 0 + 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 + 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) + # n_edges = len(me1.edges) + # n_faces = len(me1.polygons) # Component transformations - loc = ob1.location - dim = ob1.dimensions - scale = ob1.scale + # loc = ob1.location + # dim = ob1.dimensions + # scale = ob1.scale # Create empty lists new_verts = [] @@ -99,44 +116,44 @@ def tassellate(ob0, ob1, offset, zscale, gen_modifiers, com_modifiers, mode, new_verts_np = np.array(()) # Component bounding box - min = Vector((0,0,0)) - max = Vector((0,0,0)) + min_c = Vector((0, 0, 0)) + max_c = 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] + if vert[0] < min_c[0] or first: + min_c[0] = vert[0] + if vert[1] < min_c[1] or first: + min_c[1] = vert[1] + if vert[2] < min_c[2] or first: + min_c[2] = vert[2] + if vert[0] > max_c[0] or first: + max_c[0] = vert[0] + if vert[1] > max_c[1] or first: + max_c[1] = vert[1] + if vert[2] > max_c[2] or first: + max_c[2] = vert[2] first = False - bb = max-min + bb = max_c - min_c # adaptive XY verts1 = [] for v in me1.vertices: - if mode=="ADAPTIVE": - vert = v.co - min#( ob1.matrix_world * v.co ) - min + if mode == "ADAPTIVE": + vert = v.co - min_c # (ob1.matrix_world * v.co) - min_c 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 + 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 + vert[2] = (vert[2] - min_c[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] + 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] @@ -155,26 +172,26 @@ def tassellate(ob0, ob1, offset, zscale, gen_modifiers, com_modifiers, mode, # Read active key active_key = ob1.active_shape_key_index - if active_key == 0: active_key = 1 + 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 + if mode == "ADAPTIVE": + vert = v.co - min_c vert[0] = vert[0] / bb[0] vert[1] = vert[1] / bb[1] - vert[2] = (vert[2] + (-0.5 + offset*0.5)*bb[2]) * zscale + 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]) * \ + vert[2] = (vert[2] - min_c[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] + 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: @@ -194,10 +211,10 @@ def tassellate(ob0, ob1, offset, zscale, gen_modifiers, com_modifiers, mode, if fill_mode == 'FAN': fan_verts = [v.co.to_tuple() for v in me0.vertices] fan_polygons = [] - selected_faces = [] + # selected_faces = [] for p in base_polygons: - #if bool_selection and not p.select: continue - fan_center = Vector((0,0,0)) + # 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) @@ -206,22 +223,23 @@ def tassellate(ob0, ob1, offset, zscale, gen_modifiers, com_modifiers, mode, # Vertex Group if bool_vertex_group: - center_weight = sum([weight[i] for i in p.vertices])/ \ - len(p.vertices) + 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)], + p.vertices[(i + 1) % len(p.vertices)], last_vert, last_vert)) - #if bool_selection: selected_faces.append(p.select) + # 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] + """ + for i in range(len(selected_faces)): + fan_me.polygons[i].select = selected_faces[i] + """ count = 0 # necessary for UV calculation # TESSELLATION @@ -231,41 +249,44 @@ def tassellate(ob0, ob1, offset, zscale, gen_modifiers, com_modifiers, mode, if rotation_mode == 'RANDOM': shifted_vertices = [] n_poly_verts = len(p.vertices) - rand = random.randint(0,n_poly_verts) + rand = random.randint(0, n_poly_verts) for i in range(n_poly_verts): - shifted_vertices.append(p.vertices[(i+rand)%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) + 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': + 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) + 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) + 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 + 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) + 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) + 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 + \ + v03 = (me0.uv_layers.active.data[count].uv + me0.uv_layers.active.data[count].uv) v1203 = v03 - v12 v1203.normalize() @@ -274,11 +295,15 @@ def tassellate(ob0, ob1, offset, zscale, gen_modifiers, com_modifiers, mode, 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] + 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] + 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]) @@ -286,8 +311,10 @@ def tassellate(ob0, ob1, offset, zscale, gen_modifiers, com_modifiers, mode, if bool_vertex_group: ws0 = [] for i in vertUV: - try: ws0.append(weight[i]) - except: ws0.append(0) + try: + ws0.append(weight[i]) + except: + ws0.append(0) ws0 = np.array(ws0) count += len(p.vertices) @@ -300,8 +327,10 @@ def tassellate(ob0, ob1, offset, zscale, gen_modifiers, com_modifiers, mode, if bool_vertex_group: ws0 = [] for i in p.vertices: - try: ws0.append(weight[i]) - except: ws0.append(0) + try: + ws0.append(weight[i]) + except: + ws0.append(0) ws0 = np.array(ws0) # considering only 4 vertices @@ -309,43 +338,44 @@ def tassellate(ob0, ob1, offset, zscale, gen_modifiers, com_modifiers, mode, 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 + 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 + 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) + 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 + 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 + 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 + 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_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 + if bool_vertex_group: + new_vertex_group_np = w2 else: # Appending vertices new_verts_np = np.concatenate((new_verts_np, v3), axis=0) @@ -354,9 +384,11 @@ def tassellate(ob0, ob1, offset, zscale, gen_modifiers, com_modifiers, mode, 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]) + 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]) + for e in es1: + new_edges.append([i + n_verts * j for i in e]) j += 1 @@ -364,7 +396,6 @@ def tassellate(ob0, ob1, offset, zscale, gen_modifiers, com_modifiers, mode, 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) @@ -400,88 +431,141 @@ def store_parameters(operator, ob): 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): +class tissue_tessellate_prop(PropertyGroup): + generator = StringProperty() + component = StringProperty() + offset = FloatProperty() + zscale = FloatProperty(default=1) + merge = BoolProperty() + merge_thres = FloatProperty() + gen_modifiers = BoolProperty() + com_modifiers = BoolProperty() + mode = StringProperty() + rotation_mode = StringProperty() + scale_mode = StringProperty() + fill_mode = StringProperty() + bool_random = BoolProperty() + random_seed = IntProperty() + vertexgroup = StringProperty() + bool_vertex_group = BoolProperty() + bool_selection = BoolProperty() + bool_shapekeys = BoolProperty() + + +class tessellate(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.") + "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")) + object_name = StringProperty( + name="", + description="Name of the generated object" + ) + zscale = FloatProperty( + name="Scale", + default=1, + soft_min=0, + soft_max=10, + description="Scale factor for the component thickness" + ) + scale_mode = EnumProperty( + items=(('CONSTANT', "Constant", ""), ('ADAPTIVE', "Proportional", "")), + default='CONSTANT', + name="Z-Scale according to faces size" + ) + offset = FloatProperty( + name="Surface Offset", + default=0, + min=-1, max=1, + soft_min=-1, + soft_max=1, + description="Surface offset" + ) + mode = EnumProperty( + items=(('CONSTANT', "Constant", ""), ('ADAPTIVE', "Adaptive", "")), + default='ADAPTIVE', + name="Component Mode" + ) + rotation_mode = EnumProperty( + items=(('RANDOM', "Random", ""), + ('UV', "Active UV", ""), + ('DEFAULT', "Default", "")), + default='DEFAULT', + name="Component Rotation" + ) + fill_mode = EnumProperty( + items=(('QUAD', "Quad", ""), ('FAN', "Fan", "")), + default='QUAD', + name="Fill Mode" + ) + gen_modifiers = BoolProperty( + name="Generator Modifiers", + default=False, + description="Apply modifiers to base object" + ) + com_modifiers = BoolProperty( + name="Component Modifiers", + default=False, + description="Apply modifiers to component object" + ) + merge = BoolProperty( + name="Merge", + default=False, + description="Merge vertices in adjacent duplicates" + ) + merge_thres = FloatProperty( + name="Distance", + default=0.001, + soft_min=0, + soft_max=10, + description="Limit below which to merge vertices" + ) + generator = StringProperty( + name="", + description="Base object for the tessellation" + ) + component = StringProperty( + name="", + description="Component object for the tessellation" + ) + bool_random = BoolProperty( + name="Randomize", + default=False, + description="Randomize component rotation" + ) + random_seed = IntProperty( + name="Seed", + default=0, + soft_min=0, + soft_max=10, + description="Random seed" + ) + bool_vertex_group = BoolProperty( + name="Map Vertex Group", + default=False, + description="Map the active " + "Vertex Group from the Base object to generated geometry" + ) + bool_selection = BoolProperty( + name="On selected Faces", + default=False, + description="Create Tessellation only on selected faces" + ) + bool_shapekeys = 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 = "" + @staticmethod + def check_gen_comp(checking): + # note pass the stored name key in here to check it out + return checking in bpy.data.objects.keys() + def draw(self, context): try: bool_working = self.working_on == self.object_name and \ @@ -495,7 +579,8 @@ class tessellate(bpy.types.Operator): if len(sel) == 2: bool_meshes = True for o in sel: - if o.type != 'MESH': bool_meshes = False + if o.type != 'MESH': + bool_meshes = False if len(sel) != 2 and not bool_working: layout = self.layout @@ -515,27 +600,38 @@ class tessellate(bpy.types.Operator): self.generator = ob0.name for o in sel: - if(o.name == ob0.name or o.type != 'MESH'): continue + if (o.name == ob0.name or o.type != 'MESH'): + continue else: ob1 = o self.component = o.name - no_component = False + self.no_component = False break - # Checks for Tool Shelf panel, it loose the original Selection + # Checks for Tool Shelf panel, it lost 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 + # checks if the objects were deleted + if self.check_gen_comp( + bpy.context.active_object.tissue_tessellate.component): + ob1 = bpy.data.objects[ + bpy.context.active_object.tissue_tessellate.component + ] + self.component = ob1.name + + if self.check_gen_comp( + bpy.context.active_object.tissue_tessellate.generator): + ob0 = bpy.data.objects[ + bpy.context.active_object.tissue_tessellate.generator + ] + self.generator = ob0.name + self.no_component = False # new object name if self.object_name == "": - if self.generator == "": self.object_name = "Tessellation" - else: self.object_name = self.generator + "_Tessellation" + if self.generator == "": + self.object_name = "Tessellation" + else: + self.object_name = self.generator + "_Tessellation" layout = self.layout # Base and Component @@ -546,12 +642,16 @@ class tessellate(bpy.types.Operator): 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: + + if not self.check_gen_comp(self.generator) or \ + 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: + + if not self.check_gen_comp(self.component) or \ + len(bpy.data.objects[self.component].modifiers) == 0: col2.enabled = False self.com_modifiers = False @@ -568,18 +668,29 @@ class tessellate(bpy.types.Operator): # 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 + 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 + 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) @@ -622,10 +733,14 @@ class tessellate(bpy.types.Operator): 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: + + if not self.check_gen_comp(self.generator) or \ + 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') + check_name = bpy.data.objects[self.generator].name if \ + self.check_gen_comp(self.generator) else "None" + row.label(text="'" + check_name + + "' doesn't have UV Maps", icon='ERROR') uv_error = True if uv_error: row = col.row(align=True) @@ -661,7 +776,9 @@ class tessellate(bpy.types.Operator): col = layout.column(align=True) row = col.row(align=True) row.prop(self, "merge") - if self.merge: row.prop(self, "merge_thres") + + if self.merge: + row.prop(self, "merge_thres") row = col.row(align=True) # ADVANCED @@ -671,21 +788,30 @@ class tessellate(bpy.types.Operator): 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: + + if self.check_gen_comp(self.generator) and \ + len(bpy.data.objects[self.generator].vertex_groups) == 0: col2.enabled = False - bool_vertex_group = False + self.bool_vertex_group = False + col2 = row.column(align=True) + + if not self.check_gen_comp(self.generator) or \ + not self.check_gen_comp(self.component): + return + 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: + bpy.data.objects[self.component].data.shape_keys is None: col2.enabled = False - bool_shapekeys = False + self.bool_shapekeys = False elif len(bpy.data.objects[self.generator].vertex_groups) == 0 or \ - bpy.data.objects[self.component].data.shape_keys != None: + bpy.data.objects[self.component].data.shape_keys is not None: if len(bpy.data.objects[ self.component].data.shape_keys.key_blocks) < 2: col2.enabled = False - bool_shapekeys = False + self.bool_shapekeys = False def execute(self, context): try: @@ -698,14 +824,15 @@ class tessellate(bpy.types.Operator): sel = bpy.context.selected_objects no_component = True for o in sel: - if(o.name == ob0.name or o.type != 'MESH'): continue + 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 + # Checks for Tool Shelf panel, it lost the original Selection if bpy.context.active_object == self.object_name: ob1 = bpy.data.objects[ bpy.context.active_object.tissue_tessellate.component] @@ -715,32 +842,38 @@ class tessellate(bpy.types.Operator): self.generator = ob0.name no_component = False - if(no_component): - #self.report({'ERROR'}, "A component mesh object must be selected") + 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 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') + if bpy.ops.object.select_all.poll(): + 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) + 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!" @@ -748,7 +881,7 @@ class tessellate(bpy.types.Operator): return {'CANCELLED'} new_ob.name = self.object_name - #new_ob = bpy.data.objects.new(self.object_name, new_me) + # new_ob = bpy.data.objects.new(self.object_name, new_me) new_ob.location = ob0.location new_ob.matrix_world = ob0.matrix_world @@ -758,7 +891,7 @@ class tessellate(bpy.types.Operator): new_ob.select = True bpy.context.scene.objects.active = new_ob if self.merge: - bpy.ops.object.mode_set(mode = 'EDIT') + 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( @@ -767,7 +900,7 @@ class tessellate(bpy.types.Operator): use_verts=False) bpy.ops.mesh.remove_doubles( threshold=self.merge_thres, use_unselected=False) - bpy.ops.object.mode_set(mode = 'OBJECT') + 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 @@ -778,8 +911,8 @@ class tessellate(bpy.types.Operator): # MATERIALS try: # create materials list - polygon_materials = [p.material_index for p in ob1.data.polygons]*int( - len(new_ob.data.polygons) / len(ob1.data.polygons)) + 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)): @@ -800,22 +933,27 @@ class tessellate(bpy.types.Operator): return context.window_manager.invoke_props_dialog(self) -class update_tessellate(bpy.types.Operator): -#class adaptive_duplifaces(bpy.types.Panel): +class update_tessellate(Operator): 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 + except: + return False + + @staticmethod + def check_gen_comp(checking): + # note pass the stored name key in here to check it out + return checking in bpy.data.objects.keys() def execute(self, context): ob = bpy.context.active_object @@ -838,20 +976,26 @@ class update_tessellate(bpy.types.Operator): bool_shapekeys = ob.tissue_tessellate.bool_shapekeys mode = ob.tissue_tessellate.mode - if(generator == "" or component == ""): + if not self.check_gen_comp(generator) or \ + not self.check_gen_comp(component): + self.report({'ERROR'}, + "Base or Component Objects are missing from the data " + "(Most likely deleted or renamed)") + return {'CANCELLED'} + + 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) + 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!" @@ -861,7 +1005,7 @@ class update_tessellate(bpy.types.Operator): ob.data = temp_ob.data bpy.data.objects.remove(temp_ob) if merge: - bpy.ops.object.mode_set(mode = 'EDIT') + 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( @@ -869,15 +1013,15 @@ class update_tessellate(bpy.types.Operator): 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='OBJECT') bpy.ops.object.mode_set(mode='EDIT') bpy.ops.object.mode_set(mode='OBJECT') # MATERIALS try: # create materials list - polygon_materials = [p.material_index for p in ob1.data.polygons]*int( - len(ob.data.polygons) / len(ob1.data.polygons)) + 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)): @@ -895,75 +1039,126 @@ class update_tessellate(bpy.types.Operator): return True -class settings_tessellate(bpy.types.Operator): -#class adaptive_duplifaces(bpy.types.Panel): +class settings_tessellate(Operator): 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_description = ("Update the tessellated mesh according to base and component changes\n" + "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")) + object_name = StringProperty( + name="", + description="Name of the generated object" + ) + zscale = FloatProperty( + name="Scale", + default=1, + soft_min=0, + soft_max=10, + description="Scale factor for the component thickness" + ) + scale_mode = EnumProperty( + items=(('CONSTANT', "Constant", ""), ('ADAPTIVE', "Proportional", "")), + default='ADAPTIVE', + name="Scale variation" + ) + offset = FloatProperty( + name="Surface Offset", + default=0, + min=-1, max=1, + soft_min=-1, + soft_max=1, + description="Surface offset" + ) + mode = EnumProperty( + items=(('CONSTANT', "Constant", ""), ('ADAPTIVE', "Adaptive", "")), + default='ADAPTIVE', + name="Component Mode" + ) + rotation_mode = EnumProperty( + items=(('RANDOM', "Random", ""), ('UV', "Active UV", ""), + ('DEFAULT', "Default", "")), + default='DEFAULT', + name="Component Rotation" + ) + fill_mode = EnumProperty( + items=(('QUAD', "Quad", ""), ('FAN', "Fan", "")), + default='QUAD', + name="Fill Mode" + ) + gen_modifiers = BoolProperty( + name="Generator Modifiers", + default=False, + description="Apply modifiers to base object" + ) + com_modifiers = BoolProperty( + name="Component Modifiers", + default=False, + description="Apply modifiers to component object" + ) + merge = BoolProperty( + name="Merge", + default=False, + description="Merge vertices in adjacent duplicates" + ) + merge_thres = FloatProperty( + name="Distance", + default=0.001, + soft_min=0, + soft_max=10, + description="Limit below which to merge vertices" + ) + generator = StringProperty( + name="", + description="Base object for the tessellation" + ) + component = StringProperty( + name="", + description="Component object for the tessellation" + ) + bool_random = BoolProperty( + name="Randomize", + default=False, + description="Randomize component rotation" + ) + random_seed = IntProperty( + name="Seed", + default=0, + soft_min=0, + soft_max=10, + description="Random seed" + ) + bool_vertex_group = BoolProperty( + name="Map Vertex Group", + default=False, + description="Map on generated " + "geometry the active Vertex Group from the base object" + ) + bool_selection = BoolProperty( + name="On selected Faces", + default=False, + description="Create Tessellation only on select faces" + ) + bool_shapekeys = 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 + except: + return False + + @staticmethod + def check_gen_comp(checking): + # note pass the stored name key in here to check it out + return checking in bpy.data.objects.keys() def draw(self, context): layout = self.layout @@ -990,7 +1185,22 @@ class settings_tessellate(bpy.types.Operator): # start drawing layout = self.layout - #ob0 = bpy.context.active_object + + # check for keys in data - as the objects can be deleted or renamed + if not self.check_gen_comp(self.generator) or \ + not self.check_gen_comp(self.component): + + layout.label(text="Base or Component Objects are missing from the data", + icon="INFO") + layout.label(text="(Most likely deleted or renamed)", + icon="BLANK1") + layout.label(text="Settings could not be altered anymore", + icon="BLANK1") + layout.label(text="Please re-run Tesselate with two new selected objects", + icon="BLANK1") + return + + # ob0 = bpy.context.active_object # Base and Component col = layout.column(align=True) row = col.row(align=True) @@ -1007,11 +1217,14 @@ class settings_tessellate(bpy.types.Operator): 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 @@ -1026,19 +1239,28 @@ class settings_tessellate(bpy.types.Operator): 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 + 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.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 + 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) @@ -1069,9 +1291,11 @@ class settings_tessellate(bpy.types.Operator): 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': @@ -1079,9 +1303,10 @@ class settings_tessellate(bpy.types.Operator): 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 + \ + row.label(text="'" + bpy.data.objects[self.generator].name + " doesn't have UV Maps", icon='ERROR') uv_error = True if uv_error: @@ -1114,10 +1339,11 @@ class settings_tessellate(bpy.types.Operator): col = layout.column(align=True) row = col.row(align=True) row.prop(self, "merge") - if self.merge: row.prop(self, "merge_thres") + if self.merge: + row.prop(self, "merge_thres") row = col.row(align=True) - ### ADVANCED ### + # ADVANCED # col = layout.column(align=True) tessellate.rotation_mode @@ -1126,27 +1352,27 @@ class settings_tessellate(bpy.types.Operator): 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 + self.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: + bpy.data.objects[self.component].data.shape_keys is None: col2.enabled = False - bool_shapekeys = False + self.bool_shapekeys = False elif len(bpy.data.objects[self.generator].vertex_groups) == 0 or \ - bpy.data.objects[self.component].data.shape_keys != None: + bpy.data.objects[self.component].data.shape_keys is not None: if len(bpy.data.objects[self.component].data.shape_keys.key_blocks) < 2: col2.enabled = False - bool_shapekeys = False + self.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 @@ -1165,31 +1391,38 @@ class settings_tessellate(bpy.types.Operator): self.bool_selection = self.ob.tissue_tessellate.bool_selection self.bool_shapekeys = self.ob.tissue_tessellate.bool_shapekeys - if(self.generator == "" or self.component == ""): + if not self.check_gen_comp(self.generator) or \ + not self.check_gen_comp(self.component): + # do nothing as the Warning was already done in it UI + return {'CANCELLED'} + + if (self.generator == "" or self.component == ""): self.report({'ERROR'}, - "Active object must be Tessellate before Update") + "Active object must be Tessellated before Update") return {'CANCELLED'} - if(bpy.data.objects[self.generator].type != 'MESH'): + + 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'): + + 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) + 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!" + message = "Zero faces selected in the Base mesh" self.report({'ERROR'}, message) + return {'CANCELLED'} # Transfer mesh data @@ -1200,16 +1433,18 @@ class settings_tessellate(bpy.types.Operator): 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.object.mode_set(mode='EDIT') bpy.ops.mesh.select_mode( use_extend=False, use_expand=False, type='VERT') bpy.ops.mesh.select_non_manifold( @@ -1217,7 +1452,7 @@ class settings_tessellate(bpy.types.Operator): 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') + 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') @@ -1226,7 +1461,7 @@ class settings_tessellate(bpy.types.Operator): try: # create materials list polygon_materials = [p.material_index for p in ob1.data.polygons] * \ - int(len(self.ob.data.polygons) / len(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)): @@ -1244,16 +1479,15 @@ class settings_tessellate(bpy.types.Operator): return True def invoke(self, context, event): - return context.window_manager.invoke_props_dialog(self) + return context.window_manager.invoke_props_dialog(self, width=400) -class tessellate_panel(bpy.types.Panel): +class tessellate_panel(Panel): bl_label = "Tissue" bl_category = "Create" bl_space_type = "VIEW_3D" bl_region_type = "TOOLS" bl_options = {'DEFAULT_CLOSED'} - #bl_context = "objectmode" @classmethod def poll(cls, context): @@ -1261,42 +1495,32 @@ class tessellate_panel(bpy.types.Panel): def draw(self, context): layout = self.layout + col = layout.column(align=True) col.label(text="Tessellate Add:") - col.operator("object.tessellate")#, icon="STRANDS") - #col.enable = False - #col.operator("object.adaptive_duplifaces", icon="MESH_CUBE") + col.operator("object.tessellate") + col = layout.column(align=True) col.label(text="Tessellate 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 col.separator() col.label(text="Other:") col.operator("object.lattice_along_surface", icon="OUTLINER_OB_LATTICE") - #col.separator() - #col.label(text="Add Modifier:") - try: - if bpy.context.object.type == 'MESH': - col.operator("object.uv_to_mesh", icon="GROUP_UVS") - except: - pass - + act = context.active_object + if act and act.type == 'MESH': + col.operator("object.uv_to_mesh", icon="GROUP_UVS") -class rotate_face(bpy.types.Operator): +class rotate_face(Operator): bl_idname = "mesh.rotate_face" bl_label = "Rotate Faces" - bl_description = "Rotate selected faces and update tessellated meshes." + bl_description = "Rotate selected faces and update tessellated meshes" bl_options = {'REGISTER', 'UNDO'} @classmethod @@ -1314,17 +1538,17 @@ class rotate_face(bpy.types.Operator): 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 + # 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]: + for o in [obj for obj in bpy.data.objects if + obj.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'} diff --git a/mesh_tissue/uv_to_mesh.py b/mesh_tissue/uv_to_mesh.py index ad119f4a..7c64b5f5 100644 --- a/mesh_tissue/uv_to_mesh.py +++ b/mesh_tissue/uv_to_mesh.py @@ -16,8 +16,8 @@ # # ##### END GPL LICENSE BLOCK ##### -#---------------------------------- UV to MESH --------------------------------# -#--------------------------------- version 0.1 --------------------------------# +# --------------------------------- UV to MESH ------------------------------- # +# -------------------------------- version 0.1.1 ----------------------------- # # # # Create a new Mesh based on active UV # # # @@ -26,57 +26,67 @@ # # # http://www.co-de-it.com/ # # # -################################################################################ - -import bpy -import bmesh, math -from mathutils import Vector +# ############################################################################ # bl_info = { "name": "UV to Mesh", "author": "Alessandro Zomparelli (Co-de-iT)", - "version": (0, 1), + "version": (0, 1, 1), "blender": (2, 7, 9), "location": "", "description": "Create a new Mesh based on active UV", "warning": "", "wiki_url": "", - "tracker_url": "", "category": "Mesh"} -class uv_to_mesh(bpy.types.Operator): - bl_idname = "object.uv_to_mesh" - bl_label = "UV to Mesh" - bl_options = {'REGISTER', 'UNDO'} - bl_description = ("Create a new Mesh based on active UV") - apply_modifiers = bpy.props.BoolProperty( - name="Apply Modifiers", default=False, - description="Apply object's modifiers") +import bpy +import math +from bpy.types import Operator +from bpy.props import BoolProperty +from mathutils import Vector - vertex_groups = bpy.props.BoolProperty( - name="Keep Vertex Groups", default=False, - description="Transfer all the Vertex Groups") - materials = bpy.props.BoolProperty( - name="Keep Materials", default=True, - description="Transfer all the Materials") +class uv_to_mesh(Operator): + bl_idname = "object.uv_to_mesh" + bl_label = "UV to Mesh" + bl_description = ("Create a new Mesh based on active UV") + bl_options = {'REGISTER', 'UNDO'} - auto_scale = bpy.props.BoolProperty( - name="Resize", default=True, - description="Scale the new object in order to preserve the average surface area") + apply_modifiers = BoolProperty( + name="Apply Modifiers", + default=False, + description="Apply object's modifiers" + ) + vertex_groups = BoolProperty( + name="Keep Vertex Groups", + default=False, + description="Transfer all the Vertex Groups" + ) + materials = BoolProperty( + name="Keep Materials", + default=True, + description="Transfer all the Materials" + ) + auto_scale = BoolProperty( + name="Resize", + default=True, + description="Scale the new object in order to preserve the average surface area" + ) def execute(self, context): bpy.ops.object.mode_set(mode='OBJECT') - for o in bpy.data.objects: o.select = False + for o in bpy.data.objects: + o.select = False bpy.context.object.select = True + if self.apply_modifiers: bpy.ops.object.duplicate_move() bpy.ops.object.convert(target='MESH') ob0 = bpy.context.object me0 = ob0.to_mesh(bpy.context.scene, - apply_modifiers=self.apply_modifiers, settings = 'PREVIEW') + apply_modifiers=self.apply_modifiers, settings='PREVIEW') area = 0 verts = [] @@ -89,8 +99,9 @@ class uv_to_mesh(bpy.types.Operator): try: for loop in face.loop_indices: uv = me0.uv_layers.active.data[loop].uv - if uv.x != 0 and uv.y != 0: store = True - new_vert = Vector((uv.x,uv.y,0)) + if uv.x != 0 and uv.y != 0: + store = True + new_vert = Vector((uv.x, uv.y, 0)) verts.append(new_vert) uv_face.append(loop) if store: @@ -98,10 +109,12 @@ class uv_to_mesh(bpy.types.Operator): face_materials.append(face.material_index) except: self.report({'ERROR'}, "Missing UV Map") + return {'CANCELLED'} + name = ob0.name + 'UV' # Create mesh and object - me = bpy.data.meshes.new(name+'Mesh') + me = bpy.data.meshes.new(name + 'Mesh') ob = bpy.data.objects.new(name, me) # Link object to scene and make active @@ -120,6 +133,7 @@ class uv_to_mesh(bpy.types.Operator): new_area += p.area if new_area == 0: self.report({'ERROR'}, "Impossible to generate mesh from UV") + return {'CANCELLED'} # VERTEX GROUPS @@ -136,8 +150,9 @@ class uv_to_mesh(bpy.types.Operator): ob0.select = False if self.auto_scale: - scaleFactor = math.pow(area/new_area,1/2) - ob.scale = Vector((scaleFactor,scaleFactor,scaleFactor)) + scaleFactor = math.pow(area / new_area, 1 / 2) + ob.scale = Vector((scaleFactor, scaleFactor, scaleFactor)) + bpy.ops.object.mode_set(mode='EDIT', toggle=False) bpy.ops.mesh.remove_doubles(threshold=1e-06) bpy.ops.object.mode_set(mode='OBJECT', toggle=False) |