mirror of http://CODE.RHODECODE.COM/u/O/O/O
0000OOOO0000
4 years ago
committed by
GitHub
1 changed files with 0 additions and 477 deletions
@ -1,477 +0,0 @@ |
|||||||
# ##### BEGIN GPL LICENSE BLOCK ##### |
|
||||||
# |
|
||||||
# This program is free software; you can redistribute it and/or |
|
||||||
# modify it under the terms of the GNU General Public License |
|
||||||
# as published by the Free Software Foundation; either version 2 |
|
||||||
# of the License, or (at your option) any later version. |
|
||||||
# |
|
||||||
# This program is distributed in the hope that it will be useful, |
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
||||||
# GNU General Public License for more details. |
|
||||||
# |
|
||||||
# You should have received a copy of the GNU General Public License |
|
||||||
# along with this program; if not, write to the Free Software Foundation, |
|
||||||
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
||||||
# |
|
||||||
# ##### END GPL LICENSE BLOCK ##### |
|
||||||
# --------------------------- LATTICE ALONG SURFACE -------------------------- # |
|
||||||
# -------------------------------- version 0.3 ------------------------------- # |
|
||||||
# # |
|
||||||
# Automatically generate and assign a lattice that follows the active surface. # |
|
||||||
# # |
|
||||||
# (c) Alessandro Zomparelli # |
|
||||||
# (2017) # |
|
||||||
# # |
|
||||||
# http://www.co-de-it.com/ # |
|
||||||
# # |
|
||||||
# ############################################################################ # |
|
||||||
|
|
||||||
import bpy |
|
||||||
import bmesh |
|
||||||
from bpy.types import Operator |
|
||||||
from bpy.props import (BoolProperty, StringProperty, FloatProperty) |
|
||||||
from mathutils import Vector |
|
||||||
|
|
||||||
from .utils import * |
|
||||||
|
|
||||||
|
|
||||||
def not_in(element, grid): |
|
||||||
output = True |
|
||||||
for loop in grid: |
|
||||||
if element in loop: |
|
||||||
output = False |
|
||||||
break |
|
||||||
return output |
|
||||||
|
|
||||||
|
|
||||||
def grid_from_mesh(mesh, swap_uv): |
|
||||||
bm = bmesh.new() |
|
||||||
bm.from_mesh(mesh) |
|
||||||
verts_grid = [] |
|
||||||
edges_grid = [] |
|
||||||
faces_grid = [] |
|
||||||
|
|
||||||
running_grid = True |
|
||||||
while running_grid: |
|
||||||
verts_loop = [] |
|
||||||
edges_loop = [] |
|
||||||
faces_loop = [] |
|
||||||
|
|
||||||
# storing first point |
|
||||||
verts_candidates = [] |
|
||||||
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 |
|
||||||
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 |
|
||||||
vert.select = True |
|
||||||
verts_loop.append(vert.index) |
|
||||||
break |
|
||||||
|
|
||||||
running_loop = len(verts_loop) > 0 |
|
||||||
|
|
||||||
while running_loop: |
|
||||||
bm.verts.ensure_lookup_table() |
|
||||||
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 |
|
||||||
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 # |
|
||||||
# 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) |
|
||||||
# continuing the loop |
|
||||||
else: |
|
||||||
for edge in link_edges: |
|
||||||
for vert in edge.verts: |
|
||||||
store_data = False |
|
||||||
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 |
|
||||||
else: |
|
||||||
store_data = True |
|
||||||
if store_data: |
|
||||||
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) |
|
||||||
break |
|
||||||
# ending condition |
|
||||||
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 |
|
||||||
|
|
||||||
return verts_grid, edges_grid, faces_grid |
|
||||||
|
|
||||||
|
|
||||||
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") |
|
||||||
bl_options = {'REGISTER', 'UNDO'} |
|
||||||
|
|
||||||
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" |
|
||||||
) |
|
||||||
grid_object = "" |
|
||||||
source_object = "" |
|
||||||
|
|
||||||
@classmethod |
|
||||||
def poll(cls, context): |
|
||||||
try: return bpy.context.object.mode == 'OBJECT' |
|
||||||
except: return False |
|
||||||
|
|
||||||
def draw(self, context): |
|
||||||
layout = self.layout |
|
||||||
col = layout.column(align=True) |
|
||||||
col.label(text="Thickness:") |
|
||||||
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 |
|
||||||
) |
|
||||||
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 |
|
||||||
) |
|
||||||
row = col.row() |
|
||||||
row.prop(self, "use_groups") |
|
||||||
col.separator() |
|
||||||
col.label(text="Scale:") |
|
||||||
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 |
|
||||||
) |
|
||||||
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 |
|
||||||
) |
|
||||||
col.separator() |
|
||||||
col.label(text="Flip:") |
|
||||||
row = col.row() |
|
||||||
row.prop(self, "flipU", text="U") |
|
||||||
row.prop(self, "flipV", text="V") |
|
||||||
row.prop(self, "flipW", text="W") |
|
||||||
col.prop(self, "swapUV") |
|
||||||
col.prop(self, "flipNormals") |
|
||||||
col.separator() |
|
||||||
col.label(text="Lattice Options:") |
|
||||||
col.prop(self, "high_quality_lattice") |
|
||||||
col.prop(self, "hide_lattice") |
|
||||||
col.prop(self, "set_parent") |
|
||||||
|
|
||||||
def execute(self, context): |
|
||||||
if self.source_object == self.grid_object == "" or True: |
|
||||||
if len(bpy.context.selected_objects) != 2: |
|
||||||
self.report({'ERROR'}, "Please, select two objects") |
|
||||||
return {'CANCELLED'} |
|
||||||
grid_obj = bpy.context.object |
|
||||||
if grid_obj.type not in ('MESH', 'CURVE', 'SURFACE'): |
|
||||||
self.report({'ERROR'}, "The surface object is not valid. Only Mesh," |
|
||||||
"Curve and Surface objects are allowed.") |
|
||||||
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'): |
|
||||||
obj = o |
|
||||||
o.select_set(False) |
|
||||||
break |
|
||||||
try: |
|
||||||
obj_dim = obj.dimensions |
|
||||||
obj_me = simple_to_mesh(obj)#obj.to_mesh(bpy.context.depsgraph, apply_modifiers=True) |
|
||||||
except: |
|
||||||
self.report({'ERROR'}, "The object to deform is not valid. Only " |
|
||||||
"Mesh, Curve, Surface and Font objects are allowed.") |
|
||||||
return {'CANCELLED'} |
|
||||||
self.grid_object = grid_obj.name |
|
||||||
self.source_object = obj.name |
|
||||||
else: |
|
||||||
grid_obj = bpy.data.objects[self.grid_object] |
|
||||||
obj = bpy.data.objects[self.source_object] |
|
||||||
obj_me = simple_to_mesh(obj)# obj.to_mesh(bpy.context.depsgraph, apply_modifiers=True) |
|
||||||
for o in bpy.context.selected_objects: o.select_set(False) |
|
||||||
grid_obj.select_set(True) |
|
||||||
bpy.context.view_layer.objects.active = grid_obj |
|
||||||
|
|
||||||
temp_grid_obj = grid_obj.copy() |
|
||||||
temp_grid_obj.data = simple_to_mesh(grid_obj) |
|
||||||
grid_mesh = temp_grid_obj.data |
|
||||||
for v in grid_mesh.vertices: |
|
||||||
v.co = grid_obj.matrix_world @ v.co |
|
||||||
grid_mesh.calc_normals() |
|
||||||
|
|
||||||
if len(grid_mesh.polygons) > 64 * 64: |
|
||||||
bpy.data.objects.remove(temp_grid_obj) |
|
||||||
bpy.context.view_layer.objects.active = obj |
|
||||||
obj.select_set(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)) |
|
||||||
first = True |
|
||||||
for v in obj_me.vertices: |
|
||||||
v0 = v.co.copy() |
|
||||||
vert = obj.matrix_world @ v0 |
|
||||||
if vert[0] < min[0] or first: |
|
||||||
min[0] = vert[0] |
|
||||||
if vert[1] < min[1] or first: |
|
||||||
min[1] = vert[1] |
|
||||||
if vert[2] < min[2] or first: |
|
||||||
min[2] = vert[2] |
|
||||||
if vert[0] > max[0] or first: |
|
||||||
max[0] = vert[0] |
|
||||||
if vert[1] > max[1] or first: |
|
||||||
max[1] = vert[1] |
|
||||||
if vert[2] > max[2] or first: |
|
||||||
max[2] = vert[2] |
|
||||||
first = False |
|
||||||
|
|
||||||
bb = max - min |
|
||||||
lattice_loc = (max + min) / 2 |
|
||||||
bpy.ops.object.add(type='LATTICE') |
|
||||||
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 |
|
||||||
|
|
||||||
bpy.context.view_layer.objects.active = obj |
|
||||||
bpy.ops.object.modifier_add(type='LATTICE') |
|
||||||
obj.modifiers[-1].object = lattice |
|
||||||
|
|
||||||
# set as parent |
|
||||||
if self.set_parent: |
|
||||||
obj.select_set(True) |
|
||||||
lattice.select_set(True) |
|
||||||
bpy.context.view_layer.objects.active = lattice |
|
||||||
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 |
|
||||||
) |
|
||||||
nu = len(verts_grid) |
|
||||||
nv = len(verts_grid[0]) |
|
||||||
nw = 2 |
|
||||||
scale_normal = self.thickness |
|
||||||
|
|
||||||
try: |
|
||||||
lattice.data.points_u = nu |
|
||||||
lattice.data.points_v = nv |
|
||||||
lattice.data.points_w = nw |
|
||||||
for i in range(nu): |
|
||||||
for j in range(nv): |
|
||||||
for w in range(nw): |
|
||||||
if self.use_groups: |
|
||||||
try: |
|
||||||
displace = temp_grid_obj.vertex_groups.active.weight( |
|
||||||
verts_grid[i][j]) * scale_normal * bb.z |
|
||||||
except: |
|
||||||
displace = 0#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') |
|
||||||
temp_grid_obj.select_set(True) |
|
||||||
lattice.select_set(True) |
|
||||||
obj.select_set(False) |
|
||||||
bpy.ops.object.delete(use_global=False) |
|
||||||
bpy.context.view_layer.objects.active = obj |
|
||||||
obj.select_set(True) |
|
||||||
bpy.ops.object.modifier_remove(modifier=obj.modifiers[-1].name) |
|
||||||
if nu > 64 or nv > 64: |
|
||||||
self.report({'ERROR'}, "Maximum resolution allowed for Lattice is 64") |
|
||||||
return {'CANCELLED'} |
|
||||||
else: |
|
||||||
self.report({'ERROR'}, "The grid mesh is not correct") |
|
||||||
return {'CANCELLED'} |
|
||||||
|
|
||||||
bpy.ops.object.mode_set(mode='OBJECT') |
|
||||||
#grid_obj.select_set(True) |
|
||||||
#lattice.select_set(False) |
|
||||||
obj.select_set(False) |
|
||||||
#bpy.ops.object.delete(use_global=False) |
|
||||||
bpy.context.view_layer.objects.active = lattice |
|
||||||
lattice.select_set(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.view_layer.objects.active = obj |
|
||||||
obj.select_set(True) |
|
||||||
lattice.select_set(False) |
|
||||||
|
|
||||||
if self.flipNormals: |
|
||||||
try: |
|
||||||
bpy.ops.object.mode_set(mode='EDIT') |
|
||||||
bpy.ops.mesh.select_all(action='SELECT') |
|
||||||
bpy.ops.mesh.flip_normals() |
|
||||||
bpy.ops.object.mode_set(mode='OBJECT') |
|
||||||
except: |
|
||||||
pass |
|
||||||
bpy.data.meshes.remove(grid_mesh) |
|
||||||
bpy.data.meshes.remove(obj_me) |
|
||||||
|
|
||||||
return {'FINISHED'} |
|
Loading…
Reference in new issue