new addon simple_deform_helper #104464
@ -5,22 +5,23 @@ import gpu
|
|||||||
from gpu_extras.batch import batch_for_shader
|
from gpu_extras.batch import batch_for_shader
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
|
|
||||||
|
from .update import change_active_object, simple_update
|
||||||
from .utils import GizmoUtils
|
from .utils import GizmoUtils
|
||||||
|
|
||||||
|
|
||||||
class Handler:
|
class Handler:
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_handler(cls):
|
def add_handler(cls):
|
||||||
if 'handler' not in cls.G_GizmoData:
|
if 'handler' not in cls.G_HandleData:
|
||||||
cls.G_GizmoData['handler'] = bpy.types.SpaceView3D.draw_handler_add(
|
cls.G_HandleData['handler'] = bpy.types.SpaceView3D.draw_handler_add(
|
||||||
Draw3D().draw, (), 'WINDOW', 'POST_VIEW')
|
Draw3D().draw, (), 'WINDOW', 'POST_VIEW')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def del_handler_text(cls):
|
def del_handler_text(cls):
|
||||||
if 'scale_text' in cls.G_GizmoData:
|
if 'scale_text' in cls.G_HandleData:
|
||||||
bpy.types.SpaceView3D.draw_handler_remove(
|
bpy.types.SpaceView3D.draw_handler_remove(
|
||||||
cls.G_GizmoData['scale_text'], 'WINDOW')
|
cls.G_HandleData['scale_text'], 'WINDOW')
|
||||||
cls.G_GizmoData.pop('scale_text')
|
cls.G_HandleData.pop('scale_text')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def del_handler(cls):
|
def del_handler(cls):
|
||||||
@ -33,10 +34,10 @@ class Handler:
|
|||||||
|
|
||||||
cls.del_handler_text()
|
cls.del_handler_text()
|
||||||
|
|
||||||
if 'handler' in cls.G_GizmoData:
|
if 'handler' in cls.G_HandleData:
|
||||||
bpy.types.SpaceView3D.draw_handler_remove(
|
bpy.types.SpaceView3D.draw_handler_remove(
|
||||||
cls.G_GizmoData['handler'], 'WINDOW')
|
cls.G_HandleData['handler'], 'WINDOW')
|
||||||
cls.G_GizmoData.clear()
|
cls.G_HandleData.clear()
|
||||||
|
|
||||||
|
|
||||||
class DrawPublic:
|
class DrawPublic:
|
||||||
@ -94,12 +95,14 @@ class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler):
|
|||||||
gpu.state.depth_test_set('ALWAYS')
|
gpu.state.depth_test_set('ALWAYS')
|
||||||
|
|
||||||
context = bpy.context
|
context = bpy.context
|
||||||
if self.simple_deform_public_poll(context):
|
if simple_update.timers_update_poll():
|
||||||
self.draw_3d(context)
|
is_switch_obj = change_active_object.is_change_active_object(False)
|
||||||
|
if self.simple_deform_public_poll(context) and not is_switch_obj:
|
||||||
|
self.draw_3d(context)
|
||||||
|
|
||||||
def draw_3d(self, context):
|
def draw_3d(self, context):
|
||||||
self.draw_scale_text(self.obj)
|
self.draw_scale_text(self.obj)
|
||||||
if not self.modifier_origin_angle_is_available:
|
if not self.modifier_origin_is_available:
|
||||||
self.draw_bound_box()
|
self.draw_bound_box()
|
||||||
elif self.simple_deform_show_gizmo_poll(context):
|
elif self.simple_deform_show_gizmo_poll(context):
|
||||||
# draw bound box
|
# draw bound box
|
||||||
@ -136,20 +139,21 @@ class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler):
|
|||||||
|
|
||||||
def draw_deform_mesh(self):
|
def draw_deform_mesh(self):
|
||||||
ob = self.obj
|
ob = self.obj
|
||||||
handler_dit = self.G_GizmoData
|
deform_data = self.G_DeformDrawData
|
||||||
active = self.modifier
|
active = self.modifier
|
||||||
# draw deform mesh
|
# draw deform mesh
|
||||||
if 'simple_deform_box_data' in handler_dit and self.pref.update_deform_wireframe:
|
if 'simple_deform_bound_data' in deform_data and self.pref.update_deform_wireframe:
|
||||||
pos, indices, mat, mod_data, limits = handler_dit['simple_deform_box_data']
|
modifiers = self.get_modifiers_parameter(self.modifier)
|
||||||
if ([getattr(active, i) for i in self.G_MODIFIERS_PROPERTY] == mod_data) and (
|
pos, indices, mat, mod_data, limits = deform_data['simple_deform_bound_data']
|
||||||
ob.matrix_world == mat) and limits == active.limits[:]:
|
is_limits = limits == active.limits[:]
|
||||||
self.draw_3d_shader(
|
is_mat = (ob.matrix_world == mat)
|
||||||
pos, indices, self.pref.deform_wireframe_color)
|
if modifiers == mod_data and is_mat and is_limits:
|
||||||
|
self.draw_3d_shader(pos, indices, self.pref.deform_wireframe_color)
|
||||||
|
|
||||||
def draw_scale_text(self, ob):
|
def draw_scale_text(self, ob):
|
||||||
scale_error = (ob.scale != Vector((1, 1, 1)))
|
scale_error = (ob.scale != Vector((1, 1, 1)))
|
||||||
if scale_error and ('scale_text' not in self.G_GizmoData):
|
if scale_error and ('scale_text' not in self.G_HandleData):
|
||||||
self.G_GizmoData['scale_text'] = bpy.types.SpaceView3D.draw_handler_add(
|
self.G_HandleData['scale_text'] = bpy.types.SpaceView3D.draw_handler_add(
|
||||||
self.draw_str, (), 'WINDOW', 'POST_PIXEL')
|
self.draw_str, (), 'WINDOW', 'POST_PIXEL')
|
||||||
elif not scale_error:
|
elif not scale_error:
|
||||||
self.del_handler_text()
|
self.del_handler_text()
|
||||||
|
@ -6,6 +6,7 @@ from bpy.types import (
|
|||||||
GizmoGroup,
|
GizmoGroup,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from ..update import change_active_modifier_parameter
|
||||||
from ..utils import GizmoUtils, GizmoGroupUtils
|
from ..utils import GizmoUtils, GizmoGroupUtils
|
||||||
|
|
||||||
|
|
||||||
@ -109,10 +110,13 @@ class AngleGizmo(Gizmo, AngleUpdate):
|
|||||||
return {'RUNNING_MODAL'}
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
def modal(self, context, event, tweak):
|
def modal(self, context, event, tweak):
|
||||||
self.clear_cache()
|
self.clear_point_cache()
|
||||||
|
|
||||||
self.update_prop_value(event, tweak)
|
self.update_prop_value(event, tweak)
|
||||||
|
self.update_deform_wireframe()
|
||||||
self.update_header_text(context)
|
self.update_header_text(context)
|
||||||
|
change_active_modifier_parameter.update_modifier_parameter()
|
||||||
|
self.tag_redraw(context)
|
||||||
return self.event_handle(event)
|
return self.event_handle(event)
|
||||||
|
|
||||||
def exit(self, context, cancel):
|
def exit(self, context, cancel):
|
||||||
|
@ -14,12 +14,7 @@ class CustomGizmo(Gizmo, GizmoUtils):
|
|||||||
custom_shape: dict
|
custom_shape: dict
|
||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
self.draw_type = 'None_GizmoGroup_'
|
self.init_setup()
|
||||||
if not hasattr(self, 'custom_shape'):
|
|
||||||
self.custom_shape = {}
|
|
||||||
for i in self.G_CustomShape:
|
|
||||||
self.custom_shape[i] = self.new_custom_shape(
|
|
||||||
'TRIS', self.G_CustomShape[i])
|
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
self.draw_custom_shape(self.custom_shape[self.draw_type])
|
self.draw_custom_shape(self.custom_shape[self.draw_type])
|
||||||
@ -29,6 +24,7 @@ class CustomGizmo(Gizmo, GizmoUtils):
|
|||||||
self.custom_shape[self.draw_type], select_id=select_id)
|
self.custom_shape[self.draw_type], select_id=select_id)
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
|
self.init_invoke(context, event)
|
||||||
return {'RUNNING_MODAL'}
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
def modal(self, context, event, tweak):
|
def modal(self, context, event, tweak):
|
||||||
|
@ -34,17 +34,18 @@ class SetDeformGizmoGroup(GizmoGroup, GizmoGroupUtils):
|
|||||||
setattr(self, f'deform_axis_{axis.lower()}', gizmo)
|
setattr(self, f'deform_axis_{axis.lower()}', gizmo)
|
||||||
|
|
||||||
def draw_prepare(self, context):
|
def draw_prepare(self, context):
|
||||||
|
bound = self.modifier_bound_co
|
||||||
if 'co' in self.G_GizmoData:
|
if bound:
|
||||||
obj = self.get_depsgraph(self.obj)
|
obj = self.get_depsgraph(self.obj)
|
||||||
dimensions = obj.dimensions
|
dimensions = obj.dimensions
|
||||||
|
|
||||||
def _mat(f):
|
def mat(f):
|
||||||
co = self.G_GizmoData['co'][0]
|
b = bound[0]
|
||||||
co = (co[0] + (max(dimensions) * f), co[1],
|
co = (b[0] + (max(dimensions) * f),
|
||||||
co[2] - (min(dimensions) * 0.3))
|
b[1],
|
||||||
|
b[2] - (min(dimensions) * 0.3))
|
||||||
return self.obj_matrix_world @ Vector(co)
|
return self.obj_matrix_world @ Vector(co)
|
||||||
|
|
||||||
self.deform_axis_x.matrix_basis.translation = _mat(0)
|
self.deform_axis_x.matrix_basis.translation = mat(0)
|
||||||
self.deform_axis_y.matrix_basis.translation = _mat(0.3)
|
self.deform_axis_y.matrix_basis.translation = mat(0.3)
|
||||||
self.deform_axis_z.matrix_basis.translation = _mat(0.6)
|
self.deform_axis_z.matrix_basis.translation = mat(0.6)
|
||||||
|
@ -6,6 +6,7 @@ from bpy.types import Gizmo, GizmoGroup
|
|||||||
from bpy_extras import view3d_utils
|
from bpy_extras import view3d_utils
|
||||||
from mathutils import Vector
|
from mathutils import Vector
|
||||||
|
|
||||||
|
from ..update import change_active_modifier_parameter
|
||||||
from ..utils import GizmoUtils, GizmoGroupUtils
|
from ..utils import GizmoUtils, GizmoGroupUtils
|
||||||
|
|
||||||
|
|
||||||
@ -201,22 +202,26 @@ 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_point_cache()
|
||||||
self.clear_cache()
|
|
||||||
|
|
||||||
if self.modifier_is_use_origin_axis:
|
if self.modifier_is_use_origin_axis:
|
||||||
self.new_origin_empty_object()
|
self.new_origin_empty_object()
|
||||||
|
# return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
self.difference_value = self.modifier_up_limits - self.modifier_down_limits
|
self.difference_value = self.modifier_up_limits - self.modifier_down_limits
|
||||||
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)
|
try:
|
||||||
self.clear_cache()
|
self.set_prop_value(event)
|
||||||
self.update_object_origin_matrix()
|
self.clear_point_cache()
|
||||||
# self.update_deform_wireframe(self.get_depsgraph(origin_object))
|
self.update_object_origin_matrix()
|
||||||
|
except Exception:
|
||||||
|
...
|
||||||
|
# return {'FINISHED'}
|
||||||
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)
|
change_active_modifier_parameter.update_modifier_parameter()
|
||||||
|
self.update_deform_wireframe()
|
||||||
return return_handle
|
return return_handle
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ class DeformAxisOperator(Operator, GizmoUtils):
|
|||||||
return {'RUNNING_MODAL'}
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
def modal(self, context, event):
|
def modal(self, context, event):
|
||||||
self.clear_cache()
|
self.clear_point_cache()
|
||||||
mod = context.object.modifiers.active
|
mod = context.object.modifiers.active
|
||||||
mod.deform_axis = self.Deform_Axis
|
mod.deform_axis = self.Deform_Axis
|
||||||
empty = self.new_origin_empty_object()
|
empty = self.new_origin_empty_object()
|
||||||
@ -39,7 +39,7 @@ class DeformAxisOperator(Operator, GizmoUtils):
|
|||||||
('max_z', self.Z_Value),
|
('max_z', self.Z_Value),
|
||||||
('min_z', self.Z_Value),
|
('min_z', self.Z_Value),
|
||||||
):
|
):
|
||||||
setattr(empty.constraints[self.G_CON_LIMIT_NAME], limit, value)
|
setattr(empty.constraints[self.G_NAME_CON_LIMIT], limit, value)
|
||||||
|
|
||||||
if ((not is_positive) and self.Is_Positive) or (is_positive and (not self.Is_Positive)):
|
if ((not is_positive) and self.Is_Positive) or (is_positive and (not self.Is_Positive)):
|
||||||
mod.angle = mod.angle * -1
|
mod.angle = mod.angle * -1
|
||||||
|
@ -41,7 +41,7 @@ class SimpleDeformHelperToolPanel(Panel, GizmoUtils):
|
|||||||
'show_set_axis_button',
|
'show_set_axis_button',
|
||||||
icon='EMPTY_AXIS',
|
icon='EMPTY_AXIS',
|
||||||
text='')
|
text='')
|
||||||
if mod.deform_method == 'BEND':
|
if pref.modifier_deform_method_is_bend:
|
||||||
layout.prop(pref,
|
layout.prop(pref,
|
||||||
'display_bend_axis_switch_gizmo',
|
'display_bend_axis_switch_gizmo',
|
||||||
toggle=1)
|
toggle=1)
|
||||||
|
@ -96,12 +96,10 @@ class SimpleDeformGizmoAddonPreferences(AddonPreferences, GizmoUtils):
|
|||||||
row.prop(mod, show_type)
|
row.prop(mod, show_type)
|
||||||
|
|
||||||
|
|
||||||
class SimpleDeformGizmoObjectPropertyGroup(PropertyGroup):
|
class SimpleDeformGizmoObjectPropertyGroup(PropertyGroup, GizmoUtils):
|
||||||
def _limits_up(self, context):
|
def _limits_up(self, context):
|
||||||
mod = context.object.modifiers
|
if self.active_modifier_is_simple_deform:
|
||||||
if mod and (mod.active.type == 'SIMPLE_DEFORM'):
|
self.modifier.limits[1] = self.up_limits
|
||||||
mod = mod.active
|
|
||||||
mod.limits[1] = self.up_limits
|
|
||||||
|
|
||||||
up_limits: FloatProperty(name='up',
|
up_limits: FloatProperty(name='up',
|
||||||
description='UP Limits(Red)',
|
description='UP Limits(Red)',
|
||||||
@ -111,10 +109,8 @@ class SimpleDeformGizmoObjectPropertyGroup(PropertyGroup):
|
|||||||
min=0)
|
min=0)
|
||||||
|
|
||||||
def _limits_down(self, context):
|
def _limits_down(self, context):
|
||||||
mod = context.object.modifiers
|
if self.active_modifier_is_simple_deform:
|
||||||
if mod and (mod.active.type == 'SIMPLE_DEFORM'):
|
self.modifier.limits[0] = self.down_limits
|
||||||
mod = mod.active
|
|
||||||
mod.limits[0] = self.down_limits
|
|
||||||
|
|
||||||
down_limits: FloatProperty(name='down',
|
down_limits: FloatProperty(name='down',
|
||||||
description='Lower limit(Green)',
|
description='Lower limit(Green)',
|
||||||
|
@ -1,78 +1,147 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
from functools import cache
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
|
||||||
from .utils import GizmoUpdate
|
from .utils import GizmoUpdate
|
||||||
|
|
||||||
|
gizmo = GizmoUpdate()
|
||||||
|
|
||||||
|
"""depsgraph_update_post cannot listen to users modifying modifier parameters
|
||||||
|
Use timers to watch and use cache
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class update_public:
|
class update_public:
|
||||||
_event_func_list = {}
|
_events_func_list = {}
|
||||||
update_func: 'function'
|
|
||||||
tmp_save_data = {}
|
|
||||||
run_time = 0.2
|
run_time = 0.2
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def timers_update_poll(cls) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@cache
|
||||||
|
def update_poll(cls) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _update_func_call_timer(cls):
|
||||||
|
if cls.timers_update_poll():
|
||||||
|
for c, func_list in cls._events_func_list.items():
|
||||||
|
if func_list and c.update_poll():
|
||||||
|
for func in func_list:
|
||||||
|
func()
|
||||||
|
cls.clear_cache_events()
|
||||||
|
return cls.run_time
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def clear_cache_events(cls):
|
||||||
|
for cl in cls._events_func_list.keys():
|
||||||
|
if getattr(cl, 'clear_cache', False):
|
||||||
|
cl.clear_cache()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def clear_cache(cls):
|
||||||
|
cls.update_poll.cache_clear()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def append(cls, item):
|
||||||
|
if cls not in cls._events_func_list:
|
||||||
|
cls._events_func_list[cls] = []
|
||||||
|
cls._events_func_list[cls].append(item)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def remove(cls, item):
|
||||||
|
if item in cls._events_func_list[cls]:
|
||||||
|
cls._events_func_list[cls].remove(item)
|
||||||
|
|
||||||
|
# --------------- reg and unreg
|
||||||
@classmethod
|
@classmethod
|
||||||
def register(cls):
|
def register(cls):
|
||||||
import bpy
|
from bpy.app import timers
|
||||||
bpy.app.timers.register(cls.update_func, persistent=True)
|
func = cls._update_func_call_timer
|
||||||
|
if not timers.is_registered(func):
|
||||||
|
timers.register(func, persistent=True)
|
||||||
|
else:
|
||||||
|
print('cls timers is registered', cls)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def unregister(cls):
|
def unregister(cls):
|
||||||
from bpy.app import timers
|
from bpy.app import timers
|
||||||
func = cls.update_func
|
func = cls._update_func_call_timer
|
||||||
if timers.is_registered(func):
|
if timers.is_registered(func):
|
||||||
timers.unregister(func)
|
timers.unregister(func)
|
||||||
|
else:
|
||||||
@classmethod
|
print('cls timers is not registered', cls)
|
||||||
def _update_call(cls):
|
cls._events_func_list.clear()
|
||||||
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):
|
class simple_update(update_public, GizmoUpdate):
|
||||||
tmp_save_data = {}
|
tmp_save_data = {}
|
||||||
from bpy.app.handlers import depsgraph_update_post, persistent
|
|
||||||
handler_type = depsgraph_update_post
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update_func(cls):
|
def timers_update_poll(cls):
|
||||||
import bpy
|
obj = bpy.context.object
|
||||||
name = bpy.context.object.name
|
if not cls.context_mode_is_object():
|
||||||
key = 'active_object'
|
...
|
||||||
if key not in cls.tmp_save_data or cls.tmp_save_data[key] != name:
|
elif not obj:
|
||||||
cls._update_call()
|
...
|
||||||
cls.tmp_save_data[key] = name
|
elif not cls.obj_type_is_mesh_or_lattice(obj):
|
||||||
return cls.run_time
|
...
|
||||||
|
elif cls.mod_is_simple_deform_type(obj.modifiers.active):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class change_active_simple_deform_modifier(update_public):
|
class change_active_object(simple_update):
|
||||||
|
@classmethod
|
||||||
|
@cache
|
||||||
|
def update_poll(cls):
|
||||||
|
return cls.is_change_active_object()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update_func(cls):
|
def is_change_active_object(cls, change_data=True):
|
||||||
import bpy
|
import bpy
|
||||||
obj = bpy.context.object
|
obj = bpy.context.object
|
||||||
if not obj or obj.type != 'MESH':
|
|
||||||
return cls.run_time
|
|
||||||
|
|
||||||
name = obj.name
|
name = obj.name
|
||||||
key = 'active_object'
|
key = 'active_object'
|
||||||
|
if key not in cls.tmp_save_data:
|
||||||
|
if change_data:
|
||||||
|
cls.tmp_save_data[key] = name
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif cls.tmp_save_data[key] != name:
|
||||||
|
if change_data:
|
||||||
|
cls.tmp_save_data[key] = name
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class change_active_simple_deform_modifier(simple_update):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@cache
|
||||||
|
def update_poll(cls):
|
||||||
|
return cls.is_change_active_simple_deform()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_change_active_simple_deform(cls) -> bool:
|
||||||
|
import bpy
|
||||||
|
obj = bpy.context.object
|
||||||
modifiers = cls.get_modifiers_data(obj)
|
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:
|
def update():
|
||||||
cls.tmp_save_data['modifiers'] = modifiers
|
cls.tmp_save_data['modifiers'] = modifiers
|
||||||
cls.tmp_save_data[key] = name
|
|
||||||
elif change_modifiers:
|
if change_active_object.update_poll():
|
||||||
cls.tmp_save_data['modifiers'] = modifiers
|
update()
|
||||||
cls._update_call()
|
elif 'modifiers' not in cls.tmp_save_data:
|
||||||
return cls.run_time
|
update()
|
||||||
|
elif cls.tmp_save_data['modifiers'] != modifiers:
|
||||||
|
update()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_modifiers_data(cls, obj):
|
def get_modifiers_data(cls, obj):
|
||||||
@ -81,20 +150,52 @@ class change_active_simple_deform_modifier(update_public):
|
|||||||
'modifiers': list(i.name for i in obj.modifiers)}
|
'modifiers': list(i.name for i in obj.modifiers)}
|
||||||
|
|
||||||
|
|
||||||
gizmo = GizmoUpdate()
|
class change_active_modifier_parameter(simple_update):
|
||||||
|
key = 'active_modifier_parameter'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@cache
|
||||||
|
def update_poll(cls):
|
||||||
|
return gizmo.active_modifier_is_simple_deform and cls.is_change_active_simple_parameter()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update_modifier_parameter(cls, modifier_parameter=None):
|
||||||
|
"""Run this function when the gizmo is updated to avoid duplicate updates
|
||||||
|
"""
|
||||||
|
if not modifier_parameter:
|
||||||
|
modifier_parameter = cls.get_modifiers_parameter(gizmo.modifier)
|
||||||
|
cls.tmp_save_data[cls.key] = modifier_parameter
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def change_modifier_parameter(cls) -> bool:
|
||||||
|
mod_data = cls.get_modifiers_parameter(gizmo.modifier)
|
||||||
|
return cls.key in cls.tmp_save_data and cls.tmp_save_data[cls.key] == mod_data
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_change_active_simple_parameter(cls):
|
||||||
|
parameter = cls.get_modifiers_parameter(gizmo.modifier)
|
||||||
|
if change_active_object.update_poll():
|
||||||
|
cls.update_modifier_parameter(parameter)
|
||||||
|
elif change_active_simple_deform_modifier.update_poll():
|
||||||
|
cls.update_modifier_parameter(parameter)
|
||||||
|
elif cls.key not in cls.tmp_save_data:
|
||||||
|
cls.update_modifier_parameter(parameter)
|
||||||
|
elif cls.tmp_save_data[cls.key] != parameter:
|
||||||
|
cls.update_modifier_parameter(parameter)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
change_active_object.register()
|
simple_update.register()
|
||||||
change_active_simple_deform_modifier.register()
|
|
||||||
|
|
||||||
change_active_object.append(gizmo.update_multiple_modifiers_data)
|
def p():
|
||||||
change_active_simple_deform_modifier.append(gizmo.update_multiple_modifiers_data)
|
gizmo.update_multiple_modifiers_data()
|
||||||
|
|
||||||
|
change_active_object.append(p)
|
||||||
|
change_active_modifier_parameter.append(p)
|
||||||
|
change_active_simple_deform_modifier.append(p)
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
change_active_object.remove(gizmo.update_multiple_modifiers_data)
|
simple_update.unregister()
|
||||||
change_active_simple_deform_modifier.remove(gizmo.update_multiple_modifiers_data)
|
|
||||||
|
|
||||||
change_active_object.unregister()
|
|
||||||
change_active_simple_deform_modifier.unregister()
|
|
||||||
|
@ -4,7 +4,6 @@ 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
|
||||||
@ -13,27 +12,38 @@ from mathutils import Vector, Matrix, Euler
|
|||||||
|
|
||||||
|
|
||||||
class PublicData:
|
class PublicData:
|
||||||
"""Public data class, all fixed data will be placed here
|
"""Public data class, where all fixed data will be placed
|
||||||
|
Classify each different type of data separately and cache it to avoid getting stuck due to excessive update frequency
|
||||||
"""
|
"""
|
||||||
G_CustomShape = {}
|
G_CustomShape = {} #
|
||||||
G_GizmoData = {}
|
G_HandleData = {} # Save draw Handle
|
||||||
G_Modifiers_Data = {}
|
|
||||||
|
G_DeformDrawData = {} # Save Deform Vertex And Indices,Update data only when updating deformation boxes
|
||||||
|
|
||||||
|
G_MultipleModifiersBoundData = {}
|
||||||
|
|
||||||
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),
|
||||||
(0, 4), (1, 5), (2, 6), (3, 7))
|
(0, 4), (1, 5), (2, 6), (3, 7)) # The order in which the 8 points of the bounding box are drawn
|
||||||
|
|
||||||
G_NAME = 'ViewSimpleDeformGizmo_' # Temporary use files prefix
|
G_NAME = 'ViewSimpleDeformGizmo_' # Temporary use files prefix
|
||||||
G_ADDON_NAME = basename(dirname(realpath(__file__))) # "simple_deform_helper"
|
|
||||||
G_CON_LIMIT_NAME = G_NAME + 'constraints_limit_rotation' # constraints name
|
|
||||||
|
|
||||||
G_MODIFIERS_PROPERTY = [ # copy modifier data
|
G_DEFORM_MESH_NAME = G_NAME + 'DeformMesh'
|
||||||
|
G_TMP_MULTIPLE_MODIFIERS_MESH = 'TMP_' + G_NAME + 'MultipleModifiersMesh'
|
||||||
|
G_SUB_LEVELS = 7
|
||||||
|
|
||||||
|
G_NAME_EMPTY_AXIS = G_NAME + '_Empty_'
|
||||||
|
G_NAME_CON_LIMIT = G_NAME + 'ConstraintsLimitRotation' # constraints name
|
||||||
|
G_NAME_CON_COPY_ROTATION = G_NAME + 'ConstraintsCopyRotation'
|
||||||
|
G_ADDON_NAME = basename(dirname(realpath(__file__))) # "simple_deform_helper"
|
||||||
|
|
||||||
|
G_MODIFIERS_PROPERTY = [ # Copy modifier data
|
||||||
'angle',
|
'angle',
|
||||||
'deform_axis',
|
'deform_axis',
|
||||||
'deform_method',
|
'deform_method',
|
||||||
'factor',
|
'factor',
|
||||||
'invert_vertex_group',
|
'invert_vertex_group',
|
||||||
'limits',
|
'limits', # bpy.types.bpy_prop_array
|
||||||
'lock_x',
|
'lock_x',
|
||||||
'lock_y',
|
'lock_y',
|
||||||
'lock_z',
|
'lock_z',
|
||||||
@ -72,10 +82,11 @@ class PublicData:
|
|||||||
def from_selected_obj_generate_json(cls):
|
def from_selected_obj_generate_json(cls):
|
||||||
"""Export selected object vertex data as gizmo custom paint data
|
"""Export selected object vertex data as gizmo custom paint data
|
||||||
The output file should be in the blender folder
|
The output file should be in the blender folder
|
||||||
|
gizmo.json
|
||||||
"""
|
"""
|
||||||
import json
|
import json
|
||||||
data = {}
|
data = {}
|
||||||
for obj in bpy.context.selected_object:
|
for obj in bpy.context.selected_objects:
|
||||||
data[obj.name] = cls.from_mesh_get_triangle_face_co(obj.data)
|
data[obj.name] = cls.from_mesh_get_triangle_face_co(obj.data)
|
||||||
print(data)
|
print(data)
|
||||||
with open('gizmo.json', 'w+') as f:
|
with open('gizmo.json', 'w+') as f:
|
||||||
@ -96,10 +107,14 @@ class PublicClass(PublicData):
|
|||||||
|
|
||||||
|
|
||||||
class PublicPoll(PublicClass):
|
class PublicPoll(PublicClass):
|
||||||
|
@classmethod
|
||||||
|
def context_mode_is_object(cls) -> bool:
|
||||||
|
return bpy.context.mode == 'OBJECT'
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def simple_deform_modifier_is_simple(cls, context):
|
def simple_deform_modifier_is_simple(cls, context):
|
||||||
"""
|
"""
|
||||||
Active Object in ('MESH', 'LATTICE')
|
Active Object in ('MESH', 'LATTICE')
|
||||||
Active Modifier Type Is 'SIMPLE_DEFORM' and show_viewport
|
Active Modifier Type Is 'SIMPLE_DEFORM' and show_viewport
|
||||||
:param context:bpy.types.Object
|
:param context:bpy.types.Object
|
||||||
:return:
|
:return:
|
||||||
@ -112,10 +127,9 @@ class PublicPoll(PublicClass):
|
|||||||
if not mod:
|
if not mod:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
available_obj_type = obj and (obj.type in ('MESH', 'LATTICE'))
|
available_obj_type = cls.obj_type_is_mesh_or_lattice(obj)
|
||||||
available_modifiers_type = mod and (mod.type == 'SIMPLE_DEFORM')
|
is_available_obj = cls.mod_is_simple_deform_type(mod) and available_obj_type
|
||||||
is_available_obj = available_modifiers_type and available_obj_type
|
is_obj_mode = cls.context_mode_is_object()
|
||||||
is_obj_mode = context.mode == 'OBJECT'
|
|
||||||
show_mod = mod.show_viewport
|
show_mod = mod.show_viewport
|
||||||
not_is_self_mesh = obj.name != cls.G_NAME
|
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
|
return is_available_obj and is_obj_mode and show_mod and not_is_self_mesh
|
||||||
@ -159,7 +173,51 @@ class PublicPoll(PublicClass):
|
|||||||
return poll and not_switch
|
return poll and not_switch
|
||||||
|
|
||||||
|
|
||||||
class PublicUtils(PublicPoll):
|
class PublicTranslate(PublicPoll):
|
||||||
|
@classmethod
|
||||||
|
def translate_text(cls, text):
|
||||||
|
return bpy.app.translations.pgettext(text)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def translate_header_text(cls, mode, value):
|
||||||
|
return cls.translate_text(mode) + ':{}'.format(value)
|
||||||
|
|
||||||
|
|
||||||
|
class GizmoClassMethod(PublicTranslate):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_depsgraph(cls, obj: 'bpy.types.Object'):
|
||||||
|
"""
|
||||||
|
@param obj: dep obj
|
||||||
|
@return: If there is no input obj, reverse the active object evaluated
|
||||||
|
"""
|
||||||
|
context = bpy.context
|
||||||
|
if obj is None:
|
||||||
|
obj = context.object
|
||||||
|
dep = context.evaluated_depsgraph_get()
|
||||||
|
return obj.evaluated_get(dep)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_vector_axis(cls, mod):
|
||||||
|
axis = mod.deform_axis
|
||||||
|
if 'BEND' == mod.deform_method:
|
||||||
|
vector_axis = Vector((0, 0, 1)) if axis in (
|
||||||
|
'Y', 'X') else Vector((1, 0, 0))
|
||||||
|
else:
|
||||||
|
vector = (Vector((1, 0, 0)) if (
|
||||||
|
axis == 'X') else Vector((0, 1, 0)))
|
||||||
|
vector_axis = Vector((0, 0, 1)) if (
|
||||||
|
axis == 'Z') else vector
|
||||||
|
return vector_axis
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_modifiers_parameter(cls, modifier):
|
||||||
|
prop = bpy.types.bpy_prop_array
|
||||||
|
return list(
|
||||||
|
getattr(modifier, i)[:] if type(getattr(modifier, i)) == prop else getattr(modifier, i)
|
||||||
|
for i in cls.G_MODIFIERS_PROPERTY
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def value_limit(cls, value, max_value=1, min_value=0):
|
def value_limit(cls, value, max_value=1, min_value=0):
|
||||||
"""
|
"""
|
||||||
@ -183,18 +241,6 @@ class PublicUtils(PublicPoll):
|
|||||||
"""
|
"""
|
||||||
return number == abs(number)
|
return number == abs(number)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_depsgraph(cls, obj: 'bpy.types.Object'):
|
|
||||||
"""
|
|
||||||
@param obj: dep obj
|
|
||||||
@return: If there is no input obj, reverse the active object evaluated
|
|
||||||
"""
|
|
||||||
context = bpy.context
|
|
||||||
if obj is None:
|
|
||||||
obj = context.object
|
|
||||||
dep = context.evaluated_depsgraph_get()
|
|
||||||
return obj.evaluated_get(dep)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def link_obj_to_active_collection(cls, obj: 'bpy.types.Object'):
|
def link_obj_to_active_collection(cls, obj: 'bpy.types.Object'):
|
||||||
context = bpy.context
|
context = bpy.context
|
||||||
@ -203,22 +249,6 @@ class PublicUtils(PublicPoll):
|
|||||||
objects.link(
|
objects.link(
|
||||||
obj)
|
obj)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def properties_is_modifier(cls) -> bool:
|
|
||||||
"""Returns whether there is a modifier property panel open in the active window.
|
|
||||||
If it is open, it returns to True else False
|
|
||||||
"""
|
|
||||||
for area in bpy.context.screen.areas:
|
|
||||||
if area.type == 'PROPERTIES':
|
|
||||||
for space in area.spaces:
|
|
||||||
if space.type == 'PROPERTIES' and space.context == 'MODIFIER':
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def bound_box_to_list(cls, obj: 'bpy.types.Object'):
|
|
||||||
return tuple(i[:] for i in obj.bound_box)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_mesh_max_min_co(cls, obj: 'bpy.context.object') -> '[Vector,Vector]':
|
def get_mesh_max_min_co(cls, obj: 'bpy.context.object') -> '[Vector,Vector]':
|
||||||
if obj.type == 'MESH':
|
if obj.type == 'MESH':
|
||||||
@ -271,36 +301,6 @@ class PublicUtils(PublicPoll):
|
|||||||
|
|
||||||
return list((aa + bb) / 2 for (aa, bb) in point_list)
|
return list((aa + bb) / 2 for (aa, bb) in point_list)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def translate_text(cls, text):
|
|
||||||
return bpy.app.translations.pgettext(text)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def translate_header_text(cls, mode, value):
|
|
||||||
return cls.translate_text(mode) + ':{}'.format(value)
|
|
||||||
|
|
||||||
|
|
||||||
class GizmoClassMethod(PublicUtils):
|
|
||||||
@classmethod
|
|
||||||
def get_vector_axis(cls, mod):
|
|
||||||
axis = mod.deform_axis
|
|
||||||
if 'BEND' == mod.deform_method:
|
|
||||||
vector_axis = Vector((0, 0, 1)) if axis in (
|
|
||||||
'Y', 'X') else Vector((1, 0, 0))
|
|
||||||
else:
|
|
||||||
vector = (Vector((1, 0, 0)) if (
|
|
||||||
axis == 'X') else Vector((0, 1, 0)))
|
|
||||||
vector_axis = Vector((0, 0, 1)) if (
|
|
||||||
axis == 'Z') else vector
|
|
||||||
return vector_axis
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_bound_co_data(cls):
|
|
||||||
if 'co' not in cls.G_GizmoData:
|
|
||||||
cls.G_GizmoData['co'] = cls.get_mesh_max_min_co(
|
|
||||||
bpy.context.object)
|
|
||||||
return cls.G_GizmoData['co']
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def tow_co_to_coordinate(cls, data):
|
def tow_co_to_coordinate(cls, data):
|
||||||
((min_x, min_y, min_z), (max_x, max_y, max_z)) = data
|
((min_x, min_y, min_z), (max_x, max_y, max_z)) = data
|
||||||
@ -315,6 +315,29 @@ class GizmoClassMethod(PublicUtils):
|
|||||||
Vector((min_x, max_y, max_z))
|
Vector((min_x, max_y, max_z))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def mod_is_simple_deform_type(cls, mod):
|
||||||
|
return mod and mod.type == 'SIMPLE_DEFORM'
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def obj_type_is_mesh_or_lattice(cls, obj: 'bpy.types.Object'):
|
||||||
|
return obj and (obj.type in ('MESH', 'LATTICE'))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_vertices_new_mesh(cls, name, vertices):
|
||||||
|
new_mesh = bpy.data.meshes.new(name)
|
||||||
|
new_mesh.from_pydata(vertices, cls.G_INDICES, [])
|
||||||
|
new_mesh.update()
|
||||||
|
return new_mesh
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def copy_modifier_parameter(cls, old_mod, new_mod):
|
||||||
|
for prop_name in cls.G_MODIFIERS_PROPERTY:
|
||||||
|
origin_value = getattr(old_mod, prop_name, None)
|
||||||
|
is_array_prop = type(origin_value) == bpy.types.bpy_prop_array
|
||||||
|
value = origin_value[:] if is_array_prop else origin_value
|
||||||
|
setattr(new_mod, prop_name, value)
|
||||||
|
|
||||||
|
|
||||||
class PublicProperty(GizmoClassMethod):
|
class PublicProperty(GizmoClassMethod):
|
||||||
|
|
||||||
@ -332,15 +355,17 @@ class PublicProperty(GizmoClassMethod):
|
|||||||
top, bottom, left, right, front, back = self.modifier_bound_box_pos
|
top, bottom, left, right, front, back = self.modifier_bound_box_pos
|
||||||
mod = self.modifier
|
mod = self.modifier
|
||||||
g_l = self.__from_up_down_point_get_limits_point
|
g_l = self.__from_up_down_point_get_limits_point
|
||||||
if self.modifier.origin:
|
origin = self.modifier.origin
|
||||||
|
if origin:
|
||||||
vector_axis = self.get_vector_axis(mod)
|
vector_axis = self.get_vector_axis(mod)
|
||||||
origin_mat = mod.origin.matrix_basis.to_3x3()
|
matrix = self.modifier.origin.matrix_local
|
||||||
axis_ = origin_mat @ vector_axis
|
origin_mat = matrix.to_3x3()
|
||||||
|
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__()):
|
||||||
i = point_lit[f][0]
|
i = point_lit[f][0]
|
||||||
j = point_lit[f][1]
|
j = point_lit[f][1]
|
||||||
angle = self.point_to_angle(i, j, f, axis_)
|
angle = self.point_to_angle(i, j, f, axis)
|
||||||
if abs(angle - 180) < 0.00001:
|
if abs(angle - 180) < 0.00001:
|
||||||
up_point, down_point = j, i
|
up_point, down_point = j, i
|
||||||
up_limits, down_limits = g_l(j, i)
|
up_limits, down_limits = g_l(j, i)
|
||||||
@ -349,7 +374,6 @@ class PublicProperty(GizmoClassMethod):
|
|||||||
up_point, down_point = i, j
|
up_point, down_point = i, j
|
||||||
up_limits, down_limits = g_l(i, j)
|
up_limits, down_limits = g_l(i, j)
|
||||||
point_lit[f][0], point_lit[f][1] = up_limits, down_limits
|
point_lit[f][0], point_lit[f][1] = up_limits, down_limits
|
||||||
|
|
||||||
[[top, bottom], [left, right], [front, back]] = point_lit
|
[[top, bottom], [left, right], [front, back]] = point_lit
|
||||||
else:
|
else:
|
||||||
axis = self.modifier_deform_axis
|
axis = self.modifier_deform_axis
|
||||||
@ -383,28 +407,31 @@ class PublicProperty(GizmoClassMethod):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def clear_cache(cls):
|
def clear_cache(cls):
|
||||||
cls._each_face_pos.cache_clear()
|
cls.clear_point_cache()
|
||||||
|
cls.clear_modifiers_data()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def clear_point_cache(cls):
|
||||||
cls._get_limits_point_and_bound_box_co.cache_clear()
|
cls._get_limits_point_and_bound_box_co.cache_clear()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def clear_data(cls):
|
def clear_modifiers_data(cls):
|
||||||
cls.G_GizmoData.clear()
|
cls.G_MultipleModifiersBoundData.clear()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def clear_modifiers_data(cls):
|
def clear_deform_data(cls):
|
||||||
cls.G_Modifiers_Data.clear()
|
cls.G_DeformDrawData.clear()
|
||||||
|
|
||||||
# --------------- Cache Data ----------------------
|
# --------------- Cache Data ----------------------
|
||||||
|
|
||||||
@property
|
|
||||||
def each_face_pos(self):
|
|
||||||
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):
|
||||||
return self.G_Modifiers_Data.get(self.modifier.name, self.get_bound_co_data())
|
def get_bound_co_data():
|
||||||
|
key = 'self.modifier.name'
|
||||||
|
if key not in self.G_MultipleModifiersBoundData:
|
||||||
|
self.G_MultipleModifiersBoundData[key] = self.get_mesh_max_min_co(self.obj)
|
||||||
|
return self.G_MultipleModifiersBoundData[key]
|
||||||
|
|
||||||
|
return self.G_MultipleModifiersBoundData.get(self.modifier.name, get_bound_co_data())
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def modifier_bound_box_pos(self):
|
def modifier_bound_box_pos(self):
|
||||||
@ -423,13 +450,12 @@ class PublicProperty(GizmoClassMethod):
|
|||||||
return self.matrix_calculation(self.obj_matrix_world, bound)
|
return self.matrix_calculation(self.obj_matrix_world, bound)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def modifier_origin_angle_is_available(self):
|
def modifier_origin_is_available(self):
|
||||||
try:
|
try:
|
||||||
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_point_cache()
|
||||||
self.clear_cache()
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# --------------- Compute Data ----------------------
|
# --------------- Compute Data ----------------------
|
||||||
@ -471,6 +497,11 @@ class PublicProperty(GizmoClassMethod):
|
|||||||
if self.active_modifier_is_simple_deform:
|
if self.active_modifier_is_simple_deform:
|
||||||
return self.modifier.deform_method in ('TWIST', 'BEND')
|
return self.modifier.deform_method in ('TWIST', 'BEND')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def modifier_deform_method_is_bend(self):
|
||||||
|
if self.active_modifier_is_simple_deform:
|
||||||
|
return self.modifier.deform_method == 'BEND'
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def modifier_up_limits(self):
|
def modifier_up_limits(self):
|
||||||
if self.modifier:
|
if self.modifier:
|
||||||
@ -483,7 +514,7 @@ class PublicProperty(GizmoClassMethod):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def active_modifier_is_simple_deform(self):
|
def active_modifier_is_simple_deform(self):
|
||||||
return self.modifier and self.modifier.type == 'SIMPLE_DEFORM'
|
return self.mod_is_simple_deform_type(self.modifier)
|
||||||
|
|
||||||
# ----- point
|
# ----- point
|
||||||
@property
|
@property
|
||||||
@ -526,7 +557,11 @@ 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' and not self.modifier.origin
|
return self.obj_origin_property_group.origin_mode != 'NOT'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def modifier_is_have_origin(self):
|
||||||
|
return self.modifier_is_use_origin_axis and self.modifier.origin
|
||||||
|
|
||||||
|
|
||||||
class GizmoUpdate(PublicProperty):
|
class GizmoUpdate(PublicProperty):
|
||||||
@ -543,7 +578,7 @@ class GizmoUpdate(PublicProperty):
|
|||||||
if origin.parent != obj:
|
if origin.parent != obj:
|
||||||
origin.parent = obj
|
origin.parent = obj
|
||||||
origin.rotation_euler.zero()
|
origin.rotation_euler.zero()
|
||||||
if not self.modifier_origin_angle_is_available:
|
if not self.modifier_origin_is_available:
|
||||||
origin.location.zero()
|
origin.location.zero()
|
||||||
origin.scale = 1, 1, 1
|
origin.scale = 1, 1, 1
|
||||||
|
|
||||||
@ -552,7 +587,7 @@ class GizmoUpdate(PublicProperty):
|
|||||||
obj = self.obj
|
obj = self.obj
|
||||||
origin = mod.origin
|
origin = mod.origin
|
||||||
if not origin:
|
if not origin:
|
||||||
new_name = self.G_NAME + '_Empty_' + str(uuid.uuid4())
|
new_name = self.G_NAME_EMPTY_AXIS + str(uuid.uuid4())
|
||||||
origin_object = bpy.data.objects.new(new_name, None)
|
origin_object = bpy.data.objects.new(new_name, None)
|
||||||
self.link_obj_to_active_collection(origin_object)
|
self.link_obj_to_active_collection(origin_object)
|
||||||
origin_object.hide_set(True)
|
origin_object.hide_set(True)
|
||||||
@ -564,7 +599,7 @@ class GizmoUpdate(PublicProperty):
|
|||||||
if origin_object == obj:
|
if origin_object == obj:
|
||||||
return
|
return
|
||||||
# add constraints
|
# add constraints
|
||||||
name = self.G_CON_LIMIT_NAME
|
name = self.G_NAME_CON_LIMIT
|
||||||
if origin_object.constraints.keys().__len__() > 2:
|
if origin_object.constraints.keys().__len__() > 2:
|
||||||
origin_object.constraints.clear()
|
origin_object.constraints.clear()
|
||||||
if name in origin_object.constraints.keys():
|
if name in origin_object.constraints.keys():
|
||||||
@ -579,7 +614,7 @@ class GizmoUpdate(PublicProperty):
|
|||||||
limit_constraints.use_limit_x = True
|
limit_constraints.use_limit_x = True
|
||||||
limit_constraints.use_limit_y = True
|
limit_constraints.use_limit_y = True
|
||||||
limit_constraints.use_limit_z = True
|
limit_constraints.use_limit_z = True
|
||||||
con_copy_name = self.G_NAME + 'constraints_copy_rotation'
|
con_copy_name = self.G_NAME_CON_COPY_ROTATION
|
||||||
if con_copy_name in origin_object.constraints.keys():
|
if con_copy_name in origin_object.constraints.keys():
|
||||||
copy_constraints = origin.constraints.get(con_copy_name)
|
copy_constraints = origin.constraints.get(con_copy_name)
|
||||||
else:
|
else:
|
||||||
@ -590,16 +625,15 @@ class GizmoUpdate(PublicProperty):
|
|||||||
copy_constraints.mix_mode = 'BEFORE'
|
copy_constraints.mix_mode = 'BEFORE'
|
||||||
copy_constraints.target_space = 'WORLD'
|
copy_constraints.target_space = 'WORLD'
|
||||||
copy_constraints.owner_space = 'WORLD'
|
copy_constraints.owner_space = 'WORLD'
|
||||||
|
origin_mode = self.obj.SimpleDeformGizmo_PropertyGroup.origin_mode
|
||||||
|
origin_object.SimpleDeformGizmo_PropertyGroup.origin_mode = origin_mode
|
||||||
self.fix_origin_parent_and_angle()
|
self.fix_origin_parent_and_angle()
|
||||||
return origin_object
|
return origin_object
|
||||||
|
|
||||||
def update_object_origin_matrix(self):
|
def update_object_origin_matrix(self):
|
||||||
st = time()
|
if self.modifier_is_have_origin:
|
||||||
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
|
|
||||||
|
|
||||||
if origin_object and is_use:
|
|
||||||
if origin_mode == 'UP_LIMITS':
|
if origin_mode == 'UP_LIMITS':
|
||||||
origin_object.matrix_world.translation = Vector(self.point_limits_up)
|
origin_object.matrix_world.translation = Vector(self.point_limits_up)
|
||||||
elif origin_mode == 'DOWN_LIMITS':
|
elif origin_mode == 'DOWN_LIMITS':
|
||||||
@ -610,80 +644,106 @@ 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
|
||||||
context = bpy.context
|
context = bpy.context
|
||||||
if obj.type not in ('MESH', 'LATTICE') or not self.simple_deform_public_poll(context):
|
if not self.obj_type_is_mesh_or_lattice(obj) or not self.simple_deform_modifier_is_simple(context):
|
||||||
return
|
return
|
||||||
self.clear_cache()
|
self.clear_point_cache()
|
||||||
|
self.clear_modifiers_data()
|
||||||
data = bpy.data
|
data = bpy.data
|
||||||
name = self.G_NAME
|
name = self.G_TMP_MULTIPLE_MODIFIERS_MESH
|
||||||
origin_object = data.objects.get(name)
|
|
||||||
|
|
||||||
# update multiple simple_deform bound data
|
# del old tmp object
|
||||||
if origin_object:
|
old_object = data.objects.get(name)
|
||||||
data.objects.remove(origin_object)
|
if old_object:
|
||||||
|
data.objects.remove(old_object)
|
||||||
|
|
||||||
if data.meshes.get(name):
|
if data.meshes.get(name):
|
||||||
data.meshes.remove(data.meshes.get(name))
|
data.meshes.remove(data.meshes.get(name))
|
||||||
|
|
||||||
vertices = self.tow_co_to_coordinate(self.get_bound_co_data())
|
"""get origin mesh bound box as multiple basic mesh
|
||||||
new_mesh = data.meshes.new(name)
|
add multiple modifiers and get depsgraph obj bound box
|
||||||
new_mesh.from_pydata(vertices, self.G_INDICES, [])
|
"""
|
||||||
new_mesh.update()
|
vertices = self.tow_co_to_coordinate(self.get_mesh_max_min_co(self.obj))
|
||||||
deform_obj = data.objects.new(name, new_mesh)
|
new_mesh = self.from_vertices_new_mesh(name, vertices)
|
||||||
|
modifiers_obj = data.objects.new(name, new_mesh)
|
||||||
|
|
||||||
self.link_obj_to_active_collection(deform_obj)
|
self.link_obj_to_active_collection(modifiers_obj)
|
||||||
if deform_obj == obj:
|
if modifiers_obj == obj: # is cycles
|
||||||
return
|
return
|
||||||
if deform_obj.parent != obj:
|
if modifiers_obj.parent != obj:
|
||||||
deform_obj.parent = obj
|
modifiers_obj.parent = obj
|
||||||
|
|
||||||
deform_obj.modifiers.clear()
|
modifiers_obj.modifiers.clear()
|
||||||
subdivision = deform_obj.modifiers.new('1', 'SUBSURF')
|
subdivision = modifiers_obj.modifiers.new('1', 'SUBSURF')
|
||||||
subdivision.levels = 7
|
subdivision.levels = self.G_SUB_LEVELS
|
||||||
self.G_GizmoData['co'] = self.get_bound_co_data()
|
|
||||||
|
|
||||||
for mo in context.object.modifiers:
|
for mod in context.object.modifiers:
|
||||||
if mo.type == 'SIMPLE_DEFORM':
|
if self.mod_is_simple_deform_type(mod):
|
||||||
obj = self.get_depsgraph(deform_obj)
|
dep_bound_tow_co = self.get_mesh_max_min_co(self.get_depsgraph(modifiers_obj))
|
||||||
self.G_Modifiers_Data[mo.name] = self.get_mesh_max_min_co(obj)
|
self.G_MultipleModifiersBoundData[mod.name] = dep_bound_tow_co
|
||||||
simple_deform = deform_obj.modifiers.new(
|
new_mod = modifiers_obj.modifiers.new(mod.name, 'SIMPLE_DEFORM')
|
||||||
mo.name, 'SIMPLE_DEFORM')
|
self.copy_modifier_parameter(mod, new_mod)
|
||||||
simple_deform.deform_method = mo.deform_method
|
data.objects.remove(modifiers_obj)
|
||||||
simple_deform.deform_axis = mo.deform_axis
|
|
||||||
simple_deform.lock_x = mo.lock_x
|
|
||||||
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.angle = mo.angle
|
|
||||||
simple_deform.show_viewport = mo.show_viewport
|
|
||||||
deform_obj.hide_select = True
|
|
||||||
deform_obj.hide_set(True)
|
|
||||||
deform_obj.hide_viewport = False
|
|
||||||
deform_obj.hide_render = True
|
|
||||||
deform_obj.hide_viewport = True
|
|
||||||
deform_obj.hide_set(True)
|
|
||||||
print('multiple_modifiers', time() - st)
|
|
||||||
|
|
||||||
def update_deform_wireframe(self, obj):
|
def update_deform_wireframe(self):
|
||||||
if not self.pref.update_deform_wireframe:
|
if not self.pref.update_deform_wireframe:
|
||||||
return
|
return
|
||||||
|
# obj = self.obj
|
||||||
|
name = self.modifier.name
|
||||||
|
deform_name = self.G_DEFORM_MESH_NAME
|
||||||
|
|
||||||
|
co = self.G_MultipleModifiersBoundData[name]
|
||||||
|
|
||||||
|
deform_obj = bpy.data.objects.get(deform_name, None)
|
||||||
|
|
||||||
|
if not deform_obj:
|
||||||
|
a, b = 0.5, -0.5
|
||||||
|
vertices = self.tow_co_to_coordinate(((b, b, b), (a, a, a)))
|
||||||
|
new_mesh = self.from_vertices_new_mesh(name, vertices)
|
||||||
|
deform_obj = bpy.data.objects.new(deform_name, new_mesh)
|
||||||
|
deform_obj.hide_select = True
|
||||||
|
# deform_obj.hide_set(True)
|
||||||
|
deform_obj.hide_render = True
|
||||||
|
deform_obj.hide_viewport = True
|
||||||
|
|
||||||
|
self.link_obj_to_active_collection(deform_obj)
|
||||||
|
|
||||||
|
deform_obj.parent = self.obj
|
||||||
|
|
||||||
|
tmv = deform_obj.hide_viewport
|
||||||
|
tmh = deform_obj.hide_get()
|
||||||
|
deform_obj.hide_viewport = False
|
||||||
|
deform_obj.hide_set(False)
|
||||||
|
|
||||||
|
# Update Matrix
|
||||||
|
deform_obj.matrix_world = Matrix()
|
||||||
|
center = (co[0] + co[1]) / 2
|
||||||
|
scale = co[1] - co[0]
|
||||||
|
deform_obj.matrix_world = self.obj_matrix_world @ deform_obj.matrix_world
|
||||||
|
deform_obj.location = center
|
||||||
|
deform_obj.scale = scale
|
||||||
|
|
||||||
|
# Update Modifier data
|
||||||
|
mods = deform_obj.modifiers
|
||||||
|
mods.clear()
|
||||||
|
subdivision = mods.new('1', 'SUBSURF')
|
||||||
|
subdivision.levels = self.G_SUB_LEVELS
|
||||||
|
|
||||||
|
new_mod = mods.new(name, 'SIMPLE_DEFORM')
|
||||||
|
self.copy_modifier_parameter(self.modifier, new_mod)
|
||||||
|
|
||||||
|
# Get vertices data
|
||||||
context = bpy.context
|
context = bpy.context
|
||||||
matrix = self.obj_matrix_world.copy()
|
obj = self.get_depsgraph(deform_obj)
|
||||||
|
matrix = deform_obj.matrix_world.copy()
|
||||||
ver_len = obj.data.vertices.__len__()
|
ver_len = obj.data.vertices.__len__()
|
||||||
edge_len = obj.data.edges.__len__()
|
edge_len = obj.data.edges.__len__()
|
||||||
|
if 'numpy_data' not in self.G_DeformDrawData:
|
||||||
if 'numpy_data' not in self.G_GizmoData:
|
self.G_DeformDrawData['numpy_data'] = {}
|
||||||
self.G_GizmoData['numpy_data'] = {}
|
numpy_data = self.G_DeformDrawData['numpy_data']
|
||||||
|
|
||||||
numpy_data = self.G_GizmoData['numpy_data']
|
|
||||||
key = (ver_len, edge_len)
|
key = (ver_len, edge_len)
|
||||||
if key in numpy_data:
|
if key in numpy_data:
|
||||||
list_edges, list_vertices = numpy_data[key]
|
list_edges, list_vertices = numpy_data[key]
|
||||||
@ -702,10 +762,13 @@ class GizmoUpdate(PublicProperty):
|
|||||||
obj.data.edges.foreach_get('vertices', list_edges)
|
obj.data.edges.foreach_get('vertices', list_edges)
|
||||||
indices = list_edges.reshape((edge_len, 2))
|
indices = list_edges.reshape((edge_len, 2))
|
||||||
|
|
||||||
|
modifiers = self.get_modifiers_parameter(self.modifier)
|
||||||
limits = context.object.modifiers.active.limits[:]
|
limits = context.object.modifiers.active.limits[:]
|
||||||
modifiers = [getattr(context.object.modifiers.active, i)
|
|
||||||
for i in self.G_MODIFIERS_PROPERTY]
|
deform_obj.hide_viewport = tmv
|
||||||
self.G_GizmoData['simple_deform_box_data'] = (ver, indices, matrix, modifiers, limits[:])
|
deform_obj.hide_set(tmh)
|
||||||
|
|
||||||
|
self.G_DeformDrawData['simple_deform_bound_data'] = (ver, indices, self.obj_matrix_world, modifiers, limits[:])
|
||||||
|
|
||||||
|
|
||||||
class GizmoUtils(GizmoUpdate):
|
class GizmoUtils(GizmoUpdate):
|
||||||
@ -748,16 +811,16 @@ class GizmoUtils(GizmoUpdate):
|
|||||||
|
|
||||||
def __update_matrix_func(self, context):
|
def __update_matrix_func(self, context):
|
||||||
func = getattr(self, 'update_gizmo_matrix', None)
|
func = getattr(self, 'update_gizmo_matrix', None)
|
||||||
if func and self.modifier_origin_angle_is_available:
|
if func and self.modifier_origin_is_available:
|
||||||
func(context)
|
func(context)
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
if self.modifier_origin_angle_is_available:
|
if self.modifier_origin_is_available:
|
||||||
self.draw_custom_shape(self.custom_shape[self.draw_type])
|
self.draw_custom_shape(self.custom_shape[self.draw_type])
|
||||||
self.__update_matrix_func(context)
|
self.__update_matrix_func(context)
|
||||||
|
|
||||||
def draw_select(self, context, select_id):
|
def draw_select(self, context, select_id):
|
||||||
if self.modifier_origin_angle_is_available:
|
if self.modifier_origin_is_available:
|
||||||
self.draw_custom_shape(
|
self.draw_custom_shape(
|
||||||
self.custom_shape[self.draw_type], select_id=select_id)
|
self.custom_shape[self.draw_type], select_id=select_id)
|
||||||
self.__update_matrix_func(context)
|
self.__update_matrix_func(context)
|
||||||
@ -788,6 +851,11 @@ class GizmoUtils(GizmoUpdate):
|
|||||||
self.pref.update_deform_wireframe = self.pref.update_deform_wireframe ^ True
|
self.pref.update_deform_wireframe = self.pref.update_deform_wireframe ^ True
|
||||||
return {'RUNNING_MODAL'}
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def tag_redraw(context):
|
||||||
|
if context.area:
|
||||||
|
context.area.tag_redraw()
|
||||||
|
|
||||||
|
|
||||||
class GizmoGroupUtils(GizmoUtils):
|
class GizmoGroupUtils(GizmoUtils):
|
||||||
bl_space_type = 'VIEW_3D'
|
bl_space_type = 'VIEW_3D'
|
||||||
@ -848,6 +916,22 @@ class Tmp:
|
|||||||
rot = rot.to_matrix()
|
rot = rot.to_matrix()
|
||||||
self.matrix_basis = self.matrix_basis @ rot.to_4x4()
|
self.matrix_basis = self.matrix_basis @ rot.to_4x4()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def bound_box_to_list(cls, obj: 'bpy.types.Object'):
|
||||||
|
return tuple(i[:] for i in obj.bound_box)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def properties_is_modifier(cls) -> bool:
|
||||||
|
"""Returns whether there is a modifier property panel open in the active window.
|
||||||
|
If it is open, it returns to True else False
|
||||||
|
"""
|
||||||
|
for area in bpy.context.screen.areas:
|
||||||
|
if area.type == 'PROPERTIES':
|
||||||
|
for space in area.spaces:
|
||||||
|
if space.type == 'PROPERTIES' and space.context == 'MODIFIER':
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
PublicData.load_gizmo_data()
|
PublicData.load_gizmo_data()
|
||||||
|
Loading…
Reference in New Issue
Block a user