Skip to content
Snippets Groups Projects
Commit 664ecb16 authored by lijenstina's avatar lijenstina
Browse files

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
parent 58727bbb
Branches
Tags
No related merge requests found
......@@ -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()
......@@ -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 = \
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 = \
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
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(
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 = bpy.props.BoolProperty(
name="invert", default=False, description="invert color channel")
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,25 +206,33 @@ 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:
......@@ -215,34 +264,54 @@ class vertex_group_to_vertex_colors(bpy.types.Operator):
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,7 +360,8 @@ 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
......@@ -291,52 +374,74 @@ class face_area_to_vertex_groups(bpy.types.Operator):
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
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:
......
......@@ -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(
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'))],
('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(
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 = 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")
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,12 +126,16 @@ 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
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
......@@ -122,11 +144,13 @@ class dual_mesh(bpy.types.Operator):
# 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)
use_verts=False
)
bpy.ops.mesh.extrude_region_move(
MESH_OT_extrude_region={"mirror": False},
TRANSFORM_OT_translate={"value": (0, 0, 0),
......@@ -138,25 +162,29 @@ class dual_mesh(bpy.types.Operator):
"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})
"release_confirm": False}
)
bpy.ops.mesh.select_mode(
use_extend=False, use_expand=False, type='VERT',
action='TOGGLE')
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)
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.modifier_apply(
apply_as='DATA', modifier='dual_mesh_subsurf'
)
bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.mesh.select_all(action='DESELECT')
......@@ -208,7 +236,8 @@ class dual_mesh(bpy.types.Operator):
# 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)
use_multi_face=False, use_non_contiguous=False, use_verts=True
)
bpy.ops.mesh.delete(type='VERT')
# remove middle vertices
......@@ -225,32 +254,41 @@ class dual_mesh(bpy.types.Operator):
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)
use_multi_face=False, use_non_contiguous=False, use_verts=False
)
bpy.ops.mesh.delete(type='EDGE')
bpy.ops.object.mode_set(mode='OBJECT')
ob0.data.name = mesh_name
doneMeshes.append(mesh_name)
for o in clones: o.data = ob.data
for o in sel: o.select = True
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,7 +298,7 @@ class dual_mesh_panel(bpy.types.Panel):
col.operator("object.dual_mesh")
except:
pass
'''
"""
def register():
......
......@@ -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,8 +73,13 @@ 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
......@@ -77,6 +89,7 @@ def grid_from_mesh(mesh, swap_uv):
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)]
......@@ -93,7 +106,7 @@ def grid_from_mesh(mesh, swap_uv):
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 ###
if len(faces_grid) == 0: # first loop #
edge = link_edges[swap_uv] # chose direction
for vert in edge.verts:
if vert.index != id:
......@@ -102,14 +115,18 @@ def grid_from_mesh(mesh, swap_uv):
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
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):
faces_loop.append(face.index)
......@@ -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,
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 = 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,
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 = 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")
"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.")
......@@ -282,12 +335,14 @@ class lattice_along_surface(bpy.types.Operator):
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:
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))
......@@ -307,6 +362,7 @@ 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
bpy.ops.object.add(type='LATTICE', view_align=False,
......@@ -315,9 +371,14 @@ class lattice_along_surface(bpy.types.Operator):
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
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
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
......@@ -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,24 +478,27 @@ 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():
......
......@@ -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,10 +27,22 @@
# #
# 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
......@@ -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
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
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,37 +116,37 @@ 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
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
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
......@@ -155,18 +172,18 @@ 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
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
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)
......@@ -194,7 +211,7 @@ 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))
......@@ -206,8 +223,7 @@ 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)):
......@@ -220,8 +236,10 @@ def tassellate(ob0, ob1, offset, zscale, gen_modifiers, com_modifiers, mode,
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
......@@ -240,32 +258,35 @@ def tassellate(ob0, ob1, offset, zscale, gen_modifiers, com_modifiers, mode,
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 + \
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 + \
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 + \
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 + \
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 + \
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:
if(dot1203 < 0): vertUV = p.vertices[:]
else: vertUV = p.vertices[2:] + p.vertices[:2]
vertUV = p.vertices[3:] + p.vertices[:3]
else:
if(dot1203 < 0):
vertUV = p.vertices[:]
else:
vertUV = p.vertices[2:] + p.vertices[:2]
vs0 = np.array([verts0[i].co for i in vertUV])
nvs0 = np.array([verts0[i].normal for i in vertUV])
......@@ -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
......@@ -339,13 +368,14 @@ def tassellate(ob0, ob1, offset, zscale, gen_modifiers, com_modifiers, mode,
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(
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 = 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(
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 = 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"))
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:
# 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]
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]
bpy.context.active_object.tissue_tessellate.generator
]
self.generator = ob0.name
no_component = False
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.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.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,24 +842,29 @@ class tessellate(bpy.types.Operator):
self.generator = ob0.name
no_component = False
if(no_component):
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 != "":
if bpy.ops.object.select_all.poll():
bpy.ops.object.select_all(action='TOGGLE')
new_ob = tassellate(
......@@ -740,7 +872,8 @@ class tessellate(bpy.types.Operator):
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)
self.bool_shapekeys
)
if new_ob == 0:
message = "Zero faces selected in the Base mesh!"
......@@ -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,6 +976,13 @@ class update_tessellate(bpy.types.Operator):
bool_shapekeys = ob.tissue_tessellate.bool_shapekeys
mode = ob.tissue_tessellate.mode
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")
......@@ -845,13 +990,12 @@ class update_tessellate(bpy.types.Operator):
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)
bool_vertex_group, bool_selection, bool_shapekeys
)
if temp_ob == 0:
message = "Zero faces selected in the Base mesh!"
......@@ -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(
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 = 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(
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 = bpy.props.EnumProperty(
default='ADAPTIVE',
name="Component Mode"
)
rotation_mode = 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"))
('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,6 +1185,21 @@ class settings_tessellate(bpy.types.Operator):
# start drawing
layout = self.layout
# 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)
......@@ -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 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'):
self.report({'ERROR'}, "Base object must be a Mesh")
return {'CANCELLED'}
if (bpy.data.objects[self.component].type != 'MESH'):
self.report({'ERROR'}, "Component object must be a Mesh")
return {'CANCELLED'}
ob0 = bpy.data.objects[self.generator]
ob1 = bpy.data.objects[self.component]
me0 = ob0.data
verts = me0.vertices
temp_ob = tassellate(
ob0, ob1, self.offset, self.zscale, self.gen_modifiers,
self.com_modifiers, self.mode, self.scale_mode, self.rotation_mode,
self.random_seed, self.fill_mode, self.bool_vertex_group,
self.bool_selection, self.bool_shapekeys)
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,10 +1433,12 @@ 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
......@@ -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':
act = context.active_object
if act and act.type == 'MESH':
col.operator("object.uv_to_mesh", icon="GROUP_UVS")
except:
pass
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
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'}
......
......@@ -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,50 +26,60 @@
# #
# 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')
......@@ -89,7 +99,8 @@ 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
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)
......@@ -98,7 +109,9 @@ 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')
......@@ -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
......@@ -138,6 +152,7 @@ class uv_to_mesh(bpy.types.Operator):
if self.auto_scale:
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)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment