new addon simple_deform_helper #104464
@ -1,5 +1,13 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
from . import gizmo, operators, preferences, data, update, translate, panel
|
||||
from . import (
|
||||
panel, #
|
||||
gizmo,
|
||||
utils,
|
||||
update,
|
||||
translate,
|
||||
operators,
|
||||
preferences,
|
||||
)
|
||||
|
||||
bl_info = {
|
||||
"name": "SimpleDeformHelper",
|
||||
@ -12,9 +20,59 @@ bl_info = {
|
||||
"category": "3D View"
|
||||
}
|
||||
|
||||
"""
|
||||
# -------------------------
|
||||
__init__.py:
|
||||
Register All Module
|
||||
|
||||
gizmo/__init__.py:
|
||||
Register All Gizmo
|
||||
|
||||
/angle_and_factor.py:
|
||||
Ctrl Modifier Angle
|
||||
|
||||
/bend_axis.py:
|
||||
Bend Method Switch Direction Gizmo
|
||||
|
||||
/set_deform_axis.py:
|
||||
Three Switch Deform Axis Operator Gizmo
|
||||
|
||||
/up_down_limits_point.py:
|
||||
Main control part
|
||||
use utils.py PublicProperty._get_limits_point_and_bound_box_co
|
||||
Obtain and calculate boundary box and limit point data
|
||||
|
||||
|
||||
draw.py:
|
||||
Draw 3D Bound And Line
|
||||
|
||||
gizmo.json:
|
||||
Draw Custom Shape Vertex Data
|
||||
|
||||
operator.py:
|
||||
Set Deform Axis Operator
|
||||
|
||||
panel.py:
|
||||
Draw Gizmo Tool Property in Options and Tool Settings Right
|
||||
|
||||
preferences.py:
|
||||
Addon Preferences
|
||||
|
||||
translate.py:
|
||||
temporary only Cn translate
|
||||
|
||||
update.py:
|
||||
In Change Depsgraph When Update Addon Data And Del Redundant Empty
|
||||
|
||||
utils.py:
|
||||
Main documents used
|
||||
Most computing operations are placed in classes GizmoUtils
|
||||
# -------------------------
|
||||
"""
|
||||
module_tuple = (
|
||||
panel,
|
||||
gizmo,
|
||||
utils,
|
||||
update,
|
||||
translate,
|
||||
operators,
|
||||
@ -23,8 +81,6 @@ module_tuple = (
|
||||
|
||||
|
||||
def register():
|
||||
data.Data.load_gizmo_data()
|
||||
|
||||
for item in module_tuple:
|
||||
item.register()
|
||||
|
||||
|
@ -1,58 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
from os.path import dirname, basename, realpath
|
||||
|
||||
G_MODIFIERS_PROPERTY = [ # copy modifier data
|
||||
'angle',
|
||||
'deform_axis',
|
||||
'deform_method',
|
||||
'factor',
|
||||
'invert_vertex_group',
|
||||
'limits',
|
||||
'lock_x',
|
||||
'lock_y',
|
||||
'lock_z',
|
||||
'origin',
|
||||
'show_expanded',
|
||||
'show_in_editmode',
|
||||
'vertex_group',
|
||||
]
|
||||
|
||||
G_INDICES = (
|
||||
(0, 1), (0, 2), (1, 3), (2, 3),
|
||||
(4, 5), (4, 6), (5, 7), (6, 7),
|
||||
(0, 4), (1, 5), (2, 6), (3, 7))
|
||||
|
||||
G_NAME = 'ViewSimpleDeformGizmo_' # Temporary use files
|
||||
G_CON_LIMIT_NAME = G_NAME + 'constraints_limit_rotation' # 约束名称
|
||||
G_ADDON_NAME = basename(dirname(realpath(__file__))) # "simple_deform_helper"
|
||||
|
||||
|
||||
class Data:
|
||||
G_GizmoCustomShapeDict = {}
|
||||
G_SimpleDeformGizmoHandlerDit = {}
|
||||
|
||||
@classmethod
|
||||
def load_gizmo_data(cls) -> None:
|
||||
import json
|
||||
import os
|
||||
json_path = os.path.join(os.path.dirname(__file__), "gizmo.json")
|
||||
with open(json_path, "r") as file:
|
||||
cls.G_GizmoCustomShapeDict = json.load(file)
|
||||
|
||||
@staticmethod
|
||||
def from_bmesh_get_triangle_face_co(mesh: 'bpy.types.Mesh') -> list:
|
||||
"""
|
||||
:param mesh: 输入一个网格数据
|
||||
:type mesh: bpy.data.meshes
|
||||
:return list: 反回顶点列表[[co1,co2,co3],[co1,co2,co3]...]
|
||||
"""
|
||||
import bmesh
|
||||
|
||||
bm = bmesh.new()
|
||||
bm.from_mesh(mesh)
|
||||
bm.faces.ensure_lookup_table()
|
||||
bm.verts.ensure_lookup_table()
|
||||
bmesh.ops.triangulate(bm, faces=bm.faces)
|
||||
co_list = [list(float(format(j, ".4f")) for j in vert.co) for face in bm.faces for vert in face.verts]
|
||||
bm.free()
|
||||
return co_list
|
@ -5,62 +5,44 @@ import gpu
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
from mathutils import Vector
|
||||
|
||||
from .data import G_INDICES, G_MODIFIERS_PROPERTY, G_NAME, Data
|
||||
from .utils import PublicClass, Utils
|
||||
from .update import change_active_object, simple_update
|
||||
from .utils import GizmoUtils
|
||||
|
||||
|
||||
class Handler(Data):
|
||||
class Handler:
|
||||
@classmethod
|
||||
def add_handler(cls):
|
||||
"""向3d视图添加绘制handler
|
||||
并将其存储下来
|
||||
"""
|
||||
if 'handler' not in cls.G_SimpleDeformGizmoHandlerDit:
|
||||
cls.G_SimpleDeformGizmoHandlerDit['handler'] = bpy.types.SpaceView3D.draw_handler_add(
|
||||
Draw3D.draw_bound_box, (), 'WINDOW', 'POST_VIEW')
|
||||
if 'handler' not in cls.G_HandleData:
|
||||
cls.G_HandleData['handler'] = bpy.types.SpaceView3D.draw_handler_add(
|
||||
Draw3D().draw, (), 'WINDOW', 'POST_VIEW')
|
||||
|
||||
@classmethod
|
||||
def del_handler_text(cls):
|
||||
|
||||
if 'handler_text' in cls.G_SimpleDeformGizmoHandlerDit:
|
||||
if 'scale_text' in cls.G_HandleData:
|
||||
bpy.types.SpaceView3D.draw_handler_remove(
|
||||
cls.G_SimpleDeformGizmoHandlerDit['handler_text'], 'WINDOW')
|
||||
cls.G_SimpleDeformGizmoHandlerDit.pop('handler_text')
|
||||
cls.G_HandleData['scale_text'], 'WINDOW')
|
||||
cls.G_HandleData.pop('scale_text')
|
||||
|
||||
@classmethod
|
||||
def del_handler(cls):
|
||||
data = bpy.data
|
||||
if data.meshes.get(G_NAME):
|
||||
data.meshes.remove(data.meshes.get(G_NAME))
|
||||
if data.meshes.get(cls.G_NAME):
|
||||
data.meshes.remove(data.meshes.get(cls.G_NAME))
|
||||
|
||||
if data.objects.get(G_NAME):
|
||||
data.objects.remove(data.objects.get(G_NAME))
|
||||
if data.objects.get(cls.G_NAME):
|
||||
data.objects.remove(data.objects.get(cls.G_NAME))
|
||||
|
||||
cls.del_handler_text()
|
||||
|
||||
if 'handler' in cls.G_SimpleDeformGizmoHandlerDit:
|
||||
if 'handler' in cls.G_HandleData:
|
||||
bpy.types.SpaceView3D.draw_handler_remove(
|
||||
cls.G_SimpleDeformGizmoHandlerDit['handler'], 'WINDOW')
|
||||
cls.G_SimpleDeformGizmoHandlerDit.clear()
|
||||
cls.G_HandleData['handler'], 'WINDOW')
|
||||
cls.G_HandleData.clear()
|
||||
|
||||
|
||||
class Draw3D(PublicClass, Data):
|
||||
|
||||
class DrawPublic:
|
||||
@classmethod
|
||||
def draw_3d_shader(cls, pos, indices, color=None, *, shader_name='3D_UNIFORM_COLOR', draw_type='LINES'):
|
||||
"""
|
||||
:param draw_type:
|
||||
:param shader_name:
|
||||
:param color:
|
||||
:param indices:
|
||||
:param pos:
|
||||
:type pos:list ((0,0,0),(1,1,1))
|
||||
2D_FLAT_COLOR - 2D_IMAGE - 2D_SMOOTH_COLOR - 2D_UNIFORM_COLOR - 3D_FLAT_COLOR - 3D_SMOOTH_COLOR - 3D_UNIFORM_COLOR - 3D_POLYLINE_FLAT_COLOR - 3D_POLYLINE_SMOOTH_COLOR - 3D_POLYLINE_UNIFORM_COLOR
|
||||
('POINTS', 'LINES', 'TRIS', 'LINE_STRIP', 'LINE_LOOP','TRI_STRIP',
|
||||
'TRI_FAN', 'LINES_ADJ', 'TRIS_ADJ', 'LINE_STRIP_ADJ')
|
||||
`NONE`, `ALWAYS`, `LESS`, `LESS_EQUAL`, `EQUAL`, `GREATER` and `GREATER_EQUAL`
|
||||
"""
|
||||
|
||||
shader = gpu.shader.from_builtin(shader_name)
|
||||
if draw_type == 'POINTS':
|
||||
batch = batch_for_shader(shader, draw_type, {'pos': pos})
|
||||
@ -74,6 +56,8 @@ class Draw3D(PublicClass, Data):
|
||||
|
||||
batch.draw(shader)
|
||||
|
||||
|
||||
class DrawText:
|
||||
font_info = {
|
||||
'font_id': 0,
|
||||
'handler': None,
|
||||
@ -92,8 +76,6 @@ class Draw3D(PublicClass, Data):
|
||||
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)):
|
||||
Handler.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):
|
||||
@ -102,99 +84,10 @@ class Draw3D(PublicClass, Data):
|
||||
blf.draw(font_id, text)
|
||||
blf.color(font_id, *color)
|
||||
|
||||
@classmethod
|
||||
def draw_box(cls, data, mat):
|
||||
pref = cls.pref_()
|
||||
coords = Utils.matrix_calculation(mat,
|
||||
cls.data_to_calculation(data))
|
||||
cls.draw_3d_shader(coords, G_INDICES, pref.bound_box_color)
|
||||
|
||||
@classmethod
|
||||
def data_to_calculation(cls, data):
|
||||
((min_x, min_y, min_z), (max_x, max_y, max_z)) = data
|
||||
return (
|
||||
(max_x, min_y, min_z),
|
||||
(min_x, min_y, min_z),
|
||||
(max_x, max_y, min_z),
|
||||
(min_x, max_y, min_z),
|
||||
(max_x, min_y, max_z),
|
||||
(min_x, min_y, max_z),
|
||||
(max_x, max_y, max_z),
|
||||
(min_x, max_y, max_z))
|
||||
class Draw3D(GizmoUtils, DrawPublic, DrawText, Handler):
|
||||
|
||||
@classmethod
|
||||
def draw_limits_bound_box(cls):
|
||||
|
||||
pref = cls.pref_()
|
||||
handler_dit = cls.G_SimpleDeformGizmoHandlerDit
|
||||
if 'draw_limits_bound_box' in handler_dit:
|
||||
# draw limits_bound_box
|
||||
mat, data = handler_dit['draw_limits_bound_box']
|
||||
coords = Utils.matrix_calculation(mat, cls.data_to_calculation(data))
|
||||
cls.draw_3d_shader(coords,
|
||||
G_INDICES,
|
||||
pref.limits_bound_box_color)
|
||||
|
||||
@classmethod
|
||||
def draw_limits_line(cls):
|
||||
handler_dit = cls.G_SimpleDeformGizmoHandlerDit
|
||||
if 'draw_line' in handler_dit:
|
||||
line_pos, limits_pos, = handler_dit['draw_line']
|
||||
# draw limits line
|
||||
cls.draw_3d_shader(limits_pos, ((1, 0),), (1, 1, 0, 0.5))
|
||||
# draw line
|
||||
cls.draw_3d_shader(line_pos, ((1, 0),), (1, 1, 0, 0.3))
|
||||
# draw pos
|
||||
cls.draw_3d_shader([line_pos[1]], (), (0, 1, 0, 0.5),
|
||||
shader_name='3D_UNIFORM_COLOR', draw_type='POINTS')
|
||||
|
||||
@classmethod
|
||||
def draw_deform_mesh(cls, ob, context):
|
||||
pref = cls.pref_()
|
||||
handler_dit = cls.G_SimpleDeformGizmoHandlerDit
|
||||
active = context.object.modifiers.active
|
||||
# draw deform mesh
|
||||
if 'draw' in handler_dit:
|
||||
pos, indices, mat, mod_data, limits = handler_dit['draw']
|
||||
if ([getattr(active, i) for i in G_MODIFIERS_PROPERTY] == mod_data) and (
|
||||
ob.matrix_world == mat) and limits == active.limits[:]:
|
||||
cls.draw_3d_shader(
|
||||
pos, indices, pref.deform_wireframe_color)
|
||||
|
||||
@classmethod
|
||||
def draw_scale_text(cls, ob):
|
||||
if (ob.scale != Vector((1, 1, 1))) and ('handler_text' not in cls.G_SimpleDeformGizmoHandlerDit):
|
||||
cls.G_SimpleDeformGizmoHandlerDit['handler_text'] = bpy.types.SpaceView3D.draw_handler_add(
|
||||
cls.draw_str, (), 'WINDOW', 'POST_PIXEL')
|
||||
|
||||
@classmethod
|
||||
def is_draw_box(cls, context):
|
||||
obj = context.object # 活动物体
|
||||
matrix = obj.matrix_world # 活动物体矩阵
|
||||
modifier = context.object.modifiers.active # 活动修改器
|
||||
|
||||
pref = cls.pref_()
|
||||
simple_poll = Utils.simple_deform_poll(context)
|
||||
bend = modifier and (modifier.deform_method == 'BEND')
|
||||
display_switch_axis = not pref.display_bend_axis_switch_gizmo
|
||||
|
||||
cls.draw_scale_text(obj)
|
||||
Utils.update_co_data(obj, modifier)
|
||||
|
||||
co_data = Utils.generate_co_data()
|
||||
|
||||
if simple_poll and ((not bend) or display_switch_axis):
|
||||
# draw bound box
|
||||
cls.draw_box(co_data, matrix)
|
||||
cls.draw_deform_mesh(obj, context)
|
||||
cls.draw_limits_line()
|
||||
cls.draw_limits_bound_box()
|
||||
elif simple_poll and (bend and not display_switch_axis):
|
||||
cls.draw_box(co_data, matrix)
|
||||
Utils.new_empty(obj, modifier)
|
||||
|
||||
@classmethod
|
||||
def draw_bound_box(cls):
|
||||
def draw(self):
|
||||
gpu.state.blend_set('ALPHA')
|
||||
gpu.state.line_width_set(1)
|
||||
|
||||
@ -202,7 +95,68 @@ class Draw3D(PublicClass, Data):
|
||||
gpu.state.depth_test_set('ALWAYS')
|
||||
|
||||
context = bpy.context
|
||||
if Utils.simple_deform_poll(context):
|
||||
cls.is_draw_box(context)
|
||||
else:
|
||||
Handler.del_handler()
|
||||
if simple_update.timers_update_poll():
|
||||
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):
|
||||
self.draw_scale_text(self.obj)
|
||||
if not self.modifier_origin_is_available:
|
||||
self.draw_bound_box()
|
||||
elif self.simple_deform_show_gizmo_poll(context):
|
||||
# draw bound box
|
||||
self.draw_bound_box()
|
||||
self.draw_deform_mesh()
|
||||
self.draw_limits_line()
|
||||
self.draw_limits_bound_box()
|
||||
elif self.simple_deform_show_bend_axis_witch_poll(context):
|
||||
self.draw_bound_box()
|
||||
|
||||
def draw_bound_box(self):
|
||||
coords = self.matrix_calculation(self.obj_matrix_world,
|
||||
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):
|
||||
self.draw_3d_shader(self.modifier_limits_bound_box,
|
||||
self.G_INDICES,
|
||||
self.pref.limits_bound_box_color,
|
||||
)
|
||||
|
||||
def draw_limits_line(self):
|
||||
up_point, down_point, up_limits, down_limits = self.modifier_limits_point
|
||||
# draw limits line
|
||||
self.draw_3d_shader((up_limits, down_limits), ((1, 0),), (1, 1, 0, 0.5))
|
||||
# draw line
|
||||
self.draw_3d_shader((up_point, down_point), ((1, 0),), (1, 1, 0, 0.3))
|
||||
|
||||
# draw pos
|
||||
self.draw_3d_shader([down_point], (), (0, 1, 0, 0.5),
|
||||
shader_name='3D_UNIFORM_COLOR', draw_type='POINTS')
|
||||
self.draw_3d_shader([up_point], (), (1, 0, 0, 0.5),
|
||||
shader_name='3D_UNIFORM_COLOR', draw_type='POINTS')
|
||||
|
||||
def draw_deform_mesh(self):
|
||||
ob = self.obj
|
||||
deform_data = self.G_DeformDrawData
|
||||
active = self.modifier
|
||||
# draw deform mesh
|
||||
if 'simple_deform_bound_data' in deform_data and self.pref.update_deform_wireframe:
|
||||
modifiers = self.get_modifiers_parameter(self.modifier)
|
||||
pos, indices, mat, mod_data, limits = deform_data['simple_deform_bound_data']
|
||||
is_limits = limits == active.limits[:]
|
||||
is_mat = (ob.matrix_world == mat)
|
||||
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):
|
||||
scale_error = (ob.scale != Vector((1, 1, 1)))
|
||||
if scale_error and ('scale_text' not in self.G_HandleData):
|
||||
self.G_HandleData['scale_text'] = bpy.types.SpaceView3D.draw_handler_add(
|
||||
self.draw_str, (), 'WINDOW', 'POST_PIXEL')
|
||||
elif not scale_error:
|
||||
self.del_handler_text()
|
||||
|
||||
def draw_origin_error(self):
|
||||
...
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,620 +0,0 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
import bpy
|
||||
import math
|
||||
from bpy_extras import view3d_utils
|
||||
from mathutils import Vector, Euler
|
||||
from bpy.types import (
|
||||
Gizmo,
|
||||
GizmoGroup,
|
||||
)
|
||||
|
||||
from .draw import Handler
|
||||
from .utils import Utils, PublicClass
|
||||
from .data import Data
|
||||
|
||||
|
||||
class CustomGizmo(Gizmo, Utils, Handler, Data):
|
||||
"""绘制自定义Gizmo"""
|
||||
bl_idname = '_Custom_Gizmo'
|
||||
|
||||
def setup(self):
|
||||
self.draw_type = 'None_GizmoGroup_'
|
||||
if not hasattr(self, 'custom_shape'):
|
||||
self.custom_shape = {}
|
||||
for i in self.G_GizmoCustomShapeDict:
|
||||
self.custom_shape[i] = self.new_custom_shape(
|
||||
'TRIS', self.G_GizmoCustomShapeDict[i])
|
||||
self.add_handler()
|
||||
|
||||
def draw(self, context):
|
||||
self.draw_custom_shape(self.custom_shape[self.draw_type])
|
||||
|
||||
def draw_select(self, context, select_id):
|
||||
self.draw_custom_shape(
|
||||
self.custom_shape[self.draw_type], select_id=select_id)
|
||||
|
||||
def invoke(self, context, event):
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def modal(self, context, event, tweak):
|
||||
self.add_handler()
|
||||
|
||||
self.update_bound_box(context.object)
|
||||
self.update_empty_matrix()
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
|
||||
class ViewSimpleDeformGizmo(Gizmo, Utils, Handler, Data, PublicClass):
|
||||
"""显示轴向切换拖动点Gizmo(两个点)
|
||||
"""
|
||||
bl_idname = 'ViewSimpleDeformGizmo'
|
||||
|
||||
bl_target_properties = (
|
||||
{'id': 'up_limits', 'type': 'FLOAT', 'array_length': 1},
|
||||
{'id': 'down_limits', 'type': 'FLOAT', 'array_length': 1},
|
||||
{'id': 'angle', 'type': 'FLOAT', 'array_length': 1},
|
||||
)
|
||||
|
||||
__slots__ = (
|
||||
'mod',
|
||||
'up',
|
||||
'down',
|
||||
'up_',
|
||||
'down_',
|
||||
'draw_type',
|
||||
'mouse_dpi',
|
||||
'ctrl_mode',
|
||||
'empty_object',
|
||||
'init_mouse_y',
|
||||
'init_mouse_x',
|
||||
'custom_shape',
|
||||
'int_value_angle',
|
||||
'value_deform_axis',
|
||||
'int_value_up_limits',
|
||||
'int_value_down_limits',
|
||||
'rotate_follow_modifier',
|
||||
)
|
||||
|
||||
def update_gizmo_rotate(self, axis, mod):
|
||||
if self.rotate_follow_modifier:
|
||||
rot = Euler()
|
||||
if axis == 'X' and (not self.is_positive(mod.angle)):
|
||||
rot.z = math.pi
|
||||
|
||||
elif axis == 'Y':
|
||||
if self.is_positive(mod.angle):
|
||||
rot.z = -(math.pi / 2)
|
||||
else:
|
||||
rot.z = math.pi / 2
|
||||
elif axis == 'Z':
|
||||
if self.is_positive(mod.angle):
|
||||
rot.x = rot.z = rot.y = math.pi / 2
|
||||
else:
|
||||
rot.z = rot.y = math.pi / 2
|
||||
rot.x = -(math.pi / 2)
|
||||
|
||||
rot = rot.to_matrix()
|
||||
self.matrix_basis = self.matrix_basis @ rot.to_4x4()
|
||||
|
||||
def update_draw_limits_bound_box(self, data, mod, axis, mat, up_, down_):
|
||||
top, bottom, left, right, front, back = data
|
||||
if mod.origin:
|
||||
vector_axis = self.get_vector_axis(mod)
|
||||
origin_mat = mod.origin.matrix_world.to_3x3()
|
||||
axis_ = origin_mat @ vector_axis
|
||||
point_lit = [[top, bottom], [left, right], [front, back]]
|
||||
for f in range(point_lit.__len__()):
|
||||
i = point_lit[f][0]
|
||||
j = point_lit[f][1]
|
||||
angle = self.point_to_angle(i, j, f, axis_)
|
||||
if abs(angle - 180) < 0.00001:
|
||||
point_lit[f][1], point_lit[f][0] = up_, down_
|
||||
elif abs(angle) < 0.00001:
|
||||
point_lit[f][0], point_lit[f][1] = up_, down_
|
||||
[[top, bottom], [left, right], [front, back]] = point_lit
|
||||
else:
|
||||
top, bottom, left, right, front, back = self.get_up_down_return_list(
|
||||
mod, axis, up_, down_, data)
|
||||
data = top, bottom, left, right, front, back
|
||||
(top, bottom, left, right, front,
|
||||
back) = self.matrix_calculation(mat.inverted(), data)
|
||||
self.G_SimpleDeformGizmoHandlerDit['draw_limits_bound_box'] = (
|
||||
mat, ((right[0], back[1], top[2]), (left[0], front[1], bottom[2],)))
|
||||
|
||||
def update_matrix_basis_translation(self, co, mat, up_, down_):
|
||||
if 'angle' == self.ctrl_mode:
|
||||
self.matrix_basis.translation = mat @ Vector((co[1]))
|
||||
elif 'up_limits' == self.ctrl_mode:
|
||||
self.matrix_basis.translation = up_
|
||||
elif 'down_limits' == self.ctrl_mode:
|
||||
self.matrix_basis.translation = down_
|
||||
|
||||
def update_gizmo_matrix(self, context):
|
||||
ob = context.object
|
||||
mat = ob.matrix_world
|
||||
mod = context.object.modifiers.active
|
||||
axis = mod.deform_axis
|
||||
if mod.origin:
|
||||
self.matrix_basis = mod.origin.matrix_world.normalized()
|
||||
else:
|
||||
self.matrix_basis = ob.matrix_world.normalized()
|
||||
|
||||
co = self.generate_co_data()
|
||||
self.update_gizmo_rotate(axis, mod)
|
||||
# calculation limits position
|
||||
top, bottom, left, right, front, back = self.each_face_pos(mat)
|
||||
(up, down), (up_, down_) = self.get_limits_pos(
|
||||
mod, (top, bottom, left, right, front, back))
|
||||
self.update_matrix_basis_translation(co, mat, up_, down_)
|
||||
|
||||
self.up = up
|
||||
self.down = down
|
||||
self.up_ = up_
|
||||
self.down_ = down_
|
||||
self.G_SimpleDeformGizmoHandlerDit['draw_line'] = (
|
||||
(up, down), (up_, down_))
|
||||
data = top, bottom, left, right, front, back
|
||||
self.update_draw_limits_bound_box(data, mod, axis, mat, up_, down_)
|
||||
|
||||
def setup(self):
|
||||
self.generate_co_data()
|
||||
self.draw_type = 'None_GizmoGroup_'
|
||||
self.ctrl_mode = 'angle' # up_limits , down_limits
|
||||
self.mouse_dpi = 10
|
||||
self.rotate_follow_modifier = True
|
||||
if not hasattr(self, 'custom_shape'):
|
||||
self.custom_shape = {}
|
||||
for i in self.G_GizmoCustomShapeDict:
|
||||
item = self.G_GizmoCustomShapeDict[i]
|
||||
self.custom_shape[i] = self.new_custom_shape('TRIS', item)
|
||||
self.add_handler()
|
||||
|
||||
def draw(self, context):
|
||||
self.add_handler()
|
||||
|
||||
self.update_gizmo_matrix(context)
|
||||
self.draw_custom_shape(self.custom_shape[self.draw_type])
|
||||
|
||||
def draw_select(self, context, select_id):
|
||||
self.update_gizmo_matrix(context)
|
||||
self.draw_custom_shape(
|
||||
self.custom_shape[self.draw_type], select_id=select_id)
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.init_mouse_y = event.mouse_y
|
||||
self.init_mouse_x = event.mouse_x
|
||||
mod = context.object.modifiers.active
|
||||
limits = mod.limits
|
||||
up_limits = limits[1]
|
||||
down_limits = limits[0]
|
||||
|
||||
if 'angle' == self.ctrl_mode:
|
||||
self.int_value_angle = self.target_get_value('angle')
|
||||
elif 'up_limits' == self.ctrl_mode:
|
||||
self.int_value_up_limits = up_limits
|
||||
self.target_set_value('up_limits', self.int_value_up_limits)
|
||||
elif 'down_limits' == self.ctrl_mode:
|
||||
self.int_value_down_limits = down_limits
|
||||
self.target_set_value('down_limits', self.int_value_down_limits)
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def exit(self, context, cancel):
|
||||
context.area.header_text_set(None)
|
||||
|
||||
if cancel:
|
||||
if 'angle' == self.ctrl_mode:
|
||||
self.target_set_value('angle', self.int_value_angle)
|
||||
elif 'deform_axis' == self.ctrl_mode:
|
||||
self.target_set_value('deform_axis', self.value_deform_axis)
|
||||
elif 'up_limits' == self.ctrl_mode:
|
||||
self.target_set_value('up_limits', self.int_value_up_limits)
|
||||
|
||||
elif 'down_limits' == self.ctrl_mode:
|
||||
self.target_set_value(
|
||||
'down_limits', self.int_value_down_limits)
|
||||
|
||||
def delta_update(self, context, event, delta):
|
||||
if ('draw_line' in self.G_SimpleDeformGizmoHandlerDit) and (self.ctrl_mode in ('up_limits', 'down_limits')):
|
||||
x, y = view3d_utils.location_3d_to_region_2d(
|
||||
context.region, context.space_data.region_3d, self.up)
|
||||
x2, y2 = view3d_utils.location_3d_to_region_2d(
|
||||
context.region, context.space_data.region_3d, self.down)
|
||||
|
||||
mouse_line_distance = math.sqrt(((event.mouse_region_x - x2) ** 2) +
|
||||
((event.mouse_region_y - y2) ** 2))
|
||||
straight_line_distance = math.sqrt(((x2 - x) ** 2) +
|
||||
((y2 - y) ** 2))
|
||||
delta = mouse_line_distance / \
|
||||
straight_line_distance + 0
|
||||
|
||||
v_up = Vector((x, y))
|
||||
v_down = Vector((x2, y2))
|
||||
limits_angle = v_up - v_down
|
||||
|
||||
mouse_v = Vector((event.mouse_region_x, event.mouse_region_y))
|
||||
|
||||
mouse_angle = mouse_v - v_down
|
||||
angle_ = mouse_angle.angle(limits_angle)
|
||||
if angle_ > (math.pi / 2):
|
||||
delta = 0
|
||||
return delta
|
||||
|
||||
def set_down_value(self, data, mu):
|
||||
up_limits, down_limits, delta, middle, min_value, max_value, limit_scope, difference_value, event, origin_mode = data
|
||||
value = self.value_limit(delta, max_value=mu -
|
||||
limit_scope if middle else max_value)
|
||||
self.target_set_value('down_limits', value)
|
||||
if event.ctrl:
|
||||
self.target_set_value(
|
||||
'up_limits', value + difference_value)
|
||||
elif middle:
|
||||
if origin_mode == 'LIMITS_MIDDLE':
|
||||
self.target_set_value('up_limits', mu - (value - mu))
|
||||
elif origin_mode == 'MIDDLE':
|
||||
self.target_set_value('up_limits', 1 - value)
|
||||
else:
|
||||
self.target_set_value('up_limits', up_limits)
|
||||
else:
|
||||
self.target_set_value('up_limits', up_limits)
|
||||
|
||||
def set_up_value(self, data, mu):
|
||||
up_limits, down_limits, delta, middle, min_value, max_value, limit_scope, difference_value, event, origin_mode = data
|
||||
value = self.value_limit(delta, min_value=mu +
|
||||
limit_scope if middle else min_value)
|
||||
self.target_set_value('up_limits', value)
|
||||
if event.ctrl:
|
||||
self.target_set_value(
|
||||
'down_limits', value - difference_value)
|
||||
elif middle:
|
||||
if origin_mode == 'LIMITS_MIDDLE':
|
||||
self.target_set_value('down_limits', mu - (value - mu))
|
||||
elif origin_mode == 'MIDDLE':
|
||||
self.target_set_value('down_limits', 1 - value)
|
||||
else:
|
||||
self.target_set_value('down_limits', down_limits)
|
||||
else:
|
||||
self.target_set_value('down_limits', down_limits)
|
||||
|
||||
def set_prop_value(self, data):
|
||||
up_limits, down_limits, delta, middle, min_value, max_value, limit_scope, difference_value, event, origin_mode = data
|
||||
mu = (up_limits + down_limits) / 2
|
||||
if 'angle' == self.ctrl_mode:
|
||||
value = self.int_value_angle - delta
|
||||
self.target_set_value('angle', value)
|
||||
elif 'up_limits' == self.ctrl_mode:
|
||||
self.set_up_value(data, mu)
|
||||
elif 'down_limits' == self.ctrl_mode:
|
||||
self.set_down_value(data, mu)
|
||||
|
||||
def update_header_text(self, context, mod, origin, up_limits, down_limits):
|
||||
t = lambda a: bpy.app.translations.pgettext(a)
|
||||
|
||||
if (mod.deform_method in ('TWIST', 'BEND')) and (self.ctrl_mode in ('angle',)):
|
||||
text = t("Angle") + ':{}'.format(math.degrees(mod.angle))
|
||||
elif 'up_limits' == self.ctrl_mode:
|
||||
text = t("Upper limit") + ':{}'.format(up_limits)
|
||||
elif 'down_limits' == self.ctrl_mode:
|
||||
text = t("Down limit") + ':{}'.format(down_limits)
|
||||
else:
|
||||
text = t("Coefficient") + ':{}'.format(mod.factor)
|
||||
text += ' '
|
||||
text += t(origin.bl_rna.properties[
|
||||
'origin_mode'].enum_items[origin.origin_mode].name)
|
||||
context.area.header_text_set(text)
|
||||
|
||||
def event_ops(self, event, ob, origin):
|
||||
"""通过输入键位来更改属性"""
|
||||
# event ctrl
|
||||
data_path = ('object.SimpleDeformGizmo_PropertyGroup.origin_mode',
|
||||
'object.modifiers.active.origin.SimpleDeformGizmo_PropertyGroup.origin_mode')
|
||||
|
||||
if event.type in ('WHEELUPMOUSE', 'WHEELDOWNMOUSE'):
|
||||
reverse = (event.type == 'WHEELUPMOUSE')
|
||||
for path in data_path:
|
||||
bpy.ops.wm.context_cycle_enum(
|
||||
data_path=path, reverse=reverse, wrap=True)
|
||||
elif event.type in ('X', 'Y', 'Z'):
|
||||
ob.modifiers.active.deform_axis = event.type
|
||||
elif event.type == 'A':
|
||||
self.pref.display_bend_axis_switch_gizmo = True
|
||||
return {'FINISHED'}
|
||||
self.add_handler()
|
||||
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def modal(self, context, event, tweak):
|
||||
self.update_bound_box(context.object)
|
||||
|
||||
delta = (self.init_mouse_x - event.mouse_x) / self.mouse_dpi
|
||||
ob = context.object
|
||||
mod = ob.modifiers.active
|
||||
limits = mod.limits
|
||||
up_limits = limits[1]
|
||||
down_limits = limits[0]
|
||||
origin = self.get_origin_property_group(mod, ob)
|
||||
origin_mode = origin.origin_mode
|
||||
middle = origin_mode in ('LIMITS_MIDDLE', 'MIDDLE')
|
||||
limit_scope = self.pref.modifiers_limits_tolerance
|
||||
max_value = up_limits - limit_scope
|
||||
min_value = down_limits + limit_scope
|
||||
difference_value = up_limits - down_limits
|
||||
|
||||
if 'SNAP' in tweak:
|
||||
delta = round(delta)
|
||||
if 'PRECISE' in tweak:
|
||||
delta /= self.mouse_dpi
|
||||
delta = self.delta_update(context, event, delta)
|
||||
|
||||
if origin_mode != 'NOT' and ('draw_line' in self.G_SimpleDeformGizmoHandlerDit):
|
||||
self.empty_object, _ = self.new_empty(ob, mod)
|
||||
self.G_SimpleDeformGizmoHandlerDit['empty_object'] = self.empty_object
|
||||
data = up_limits, down_limits, delta, middle, min_value, max_value, limit_scope, difference_value, event, origin_mode
|
||||
self.set_prop_value(data)
|
||||
self.update_gizmo_matrix(context)
|
||||
self.update_empty_matrix()
|
||||
self.update_bound_box(context.object)
|
||||
|
||||
self.update_header_text(context, mod, origin, up_limits, down_limits)
|
||||
self.add_handler()
|
||||
|
||||
return self.event_ops(event, ob, origin)
|
||||
|
||||
|
||||
class SimpleDeformGizmoGroup(GizmoGroup, Utils, Handler, PublicClass, Data):
|
||||
"""显示Gizmo
|
||||
"""
|
||||
bl_idname = 'OBJECT_GGT_SimpleDeformGizmoGroup'
|
||||
bl_label = 'SimpleDeformGizmoGroup'
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_options = {'3D', 'PERSISTENT', }
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
pol = cls.simple_deform_poll(context)
|
||||
pref = cls.pref_()
|
||||
deform_method = (
|
||||
pol and (context.object.modifiers.active.deform_method != 'BEND'))
|
||||
display_gizmo = pref.display_bend_axis_switch_gizmo
|
||||
switch = (not display_gizmo)
|
||||
return pol and (deform_method or switch)
|
||||
|
||||
def generate_gizmo_mode(self, gizmo_data):
|
||||
"""生成gizmo的上限下限及角度设置
|
||||
|
||||
Args:
|
||||
gizmo_data (_type_): _description_
|
||||
"""
|
||||
for i, j, k in gizmo_data:
|
||||
setattr(self, i, self.gizmos.new(j))
|
||||
gizmo = getattr(self, i)
|
||||
for f in k:
|
||||
if f == 'target_set_operator':
|
||||
gizmo.target_set_operator(k[f])
|
||||
elif f == 'target_set_prop':
|
||||
gizmo.target_set_prop(*k[f])
|
||||
else:
|
||||
setattr(gizmo, f, k[f])
|
||||
|
||||
def setup(self, context):
|
||||
sd_name = ViewSimpleDeformGizmo.bl_idname
|
||||
|
||||
add_data = (('up_limits',
|
||||
sd_name,
|
||||
{'ctrl_mode': 'up_limits',
|
||||
'draw_type': 'Sphere_GizmoGroup_',
|
||||
'mouse_dpi': 1000,
|
||||
'color': (1.0, 0, 0),
|
||||
'alpha': 0.5,
|
||||
'color_highlight': (1.0, 1.0, 1.0),
|
||||
'alpha_highlight': 0.3,
|
||||
'use_draw_modal': True,
|
||||
'scale_basis': 0.1,
|
||||
'use_draw_value': True, }),
|
||||
('down_limits',
|
||||
sd_name,
|
||||
{'ctrl_mode': 'down_limits',
|
||||
'draw_type': 'Sphere_GizmoGroup_',
|
||||
'mouse_dpi': 1000,
|
||||
'color': (0, 1.0, 0),
|
||||
'alpha': 0.5,
|
||||
'color_highlight': (1.0, 1.0, 1.0),
|
||||
'alpha_highlight': 0.3,
|
||||
'use_draw_modal': True,
|
||||
'scale_basis': 0.1,
|
||||
'use_draw_value': True, }),
|
||||
('angle',
|
||||
sd_name,
|
||||
{'ctrl_mode': 'angle',
|
||||
'draw_type': 'SimpleDeform_GizmoGroup_',
|
||||
'color': (1.0, 0.5, 1.0),
|
||||
'alpha': 0.3,
|
||||
'color_highlight': (1.0, 1.0, 1.0),
|
||||
'alpha_highlight': 0.3,
|
||||
'use_draw_modal': True,
|
||||
'scale_basis': 0.1,
|
||||
'use_draw_value': True,
|
||||
'mouse_dpi': 100,
|
||||
}),
|
||||
)
|
||||
|
||||
self.generate_gizmo_mode(add_data)
|
||||
|
||||
data_path = 'object.modifiers.active.deform_axis'
|
||||
set_enum = 'wm.context_set_enum'
|
||||
|
||||
for axis in ('X', 'Y', 'Z'):
|
||||
# show toggle axis button
|
||||
gizmo = self.gizmos.new('GIZMO_GT_button_2d')
|
||||
gizmo.icon = f'EVENT_{axis.upper()}'
|
||||
gizmo.draw_options = {'BACKDROP', 'HELPLINE'}
|
||||
ops = gizmo.target_set_operator(set_enum)
|
||||
ops.data_path = data_path
|
||||
ops.value = axis
|
||||
gizmo.color = (0, 0, 0)
|
||||
gizmo.alpha = 0.3
|
||||
gizmo.color_highlight = 1.0, 1.0, 1.0
|
||||
gizmo.alpha_highlight = 0.3
|
||||
gizmo.use_draw_modal = True
|
||||
gizmo.use_draw_value = True
|
||||
gizmo.scale_basis = 0.1
|
||||
setattr(self, f'deform_axis_{axis.lower()}', gizmo)
|
||||
self.add_handler()
|
||||
|
||||
def refresh(self, context):
|
||||
pro = context.object.SimpleDeformGizmo_PropertyGroup
|
||||
|
||||
self.angle.target_set_prop('angle',
|
||||
context.object.modifiers.active,
|
||||
'angle')
|
||||
self.down_limits.target_set_prop('down_limits',
|
||||
pro,
|
||||
'down_limits')
|
||||
self.down_limits.target_set_prop('up_limits',
|
||||
pro,
|
||||
'up_limits')
|
||||
self.up_limits.target_set_prop('down_limits',
|
||||
pro,
|
||||
'down_limits')
|
||||
self.up_limits.target_set_prop('up_limits',
|
||||
pro,
|
||||
'up_limits')
|
||||
self.add_handler()
|
||||
|
||||
def draw_prepare(self, context):
|
||||
ob = context.object
|
||||
mat = ob.matrix_world
|
||||
|
||||
if 'co' in self.G_SimpleDeformGizmoHandlerDit:
|
||||
def _mat(f):
|
||||
co = self.G_SimpleDeformGizmoHandlerDit['co'][0]
|
||||
co = (co[0] + (max(ob.dimensions) * f), co[1],
|
||||
co[2] - (min(ob.dimensions) * 0.3))
|
||||
return mat @ Vector(co)
|
||||
|
||||
self.deform_axis_x.matrix_basis.translation = _mat(0)
|
||||
self.deform_axis_y.matrix_basis.translation = _mat(0.3)
|
||||
self.deform_axis_z.matrix_basis.translation = _mat(0.6)
|
||||
self.add_handler()
|
||||
|
||||
def invoke_prepare(self, context, gizmo):
|
||||
self.add_handler()
|
||||
|
||||
|
||||
class SimpleDeformGizmoGroupDisplayBendAxiSwitchGizmo(GizmoGroup, Utils, Handler, PublicClass):
|
||||
"""绘制切换变型轴的
|
||||
变换方向
|
||||
"""
|
||||
bl_idname = 'OBJECT_GGT_SimpleDeformGizmoGroup_display_bend_axis_switch_gizmo'
|
||||
bl_label = 'SimpleDeformGizmoGroup_display_bend_axis_switch_gizmo'
|
||||
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'WINDOW'
|
||||
bl_options = {
|
||||
'3D',
|
||||
'PERSISTENT',
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
pref = cls.pref_()
|
||||
simple = cls.simple_deform_poll(context)
|
||||
bend = simple and (
|
||||
context.object.modifiers.active.deform_method == 'BEND')
|
||||
switch_axis = (pref.display_bend_axis_switch_gizmo == True)
|
||||
return switch_axis and bend
|
||||
|
||||
def setup(self, context):
|
||||
_draw_type = 'SimpleDeform_Bend_Direction_'
|
||||
_color_a = 1, 0, 0
|
||||
_color_b = 0, 1, 0
|
||||
self.add_handler()
|
||||
|
||||
for na, axis, rot, positive in (
|
||||
('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),
|
||||
('bottom_b', 'X', (math.radians(90), 0, 0), False),
|
||||
|
||||
('left_a', 'Y', (math.radians(90), 0, 0), False),
|
||||
('left_b', 'Y', (0, 0, 0), False),
|
||||
|
||||
('right_a', 'Y', (math.radians(90), 0, 0), True),
|
||||
('right_b', 'Y', (0, 0, 0), True),
|
||||
|
||||
('front_a', 'Z', (0, 0, 0), False),
|
||||
('front_b', 'X', (0, 0, 0), False),
|
||||
|
||||
('back_a', 'Z', (0, 0, 0), True),
|
||||
('back_b', 'X', (0, 0, 0), True),):
|
||||
_a = (na.split('_')[1] == 'a')
|
||||
setattr(self, na, self.gizmos.new(CustomGizmo.bl_idname))
|
||||
gizmo = getattr(self, na)
|
||||
gizmo.mode = na
|
||||
gizmo.draw_type = _draw_type
|
||||
gizmo.color = _color_a if _a else _color_b
|
||||
gizmo.alpha = 0.3
|
||||
gizmo.color_highlight = 1.0, 1.0, 1.0
|
||||
gizmo.alpha_highlight = 1
|
||||
gizmo.use_draw_modal = True
|
||||
gizmo.scale_basis = 0.2
|
||||
gizmo.use_draw_value = True
|
||||
ops = gizmo.target_set_operator(
|
||||
'simple_deform_gizmo.deform_axis')
|
||||
ops.Deform_Axis = axis
|
||||
ops.X_Value = rot[0]
|
||||
ops.Y_Value = rot[1]
|
||||
ops.Z_Value = rot[2]
|
||||
ops.Is_Positive = positive
|
||||
|
||||
def draw_prepare(self, context):
|
||||
ob = context.object
|
||||
mat = ob.matrix_world
|
||||
top, bottom, left, right, front, back = self.each_face_pos(mat)
|
||||
|
||||
rad = math.radians
|
||||
for_list = (
|
||||
('top_a', top, (0, 0, 0),),
|
||||
('top_b', top, (0, 0, rad(90)),),
|
||||
|
||||
('bottom_a', bottom, (0, rad(180), 0),),
|
||||
('bottom_b', bottom, (0, rad(180), rad(90)),),
|
||||
|
||||
('left_a', left, (rad(-90), 0, rad(90)),),
|
||||
('left_b', left, (0, rad(-90), 0),),
|
||||
|
||||
('right_a', right, (rad(90), 0, rad(90)),),
|
||||
('right_b', right, (0, rad(90), 0),),
|
||||
|
||||
('front_a', front, (rad(90), 0, 0),),
|
||||
('front_b', front, (rad(90), rad(90), 0),),
|
||||
|
||||
('back_a', back, (rad(-90), 0, 0),),
|
||||
('back_b', back, (rad(-90), rad(-90), 0),),
|
||||
)
|
||||
for i, j, w, in for_list:
|
||||
gizmo = getattr(self, i, False)
|
||||
rot = Euler(w, 'XYZ').to_matrix().to_4x4()
|
||||
|
||||
gizmo.matrix_basis = mat.to_euler().to_matrix().to_4x4() @ rot
|
||||
gizmo.matrix_basis.translation = Vector(j)
|
||||
|
||||
|
||||
class_list = (
|
||||
CustomGizmo,
|
||||
ViewSimpleDeformGizmo,
|
||||
SimpleDeformGizmoGroup,
|
||||
SimpleDeformGizmoGroupDisplayBendAxiSwitchGizmo,
|
||||
)
|
||||
|
||||
register_class, unregister_class = bpy.utils.register_classes_factory(class_list)
|
||||
|
||||
|
||||
def register():
|
||||
register_class()
|
||||
|
||||
|
||||
def unregister():
|
||||
Handler.del_handler()
|
||||
unregister_class()
|
32
simple_deform_helper/gizmo/__init__.py
Normal file
32
simple_deform_helper/gizmo/__init__.py
Normal file
@ -0,0 +1,32 @@
|
||||
import bpy
|
||||
|
||||
from .angle_and_factor import AngleGizmoGroup, AngleGizmo
|
||||
from .bend_axis import BendAxiSwitchGizmoGroup, CustomGizmo
|
||||
from .set_deform_axis import SetDeformGizmoGroup
|
||||
from .up_down_limits_point import UpDownLimitsGizmo, UpDownLimitsGizmoGroup
|
||||
from ..draw import Draw3D
|
||||
|
||||
class_list = (
|
||||
UpDownLimitsGizmo,
|
||||
UpDownLimitsGizmoGroup,
|
||||
|
||||
AngleGizmo,
|
||||
AngleGizmoGroup,
|
||||
|
||||
CustomGizmo,
|
||||
BendAxiSwitchGizmoGroup,
|
||||
|
||||
SetDeformGizmoGroup,
|
||||
)
|
||||
|
||||
register_class, unregister_class = bpy.utils.register_classes_factory(class_list)
|
||||
|
||||
|
||||
def register():
|
||||
Draw3D.add_handler()
|
||||
register_class()
|
||||
|
||||
|
||||
def unregister():
|
||||
Draw3D.del_handler()
|
||||
unregister_class()
|
161
simple_deform_helper/gizmo/angle_and_factor.py
Normal file
161
simple_deform_helper/gizmo/angle_and_factor.py
Normal file
@ -0,0 +1,161 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
import math
|
||||
|
||||
from bpy.types import Gizmo
|
||||
from bpy.types import (
|
||||
GizmoGroup,
|
||||
)
|
||||
|
||||
from ..update import change_active_modifier_parameter
|
||||
from ..utils import GizmoUtils, GizmoGroupUtils
|
||||
|
||||
|
||||
class AngleUpdate(GizmoUtils):
|
||||
int_value_degrees: float
|
||||
tmp_value_angle: float
|
||||
|
||||
def get_snap(self, delta, tweak):
|
||||
is_snap = 'SNAP' in tweak
|
||||
is_precise = 'PRECISE' in tweak
|
||||
if is_snap and is_precise:
|
||||
delta = round(delta)
|
||||
elif is_snap:
|
||||
delta //= 5
|
||||
delta *= 5
|
||||
elif is_precise:
|
||||
delta /= self.mouse_dpi
|
||||
delta //= 0.01
|
||||
delta *= 0.01
|
||||
return delta
|
||||
|
||||
def update_prop_value(self, event, tweak):
|
||||
def v(va):
|
||||
self.target_set_value('angle', math.radians(va))
|
||||
|
||||
not_c_l = not event.alt and not event.ctrl
|
||||
is_only_shift = event.shift and not_c_l
|
||||
|
||||
change_angle = self.get_delta(event)
|
||||
if is_only_shift:
|
||||
change_angle /= 50
|
||||
new_value = self.tmp_value_angle - change_angle
|
||||
old_value = self.target_get_value('angle')
|
||||
snap_value = self.get_snap(new_value, tweak)
|
||||
|
||||
is_shift = event.type == 'LEFT_SHIFT'
|
||||
if is_only_shift:
|
||||
if event.value == 'PRESS':
|
||||
self.init_mouse_region_x = event.mouse_region_x
|
||||
self.tmp_value_angle = int(math.degrees(old_value))
|
||||
v(self.tmp_value_angle)
|
||||
return
|
||||
|
||||
value = (self.tmp_value_angle - change_angle) // 0.01 * 0.01
|
||||
v(value)
|
||||
return
|
||||
|
||||
elif not_c_l and not event.shift and is_shift and event.value == 'RELEASE':
|
||||
self.init_mouse_region_x = event.mouse_region_x
|
||||
# new_value = self.tmp_value_angle = math.degrees(old_value)
|
||||
return
|
||||
v(snap_value)
|
||||
|
||||
def update_gizmo_matrix(self, context):
|
||||
matrix = context.object.matrix_world
|
||||
point = self.modifier_bound_co[1]
|
||||
self.matrix_basis = self.obj_matrix_world
|
||||
self.matrix_basis.translation = matrix @ point
|
||||
|
||||
def update_header_text(self, context):
|
||||
te = self.translate_text
|
||||
text = te(self.modifier.deform_method.title()) + ' '
|
||||
|
||||
if self.modifier_is_use_angle_value:
|
||||
value = round(math.degrees(self.modifier_angle), 3)
|
||||
text += self.translate_header_text('Angle', value)
|
||||
else:
|
||||
value = round(self.modifier.factor, 3)
|
||||
text += self.translate_header_text('Coefficient', value)
|
||||
context.area.header_text_set(text)
|
||||
|
||||
|
||||
class AngleGizmo(Gizmo, AngleUpdate):
|
||||
bl_idname = 'ViewSimpleAngleGizmo'
|
||||
|
||||
bl_target_properties = (
|
||||
{'id': 'up_limits', 'type': 'FLOAT', 'array_length': 1},
|
||||
{'id': 'down_limits', 'type': 'FLOAT', 'array_length': 1},
|
||||
{'id': 'angle', 'type': 'FLOAT', 'array_length': 1},
|
||||
)
|
||||
|
||||
__slots__ = (
|
||||
'draw_type',
|
||||
'mouse_dpi',
|
||||
'empty_object',
|
||||
'custom_shape',
|
||||
'tmp_value_angle',
|
||||
'int_value_degrees',
|
||||
'init_mouse_region_y',
|
||||
'init_mouse_region_x',
|
||||
)
|
||||
|
||||
def setup(self):
|
||||
self.init_setup()
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.init_invoke(context, event)
|
||||
self.int_value_degrees = self.target_get_value('angle')
|
||||
angle = math.degrees(self.int_value_degrees)
|
||||
self.tmp_value_angle = angle
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def modal(self, context, event, tweak):
|
||||
self.clear_point_cache()
|
||||
|
||||
self.update_prop_value(event, tweak)
|
||||
self.update_deform_wireframe()
|
||||
self.update_header_text(context)
|
||||
change_active_modifier_parameter.update_modifier_parameter()
|
||||
self.tag_redraw(context)
|
||||
return self.event_handle(event)
|
||||
|
||||
def exit(self, context, cancel):
|
||||
context.area.header_text_set(None)
|
||||
if cancel:
|
||||
self.target_set_value('angle', self.int_value_degrees)
|
||||
|
||||
|
||||
class AngleGizmoGroup(GizmoGroup, GizmoGroupUtils):
|
||||
"""ShowGizmo
|
||||
"""
|
||||
bl_idname = 'OBJECT_GGT_SimpleDeformGizmoGroup'
|
||||
bl_label = 'AngleGizmoGroup'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return cls.simple_deform_show_gizmo_poll(context)
|
||||
|
||||
def setup(self, context):
|
||||
sd_name = AngleGizmo.bl_idname
|
||||
|
||||
add_data = [
|
||||
('angle',
|
||||
sd_name,
|
||||
{'draw_type': 'SimpleDeform_GizmoGroup_',
|
||||
'color': (1.0, 0.5, 1.0),
|
||||
'alpha': 0.3,
|
||||
'color_highlight': (1.0, 1.0, 1.0),
|
||||
'alpha_highlight': 0.3,
|
||||
'use_draw_modal': True,
|
||||
'scale_basis': 0.1,
|
||||
'use_draw_value': True,
|
||||
'mouse_dpi': 5,
|
||||
}),
|
||||
]
|
||||
|
||||
self.generate_gizmo(add_data)
|
||||
|
||||
def refresh(self, context):
|
||||
self.angle.target_set_prop('angle',
|
||||
context.object.modifiers.active,
|
||||
'angle')
|
115
simple_deform_helper/gizmo/bend_axis.py
Normal file
115
simple_deform_helper/gizmo/bend_axis.py
Normal file
@ -0,0 +1,115 @@
|
||||
import math
|
||||
|
||||
from bpy.types import GizmoGroup
|
||||
from bpy_types import Gizmo
|
||||
from mathutils import Euler, Vector
|
||||
|
||||
from ..utils import GizmoUtils, GizmoGroupUtils
|
||||
|
||||
|
||||
class CustomGizmo(Gizmo, GizmoUtils):
|
||||
"""Draw Custom Gizmo"""
|
||||
bl_idname = '_Custom_Gizmo'
|
||||
draw_type: str
|
||||
custom_shape: dict
|
||||
|
||||
def setup(self):
|
||||
self.init_setup()
|
||||
|
||||
def draw(self, context):
|
||||
self.draw_custom_shape(self.custom_shape[self.draw_type])
|
||||
|
||||
def draw_select(self, context, select_id):
|
||||
self.draw_custom_shape(
|
||||
self.custom_shape[self.draw_type], select_id=select_id)
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.init_invoke(context, event)
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def modal(self, context, event, tweak):
|
||||
self.update_empty_matrix()
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
|
||||
class BendAxiSwitchGizmoGroup(GizmoGroup, GizmoGroupUtils):
|
||||
bl_idname = 'OBJECT_GGT_SimpleDeformGizmoGroup_display_bend_axis_switch_gizmo'
|
||||
bl_label = 'SimpleDeformGizmoGroup_display_bend_axis_switch_gizmo'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return cls.simple_deform_show_bend_axis_witch_poll(context)
|
||||
|
||||
def setup(self, context):
|
||||
_draw_type = 'SimpleDeform_Bend_Direction_'
|
||||
_color_a = 1, 0, 0
|
||||
_color_b = 0, 1, 0
|
||||
|
||||
for na, axis, rot, positive in (
|
||||
('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),
|
||||
('bottom_b', 'X', (math.radians(90), 0, 0), False),
|
||||
|
||||
('left_a', 'Y', (math.radians(90), 0, 0), False),
|
||||
('left_b', 'Y', (0, 0, 0), False),
|
||||
|
||||
('right_a', 'Y', (math.radians(90), 0, 0), True),
|
||||
('right_b', 'Y', (0, 0, 0), True),
|
||||
|
||||
('front_a', 'Z', (0, 0, 0), False),
|
||||
('front_b', 'X', (0, 0, 0), False),
|
||||
|
||||
('back_a', 'Z', (0, 0, 0), True),
|
||||
('back_b', 'X', (0, 0, 0), True),):
|
||||
_a = (na.split('_')[1] == 'a')
|
||||
setattr(self, na, self.gizmos.new(CustomGizmo.bl_idname))
|
||||
gizmo = getattr(self, na)
|
||||
gizmo.mode = na
|
||||
gizmo.draw_type = _draw_type
|
||||
gizmo.color = _color_a if _a else _color_b
|
||||
gizmo.alpha = 0.3
|
||||
gizmo.color_highlight = 1.0, 1.0, 1.0
|
||||
gizmo.alpha_highlight = 1
|
||||
gizmo.use_draw_modal = True
|
||||
gizmo.scale_basis = 0.2
|
||||
gizmo.use_draw_value = True
|
||||
ops = gizmo.target_set_operator(
|
||||
'simple_deform_gizmo.deform_axis')
|
||||
ops.Deform_Axis = axis
|
||||
ops.X_Value = rot[0]
|
||||
ops.Y_Value = rot[1]
|
||||
ops.Z_Value = rot[2]
|
||||
ops.Is_Positive = positive
|
||||
|
||||
def draw_prepare(self, context):
|
||||
ob = context.object
|
||||
mat = ob.matrix_world
|
||||
top, bottom, left, right, front, back = self.modifier_bound_box_pos
|
||||
|
||||
rad = math.radians
|
||||
for_list = (
|
||||
('top_a', top, (0, 0, 0),),
|
||||
('top_b', top, (0, 0, rad(90)),),
|
||||
|
||||
('bottom_a', bottom, (0, rad(180), 0),),
|
||||
('bottom_b', bottom, (0, rad(180), rad(90)),),
|
||||
|
||||
('left_a', left, (rad(-90), 0, rad(90)),),
|
||||
('left_b', left, (0, rad(-90), 0),),
|
||||
|
||||
('right_a', right, (rad(90), 0, rad(90)),),
|
||||
('right_b', right, (0, rad(90), 0),),
|
||||
|
||||
('front_a', front, (rad(90), 0, 0),),
|
||||
('front_b', front, (rad(90), rad(90), 0),),
|
||||
|
||||
('back_a', back, (rad(-90), 0, 0),),
|
||||
('back_b', back, (rad(-90), rad(-90), 0),),
|
||||
)
|
||||
for i, j, w, in for_list:
|
||||
gizmo = getattr(self, i, False)
|
||||
rot = Euler(w, 'XYZ').to_matrix().to_4x4()
|
||||
gizmo.matrix_basis = mat.to_euler().to_matrix().to_4x4() @ rot
|
||||
gizmo.matrix_basis.translation = self.obj_matrix_world @ Vector(j)
|
51
simple_deform_helper/gizmo/set_deform_axis.py
Normal file
51
simple_deform_helper/gizmo/set_deform_axis.py
Normal file
@ -0,0 +1,51 @@
|
||||
from bpy.types import GizmoGroup
|
||||
from mathutils import Vector
|
||||
|
||||
from ..utils import GizmoGroupUtils
|
||||
|
||||
|
||||
class SetDeformGizmoGroup(GizmoGroup, GizmoGroupUtils):
|
||||
bl_idname = 'OBJECT_GGT_SetDeformGizmoGroup'
|
||||
bl_label = 'SetDeformGizmoGroup'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, 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'
|
||||
set_enum = 'wm.context_set_enum'
|
||||
|
||||
for axis in ('X', 'Y', 'Z'):
|
||||
# show toggle axis button
|
||||
gizmo = self.gizmos.new('GIZMO_GT_button_2d')
|
||||
gizmo.icon = f'EVENT_{axis.upper()}'
|
||||
gizmo.draw_options = {'BACKDROP', 'HELPLINE'}
|
||||
ops = gizmo.target_set_operator(set_enum)
|
||||
ops.data_path = data_path
|
||||
ops.value = axis
|
||||
gizmo.color = (0, 0, 0)
|
||||
gizmo.alpha = 0.3
|
||||
gizmo.color_highlight = 1.0, 1.0, 1.0
|
||||
gizmo.alpha_highlight = 0.3
|
||||
gizmo.use_draw_modal = True
|
||||
gizmo.use_draw_value = True
|
||||
gizmo.scale_basis = 0.1
|
||||
setattr(self, f'deform_axis_{axis.lower()}', gizmo)
|
||||
|
||||
def draw_prepare(self, context):
|
||||
bound = self.modifier_bound_co
|
||||
if bound:
|
||||
obj = self.get_depsgraph(self.obj)
|
||||
dimensions = obj.dimensions
|
||||
|
||||
def mat(f):
|
||||
b = bound[0]
|
||||
co = (b[0] + (max(dimensions) * f),
|
||||
b[1],
|
||||
b[2] - (min(dimensions) * 0.3))
|
||||
return self.obj_matrix_world @ Vector(co)
|
||||
|
||||
self.deform_axis_x.matrix_basis.translation = mat(0)
|
||||
self.deform_axis_y.matrix_basis.translation = mat(0.3)
|
||||
self.deform_axis_z.matrix_basis.translation = mat(0.6)
|
270
simple_deform_helper/gizmo/up_down_limits_point.py
Normal file
270
simple_deform_helper/gizmo/up_down_limits_point.py
Normal file
@ -0,0 +1,270 @@
|
||||
import math
|
||||
from time import time
|
||||
|
||||
import bpy
|
||||
from bpy.types import Gizmo, GizmoGroup
|
||||
from bpy_extras import view3d_utils
|
||||
from mathutils import Vector
|
||||
|
||||
from ..update import change_active_modifier_parameter
|
||||
from ..utils import GizmoUtils, GizmoGroupUtils
|
||||
|
||||
|
||||
class GizmoProperty(GizmoUtils):
|
||||
ctrl_mode: str
|
||||
int_value_up_limits: int
|
||||
int_value_down_limits: int
|
||||
|
||||
@property
|
||||
def is_up_limits_mode(self):
|
||||
return self.ctrl_mode == 'up_limits'
|
||||
|
||||
@property
|
||||
def is_down_limits_mode(self):
|
||||
return self.ctrl_mode == 'down_limits'
|
||||
|
||||
@property
|
||||
def limit_scope(self):
|
||||
return self.pref.modifiers_limits_tolerance
|
||||
|
||||
@property
|
||||
def limits_min_value(self):
|
||||
return self.modifier_down_limits + self.limit_scope
|
||||
|
||||
@property
|
||||
def limits_max_value(self):
|
||||
return self.modifier_up_limits - self.limit_scope
|
||||
|
||||
# ----get func
|
||||
|
||||
def get_up_limits_value(self, event):
|
||||
delta = self.get_delta(event)
|
||||
mid = self.middle_limits_value + self.limit_scope
|
||||
min_value = mid if self.is_middle_mode else self.limits_min_value
|
||||
return self.value_limit(delta, min_value=min_value)
|
||||
|
||||
def get_down_limits_value(self, event):
|
||||
delta = self.get_delta(event)
|
||||
mid = self.middle_limits_value - self.limit_scope
|
||||
max_value = mid if self.is_middle_mode else self.limits_max_value
|
||||
return self.value_limit(delta, max_value=max_value)
|
||||
|
||||
def get_delta(self, event):
|
||||
context = bpy.context
|
||||
x, y = view3d_utils.location_3d_to_region_2d(
|
||||
context.region, context.space_data.region_3d, self.point_up)
|
||||
x2, y2 = view3d_utils.location_3d_to_region_2d(
|
||||
context.region, context.space_data.region_3d, self.point_down)
|
||||
|
||||
mouse_line_distance = math.sqrt(((event.mouse_region_x - x2) ** 2) +
|
||||
((event.mouse_region_y - y2) ** 2))
|
||||
straight_line_distance = math.sqrt(((x2 - x) ** 2) +
|
||||
((y2 - y) ** 2))
|
||||
delta = mouse_line_distance / straight_line_distance + 0
|
||||
|
||||
v_up = Vector((x, y))
|
||||
v_down = Vector((x2, y2))
|
||||
limits_angle = v_up - v_down
|
||||
|
||||
mouse_v = Vector((event.mouse_region_x, event.mouse_region_y))
|
||||
|
||||
mouse_angle = mouse_v - v_down
|
||||
angle_ = mouse_angle.angle(limits_angle)
|
||||
if angle_ > (math.pi / 2):
|
||||
delta = 0
|
||||
return delta
|
||||
|
||||
|
||||
class GizmoUpdate(GizmoProperty):
|
||||
# ---update gizmo matrix
|
||||
def update_gizmo_matrix(self, context):
|
||||
self.align_orientation_to_user_perspective(context)
|
||||
self.align_point_to_limits_point()
|
||||
|
||||
def align_orientation_to_user_perspective(self, context):
|
||||
rotation = context.space_data.region_3d.view_matrix.inverted().to_quaternion()
|
||||
matrix = rotation.to_matrix().to_4x4()
|
||||
self.matrix_basis = matrix
|
||||
|
||||
def align_point_to_limits_point(self):
|
||||
if self.is_up_limits_mode:
|
||||
self.matrix_basis.translation = self.point_limits_up
|
||||
elif self.is_down_limits_mode:
|
||||
self.matrix_basis.translation = self.point_limits_down
|
||||
|
||||
# ---- set prop
|
||||
def set_prop_value(self, event):
|
||||
if self.is_up_limits_mode:
|
||||
self.set_up_value(event)
|
||||
elif self.is_down_limits_mode:
|
||||
self.set_down_value(event)
|
||||
|
||||
def set_down_value(self, event):
|
||||
value = self.get_down_limits_value(event)
|
||||
self.target_set_value('down_limits', value)
|
||||
if event.ctrl:
|
||||
self.target_set_value('up_limits', value + self.difference_value)
|
||||
elif self.is_middle_mode:
|
||||
if self.origin_mode == 'LIMITS_MIDDLE':
|
||||
mu = self.middle_limits_value
|
||||
v = mu - (value - mu)
|
||||
self.target_set_value('up_limits', v)
|
||||
elif self.origin_mode == 'MIDDLE':
|
||||
self.target_set_value('up_limits', 1 - value)
|
||||
else:
|
||||
self.target_set_value('up_limits', self.modifier_up_limits)
|
||||
else:
|
||||
self.target_set_value('up_limits', self.modifier_up_limits)
|
||||
|
||||
def set_up_value(self, event):
|
||||
value = self.get_up_limits_value(event)
|
||||
self.target_set_value('up_limits', value)
|
||||
if event.ctrl:
|
||||
self.target_set_value('down_limits', value - self.difference_value)
|
||||
elif self.is_middle_mode:
|
||||
if self.origin_mode == 'LIMITS_MIDDLE':
|
||||
mu = self.middle_limits_value
|
||||
value = mu - (value - mu)
|
||||
self.target_set_value('down_limits', value)
|
||||
elif self.origin_mode == 'MIDDLE':
|
||||
self.target_set_value('down_limits', 1 - value)
|
||||
else:
|
||||
self.target_set_value('down_limits', self.modifier_down_limits)
|
||||
else:
|
||||
self.target_set_value('down_limits', self.modifier_down_limits)
|
||||
|
||||
# -------
|
||||
def update_header_text(self, context):
|
||||
origin = self.obj_origin_property_group
|
||||
mode = origin.bl_rna.properties['origin_mode'].enum_items[origin.origin_mode].name
|
||||
|
||||
te = self.translate_text
|
||||
t = self.translate_header_text
|
||||
text = te(self.modifier.deform_method.title()) + ' ' + te(mode) + ' '
|
||||
if self.is_up_limits_mode:
|
||||
value = round(self.modifier_up_limits, 3)
|
||||
text += t('Up limit', value)
|
||||
elif self.is_down_limits_mode:
|
||||
value = round(self.modifier_down_limits, 3)
|
||||
text += t('Down limit', value)
|
||||
context.area.header_text_set(text)
|
||||
|
||||
|
||||
class UpDownLimitsGizmo(Gizmo, GizmoUpdate):
|
||||
bl_idname = 'UpDownLimitsGizmo'
|
||||
bl_label = 'UpDownLimitsGizmo'
|
||||
bl_target_properties = (
|
||||
{'id': 'up_limits', 'type': 'FLOAT', 'array_length': 1},
|
||||
{'id': 'down_limits', 'type': 'FLOAT', 'array_length': 1},
|
||||
)
|
||||
bl_options = {'UNDO', 'GRAB_CURSOR'}
|
||||
|
||||
__slots__ = (
|
||||
'mod',
|
||||
'up_limits',
|
||||
'down_limits',
|
||||
'draw_type',
|
||||
'mouse_dpi',
|
||||
'ctrl_mode',
|
||||
'difference_value',
|
||||
'middle_limits_value',
|
||||
'init_mouse_region_y',
|
||||
'init_mouse_region_x',
|
||||
'custom_shape',
|
||||
'int_value_up_limits',
|
||||
'int_value_down_limits',
|
||||
)
|
||||
difference_value: float
|
||||
middle_limits_value: float
|
||||
|
||||
def setup(self):
|
||||
self.mouse_dpi = 10
|
||||
self.init_setup()
|
||||
|
||||
def invoke(self, context, event):
|
||||
self.init_invoke(context, event)
|
||||
|
||||
if self.is_up_limits_mode:
|
||||
self.int_value_up_limits = up_limits = self.modifier_up_limits
|
||||
self.target_set_value('up_limits', up_limits)
|
||||
elif self.is_down_limits_mode:
|
||||
self.int_value_down_limits = down_limits = self.modifier_down_limits
|
||||
self.target_set_value('down_limits', down_limits)
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def exit(self, context, cancel):
|
||||
context.area.header_text_set(None)
|
||||
if cancel:
|
||||
if self.is_up_limits_mode:
|
||||
self.target_set_value('up_limits', self.int_value_up_limits)
|
||||
elif self.is_down_limits_mode:
|
||||
self.target_set_value(
|
||||
'down_limits', self.int_value_down_limits)
|
||||
|
||||
def modal(self, context, event, tweak):
|
||||
self.clear_point_cache()
|
||||
|
||||
if self.modifier_is_use_origin_axis:
|
||||
self.new_origin_empty_object()
|
||||
# return {'RUNNING_MODAL'}
|
||||
|
||||
self.difference_value = self.modifier_up_limits - self.modifier_down_limits
|
||||
self.middle_limits_value = (self.modifier_up_limits + self.modifier_down_limits) / 2
|
||||
|
||||
try:
|
||||
self.set_prop_value(event)
|
||||
self.clear_point_cache()
|
||||
self.update_object_origin_matrix()
|
||||
except Exception:
|
||||
...
|
||||
# return {'FINISHED'}
|
||||
self.update_header_text(context)
|
||||
return_handle = self.event_handle(event)
|
||||
change_active_modifier_parameter.update_modifier_parameter()
|
||||
self.update_deform_wireframe()
|
||||
return return_handle
|
||||
|
||||
|
||||
class UpDownLimitsGizmoGroup(GizmoGroup, GizmoGroupUtils):
|
||||
bl_idname = 'OBJECT_GGT_UpDownLimitsGizmoGroup'
|
||||
bl_label = 'UpDownLimitsGizmoGroup'
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return cls.simple_deform_show_gizmo_poll(context)
|
||||
|
||||
def setup(self, context):
|
||||
sd_name = UpDownLimitsGizmo.bl_idname
|
||||
gizmo_data = [
|
||||
('up_limits',
|
||||
sd_name,
|
||||
{'ctrl_mode': 'up_limits',
|
||||
'draw_type': 'Sphere_GizmoGroup_',
|
||||
'mouse_dpi': 1000,
|
||||
'color': (1.0, 0, 0),
|
||||
'alpha': 0.5,
|
||||
'color_highlight': (1.0, 1.0, 1.0),
|
||||
'alpha_highlight': 0.3,
|
||||
'use_draw_modal': True,
|
||||
'scale_basis': 0.1,
|
||||
'use_draw_value': True, }),
|
||||
('down_limits',
|
||||
sd_name,
|
||||
{'ctrl_mode': 'down_limits',
|
||||
'draw_type': 'Sphere_GizmoGroup_',
|
||||
'mouse_dpi': 1000,
|
||||
'color': (0, 1.0, 0),
|
||||
'alpha': 0.5,
|
||||
'color_highlight': (1.0, 1.0, 1.0),
|
||||
'alpha_highlight': 0.3,
|
||||
'use_draw_modal': True,
|
||||
'scale_basis': 0.1,
|
||||
'use_draw_value': True, }),
|
||||
]
|
||||
self.generate_gizmo(gizmo_data)
|
||||
|
||||
def refresh(self, context):
|
||||
pro = context.object.SimpleDeformGizmo_PropertyGroup
|
||||
for i in (self.down_limits, self.up_limits):
|
||||
for j in ('down_limits', 'up_limits'):
|
||||
i.target_set_prop(j, pro, j)
|
@ -4,10 +4,10 @@ import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import FloatProperty, StringProperty, BoolProperty
|
||||
|
||||
from .utils import PublicClass
|
||||
from .utils import GizmoUtils
|
||||
|
||||
|
||||
class DeformAxisOperator(Operator, PublicClass):
|
||||
class DeformAxisOperator(Operator, GizmoUtils):
|
||||
bl_idname = 'simple_deform_gizmo.deform_axis'
|
||||
bl_label = 'deform_axis'
|
||||
bl_description = 'deform_axis operator'
|
||||
@ -26,12 +26,11 @@ class DeformAxisOperator(Operator, PublicClass):
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def modal(self, context, event):
|
||||
from .gizmo import Utils
|
||||
|
||||
self.clear_point_cache()
|
||||
mod = context.object.modifiers.active
|
||||
mod.deform_axis = self.Deform_Axis
|
||||
empty, con_limit_name = Utils.new_empty(context.object, mod)
|
||||
is_positive = Utils.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,15 +39,13 @@ class DeformAxisOperator(Operator, PublicClass):
|
||||
('max_z', self.Z_Value),
|
||||
('min_z', self.Z_Value),
|
||||
):
|
||||
setattr(empty.constraints[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)):
|
||||
mod.angle = mod.angle * -1
|
||||
|
||||
if not event.ctrl:
|
||||
self.pref.display_bend_axis_switch_gizmo = False
|
||||
|
||||
Utils.update_bound_box(context.object)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
|
@ -2,10 +2,10 @@
|
||||
import bpy
|
||||
from bpy.types import Panel, VIEW3D_HT_tool_header
|
||||
|
||||
from .utils import PublicClass, Utils
|
||||
from .utils import GizmoUtils
|
||||
|
||||
|
||||
class SimpleDeformHelperToolPanel(Panel, PublicClass):
|
||||
class SimpleDeformHelperToolPanel(Panel, GizmoUtils):
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'UI'
|
||||
bl_category = 'Tool'
|
||||
@ -16,7 +16,7 @@ class SimpleDeformHelperToolPanel(Panel, PublicClass):
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return Utils.simple_deform_poll(context)
|
||||
return cls.simple_deform_public_poll(context)
|
||||
|
||||
def draw(self, context):
|
||||
cls = SimpleDeformHelperToolPanel
|
||||
@ -34,13 +34,20 @@ class SimpleDeformHelperToolPanel(Panel, PublicClass):
|
||||
'origin_mode',
|
||||
text='')
|
||||
layout.prop(pref,
|
||||
'modifiers_limits_tolerance',
|
||||
'update_deform_wireframe',
|
||||
icon='MOD_WIREFRAME',
|
||||
text='')
|
||||
|
||||
if mod.deform_method == 'BEND':
|
||||
layout.prop(pref,
|
||||
'show_set_axis_button',
|
||||
icon='EMPTY_AXIS',
|
||||
text='')
|
||||
if pref.modifier_deform_method_is_bend:
|
||||
layout.prop(pref,
|
||||
'display_bend_axis_switch_gizmo',
|
||||
toggle=1)
|
||||
layout.prop(pref,
|
||||
'modifiers_limits_tolerance',
|
||||
text='')
|
||||
|
||||
|
||||
class_list = (
|
||||
|
@ -1,6 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import os
|
||||
import bpy
|
||||
from bpy.props import (FloatProperty,
|
||||
PointerProperty,
|
||||
@ -12,12 +11,11 @@ from bpy.types import (
|
||||
PropertyGroup,
|
||||
)
|
||||
|
||||
from .data import G_ADDON_NAME
|
||||
from .utils import PublicClass, Utils
|
||||
from .utils import GizmoUtils
|
||||
|
||||
|
||||
class SimpleDeformGizmoAddonPreferences(AddonPreferences, PublicClass):
|
||||
bl_idname = G_ADDON_NAME
|
||||
class SimpleDeformGizmoAddonPreferences(AddonPreferences, GizmoUtils):
|
||||
bl_idname = GizmoUtils.G_ADDON_NAME
|
||||
|
||||
deform_wireframe_color: FloatVectorProperty(
|
||||
name='Deform Wireframe',
|
||||
@ -29,7 +27,7 @@ class SimpleDeformGizmoAddonPreferences(AddonPreferences, PublicClass):
|
||||
bound_box_color: FloatVectorProperty(
|
||||
name='Bound Box',
|
||||
description='Draw Bound Box Color',
|
||||
default=(1, 0, 0, 0.1),
|
||||
default=(1, 0, 0, 0.5),
|
||||
soft_max=1,
|
||||
soft_min=0,
|
||||
size=4,
|
||||
@ -50,20 +48,38 @@ class SimpleDeformGizmoAddonPreferences(AddonPreferences, PublicClass):
|
||||
min=0.0001
|
||||
)
|
||||
display_bend_axis_switch_gizmo: BoolProperty(
|
||||
name='Show Toggle Axis Gizmo',
|
||||
name='Show Toggle Bend Axis Gizmo',
|
||||
default=False,
|
||||
options={'SKIP_SAVE'})
|
||||
|
||||
update_deform_wireframe: BoolProperty(
|
||||
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()
|
||||
box = col.box()
|
||||
for text in ("You can press the following shortcut keys when dragging values",
|
||||
" Wheel: Switch Origin Ctrl Mode",
|
||||
" X、Y、Z: Switch Modifier Deform Axis",
|
||||
" W: Switch Deform Wireframe Show",
|
||||
" A: Switch To Select Bend Axis Mode(deform_method=='BEND')",):
|
||||
box.label(text=self.translate_text(text))
|
||||
|
||||
col.prop(self, 'deform_wireframe_color')
|
||||
col.prop(self, 'bound_box_color')
|
||||
col.prop(self, 'limits_bound_box_color')
|
||||
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 Utils.simple_deform_poll(context):
|
||||
if GizmoUtils.simple_deform_public_poll(context):
|
||||
row = self.layout.row()
|
||||
obj = context.object
|
||||
mod = obj.modifiers.active
|
||||
@ -80,13 +96,10 @@ class SimpleDeformGizmoAddonPreferences(AddonPreferences, PublicClass):
|
||||
row.prop(mod, show_type)
|
||||
|
||||
|
||||
class SimpleDeformGizmoObjectPropertyGroup(PropertyGroup):
|
||||
|
||||
class SimpleDeformGizmoObjectPropertyGroup(PropertyGroup, GizmoUtils):
|
||||
def _limits_up(self, context):
|
||||
mod = context.object.modifiers
|
||||
if mod and (mod.active.type == 'SIMPLE_DEFORM'):
|
||||
mod = mod.active
|
||||
mod.limits[1] = self.up_limits
|
||||
if self.active_modifier_is_simple_deform:
|
||||
self.modifier.limits[1] = self.up_limits
|
||||
|
||||
up_limits: FloatProperty(name='up',
|
||||
description='UP Limits(Red)',
|
||||
@ -96,10 +109,8 @@ class SimpleDeformGizmoObjectPropertyGroup(PropertyGroup):
|
||||
min=0)
|
||||
|
||||
def _limits_down(self, context):
|
||||
mod = context.object.modifiers
|
||||
if mod and (mod.active.type == 'SIMPLE_DEFORM'):
|
||||
mod = mod.active
|
||||
mod.limits[0] = self.down_limits
|
||||
if self.active_modifier_is_simple_deform:
|
||||
self.modifier.limits[0] = self.down_limits
|
||||
|
||||
down_limits: FloatProperty(name='down',
|
||||
description='Lower limit(Green)',
|
||||
@ -144,7 +155,7 @@ register_class, unregister_class = bpy.utils.register_classes_factory(class_list
|
||||
def register():
|
||||
register_class()
|
||||
|
||||
PublicClass.pref_().display_bend_axis_switch_gizmo = False
|
||||
GizmoUtils.pref_().display_bend_axis_switch_gizmo = False
|
||||
bpy.types.Object.SimpleDeformGizmo_PropertyGroup = PointerProperty(
|
||||
type=SimpleDeformGizmoObjectPropertyGroup,
|
||||
name='SimpleDeformGizmo_PropertyGroup')
|
||||
|
@ -11,12 +11,28 @@ def origin_text(a, b):
|
||||
translations_dict = {
|
||||
"zh_CN": {
|
||||
("上下文", "原文"): "翻译文字",
|
||||
("*", "Show Toggle Axis Gizmo"): "显示切换轴向Gizmo",
|
||||
("*", "Show Toggle Bend Axis Gizmo"): "显示切换弯曲轴向Gizmo",
|
||||
|
||||
("*", "You can press the following shortcut keys when dragging values"):
|
||||
"拖动值时可以按以下快捷键",
|
||||
("*", " Wheel: Switch Origin Ctrl Mode"):
|
||||
" 滚轮: 切换原点控制模式",
|
||||
("*", " X、Y、Z: Switch Modifier Deform Axis"):
|
||||
" X、Y、Z: 切换修改器型变轴",
|
||||
("*", " W: Switch Deform Wireframe Show"):
|
||||
" W: 切换形变线框显示",
|
||||
("*",
|
||||
" A: Switch To Select Bend Axis Mode(deform_method=='BEND')"):
|
||||
" A: 切换到选择弯曲轴模式(形变方法='弯曲')",
|
||||
("*", "Show Set Axis Button"): "显示设置轴向Gizmo",
|
||||
|
||||
("*", "Follow Upper Limit(Red)"): "跟随上限(红色)",
|
||||
("*", "Follow Lower Limit(Green)"): "跟随下限(绿色)",
|
||||
("*", "Lower limit(Green)"): "下限(绿色)",
|
||||
("*", "UP Limits(Red)"): "上限(红色)",
|
||||
("*", "Down limit"): "下限",
|
||||
("*", "Up limit"): "上限",
|
||||
("*", "Show Deform Wireframe"): "显示形变线框",
|
||||
("*", "Minimum value between upper and lower limits"): "上限与下限之间的最小值",
|
||||
("*", "Upper and lower limit tolerance"): "上下限容差",
|
||||
("*", "Draw Upper and lower limit Bound Box Color"): "绘制网格上限下限边界线框的颜色",
|
||||
|
@ -1,25 +1,201 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
from functools import cache
|
||||
|
||||
import bpy
|
||||
from bpy.app.handlers import depsgraph_update_post, persistent
|
||||
|
||||
from .utils import GizmoUpdate
|
||||
|
||||
@persistent
|
||||
def remove_not_use_empty(scene, dep):
|
||||
"""循环场景内的所有物体,找出没用的空物体并删掉
|
||||
gizmo = GizmoUpdate()
|
||||
|
||||
"""depsgraph_update_post cannot listen to users modifying modifier parameters
|
||||
Use timers to watch and use cache
|
||||
"""
|
||||
remove_name: str = "ViewSimpleDeformGizmo__Empty_"
|
||||
context = bpy.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) # remove object
|
||||
|
||||
|
||||
class update_public:
|
||||
_events_func_list = {}
|
||||
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
|
||||
def register(cls):
|
||||
from bpy.app import timers
|
||||
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
|
||||
def unregister(cls):
|
||||
from bpy.app import timers
|
||||
func = cls._update_func_call_timer
|
||||
if timers.is_registered(func):
|
||||
timers.unregister(func)
|
||||
else:
|
||||
print('cls timers is not registered', cls)
|
||||
cls._events_func_list.clear()
|
||||
|
||||
|
||||
class simple_update(update_public, GizmoUpdate):
|
||||
tmp_save_data = {}
|
||||
|
||||
@classmethod
|
||||
def timers_update_poll(cls):
|
||||
obj = bpy.context.object
|
||||
if not cls.context_mode_is_object():
|
||||
...
|
||||
elif not obj:
|
||||
...
|
||||
elif not cls.obj_type_is_mesh_or_lattice(obj):
|
||||
...
|
||||
elif cls.mod_is_simple_deform_type(obj.modifiers.active):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class change_active_object(simple_update):
|
||||
@classmethod
|
||||
@cache
|
||||
def update_poll(cls):
|
||||
return cls.is_change_active_object()
|
||||
|
||||
@classmethod
|
||||
def is_change_active_object(cls, change_data=True):
|
||||
import bpy
|
||||
obj = bpy.context.object
|
||||
name = obj.name
|
||||
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)
|
||||
|
||||
def update():
|
||||
cls.tmp_save_data['modifiers'] = modifiers
|
||||
|
||||
if change_active_object.update_poll():
|
||||
update()
|
||||
elif 'modifiers' not in cls.tmp_save_data:
|
||||
update()
|
||||
elif cls.tmp_save_data['modifiers'] != modifiers:
|
||||
update()
|
||||
return True
|
||||
return False
|
||||
|
||||
@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)}
|
||||
|
||||
|
||||
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():
|
||||
depsgraph_update_post.append(remove_not_use_empty)
|
||||
simple_update.register()
|
||||
|
||||
def p():
|
||||
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():
|
||||
depsgraph_update_post.remove(remove_not_use_empty)
|
||||
simple_update.unregister()
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user