new addon simple_deform_helper #104464

Closed
EMM wants to merge 29 commits from Guai_Wo_Ge_EMM/blender-addons:simple_deform_helper into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
10 changed files with 93 additions and 95 deletions
Showing only changes of commit 9c5a8ebaf3 - Show all commits

View File

@ -17,10 +17,10 @@ class Handler:
@classmethod
def del_handler_text(cls):
if 'handler_text' in cls.G_GizmoData:
if 'scale_text' in cls.G_GizmoData:
bpy.types.SpaceView3D.draw_handler_remove(
cls.G_GizmoData['handler_text'], 'WINDOW')
cls.G_GizmoData.pop('handler_text')
cls.G_GizmoData['scale_text'], 'WINDOW')
cls.G_GizmoData.pop('scale_text')
@classmethod
def del_handler(cls):
@ -75,8 +75,6 @@ class DrawText:
f'The scaling value of the object {obj.name_full} is not 1,'
f' which will cause the deformation of the simple deformation modifier.'
f' Please apply the scaling before deformation')
if obj.scale == Vector((1, 1, 1)):
cls.del_handler_text()
@classmethod
def draw_text(cls, x, y, text='Hello Word', font_id=0, size=10, *, color=(0.5, 0.5, 0.5, 1), dpi=72, column=0):
@ -105,7 +103,7 @@ class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler):
self.draw_scale_text(obj)
if not self.modifier_origin_angle_is_available:
self.draw_bound_box()
if self.simple_deform_show_gizmo_poll(context):
elif self.simple_deform_show_gizmo_poll(context):
# draw bound box
self.draw_bound_box()
self.draw_deform_mesh()
@ -113,11 +111,10 @@ class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler):
self.draw_limits_bound_box()
elif self.simple_deform_show_bend_axis_witch_poll(context):
self.draw_bound_box()
# self.new_empty(obj, modifier)
def draw_bound_box(self):
coords = self.matrix_calculation(self.obj_matrix_world,
self.tow_co_to_coordinate(self.get_bound_co_data()))
self.tow_co_to_coordinate(self.modifier_bound_co))
self.draw_3d_shader(coords, self.G_INDICES, self.pref.bound_box_color)
def draw_limits_bound_box(self):
@ -143,8 +140,8 @@ class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler):
handler_dit = self.G_GizmoData
active = self.modifier
# draw deform mesh
if 'draw' in handler_dit:
pos, indices, mat, mod_data, limits = handler_dit['draw']
if 'simple_deform_box_data' in handler_dit and self.pref.update_deform_wireframe:
pos, indices, mat, mod_data, limits = handler_dit['simple_deform_box_data']
if ([getattr(active, i) for i in self.G_MODIFIERS_PROPERTY] == mod_data) and (
ob.matrix_world == mat) and limits == active.limits[:]:
self.draw_3d_shader(
@ -152,8 +149,8 @@ class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler):
def draw_scale_text(self, ob):
scale_error = (ob.scale != Vector((1, 1, 1)))
if scale_error and ('handler_text' not in self.G_GizmoData):
self.G_GizmoData['handler_text'] = bpy.types.SpaceView3D.draw_handler_add(
if scale_error and ('scale_text' not in self.G_GizmoData):
self.G_GizmoData['scale_text'] = bpy.types.SpaceView3D.draw_handler_add(
self.draw_str, (), 'WINDOW', 'POST_PIXEL')
def draw_origin_error(self):

View File

@ -19,12 +19,11 @@ class AngleUpdate(GizmoUtils):
new_value = (self.get_snap(value, tweak))
old_value = math.degrees(self.target_get_value('angle'))
print(new_value, old_value)
self.target_set_value('angle', math.radians(new_value))
def update_gizmo_matrix(self, context):
matrix = context.object.matrix_world
point = self.get_bound_co_data()[1]
point = self.modifier_bound_co[1]
self.matrix_basis = self.obj_matrix_world
self.matrix_basis.translation = matrix @ point
@ -72,7 +71,7 @@ class AngleGizmo(Gizmo, AngleUpdate):
self.update_prop_value(event, tweak)
self.update_header_text(context)
self.update_deform_wireframe()
self.update_multiple_modifiers_data()
return {'RUNNING_MODAL'}
def exit(self, context, cancel):

View File

@ -53,7 +53,7 @@ class BendAxiSwitchGizmoGroup(GizmoGroup, GizmoGroupUtils):
_color_b = 0, 1, 0
for na, axis, rot, positive in (
('top_a', 'X', (math.radians(90), 0, math.radians(9 - 0)), True),
('top_a', 'X', (math.radians(90), 0, math.radians(90)), True),
('top_b', 'X', (math.radians(90), 0, 0), True),
('bottom_a', 'X', (math.radians(90), 0, math.radians(90)), False),
@ -93,7 +93,7 @@ class BendAxiSwitchGizmoGroup(GizmoGroup, GizmoGroupUtils):
def draw_prepare(self, context):
ob = context.object
mat = ob.matrix_world
top, bottom, left, right, front, back = self.each_face_pos
top, bottom, left, right, front, back = self.modifier_bound_box_pos
rad = math.radians
for_list = (

View File

@ -10,7 +10,7 @@ class SetDeformGizmoGroup(GizmoGroup, GizmoGroupUtils):
@classmethod
def poll(cls, context):
return cls.simple_deform_show_gizmo_poll(context)
return cls.simple_deform_show_gizmo_poll(context) and cls.pref_().show_set_axis_button
def setup(self, context):
data_path = 'object.modifiers.active.deform_axis'
@ -36,10 +36,13 @@ class SetDeformGizmoGroup(GizmoGroup, GizmoGroupUtils):
def draw_prepare(self, context):
if 'co' in self.G_GizmoData:
obj = self.get_depsgraph(self.obj)
dimensions = obj.dimensions
def _mat(f):
co = self.G_GizmoData['co'][0]
co = (co[0] + (max(self.obj.dimensions) * f), co[1],
co[2] - (min(self.obj.dimensions) * 0.3))
co = (co[0] + (max(dimensions) * f), co[1],
co[2] - (min(dimensions) * 0.3))
return self.obj_matrix_world @ Vector(co)
self.deform_axis_x.matrix_basis.translation = _mat(0)

View File

@ -147,30 +147,6 @@ class GizmoUpdate(GizmoProperty):
text += t('Down limit', value)
context.area.header_text_set(text)
# def update_modifiers_origin_matrix(self):
# ob = bpy.context.object
# for mod in ob.modifiers:
# if mod.type == 'SIMPLE_DEFORM':
# self.update_matrix(mod, ob)
#
# def update_matrix(self, mod, ob):
# # if mod.deform_method == 'BEND':
# # cls.new_empty(ob, mod)
# origin_object = mod.origin
# if mod.origin:
# modifiers_co = self.G_GizmoData['modifiers_co']
# for index, mod_name in enumerate(modifiers_co):
# co_items = list(modifiers_co.items())
# if mod.name == mod_name:
# data = co_items[index - 1][1] if (
# index or (index != 1)) else modifiers_co['co']
# (up, down), (up_, down_) = self.get_limits_pos(
# mod, self.co_to_direction(ob.matrix_world.copy(), data))
# origin_mode = self.obj_origin_property_group(
# mod, ob).origin_mode
# self.set_empty_obj_matrix(
# origin_mode, origin_object, up_, down_, up, down)
class UpDownLimitsGizmo(Gizmo, GizmoUpdate):
bl_idname = 'UpDownLimitsGizmo'
@ -234,7 +210,7 @@ class UpDownLimitsGizmo(Gizmo, GizmoUpdate):
self.set_prop_value(event)
self.update_object_origin_matrix()
self.update_deform_wireframe()
self.update_multiple_modifiers_data()
self.update_header_text(context)
return self.event_handle(event)

View File

@ -26,12 +26,11 @@ class DeformAxisOperator(Operator, GizmoUtils):
return {'RUNNING_MODAL'}
def modal(self, context, event):
from .gizmo.angle_and_factor import GizmoUtils
self.clear_cache()
mod = context.object.modifiers.active
mod.deform_axis = self.Deform_Axis
empty, con_limit_name = GizmoUtils.new_origin_empty_object(context.object, mod)
is_positive = GizmoUtils.is_positive(mod.angle)
empty = self.new_origin_empty_object()
is_positive = self.is_positive(mod.angle)
for limit, value in (('max_x', self.X_Value),
('min_x', self.X_Value),
@ -40,13 +39,14 @@ class DeformAxisOperator(Operator, GizmoUtils):
('max_z', self.Z_Value),
('min_z', self.Z_Value),
):
setattr(empty.constraints[con_limit_name], limit, value)
setattr(empty.constraints[self.G_CON_LIMIT_NAME], limit, value)
if ((not is_positive) and self.Is_Positive) or (is_positive and (not self.Is_Positive)):
mod.angle = mod.angle * -1
if not event.ctrl:
self.pref.display_bend_axis_switch_gizmo = False
# self.new_origin_empty_object()
return {'FINISHED'}

View File

@ -33,20 +33,21 @@ class SimpleDeformHelperToolPanel(Panel, GizmoUtils):
layout.prop(ctrl_obj,
'origin_mode',
text='')
layout.prop(pref,
'update_deform_wireframe',
icon='MOD_WIREFRAME',
text='Deform Wireframe')
layout.prop(pref,
'modifiers_limits_tolerance',
text='')
layout.prop(pref,
'show_set_axis_button',
icon='EMPTY_AXIS',
text='')
if mod.deform_method == 'BEND':
layout.prop(pref,
'display_bend_axis_switch_gizmo',
toggle=1)
layout.prop(pref,
'modifiers_limits_tolerance',
text='')
class_list = (

View File

@ -53,8 +53,12 @@ class SimpleDeformGizmoAddonPreferences(AddonPreferences, GizmoUtils):
options={'SKIP_SAVE'})
update_deform_wireframe: BoolProperty(
name='Update Deform Wireframe',
default=True)
name='Show Deform Wireframe',
default=False)
show_set_axis_button: BoolProperty(
name='Show Set Axis Button',
default=False)
def draw(self, context):
col = self.layout.column()
@ -64,6 +68,7 @@ class SimpleDeformGizmoAddonPreferences(AddonPreferences, GizmoUtils):
col.prop(self, 'modifiers_limits_tolerance')
col.prop(self, 'display_bend_axis_switch_gizmo')
col.prop(self, 'update_deform_wireframe', icon='MOD_WIREFRAME', )
col.prop(self, 'show_set_axis_button', icon='EMPTY_AXIS', )
def draw_header_tool_settings(self, context):
if GizmoUtils.simple_deform_public_poll(context):

View File

@ -10,10 +10,10 @@ from .utils import GizmoUpdate
def remove_not_use_empty(scene, dep):
"""循环场景内的所有物体,找出没用的空物体并删掉
"""
GizmoUpdate.clear_cache()
remove_name: str = "ViewSimpleDeformGizmo_"
context = bpy.context
if GizmoUpdate.simple_deform_modifier_is_simple(context):
GizmoUpdate.clear_cache()
for obj in context.scene.objects:
is_empty = obj.type == "EMPTY"
not_parent = not obj.parent

View File

@ -115,7 +115,8 @@ class PublicPoll(PublicClass):
is_available_obj = available_modifiers_type and available_obj_type
is_obj_mode = context.mode == 'OBJECT'
show_mod = mod.show_viewport
return is_available_obj and is_obj_mode and show_mod
not_is_self_mesh = obj.name != cls.G_NAME
return is_available_obj and is_obj_mode and show_mod and not_is_self_mesh
@classmethod
def simple_deform_public_poll(cls, context: 'bpy.types.context') -> bool:
@ -228,7 +229,7 @@ class PublicUtils(PublicPoll):
list_vertices = np.zeros(ver_len * 3, dtype=np.float32)
obj.data.points.foreach_get('co', list_vertices)
list_vertices = list_vertices.reshape(ver_len, 3)
return Vector(list_vertices.min(axis=0)), Vector(list_vertices.max(axis=0))
return Vector(list_vertices.min(axis=0)).freeze(), Vector(list_vertices.max(axis=0)).freeze()
@classmethod
def matrix_calculation(cls, mat: 'Matrix', calculation_list: 'list') -> list:
@ -326,7 +327,7 @@ class PublicProperty(GizmoClassMethod):
@cache
def _get_limits_point_and_bound_box_co(self):
top, bottom, left, right, front, back = self.each_face_pos
top, bottom, left, right, front, back = self.modifier_bound_box_pos
mod = self.modifier
g_l = self.__from_up_down_point_get_limits_point
if self.modifier.origin:
@ -381,14 +382,13 @@ class PublicProperty(GizmoClassMethod):
# ----------------------
@cache
def _each_face_pos(self, mat):
return self.co_to_direction(mat, self.get_bound_co_data())
def _each_face_pos(self, mat, co):
return self.co_to_direction(mat, co)
@classmethod
def clear_cache(cls):
cls._each_face_pos.cache_clear()
cls._get_limits_point_and_bound_box_co.cache_clear()
cls.clear_data()
@classmethod
def clear_data(cls):
@ -399,7 +399,15 @@ class PublicProperty(GizmoClassMethod):
@property
def each_face_pos(self):
return self._each_face_pos(self.obj_matrix_world)
return self._each_face_pos(self.obj_matrix_world, self.get_bound_co_data())
@property
def modifier_bound_co(self):
return self.G_GizmoData['modifiers_co'].get(self.modifier.name, self.get_bound_co_data())
@property
def modifier_bound_box_pos(self):
return self._each_face_pos(self.obj_matrix_world, self.modifier_bound_co)
@property
def modifier_limits_point(self):
@ -417,6 +425,7 @@ class PublicProperty(GizmoClassMethod):
self._get_limits_point_and_bound_box_co()
return True
except UnboundLocalError:
self.clear_cache()
return False
# --------------- Compute Data ----------------------
@ -532,13 +541,14 @@ class GizmoUpdate(PublicProperty):
else:
origin_object = mod.origin
origin_object.hide_viewport = False
if origin_object == obj:
return
if origin_object.parent != obj:
origin_object.parent = obj
# add constraints
name = self.G_CON_LIMIT_NAME
if origin_object.constraints.keys().__len__() > 1:
if origin_object.constraints.keys().__len__() > 2:
origin_object.constraints.clear()
if name in origin_object.constraints.keys():
limit_constraints = origin.constraints.get(name)
@ -566,6 +576,8 @@ class GizmoUpdate(PublicProperty):
origin_object.rotation_euler.zero()
origin_object.scale = 1, 1, 1
return origin_object
def update_object_origin_matrix(self):
self.clear_cache()
origin_mode = self.origin_mode
@ -582,29 +594,26 @@ class GizmoUpdate(PublicProperty):
translation = (self.point_up + self.point_down) / 2
empty_object.matrix_world.translation = translation
def update_deform_wireframe(self):
if not self.pref.update_deform_wireframe:
return
context = bpy.context
data = bpy.data
def update_multiple_modifiers_data(self):
self.clear_data()
obj = self.obj
data = bpy.data
context = bpy.context
matrix = obj.matrix_world.copy() # 物体矩阵
# add simple_deform mesh
name = self.G_NAME
vertices = self.matrix_calculation(self.obj_matrix_world.inverted(), self.modifier_limits_bound_box)
if data.objects.get(name):
data.objects.remove(data.objects.get(name))
if data.meshes.get(name):
data.meshes.remove(data.meshes.get(name))
vertices = self.tow_co_to_coordinate(self.get_bound_co_data())
new_mesh = data.meshes.new(name)
new_mesh.from_pydata(vertices, self.G_INDICES, [])
new_mesh.update()
deform_obj = data.objects.get(name, None)
if deform_obj and deform_obj.type == 'MESH':
deform_obj.data = new_mesh
else:
if deform_obj:
data.objects.remove(deform_obj)
deform_obj = data.objects.new(name, new_mesh)
deform_obj = data.objects.new(name, new_mesh)
self.link_obj_to_active_collection(deform_obj)
if deform_obj == obj:
@ -616,8 +625,12 @@ class GizmoUpdate(PublicProperty):
subdivision = deform_obj.modifiers.new('1', 'SUBSURF')
subdivision.levels = 7
self.G_GizmoData['modifiers_co']['co'] = self.get_bound_co_data()
for mo in context.object.modifiers:
if mo.type == 'SIMPLE_DEFORM':
obj = self.get_depsgraph(deform_obj)
self.G_GizmoData['modifiers_co'][mo.name] = self.get_mesh_max_min_co(
obj)
simple_deform = deform_obj.modifiers.new(
mo.name, 'SIMPLE_DEFORM')
simple_deform.deform_method = mo.deform_method
@ -626,19 +639,23 @@ class GizmoUpdate(PublicProperty):
simple_deform.lock_y = mo.lock_y
simple_deform.lock_z = mo.lock_z
simple_deform.origin = mo.origin
# simple_deform.limits[1] = mo.limits[1]
# simple_deform.limits[0] = mo.limits[0]
simple_deform.limits[1] = mo.limits[1]
simple_deform.limits[0] = mo.limits[0]
simple_deform.angle = mo.angle
simple_deform.show_viewport = mo.show_viewport
obj = PublicUtils.get_depsgraph(deform_obj)
self.G_GizmoData['modifiers_co'][mo.name] = self.get_mesh_max_min_co(
obj)
# deform_obj.hide_set(True)
# deform_obj.hide_viewport = False
# deform_obj.hide_select = True
# deform_obj.hide_render = True
# deform_obj.hide_viewport = True
# deform_obj.hide_set(True)
deform_obj.hide_select = True
deform_obj.hide_set(True)
deform_obj.hide_viewport = False
self.update_deform_wireframe(self.get_depsgraph(deform_obj))
deform_obj.hide_render = True
deform_obj.hide_viewport = True
deform_obj.hide_set(True)
def update_deform_wireframe(self, obj):
if not self.pref.update_deform_wireframe:
return
context = bpy.context
matrix = self.obj_matrix_world.copy()
ver_len = obj.data.vertices.__len__()
edge_len = obj.data.edges.__len__()
@ -667,7 +684,7 @@ class GizmoUpdate(PublicProperty):
limits = context.object.modifiers.active.limits[:]
modifiers = [getattr(context.object.modifiers.active, i)
for i in self.G_MODIFIERS_PROPERTY]
self.G_GizmoData['draw'] = (ver, indices, matrix, modifiers, limits)
self.G_GizmoData['simple_deform_box_data'] = (ver, indices, matrix, modifiers, limits[:])
class GizmoUtils(GizmoUpdate):