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.
6 changed files with 132 additions and 78 deletions
Showing only changes of commit 1a284b677b - Show all commits

View File

@ -122,15 +122,16 @@ class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler):
) )
def draw_limits_line(self): def draw_limits_line(self):
line_pos, limits_pos, = self.modifier_limits_point up_point, down_point, up_limits, down_limits = self.modifier_limits_point
# draw limits line # draw limits line
self.draw_3d_shader(limits_pos, ((1, 0),), (1, 1, 0, 0.5)) self.draw_3d_shader((up_limits, down_limits), ((1, 0),), (1, 1, 0, 0.5))
# draw line # draw line
self.draw_3d_shader(line_pos, ((1, 0),), (1, 1, 0, 0.3)) self.draw_3d_shader((up_point, down_point), ((1, 0),), (1, 1, 0, 0.3))
# draw pos # draw pos
self.draw_3d_shader([line_pos[1]], (), (0, 1, 0, 0.5), self.draw_3d_shader([down_point], (), (0, 1, 0, 0.5),
shader_name='3D_UNIFORM_COLOR', draw_type='POINTS') shader_name='3D_UNIFORM_COLOR', draw_type='POINTS')
self.draw_3d_shader([line_pos[0]], (), (1, 0, 0, 0.5), self.draw_3d_shader([up_point], (), (1, 0, 0, 0.5),
shader_name='3D_UNIFORM_COLOR', draw_type='POINTS') shader_name='3D_UNIFORM_COLOR', draw_type='POINTS')
def draw_deform_mesh(self): def draw_deform_mesh(self):

View File

@ -55,7 +55,7 @@ class AngleUpdate(GizmoUtils):
elif not_c_l and not event.shift and is_shift and event.value == 'RELEASE': elif not_c_l and not event.shift and is_shift and event.value == 'RELEASE':
self.init_mouse_region_x = event.mouse_region_x self.init_mouse_region_x = event.mouse_region_x
new_value = self.tmp_value_angle = math.degrees(old_value) # new_value = self.tmp_value_angle = math.degrees(old_value)
return return
v(snap_value) v(snap_value)
@ -113,7 +113,6 @@ class AngleGizmo(Gizmo, AngleUpdate):
self.update_prop_value(event, tweak) self.update_prop_value(event, tweak)
self.update_header_text(context) self.update_header_text(context)
self.update_multiple_modifiers_data()
return self.event_handle(event) return self.event_handle(event)
def exit(self, context, cancel): def exit(self, context, cancel):

View File

@ -116,4 +116,4 @@ class BendAxiSwitchGizmoGroup(GizmoGroup, GizmoGroupUtils):
gizmo = getattr(self, i, False) gizmo = getattr(self, i, False)
rot = Euler(w, 'XYZ').to_matrix().to_4x4() rot = Euler(w, 'XYZ').to_matrix().to_4x4()
gizmo.matrix_basis = mat.to_euler().to_matrix().to_4x4() @ rot gizmo.matrix_basis = mat.to_euler().to_matrix().to_4x4() @ rot
gizmo.matrix_basis.translation = Vector(j) gizmo.matrix_basis.translation = self.obj_matrix_world @ Vector(j)

View File

@ -1,4 +1,5 @@
import math import math
from time import time
import bpy import bpy
from bpy.types import Gizmo, GizmoGroup from bpy.types import Gizmo, GizmoGroup
@ -200,6 +201,7 @@ class UpDownLimitsGizmo(Gizmo, GizmoUpdate):
'down_limits', self.int_value_down_limits) 'down_limits', self.int_value_down_limits)
def modal(self, context, event, tweak): def modal(self, context, event, tweak):
st = time()
self.clear_cache() self.clear_cache()
if self.modifier_is_use_origin_axis: if self.modifier_is_use_origin_axis:
@ -209,12 +211,12 @@ class UpDownLimitsGizmo(Gizmo, GizmoUpdate):
self.middle_limits_value = (self.modifier_up_limits + self.modifier_down_limits) / 2 self.middle_limits_value = (self.modifier_up_limits + self.modifier_down_limits) / 2
self.set_prop_value(event) self.set_prop_value(event)
self.clear_data()
self.clear_cache() self.clear_cache()
self.update_multiple_modifiers_data()
self.update_object_origin_matrix() self.update_object_origin_matrix()
# self.update_deform_wireframe(self.get_depsgraph(origin_object))
self.update_header_text(context) self.update_header_text(context)
return_handle = self.event_handle(event) return_handle = self.event_handle(event)
print('modal time sum ', time() - st)
return return_handle return return_handle

View File

@ -1,42 +1,100 @@
# SPDX-License-Identifier: GPL-2.0-or-later # SPDX-License-Identifier: GPL-2.0-or-later
from time import time
import bpy
from bpy.app.handlers import depsgraph_update_post, persistent
from .utils import GizmoUpdate from .utils import GizmoUpdate
@persistent class update_public:
def remove_not_use_empty(scene, dep): _event_func_list = {}
"""Remove unused Empty Object update_func: 'function'
""" tmp_save_data = {}
remove_name: str = "ViewSimpleDeformGizmo_" run_time = 0.2
context = bpy.context
gizmo = GizmoUpdate()
gizmo.fix_origin_parent_and_angle()
# remove redundant empty object
# simple update data if change active object on update @classmethod
name = gizmo.obj.name def register(cls):
update_data = name not in gizmo.G_GizmoData or name != gizmo.G_GizmoData['active_object'] import bpy
if update_data: bpy.app.timers.register(cls.update_func, persistent=True)
gizmo.clear_data()
gizmo.G_GizmoData[name] = name
gizmo.G_GizmoData['active_object'] = name
gizmo.G_Modifiers_TMP_Save_Data.clear()
if gizmo.simple_deform_modifier_is_simple(context):
for obj in context.scene.objects:
is_empty = obj.type == "EMPTY"
not_parent = not obj.parent
if remove_name in obj.name and not_parent and is_empty:
bpy.data.objects.remove(obj)
@classmethod
def unregister(cls):
from bpy.app import timers
func = cls.update_func
if timers.is_registered(func):
timers.unregister(func)
@classmethod
def _update_call(cls):
for i in cls._event_func_list[cls]:
i()
@classmethod
def append(cls, item):
if cls not in cls._event_func_list:
cls._event_func_list[cls] = []
cls._event_func_list[cls].append(item)
@classmethod
def remove(cls, item):
if item in cls._event_func_list[cls]:
cls._event_func_list[cls].remove(item)
class change_active_object(update_public):
tmp_save_data = {}
from bpy.app.handlers import depsgraph_update_post, persistent
handler_type = depsgraph_update_post
@classmethod
def update_func(cls):
import bpy
name = bpy.context.object.name
key = 'active_object'
if key not in cls.tmp_save_data or cls.tmp_save_data[key] != name:
cls._update_call()
cls.tmp_save_data[key] = name
return cls.run_time
class change_active_simple_deform_modifier(update_public):
@classmethod
def update_func(cls):
import bpy
obj = bpy.context.object
if not obj or obj.type != 'MESH':
return cls.run_time
name = obj.name
key = 'active_object'
modifiers = cls.get_modifiers_data(obj)
change_modifiers = 'modifiers' not in cls.tmp_save_data or cls.tmp_save_data['modifiers'] != modifiers
if key not in cls.tmp_save_data or cls.tmp_save_data[key] != name:
cls.tmp_save_data['modifiers'] = modifiers
cls.tmp_save_data[key] = name
elif change_modifiers:
cls.tmp_save_data['modifiers'] = modifiers
cls._update_call()
return cls.run_time
@classmethod
def get_modifiers_data(cls, obj):
return {'obj': obj.name,
'active_modifier': getattr(obj.modifiers.active, 'name', None),
'modifiers': list(i.name for i in obj.modifiers)}
gizmo = GizmoUpdate()
def register(): def register():
depsgraph_update_post.append(remove_not_use_empty) change_active_object.register()
change_active_simple_deform_modifier.register()
change_active_object.append(gizmo.update_multiple_modifiers_data)
change_active_simple_deform_modifier.append(gizmo.update_multiple_modifiers_data)
def unregister(): def unregister():
depsgraph_update_post.remove(remove_not_use_empty) change_active_object.remove(gizmo.update_multiple_modifiers_data)
change_active_simple_deform_modifier.remove(gizmo.update_multiple_modifiers_data)
change_active_object.unregister()
change_active_simple_deform_modifier.unregister()

View File

@ -4,6 +4,7 @@ import math
import uuid import uuid
from functools import cache from functools import cache
from os.path import dirname, basename, realpath from os.path import dirname, basename, realpath
from time import time
import bpy import bpy
import numpy as np import numpy as np
@ -17,7 +18,6 @@ class PublicData:
G_CustomShape = {} G_CustomShape = {}
G_GizmoData = {} G_GizmoData = {}
G_Modifiers_Data = {} G_Modifiers_Data = {}
G_Modifiers_TMP_Save_Data = {}
G_INDICES = ( G_INDICES = (
(0, 1), (0, 2), (1, 3), (2, 3), (0, 1), (0, 2), (1, 3), (2, 3),
(4, 5), (4, 6), (5, 7), (6, 7), (4, 5), (4, 6), (5, 7), (6, 7),
@ -315,12 +315,6 @@ class GizmoClassMethod(PublicUtils):
Vector((min_x, max_y, max_z)) Vector((min_x, max_y, max_z))
) )
@classmethod
def get_modifiers_data(cls, obj):
return {'obj': obj.name,
'active_modifier': obj.modifiers.active.name,
'modifiers': list(i.name for i in obj.modifiers)}
class PublicProperty(GizmoClassMethod): class PublicProperty(GizmoClassMethod):
@ -340,7 +334,7 @@ class PublicProperty(GizmoClassMethod):
g_l = self.__from_up_down_point_get_limits_point g_l = self.__from_up_down_point_get_limits_point
if self.modifier.origin: if self.modifier.origin:
vector_axis = self.get_vector_axis(mod) vector_axis = self.get_vector_axis(mod)
origin_mat = mod.origin.matrix_world.to_3x3() origin_mat = mod.origin.matrix_basis.to_3x3()
axis_ = origin_mat @ vector_axis axis_ = origin_mat @ vector_axis
point_lit = [[top, bottom], [left, right], [front, back]] point_lit = [[top, bottom], [left, right], [front, back]]
for f in range(point_lit.__len__()): for f in range(point_lit.__len__()):
@ -378,17 +372,11 @@ class PublicProperty(GizmoClassMethod):
up_point, down_point = top, bottom up_point, down_point = top, bottom
top, bottom = up_limits, down_limits = g_l(top, bottom) top, bottom = up_limits, down_limits = g_l(top, bottom)
(top, bottom, left, points = (up_point, down_point, up_limits, down_limits)
right, front, back) = self.matrix_calculation(self.obj_matrix_world.inverted(),
(top, bottom, left, right, front, back))
points = ((up_point, down_point), (up_limits, down_limits))
each_point = ((right[0], back[1], top[2]), (left[0], front[1], bottom[2],)) each_point = ((right[0], back[1], top[2]), (left[0], front[1], bottom[2],))
box_bound_point = self.matrix_calculation(self.obj_matrix_world, self.tow_co_to_coordinate(each_point)) return points, self.tow_co_to_coordinate(each_point)
return points, box_bound_point
# ---------------------- # ----------------------
@cache @cache
def _each_face_pos(self, mat, co): def _each_face_pos(self, mat, co):
return self.co_to_direction(mat, co) return self.co_to_direction(mat, co)
@ -401,13 +389,18 @@ class PublicProperty(GizmoClassMethod):
@classmethod @classmethod
def clear_data(cls): def clear_data(cls):
cls.G_GizmoData.clear() cls.G_GizmoData.clear()
@classmethod
def clear_modifiers_data(cls):
cls.G_Modifiers_Data.clear() cls.G_Modifiers_Data.clear()
# --------------- Cache Data ---------------------- # --------------- Cache Data ----------------------
@property @property
def each_face_pos(self): def each_face_pos(self):
return self._each_face_pos(self.obj_matrix_world, self.get_bound_co_data()) matrix = Matrix()
matrix.freeze()
return self._each_face_pos(matrix, self.get_bound_co_data())
@property @property
def modifier_bound_co(self): def modifier_bound_co(self):
@ -415,17 +408,19 @@ class PublicProperty(GizmoClassMethod):
@property @property
def modifier_bound_box_pos(self): def modifier_bound_box_pos(self):
return self._each_face_pos(self.obj_matrix_world, self.modifier_bound_co) matrix = Matrix()
matrix.freeze()
return self.co_to_direction(matrix, self.modifier_bound_co)
@property @property
def modifier_limits_point(self): def modifier_limits_point(self):
points, _ = self._get_limits_point_and_bound_box_co() points, _ = self._get_limits_point_and_bound_box_co()
return points return self.matrix_calculation(self.obj_matrix_world, points)
@property @property
def modifier_limits_bound_box(self): def modifier_limits_bound_box(self):
_, bound = self._get_limits_point_and_bound_box_co() _, bound = self._get_limits_point_and_bound_box_co()
return bound return self.matrix_calculation(self.obj_matrix_world, bound)
@property @property
def modifier_origin_angle_is_available(self): def modifier_origin_angle_is_available(self):
@ -433,6 +428,7 @@ class PublicProperty(GizmoClassMethod):
self._get_limits_point_and_bound_box_co() self._get_limits_point_and_bound_box_co()
return True return True
except UnboundLocalError: except UnboundLocalError:
print('modifier_origin_angle_is_available')
self.clear_cache() self.clear_cache()
return False return False
@ -492,19 +488,19 @@ class PublicProperty(GizmoClassMethod):
# ----- point # ----- point
@property @property
def point_up(self): def point_up(self):
return self.modifier_limits_point[0][0] return self.modifier_limits_point[0]
@property @property
def point_down(self): def point_down(self):
return self.modifier_limits_point[0][1] return self.modifier_limits_point[1]
@property @property
def point_limits_up(self): def point_limits_up(self):
return self.modifier_limits_point[1][0] return self.modifier_limits_point[2]
@property @property
def point_limits_down(self): def point_limits_down(self):
return self.modifier_limits_point[1][1] return self.modifier_limits_point[3]
# ------ # ------
@ -530,7 +526,7 @@ class PublicProperty(GizmoClassMethod):
@property @property
def modifier_is_use_origin_axis(self): def modifier_is_use_origin_axis(self):
return self.obj_origin_property_group.origin_mode != 'NOT' return self.obj_origin_property_group.origin_mode != 'NOT' and not self.modifier.origin
class GizmoUpdate(PublicProperty): class GizmoUpdate(PublicProperty):
@ -598,6 +594,7 @@ class GizmoUpdate(PublicProperty):
return origin_object return origin_object
def update_object_origin_matrix(self): def update_object_origin_matrix(self):
st = time()
origin_mode = self.origin_mode origin_mode = self.origin_mode
origin_object = self.modifier.origin origin_object = self.modifier.origin
is_use = self.modifier_is_use_origin_axis is_use = self.modifier_is_use_origin_axis
@ -613,23 +610,21 @@ class GizmoUpdate(PublicProperty):
elif origin_mode == 'MIDDLE': elif origin_mode == 'MIDDLE':
translation = (self.point_up + self.point_down) / 2 translation = (self.point_up + self.point_down) / 2
origin_object.matrix_world.translation = translation origin_object.matrix_world.translation = translation
print('update_object_origin_matrix', time() - st)
def update_multiple_modifiers_data(self): def update_multiple_modifiers_data(self):
print('update_multiple_modifiers_data', self)
st = time()
obj = self.obj obj = self.obj
data = bpy.data
context = bpy.context context = bpy.context
if obj.type not in ('MESH', 'LATTICE') or not self.simple_deform_public_poll(context):
return
self.clear_cache()
data = bpy.data
name = self.G_NAME name = self.G_NAME
origin_object = data.objects.get(name) origin_object = data.objects.get(name)
modifiers = self.G_Modifiers_TMP_Save_Data # update multiple simple_deform bound data
mods_data = self.get_modifiers_data(obj)
if 114514 in modifiers and modifiers[114514] == mods_data:
if origin_object:
self.update_deform_wireframe(self.get_depsgraph(origin_object))
return
# add simple_deform mesh
if origin_object: if origin_object:
data.objects.remove(origin_object) data.objects.remove(origin_object)
@ -656,8 +651,7 @@ class GizmoUpdate(PublicProperty):
for mo in context.object.modifiers: for mo in context.object.modifiers:
if mo.type == 'SIMPLE_DEFORM': if mo.type == 'SIMPLE_DEFORM':
obj = self.get_depsgraph(deform_obj) obj = self.get_depsgraph(deform_obj)
self.G_GizmoData[mo.name] = self.get_mesh_max_min_co( self.G_Modifiers_Data[mo.name] = self.get_mesh_max_min_co(obj)
obj)
simple_deform = deform_obj.modifiers.new( simple_deform = deform_obj.modifiers.new(
mo.name, 'SIMPLE_DEFORM') mo.name, 'SIMPLE_DEFORM')
simple_deform.deform_method = mo.deform_method simple_deform.deform_method = mo.deform_method
@ -676,7 +670,7 @@ class GizmoUpdate(PublicProperty):
deform_obj.hide_render = True deform_obj.hide_render = True
deform_obj.hide_viewport = True deform_obj.hide_viewport = True
deform_obj.hide_set(True) deform_obj.hide_set(True)
self.G_Modifiers_TMP_Save_Data[114514] = mods_data print('multiple_modifiers', time() - st)
def update_deform_wireframe(self, obj): def update_deform_wireframe(self, obj):
if not self.pref.update_deform_wireframe: if not self.pref.update_deform_wireframe: