new addon simple_deform_helper #104464
@ -1,5 +1,13 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# 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 = {
|
bl_info = {
|
||||||
"name": "SimpleDeformHelper",
|
"name": "SimpleDeformHelper",
|
||||||
@ -15,6 +23,7 @@ bl_info = {
|
|||||||
module_tuple = (
|
module_tuple = (
|
||||||
panel,
|
panel,
|
||||||
gizmo,
|
gizmo,
|
||||||
|
utils,
|
||||||
update,
|
update,
|
||||||
translate,
|
translate,
|
||||||
operators,
|
operators,
|
||||||
@ -23,8 +32,6 @@ module_tuple = (
|
|||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
data.Data.load_gizmo_data()
|
|
||||||
|
|
||||||
for item in module_tuple:
|
for item in module_tuple:
|
||||||
item.register()
|
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,23 +5,18 @@ 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 .data import G_INDICES, G_MODIFIERS_PROPERTY, G_NAME, Data
|
from .utils import PublicUtils
|
||||||
from .utils import PublicClass, Utils
|
|
||||||
|
|
||||||
|
|
||||||
class Handler(Data):
|
class Handler:
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_handler(cls):
|
def add_handler(cls):
|
||||||
"""向3d视图添加绘制handler
|
|
||||||
并将其存储下来
|
|
||||||
"""
|
|
||||||
if 'handler' not in cls.G_SimpleDeformGizmoHandlerDit:
|
if 'handler' not in cls.G_SimpleDeformGizmoHandlerDit:
|
||||||
cls.G_SimpleDeformGizmoHandlerDit['handler'] = bpy.types.SpaceView3D.draw_handler_add(
|
cls.G_SimpleDeformGizmoHandlerDit['handler'] = bpy.types.SpaceView3D.draw_handler_add(
|
||||||
Draw3D.draw_bound_box, (), 'WINDOW', 'POST_VIEW')
|
Draw3D.draw_bound_box, (), 'WINDOW', 'POST_VIEW')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def del_handler_text(cls):
|
def del_handler_text(cls):
|
||||||
|
|
||||||
if 'handler_text' in cls.G_SimpleDeformGizmoHandlerDit:
|
if 'handler_text' in cls.G_SimpleDeformGizmoHandlerDit:
|
||||||
bpy.types.SpaceView3D.draw_handler_remove(
|
bpy.types.SpaceView3D.draw_handler_remove(
|
||||||
cls.G_SimpleDeformGizmoHandlerDit['handler_text'], 'WINDOW')
|
cls.G_SimpleDeformGizmoHandlerDit['handler_text'], 'WINDOW')
|
||||||
@ -30,11 +25,11 @@ class Handler(Data):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def del_handler(cls):
|
def del_handler(cls):
|
||||||
data = bpy.data
|
data = bpy.data
|
||||||
if data.meshes.get(G_NAME):
|
if data.meshes.get(cls.G_NAME):
|
||||||
data.meshes.remove(data.meshes.get(G_NAME))
|
data.meshes.remove(data.meshes.get(cls.G_NAME))
|
||||||
|
|
||||||
if data.objects.get(G_NAME):
|
if data.objects.get(cls.G_NAME):
|
||||||
data.objects.remove(data.objects.get(G_NAME))
|
data.objects.remove(data.objects.get(cls.G_NAME))
|
||||||
|
|
||||||
cls.del_handler_text()
|
cls.del_handler_text()
|
||||||
|
|
||||||
@ -44,23 +39,9 @@ class Handler(Data):
|
|||||||
cls.G_SimpleDeformGizmoHandlerDit.clear()
|
cls.G_SimpleDeformGizmoHandlerDit.clear()
|
||||||
|
|
||||||
|
|
||||||
class Draw3D(PublicClass, Data):
|
class Draw3D(PublicUtils):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def draw_3d_shader(cls, pos, indices, color=None, *, shader_name='3D_UNIFORM_COLOR', draw_type='LINES'):
|
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)
|
shader = gpu.shader.from_builtin(shader_name)
|
||||||
if draw_type == 'POINTS':
|
if draw_type == 'POINTS':
|
||||||
batch = batch_for_shader(shader, draw_type, {'pos': pos})
|
batch = batch_for_shader(shader, draw_type, {'pos': pos})
|
||||||
@ -105,9 +86,9 @@ class Draw3D(PublicClass, Data):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def draw_box(cls, data, mat):
|
def draw_box(cls, data, mat):
|
||||||
pref = cls.pref_()
|
pref = cls.pref_()
|
||||||
coords = Utils.matrix_calculation(mat,
|
coords = PublicUtils.matrix_calculation(mat,
|
||||||
cls.data_to_calculation(data))
|
cls.data_to_calculation(data))
|
||||||
cls.draw_3d_shader(coords, G_INDICES, pref.bound_box_color)
|
cls.draw_3d_shader(coords, cls.G_INDICES, pref.bound_box_color)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def data_to_calculation(cls, data):
|
def data_to_calculation(cls, data):
|
||||||
@ -130,9 +111,9 @@ class Draw3D(PublicClass, Data):
|
|||||||
if 'draw_limits_bound_box' in handler_dit:
|
if 'draw_limits_bound_box' in handler_dit:
|
||||||
# draw limits_bound_box
|
# draw limits_bound_box
|
||||||
mat, data = handler_dit['draw_limits_bound_box']
|
mat, data = handler_dit['draw_limits_bound_box']
|
||||||
coords = Utils.matrix_calculation(mat, cls.data_to_calculation(data))
|
coords = PublicUtils.matrix_calculation(mat, cls.data_to_calculation(data))
|
||||||
cls.draw_3d_shader(coords,
|
cls.draw_3d_shader(coords,
|
||||||
G_INDICES,
|
cls.G_INDICES,
|
||||||
pref.limits_bound_box_color)
|
pref.limits_bound_box_color)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -156,7 +137,7 @@ class Draw3D(PublicClass, Data):
|
|||||||
# draw deform mesh
|
# draw deform mesh
|
||||||
if 'draw' in handler_dit:
|
if 'draw' in handler_dit:
|
||||||
pos, indices, mat, mod_data, limits = handler_dit['draw']
|
pos, indices, mat, mod_data, limits = handler_dit['draw']
|
||||||
if ([getattr(active, i) for i in G_MODIFIERS_PROPERTY] == mod_data) and (
|
if ([getattr(active, i) for i in cls.G_MODIFIERS_PROPERTY] == mod_data) and (
|
||||||
ob.matrix_world == mat) and limits == active.limits[:]:
|
ob.matrix_world == mat) and limits == active.limits[:]:
|
||||||
cls.draw_3d_shader(
|
cls.draw_3d_shader(
|
||||||
pos, indices, pref.deform_wireframe_color)
|
pos, indices, pref.deform_wireframe_color)
|
||||||
@ -174,14 +155,14 @@ class Draw3D(PublicClass, Data):
|
|||||||
modifier = context.object.modifiers.active # 活动修改器
|
modifier = context.object.modifiers.active # 活动修改器
|
||||||
|
|
||||||
pref = cls.pref_()
|
pref = cls.pref_()
|
||||||
simple_poll = Utils.simple_deform_poll(context)
|
simple_poll = PublicUtils.simple_deform_public_poll(context)
|
||||||
bend = modifier and (modifier.deform_method == 'BEND')
|
bend = modifier and (modifier.deform_method == 'BEND')
|
||||||
display_switch_axis = not pref.display_bend_axis_switch_gizmo
|
display_switch_axis = not pref.display_bend_axis_switch_gizmo
|
||||||
|
|
||||||
cls.draw_scale_text(obj)
|
cls.draw_scale_text(obj)
|
||||||
Utils.update_co_data(obj, modifier)
|
PublicUtils.update_co_data(obj, modifier)
|
||||||
|
|
||||||
co_data = Utils.generate_co_data()
|
co_data = PublicUtils.generate_co_data()
|
||||||
|
|
||||||
if simple_poll and ((not bend) or display_switch_axis):
|
if simple_poll and ((not bend) or display_switch_axis):
|
||||||
# draw bound box
|
# draw bound box
|
||||||
@ -191,7 +172,7 @@ class Draw3D(PublicClass, Data):
|
|||||||
cls.draw_limits_bound_box()
|
cls.draw_limits_bound_box()
|
||||||
elif simple_poll and (bend and not display_switch_axis):
|
elif simple_poll and (bend and not display_switch_axis):
|
||||||
cls.draw_box(co_data, matrix)
|
cls.draw_box(co_data, matrix)
|
||||||
Utils.new_empty(obj, modifier)
|
PublicUtils.new_empty(obj, modifier)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def draw_bound_box(cls):
|
def draw_bound_box(cls):
|
||||||
@ -202,7 +183,7 @@ class Draw3D(PublicClass, Data):
|
|||||||
gpu.state.depth_test_set('ALWAYS')
|
gpu.state.depth_test_set('ALWAYS')
|
||||||
|
|
||||||
context = bpy.context
|
context = bpy.context
|
||||||
if Utils.simple_deform_poll(context):
|
if PublicUtils.simple_deform_public_poll(context):
|
||||||
cls.is_draw_box(context)
|
cls.is_draw_box(context)
|
||||||
else:
|
else:
|
||||||
Handler.del_handler()
|
Handler.del_handler()
|
||||||
|
@ -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()
|
|
96
simple_deform_helper/gizmo/__init__.py
Normal file
96
simple_deform_helper/gizmo/__init__.py
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import bpy
|
||||||
|
from bpy_types import Gizmo
|
||||||
|
|
||||||
|
from draw import Handler
|
||||||
|
from utils import PublicUtils
|
||||||
|
|
||||||
|
|
||||||
|
class GizmoProperty:
|
||||||
|
|
||||||
|
@property
|
||||||
|
def obj(self):
|
||||||
|
return bpy.context.object
|
||||||
|
|
||||||
|
@property
|
||||||
|
def modifier(self):
|
||||||
|
obj = self.obj
|
||||||
|
if not obj:
|
||||||
|
return
|
||||||
|
return obj.modifiers.active
|
||||||
|
|
||||||
|
@property
|
||||||
|
def active_modifier_is_simple_deform(self):
|
||||||
|
return self.modifier and self.modifier.type == 'SIMPLE_DEFORM'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_use_angle_value(self):
|
||||||
|
if self.active_modifier_is_simple_deform:
|
||||||
|
return self.modifier.deform_method in ('TWIST', 'BEND')
|
||||||
|
|
||||||
|
|
||||||
|
class GizmoPublic(GizmoProperty, PublicUtils, Handler):
|
||||||
|
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])
|
||||||
|
|
||||||
|
|
||||||
|
class CustomGizmo(Gizmo, PublicUtils, Handler):
|
||||||
|
"""绘制自定义Gizmo"""
|
||||||
|
bl_idname = '_Custom_Gizmo'
|
||||||
|
draw_type: str
|
||||||
|
custom_shape: dict
|
||||||
|
|
||||||
|
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_list = (
|
||||||
|
ViewSimpleDeformGizmo,
|
||||||
|
SimpleDeformGizmoGroup,
|
||||||
|
)
|
||||||
|
|
||||||
|
register_class, unregister_class = bpy.utils.register_classes_factory(class_list)
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
register_class()
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
Handler.del_handler()
|
||||||
|
unregister_class()
|
107
simple_deform_helper/gizmo/bend_axis.py
Normal file
107
simple_deform_helper/gizmo/bend_axis.py
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import math
|
||||||
|
|
||||||
|
from bpy.types import GizmoGroup
|
||||||
|
from mathutils import Euler, Vector
|
||||||
|
|
||||||
|
from ..draw import Handler
|
||||||
|
from ..utils import PublicUtils
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleDeformGizmoGroupDisplayBendAxiSwitchGizmo(GizmoGroup, PublicUtils, Handler):
|
||||||
|
"""绘制切换变型轴的
|
||||||
|
变换方向
|
||||||
|
"""
|
||||||
|
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_public_poll(context)
|
||||||
|
bend = simple and (
|
||||||
|
context.object.modifiers.active.deform_method == 'BEND')
|
||||||
|
switch_axis = pref.display_bend_axis_switch_gizmo
|
||||||
|
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)
|
341
simple_deform_helper/gizmo/ctrl_tow_point.py
Normal file
341
simple_deform_helper/gizmo/ctrl_tow_point.py
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
import bpy
|
||||||
|
|
||||||
|
|
||||||
|
class GizmoProperty(Gizmo, PublicUtils, Handler):
|
||||||
|
@property
|
||||||
|
def is_angle_mode(self):
|
||||||
|
return self.ctrl_mode == 'angle'
|
||||||
|
|
||||||
|
@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'
|
||||||
|
|
||||||
|
|
||||||
|
class ViewSimpleDeformGizmo(GizmoProperty):
|
||||||
|
"""显示轴向切换拖动点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 self.is_angle_mode:
|
||||||
|
self.matrix_basis.translation = mat @ Vector((co[1]))
|
||||||
|
elif self.is_up_limits_mode:
|
||||||
|
self.matrix_basis.translation = up_
|
||||||
|
elif self.is_down_limits_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 self.is_angle_mode:
|
||||||
|
value = self.int_value_angle - delta
|
||||||
|
v = self.snap_value(value, event)
|
||||||
|
print(v)
|
||||||
|
self.target_set_value('angle', v)
|
||||||
|
elif self.is_up_limits_mode:
|
||||||
|
self.set_up_value(data, mu)
|
||||||
|
elif self.is_down_limits_mode:
|
||||||
|
self.set_down_value(data, mu)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def snap_value(value, event: 'bpy.types.Event'):
|
||||||
|
if event.ctrl:
|
||||||
|
value //= 5
|
||||||
|
elif event.ctrl and event.shift:
|
||||||
|
value //= 1
|
||||||
|
return value
|
||||||
|
|
||||||
|
def update_header_text(self, context, mod, origin, up_limits, down_limits):
|
||||||
|
translate: Callable[[Any], str] = lambda t: bpy.app.translations.pgettext(t)
|
||||||
|
mode = origin.bl_rna.properties['origin_mode'].enum_items[origin.origin_mode].name
|
||||||
|
|
||||||
|
def t_(a, b):
|
||||||
|
return translate(a) + ':{}'.format(round(b, 3))
|
||||||
|
|
||||||
|
text = translate(mode) + ' '
|
||||||
|
|
||||||
|
if self.is_use_angle_value and self.is_angle_mode:
|
||||||
|
text += t_('Angle', math.degrees(mod.angle))
|
||||||
|
elif self.is_up_limits_mode:
|
||||||
|
text += t_('Upper limit', up_limits)
|
||||||
|
elif self.is_down_limits_mode:
|
||||||
|
text += t_('Down limit', down_limits)
|
||||||
|
else:
|
||||||
|
text += t_('Coefficient', mod.factor)
|
||||||
|
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)
|
132
simple_deform_helper/gizmo/ctrl_value_and_factor.py
Normal file
132
simple_deform_helper/gizmo/ctrl_value_and_factor.py
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
from bpy.types import (
|
||||||
|
GizmoGroup,
|
||||||
|
)
|
||||||
|
from mathutils import Vector
|
||||||
|
|
||||||
|
from utils import PublicUtils
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleDeformGizmoGroup(GizmoGroup, PublicUtils):
|
||||||
|
"""显示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_public_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 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()
|
@ -4,10 +4,10 @@ import bpy
|
|||||||
from bpy.types import Operator
|
from bpy.types import Operator
|
||||||
from bpy.props import FloatProperty, StringProperty, BoolProperty
|
from bpy.props import FloatProperty, StringProperty, BoolProperty
|
||||||
|
|
||||||
from .utils import PublicClass
|
from .utils import PublicUtils
|
||||||
|
|
||||||
|
|
||||||
class DeformAxisOperator(Operator, PublicClass):
|
class DeformAxisOperator(Operator, PublicUtils):
|
||||||
bl_idname = 'simple_deform_gizmo.deform_axis'
|
bl_idname = 'simple_deform_gizmo.deform_axis'
|
||||||
bl_label = 'deform_axis'
|
bl_label = 'deform_axis'
|
||||||
bl_description = 'deform_axis operator'
|
bl_description = 'deform_axis operator'
|
||||||
@ -26,12 +26,12 @@ class DeformAxisOperator(Operator, PublicClass):
|
|||||||
return {'RUNNING_MODAL'}
|
return {'RUNNING_MODAL'}
|
||||||
|
|
||||||
def modal(self, context, event):
|
def modal(self, context, event):
|
||||||
from .gizmo import Utils
|
from gizmo.ctrl_value_and_factor import PublicUtils
|
||||||
|
|
||||||
mod = context.object.modifiers.active
|
mod = context.object.modifiers.active
|
||||||
mod.deform_axis = self.Deform_Axis
|
mod.deform_axis = self.Deform_Axis
|
||||||
empty, con_limit_name = Utils.new_empty(context.object, mod)
|
empty, con_limit_name = PublicUtils.new_empty(context.object, mod)
|
||||||
is_positive = Utils.is_positive(mod.angle)
|
is_positive = PublicUtils.is_positive(mod.angle)
|
||||||
|
|
||||||
for limit, value in (('max_x', self.X_Value),
|
for limit, value in (('max_x', self.X_Value),
|
||||||
('min_x', self.X_Value),
|
('min_x', self.X_Value),
|
||||||
@ -48,7 +48,7 @@ class DeformAxisOperator(Operator, PublicClass):
|
|||||||
if not event.ctrl:
|
if not event.ctrl:
|
||||||
self.pref.display_bend_axis_switch_gizmo = False
|
self.pref.display_bend_axis_switch_gizmo = False
|
||||||
|
|
||||||
Utils.update_bound_box(context.object)
|
PublicUtils.update_bound_box(context.object)
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
import bpy
|
import bpy
|
||||||
from bpy.types import Panel, VIEW3D_HT_tool_header
|
from bpy.types import Panel, VIEW3D_HT_tool_header
|
||||||
|
|
||||||
from .utils import PublicClass, Utils
|
from .utils import PublicUtils
|
||||||
|
|
||||||
|
|
||||||
class SimpleDeformHelperToolPanel(Panel, PublicClass):
|
class SimpleDeformHelperToolPanel(Panel, PublicUtils):
|
||||||
bl_space_type = 'VIEW_3D'
|
bl_space_type = 'VIEW_3D'
|
||||||
bl_region_type = 'UI'
|
bl_region_type = 'UI'
|
||||||
bl_category = 'Tool'
|
bl_category = 'Tool'
|
||||||
@ -16,7 +16,7 @@ class SimpleDeformHelperToolPanel(Panel, PublicClass):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return Utils.simple_deform_poll(context)
|
return PublicUtils.simple_deform_public_poll(context)
|
||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
cls = SimpleDeformHelperToolPanel
|
cls = SimpleDeformHelperToolPanel
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
import os
|
|
||||||
import bpy
|
import bpy
|
||||||
from bpy.props import (FloatProperty,
|
from bpy.props import (FloatProperty,
|
||||||
PointerProperty,
|
PointerProperty,
|
||||||
@ -12,12 +11,11 @@ from bpy.types import (
|
|||||||
PropertyGroup,
|
PropertyGroup,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .data import G_ADDON_NAME
|
from .utils import PublicUtils, GizmoUtils
|
||||||
from .utils import PublicClass, Utils
|
|
||||||
|
|
||||||
|
|
||||||
class SimpleDeformGizmoAddonPreferences(AddonPreferences, PublicClass):
|
class SimpleDeformGizmoAddonPreferences(AddonPreferences, PublicUtils):
|
||||||
bl_idname = G_ADDON_NAME
|
bl_idname = PublicUtils.G_ADDON_NAME
|
||||||
|
|
||||||
deform_wireframe_color: FloatVectorProperty(
|
deform_wireframe_color: FloatVectorProperty(
|
||||||
name='Deform Wireframe',
|
name='Deform Wireframe',
|
||||||
@ -63,7 +61,7 @@ class SimpleDeformGizmoAddonPreferences(AddonPreferences, PublicClass):
|
|||||||
col.prop(self, 'display_bend_axis_switch_gizmo')
|
col.prop(self, 'display_bend_axis_switch_gizmo')
|
||||||
|
|
||||||
def draw_header_tool_settings(self, context):
|
def draw_header_tool_settings(self, context):
|
||||||
if Utils.simple_deform_poll(context):
|
if GizmoUtils.simple_deform_public_poll(context):
|
||||||
row = self.layout.row()
|
row = self.layout.row()
|
||||||
obj = context.object
|
obj = context.object
|
||||||
mod = obj.modifiers.active
|
mod = obj.modifiers.active
|
||||||
@ -81,7 +79,6 @@ class SimpleDeformGizmoAddonPreferences(AddonPreferences, PublicClass):
|
|||||||
|
|
||||||
|
|
||||||
class SimpleDeformGizmoObjectPropertyGroup(PropertyGroup):
|
class SimpleDeformGizmoObjectPropertyGroup(PropertyGroup):
|
||||||
|
|
||||||
def _limits_up(self, context):
|
def _limits_up(self, context):
|
||||||
mod = context.object.modifiers
|
mod = context.object.modifiers
|
||||||
if mod and (mod.active.type == 'SIMPLE_DEFORM'):
|
if mod and (mod.active.type == 'SIMPLE_DEFORM'):
|
||||||
@ -144,7 +141,7 @@ register_class, unregister_class = bpy.utils.register_classes_factory(class_list
|
|||||||
def register():
|
def register():
|
||||||
register_class()
|
register_class()
|
||||||
|
|
||||||
PublicClass.pref_().display_bend_axis_switch_gizmo = False
|
PublicUtils.pref_().display_bend_axis_switch_gizmo = False
|
||||||
bpy.types.Object.SimpleDeformGizmo_PropertyGroup = PointerProperty(
|
bpy.types.Object.SimpleDeformGizmo_PropertyGroup = PointerProperty(
|
||||||
type=SimpleDeformGizmoObjectPropertyGroup,
|
type=SimpleDeformGizmoObjectPropertyGroup,
|
||||||
name='SimpleDeformGizmo_PropertyGroup')
|
name='SimpleDeformGizmo_PropertyGroup')
|
||||||
|
@ -2,19 +2,88 @@
|
|||||||
|
|
||||||
import math
|
import math
|
||||||
import uuid
|
import uuid
|
||||||
|
from os.path import dirname, basename, realpath
|
||||||
|
from typing import Callable, Any
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from bpy.types import AddonPreferences
|
from bpy.types import AddonPreferences
|
||||||
from mathutils import Vector, Matrix
|
from mathutils import Vector, Matrix
|
||||||
|
|
||||||
from .data import G_ADDON_NAME, G_NAME, G_INDICES, G_MODIFIERS_PROPERTY, G_CON_LIMIT_NAME, Data
|
|
||||||
|
class PublicData:
|
||||||
|
"""Public data class, all fixed data will be placed here
|
||||||
|
"""
|
||||||
|
G_GizmoCustomShapeDict = {}
|
||||||
|
G_SimpleDeformGizmoHandlerDit = {}
|
||||||
|
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 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
|
||||||
|
'angle',
|
||||||
|
'deform_axis',
|
||||||
|
'deform_method',
|
||||||
|
'factor',
|
||||||
|
'invert_vertex_group',
|
||||||
|
'limits',
|
||||||
|
'lock_x',
|
||||||
|
'lock_y',
|
||||||
|
'lock_z',
|
||||||
|
'origin',
|
||||||
|
'show_expanded',
|
||||||
|
'show_in_editmode',
|
||||||
|
'vertex_group',
|
||||||
|
]
|
||||||
|
|
||||||
|
@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_mesh_get_triangle_face_co(mesh: 'bpy.types.Mesh') -> list:
|
||||||
|
"""
|
||||||
|
:param mesh: input mesh read vertex
|
||||||
|
:type mesh: bpy.data.meshes
|
||||||
|
:return list: vertex coordinate list[[cox,coy,coz],[cox,coy,coz]...]
|
||||||
|
"""
|
||||||
|
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, ".3f")) for j in vert.co) for face in bm.faces for vert in face.verts]
|
||||||
|
bm.free()
|
||||||
|
return co_list
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_selected_obj_generate_json(cls):
|
||||||
|
"""Export selected object vertex data as gizmo custom paint data
|
||||||
|
The output file should be in the blender folder
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
data = {}
|
||||||
|
for obj in bpy.context.selected_object:
|
||||||
|
data[obj.name] = cls.from_mesh_get_triangle_face_co(obj.data)
|
||||||
|
print(data)
|
||||||
|
with open('gizmo.json', 'w+') as f:
|
||||||
|
f.write(json.dumps(data))
|
||||||
|
|
||||||
|
|
||||||
class PublicClass:
|
class PublicClass(PublicData):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def pref_() -> "AddonPreferences":
|
def pref_() -> "AddonPreferences":
|
||||||
return bpy.context.preferences.addons[G_ADDON_NAME].preferences
|
return bpy.context.preferences.addons[PublicData.G_ADDON_NAME].preferences
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pref(self=None) -> 'AddonPreferences':
|
def pref(self=None) -> 'AddonPreferences':
|
||||||
@ -24,37 +93,15 @@ class PublicClass:
|
|||||||
return PublicClass.pref_()
|
return PublicClass.pref_()
|
||||||
|
|
||||||
|
|
||||||
class Utils(Data):
|
class PublicUtils(PublicClass):
|
||||||
@classmethod
|
@classmethod
|
||||||
def set_reduce(cls, list_a, list_b, operation_type='-') -> list:
|
def value_limit(cls, value, max_value=1, min_value=0):
|
||||||
"""
|
"""
|
||||||
:param list_a: 列表a
|
@param value: limit value
|
||||||
:type list_a: list or set
|
@param max_value: Maximum allowed
|
||||||
:param list_b: 列表b
|
@param min_value: Minimum allowed
|
||||||
:type list_b:list or set
|
@return: If the input value is greater than the maximum value or less than the minimum value
|
||||||
:param operation_type :运算方法Enumerator in ['+','-','*','/'].
|
it will be limited to the maximum or minimum value
|
||||||
:type operation_type :str
|
|
||||||
:return list: 反回运算后的列表
|
|
||||||
"""
|
|
||||||
if operation_type == '-':
|
|
||||||
return [list_a[i] - list_b[i] for i in range(0, len(list_a))]
|
|
||||||
elif operation_type == '+':
|
|
||||||
return [list_a[i] + list_b[i] for i in range(0, len(list_a))]
|
|
||||||
elif operation_type == '/':
|
|
||||||
return [list_a[i] / list_b[i] for i in range(0, len(list_a))]
|
|
||||||
elif operation_type == '*':
|
|
||||||
return [list_a[i] * list_b[i] for i in range(0, len(list_a))]
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def value_limit(cls, value, max_value=1, min_value=0) -> float:
|
|
||||||
"""
|
|
||||||
:param value: 输入值
|
|
||||||
:type value: float
|
|
||||||
:param max_value: 允许的最大值
|
|
||||||
:type max_value: float
|
|
||||||
:param min_value: 允许的最小值
|
|
||||||
:type min_value: float
|
|
||||||
:return float: 反回小于最大值及大于最小值的浮点数
|
|
||||||
"""
|
"""
|
||||||
if value > max_value:
|
if value > max_value:
|
||||||
return max_value
|
return max_value
|
||||||
@ -71,33 +118,29 @@ class Utils(Data):
|
|||||||
return number == abs(number)
|
return number == abs(number)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_depsgraph(cls, obj: 'bpy.context.object'):
|
def get_depsgraph(cls, obj: 'bpy.types.Object'):
|
||||||
"""
|
"""
|
||||||
:param obj: 要被评估的物体
|
@param obj: dep obj
|
||||||
:type obj: bpy.types.Object
|
@return: If there is no input obj, reverse the active object evaluated
|
||||||
:return bpy.types.Object: 反回评估后的物体,计算应用修改器和实例化的数据
|
|
||||||
如果未输入物休将会评估活动物体
|
|
||||||
"""
|
"""
|
||||||
context = bpy.context
|
context = bpy.context
|
||||||
if obj is None:
|
if obj is None:
|
||||||
obj = context.object
|
obj = context.object
|
||||||
depsgraph = context.evaluated_depsgraph_get()
|
dep = context.evaluated_depsgraph_get()
|
||||||
return obj.evaluated_get(depsgraph)
|
return obj.evaluated_get(dep)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def link_active_collection(cls,
|
def link_obj_to_active_collection(cls, obj: 'bpy.types.Object'):
|
||||||
obj: 'bpy.context.object') -> \
|
|
||||||
'bpy.context.view_layer.active_layer_collection.collection.objects':
|
|
||||||
context = bpy.context
|
context = bpy.context
|
||||||
if obj.name not in context.view_layer.active_layer_collection.collection.objects:
|
objects = context.view_layer.active_layer_collection.collection.objects
|
||||||
context.view_layer.active_layer_collection.collection.objects.link(
|
if obj.name not in objects:
|
||||||
|
objects.link(
|
||||||
obj)
|
obj)
|
||||||
return context.view_layer.active_layer_collection.collection.objects
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def properties_is_modifier(cls) -> bool:
|
def properties_is_modifier(cls) -> bool:
|
||||||
"""
|
"""Returns whether there is a modifier property panel open in the active window.
|
||||||
反回活动窗口内是否有修改器属性面板被打开,如果打开则反回True else False
|
If it is open, it returns to True else False
|
||||||
"""
|
"""
|
||||||
for area in bpy.context.screen.areas:
|
for area in bpy.context.screen.areas:
|
||||||
if area.type == 'PROPERTIES':
|
if area.type == 'PROPERTIES':
|
||||||
@ -107,54 +150,9 @@ class Utils(Data):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def simple_deform_poll(cls, context: 'bpy.context') -> bool:
|
def bound_box_to_list(cls, obj: 'bpy.types.Object'):
|
||||||
"""Public poll"""
|
|
||||||
obj = context.object
|
|
||||||
if not obj:
|
|
||||||
return False
|
|
||||||
|
|
||||||
mod = obj.modifiers.active
|
|
||||||
if not mod:
|
|
||||||
return False
|
|
||||||
|
|
||||||
space = context.space_data
|
|
||||||
show_gizmo = space.show_gizmo if space.type == 'VIEW_3D' else True
|
|
||||||
|
|
||||||
available_obj_type = obj and (obj.type in ('MESH', 'LATTICE'))
|
|
||||||
available_modifiers_type = mod and (mod.type == 'SIMPLE_DEFORM')
|
|
||||||
is_available_obj = available_modifiers_type and available_obj_type
|
|
||||||
is_obj_mode = context.mode == 'OBJECT'
|
|
||||||
show_mod = mod.show_viewport
|
|
||||||
return is_available_obj and is_obj_mode and show_gizmo and show_mod
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def bound_box_to_list(cls, obj: 'bpy.context.object') -> tuple:
|
|
||||||
"""
|
|
||||||
:param obj:输入一个物体,反回物体的边界框列表
|
|
||||||
:type obj:bpy.types.Object
|
|
||||||
:return tuple:
|
|
||||||
"""
|
|
||||||
return tuple(i[:] for i in obj.bound_box)
|
return tuple(i[:] for i in obj.bound_box)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_origin_bounds(cls, obj: 'bpy.context.object') -> list:
|
|
||||||
modifiers_list = {}
|
|
||||||
for mod in obj.modifiers:
|
|
||||||
if (mod == obj.modifiers.active) or (modifiers_list != {}):
|
|
||||||
modifiers_list[mod] = (mod.show_render, mod.show_viewport)
|
|
||||||
mod.show_viewport = False
|
|
||||||
mod.show_render = False
|
|
||||||
matrix_obj = obj.matrix_world.copy()
|
|
||||||
obj.matrix_world.zero()
|
|
||||||
obj.scale = (1, 1, 1)
|
|
||||||
bound = cls.bound_box_to_list(obj)
|
|
||||||
obj.matrix_world = matrix_obj
|
|
||||||
for mod in modifiers_list:
|
|
||||||
show_render, show_viewport = modifiers_list[mod]
|
|
||||||
mod.show_render = show_render
|
|
||||||
mod.show_viewport = show_viewport
|
|
||||||
return list(bound)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_mesh_max_min_co(cls, obj: 'bpy.context.object') -> tuple:
|
def get_mesh_max_min_co(cls, obj: 'bpy.context.object') -> tuple:
|
||||||
if obj.type == 'MESH':
|
if obj.type == 'MESH':
|
||||||
@ -173,41 +171,6 @@ class Utils(Data):
|
|||||||
def matrix_calculation(cls, mat: 'Matrix', calculation_list: 'list') -> list:
|
def matrix_calculation(cls, mat: 'Matrix', calculation_list: 'list') -> list:
|
||||||
return [mat @ Vector(i) for i in calculation_list]
|
return [mat @ Vector(i) for i in calculation_list]
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_origin_property_group(cls, mod, ob):
|
|
||||||
if mod.origin:
|
|
||||||
return mod.origin.SimpleDeformGizmo_PropertyGroup
|
|
||||||
else:
|
|
||||||
return ob.SimpleDeformGizmo_PropertyGroup
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def set_empty_obj_matrix(cls, origin_mode, empty_object, up_, down_, up, down):
|
|
||||||
tow = (2, 2, 2)
|
|
||||||
if origin_mode == 'UP_LIMITS':
|
|
||||||
empty_object.matrix_world.translation = Vector(up_)
|
|
||||||
elif origin_mode == 'DOWN_LIMITS':
|
|
||||||
empty_object.matrix_world.translation = Vector(
|
|
||||||
down_)
|
|
||||||
elif origin_mode == 'LIMITS_MIDDLE':
|
|
||||||
empty_object.matrix_world.translation = cls.set_reduce(
|
|
||||||
cls.set_reduce(up_, down_, '+'), tow, '/')
|
|
||||||
elif origin_mode == 'MIDDLE':
|
|
||||||
empty_object.matrix_world.translation = cls.set_reduce(
|
|
||||||
cls.set_reduce(up, down, '+'), tow, '/')
|
|
||||||
|
|
||||||
@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
|
@classmethod
|
||||||
def point_to_angle(cls, i, j, f, axis_):
|
def point_to_angle(cls, i, j, f, axis_):
|
||||||
if i == j:
|
if i == j:
|
||||||
@ -225,6 +188,170 @@ class Utils(Data):
|
|||||||
angle = (180 * vector_value.angle(axis_) / math.pi)
|
angle = (180 * vector_value.angle(axis_) / math.pi)
|
||||||
return angle
|
return angle
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def co_to_direction(cls, mat, data):
|
||||||
|
(min_x, min_y, min_z), (max_x, max_y,
|
||||||
|
max_z) = data
|
||||||
|
a = mat @ Vector((max_x, max_y, max_z))
|
||||||
|
b = mat @ Vector((max_x, min_y, min_z))
|
||||||
|
c = mat @ Vector((min_x, max_y, min_z))
|
||||||
|
d = mat @ Vector((min_x, min_y, max_z))
|
||||||
|
return ((aa + bb) / Vector((2, 2, 2)) for aa, bb in ((a, d)
|
||||||
|
(c, b)
|
||||||
|
(c, d)
|
||||||
|
(a, b)
|
||||||
|
(d, b)
|
||||||
|
(c, a)))
|
||||||
|
|
||||||
|
|
||||||
|
class GizmoUtils(PublicUtils):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def each_face_pos(cls, mat: 'Matrix' = None):
|
||||||
|
if mat is None:
|
||||||
|
mat = Matrix()
|
||||||
|
return cls.co_to_direction(mat, cls.G_SimpleDeformGizmoHandlerDit['co'])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update_matrix(cls, mod, ob):
|
||||||
|
if mod.deform_method == 'BEND':
|
||||||
|
cls.new_empty(ob, mod)
|
||||||
|
if mod.origin:
|
||||||
|
empty_object = mod.origin
|
||||||
|
modifiers_co = cls.G_SimpleDeformGizmoHandlerDit['modifiers_co']
|
||||||
|
for index, mod_name in enumerate(modifiers_co):
|
||||||
|
co_items = list(modifiers_co.items())
|
||||||
|
if mod.name == mod_name:
|
||||||
|
data = co_items[index - 1][1] if (
|
||||||
|
index or (index != 1)) else modifiers_co['co']
|
||||||
|
(up, down), (up_, down_) = cls.get_limits_pos(
|
||||||
|
mod, cls.co_to_direction(ob.matrix_world.copy(), data))
|
||||||
|
origin_mode = cls.get_origin_property_group(
|
||||||
|
mod, ob).origin_mode
|
||||||
|
cls.set_empty_obj_matrix(
|
||||||
|
origin_mode, empty_object, up_, down_, up, down)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update_empty_matrix(cls):
|
||||||
|
ob = bpy.context.object
|
||||||
|
for mod in ob.modifiers:
|
||||||
|
if mod.type == 'SIMPLE_DEFORM':
|
||||||
|
cls.update_matrix(mod, ob)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_origin_property_group(cls, mod, ob):
|
||||||
|
if mod.origin:
|
||||||
|
return mod.origin.SimpleDeformGizmo_PropertyGroup
|
||||||
|
else:
|
||||||
|
return ob.SimpleDeformGizmo_PropertyGroup
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def simple_deform_public_poll(cls, context: 'bpy.types.context') -> bool:
|
||||||
|
"""Public poll
|
||||||
|
In 3D View
|
||||||
|
Active Object in ('MESH', 'LATTICE')
|
||||||
|
Active Modifier Type Is 'SIMPLE_DEFORM' and show_viewport
|
||||||
|
return True
|
||||||
|
"""
|
||||||
|
obj = context.object
|
||||||
|
if not obj:
|
||||||
|
return False
|
||||||
|
|
||||||
|
mod = obj.modifiers.active
|
||||||
|
if not mod:
|
||||||
|
return False
|
||||||
|
|
||||||
|
space = context.space_data
|
||||||
|
show_gizmo = space.show_gizmo if space.type == 'VIEW_3D' else True
|
||||||
|
|
||||||
|
available_obj_type = obj and (obj.type in ('MESH', 'LATTICE'))
|
||||||
|
available_modifiers_type = mod and (mod.type == 'SIMPLE_DEFORM')
|
||||||
|
is_available_obj = available_modifiers_type and available_obj_type
|
||||||
|
is_obj_mode = context.mode == 'OBJECT'
|
||||||
|
show_mod = mod.show_viewport
|
||||||
|
return is_available_obj and is_obj_mode and show_gizmo and show_mod
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_up_down_return_list(cls, mod, axis, up_, down_, data):
|
||||||
|
top, bottom, left, right, front, back = data
|
||||||
|
if 'BEND' == mod.deform_method:
|
||||||
|
if axis in ('X', 'Y'):
|
||||||
|
top = up_
|
||||||
|
bottom = down_
|
||||||
|
elif axis == 'Z':
|
||||||
|
right = up_
|
||||||
|
left = down_
|
||||||
|
else:
|
||||||
|
if axis == 'X':
|
||||||
|
right = up_
|
||||||
|
left = down_
|
||||||
|
elif axis == 'Y':
|
||||||
|
back = up_
|
||||||
|
front = down_
|
||||||
|
elif axis == 'Z':
|
||||||
|
top = up_
|
||||||
|
bottom = down_
|
||||||
|
return top, bottom, left, right, front, back
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def new_empty(cls, obj, mod):
|
||||||
|
origin = mod.origin
|
||||||
|
if origin is None:
|
||||||
|
new_name = cls.G_NAME + '_Empty_' + str(uuid.uuid4())
|
||||||
|
origin_object = bpy.data.objects.new(new_name, None)
|
||||||
|
cls.link_obj_to_active_collection(origin_object)
|
||||||
|
origin_object.hide_set(True)
|
||||||
|
origin_object.empty_display_size = min(obj.dimensions)
|
||||||
|
mod.origin = origin_object
|
||||||
|
else:
|
||||||
|
origin_object = mod.origin
|
||||||
|
origin_object.hide_viewport = False
|
||||||
|
|
||||||
|
if origin_object.parent != obj:
|
||||||
|
origin_object.parent = obj
|
||||||
|
|
||||||
|
# add constraints
|
||||||
|
con_name = cls.G_CON_LIMIT_NAME
|
||||||
|
if con_name in origin_object.constraints.keys():
|
||||||
|
limit_constraints = origin.constraints.get(con_name)
|
||||||
|
else:
|
||||||
|
limit_constraints = origin_object.constraints.new(
|
||||||
|
'LIMIT_ROTATION')
|
||||||
|
limit_constraints.name = con_name
|
||||||
|
limit_constraints.owner_space = 'WORLD'
|
||||||
|
limit_constraints.space_object = obj
|
||||||
|
limit_constraints.use_transform_limit = True
|
||||||
|
limit_constraints.use_limit_x = True
|
||||||
|
limit_constraints.use_limit_y = True
|
||||||
|
limit_constraints.use_limit_z = True
|
||||||
|
con_copy_name = cls.G_NAME + 'constraints_copy_rotation'
|
||||||
|
if con_copy_name in origin_object.constraints.keys():
|
||||||
|
copy_constraints = origin.constraints.get(con_copy_name)
|
||||||
|
else:
|
||||||
|
copy_constraints = origin_object.constraints.new(
|
||||||
|
'COPY_ROTATION')
|
||||||
|
copy_constraints.name = con_copy_name
|
||||||
|
copy_constraints.target = obj
|
||||||
|
copy_constraints.mix_mode = 'BEFORE'
|
||||||
|
copy_constraints.target_space = 'WORLD'
|
||||||
|
copy_constraints.owner_space = 'WORLD'
|
||||||
|
origin_object.rotation_euler.zero()
|
||||||
|
origin_object.scale = 1, 1, 1
|
||||||
|
return origin_object, con_name
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_empty_obj_matrix(cls, origin_mode, empty_object, up_, down_, up, down):
|
||||||
|
tow = Vector((2, 2, 2))
|
||||||
|
if origin_mode == 'UP_LIMITS':
|
||||||
|
empty_object.matrix_world.translation = Vector(up_)
|
||||||
|
elif origin_mode == 'DOWN_LIMITS':
|
||||||
|
empty_object.matrix_world.translation = Vector(
|
||||||
|
down_)
|
||||||
|
elif origin_mode == 'LIMITS_MIDDLE':
|
||||||
|
empty_object.matrix_world.translation = (up_ + down_) / tow
|
||||||
|
elif origin_mode == 'MIDDLE':
|
||||||
|
empty_object.matrix_world.translation = (up + down) / tow
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_up_down(cls, mod, axis, top, bottom, left, right, front, back):
|
def get_up_down(cls, mod, axis, top, bottom, left, right, front, back):
|
||||||
if 'BEND' == mod.deform_method:
|
if 'BEND' == mod.deform_method:
|
||||||
@ -265,21 +392,30 @@ class Utils(Data):
|
|||||||
up, down = cls.get_up_down(mod, axis, top, bottom,
|
up, down = cls.get_up_down(mod, axis, top, bottom,
|
||||||
left, right, front, back)
|
left, right, front, back)
|
||||||
|
|
||||||
ex = lambda a: cls.set_reduce(down, cls.set_reduce(cls.set_reduce(
|
ex = lambda a: down + ((up - down) * Vector((a, a, a)))
|
||||||
up, down, '-'), (a, a, a), '*'), '+')
|
|
||||||
|
|
||||||
up_ = ex(up_limits)
|
up_ = ex(up_limits)
|
||||||
down_ = ex(down_limits)
|
down_ = ex(down_limits)
|
||||||
return (up, down), (up_, down_)
|
return (up, down), (up_, down_)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update_bound_box(cls, object):
|
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 update_bound_box(cls, obj: 'bpy.types.Object'):
|
||||||
context = bpy.context
|
context = bpy.context
|
||||||
data = bpy.data
|
data = bpy.data
|
||||||
obj = object
|
|
||||||
matrix = obj.matrix_world.copy() # 物体矩阵
|
matrix = obj.matrix_world.copy() # 物体矩阵
|
||||||
|
|
||||||
# add simple_deform mesh
|
# add simple_deform mesh
|
||||||
(min_x, min_y, min_z), (max_x, max_y,
|
(min_x, min_y, min_z), (max_x, max_y,
|
||||||
max_z) = cls.get_mesh_max_min_co(object)
|
max_z) = cls.get_mesh_max_min_co(object)
|
||||||
@ -291,18 +427,19 @@ class Utils(Data):
|
|||||||
(min_x, min_y, max_z),
|
(min_x, min_y, max_z),
|
||||||
(max_x, max_y, max_z),
|
(max_x, max_y, max_z),
|
||||||
(min_x, max_y, max_z))
|
(min_x, max_y, max_z))
|
||||||
if data.objects.get(G_NAME):
|
name = cls.G_NAME
|
||||||
data.objects.remove(data.objects.get(G_NAME))
|
if data.objects.get(name):
|
||||||
|
data.objects.remove(data.objects.get(name))
|
||||||
|
|
||||||
if data.meshes.get(G_NAME):
|
if data.meshes.get(name):
|
||||||
data.meshes.remove(data.meshes.get(G_NAME))
|
data.meshes.remove(data.meshes.get(name))
|
||||||
mesh = data.meshes.new(G_NAME)
|
mesh = data.meshes.new(name)
|
||||||
mesh.from_pydata(vertexes, G_INDICES, [])
|
mesh.from_pydata(vertexes, cls.G_INDICES, [])
|
||||||
mesh.update()
|
mesh.update()
|
||||||
|
|
||||||
new_object = data.objects.new(G_NAME, mesh)
|
new_object = data.objects.new(name, mesh)
|
||||||
|
|
||||||
cls.link_active_collection(new_object)
|
cls.link_obj_to_active_collection(new_object)
|
||||||
|
|
||||||
if new_object.parent != obj:
|
if new_object.parent != obj:
|
||||||
new_object.parent = obj
|
new_object.parent = obj
|
||||||
@ -327,7 +464,7 @@ class Utils(Data):
|
|||||||
simple_deform.limits[0] = mo.limits[0]
|
simple_deform.limits[0] = mo.limits[0]
|
||||||
simple_deform.angle = mo.angle
|
simple_deform.angle = mo.angle
|
||||||
simple_deform.show_viewport = mo.show_viewport
|
simple_deform.show_viewport = mo.show_viewport
|
||||||
obj = Utils.get_depsgraph(new_object)
|
obj = PublicUtils.get_depsgraph(new_object)
|
||||||
cls.G_SimpleDeformGizmoHandlerDit['modifiers_co'][mo.name] = cls.get_mesh_max_min_co(
|
cls.G_SimpleDeformGizmoHandlerDit['modifiers_co'][mo.name] = cls.get_mesh_max_min_co(
|
||||||
obj)
|
obj)
|
||||||
new_object.hide_set(True)
|
new_object.hide_set(True)
|
||||||
@ -363,10 +500,19 @@ class Utils(Data):
|
|||||||
|
|
||||||
limits = context.object.modifiers.active.limits[:]
|
limits = context.object.modifiers.active.limits[:]
|
||||||
modifiers = [getattr(context.object.modifiers.active, i)
|
modifiers = [getattr(context.object.modifiers.active, i)
|
||||||
for i in G_MODIFIERS_PROPERTY]
|
for i in cls.G_MODIFIERS_PROPERTY]
|
||||||
|
|
||||||
cls.G_SimpleDeformGizmoHandlerDit['draw'] = (ver, indices, matrix, modifiers, limits)
|
cls.G_SimpleDeformGizmoHandlerDit['draw'] = (ver, indices, matrix, modifiers, limits)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def generate_co_data(cls):
|
||||||
|
handler_dit = cls.G_SimpleDeformGizmoHandlerDit
|
||||||
|
|
||||||
|
if 'co' not in handler_dit:
|
||||||
|
handler_dit['co'] = cls.get_mesh_max_min_co(
|
||||||
|
bpy.context.object)
|
||||||
|
return handler_dit['co']
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def update_co_data(cls, ob, mod):
|
def update_co_data(cls, ob, mod):
|
||||||
handler_dit = cls.G_SimpleDeformGizmoHandlerDit
|
handler_dit = cls.G_SimpleDeformGizmoHandlerDit
|
||||||
@ -379,137 +525,30 @@ class Utils(Data):
|
|||||||
cls.G_SimpleDeformGizmoHandlerDit['co'] = co_items[index - 1][1] if (index or (index != 1)) else \
|
cls.G_SimpleDeformGizmoHandlerDit['co'] = co_items[index - 1][1] if (index or (index != 1)) else \
|
||||||
modifiers_co['co']
|
modifiers_co['co']
|
||||||
|
|
||||||
|
|
||||||
|
class tmp:
|
||||||
@classmethod
|
@classmethod
|
||||||
def generate_co_data(cls):
|
def get_origin_bounds(cls, obj: 'bpy.types.Object') -> list:
|
||||||
handler_dit = cls.G_SimpleDeformGizmoHandlerDit
|
modifiers_dict = {}
|
||||||
|
for mod in obj.modifiers:
|
||||||
if 'co' not in handler_dit:
|
if (mod == obj.modifiers.active) or (modifiers_dict != {}):
|
||||||
handler_dit['co'] = cls.get_mesh_max_min_co(
|
modifiers_dict[mod] = (mod.show_render, mod.show_viewport)
|
||||||
bpy.context.object)
|
mod.show_viewport = False
|
||||||
return handler_dit['co']
|
mod.show_render = False
|
||||||
|
matrix_obj = obj.matrix_world.copy()
|
||||||
@classmethod
|
obj.matrix_world.zero()
|
||||||
def new_empty(cls, obj, mod):
|
obj.scale = (1, 1, 1)
|
||||||
origin = mod.origin
|
bound = cls.bound_box_to_list(obj)
|
||||||
if origin is None:
|
obj.matrix_world = matrix_obj
|
||||||
new_name = G_NAME + '_Empty_' + str(uuid.uuid4())
|
for mod in modifiers_dict:
|
||||||
origin_object = bpy.data.objects.new(new_name, None)
|
show_render, show_viewport = modifiers_dict[mod]
|
||||||
cls.link_active_collection(origin_object)
|
mod.show_render = show_render
|
||||||
origin_object.hide_set(True)
|
mod.show_viewport = show_viewport
|
||||||
origin_object.empty_display_size = min(obj.dimensions)
|
return list(bound)
|
||||||
mod.origin = origin_object
|
|
||||||
else:
|
|
||||||
origin_object = mod.origin
|
|
||||||
origin_object.hide_viewport = False
|
|
||||||
|
|
||||||
if origin_object.parent != obj:
|
|
||||||
origin_object.parent = obj
|
|
||||||
|
|
||||||
# add constraints
|
|
||||||
if G_CON_LIMIT_NAME in origin_object.constraints.keys():
|
|
||||||
limit_constraints = origin.constraints.get(G_CON_LIMIT_NAME)
|
|
||||||
else:
|
|
||||||
limit_constraints = origin_object.constraints.new(
|
|
||||||
'LIMIT_ROTATION')
|
|
||||||
limit_constraints.name = G_CON_LIMIT_NAME
|
|
||||||
limit_constraints.owner_space = 'WORLD'
|
|
||||||
limit_constraints.space_object = obj
|
|
||||||
limit_constraints.use_transform_limit = True
|
|
||||||
limit_constraints.use_limit_x = True
|
|
||||||
limit_constraints.use_limit_y = True
|
|
||||||
limit_constraints.use_limit_z = True
|
|
||||||
con_copy_name = G_NAME + 'constraints_copy_rotation'
|
|
||||||
if con_copy_name in origin_object.constraints.keys():
|
|
||||||
copy_constraints = origin.constraints.get(con_copy_name)
|
|
||||||
else:
|
|
||||||
copy_constraints = origin_object.constraints.new(
|
|
||||||
'COPY_ROTATION')
|
|
||||||
copy_constraints.name = con_copy_name
|
|
||||||
copy_constraints.target = obj
|
|
||||||
copy_constraints.mix_mode = 'BEFORE'
|
|
||||||
copy_constraints.target_space = 'WORLD'
|
|
||||||
copy_constraints.owner_space = 'WORLD'
|
|
||||||
origin_object.rotation_euler.zero()
|
|
||||||
origin_object.scale = 1, 1, 1
|
|
||||||
return origin_object, G_CON_LIMIT_NAME
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def co_to_direction(cls, mat, data):
|
|
||||||
(min_x, min_y, min_z), (max_x, max_y,
|
|
||||||
max_z) = data
|
|
||||||
a = mat @ Vector((max_x, max_y, max_z))
|
|
||||||
b = mat @ Vector((max_x, min_y, min_z))
|
|
||||||
c = mat @ Vector((min_x, max_y, min_z))
|
|
||||||
d = mat @ Vector((min_x, min_y, max_z))
|
|
||||||
|
|
||||||
def pos_get(a, b):
|
|
||||||
return cls.set_reduce(cls.set_reduce(a, b, '+'), (2, 2, 2), '/')
|
|
||||||
|
|
||||||
top = Vector(pos_get(a, d))
|
|
||||||
bottom = Vector(pos_get(c, b))
|
|
||||||
left = Vector(pos_get(c, d))
|
|
||||||
right = Vector(pos_get(a, b))
|
|
||||||
front = Vector(pos_get(d, b))
|
|
||||||
back = Vector(pos_get(c, a))
|
|
||||||
return top, bottom, left, right, front, back
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def each_face_pos(cls, mat: 'Matrix' = None):
|
|
||||||
if mat is None:
|
|
||||||
mat = Matrix()
|
|
||||||
return cls.co_to_direction(mat, cls.G_SimpleDeformGizmoHandlerDit['co'])
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def update_matrix(cls, mod, ob):
|
|
||||||
if mod.deform_method == 'BEND':
|
|
||||||
cls.new_empty(ob, mod)
|
|
||||||
if mod.origin:
|
|
||||||
empty_object = mod.origin
|
|
||||||
modifiers_co = cls.G_SimpleDeformGizmoHandlerDit['modifiers_co']
|
|
||||||
for index, mod_name in enumerate(modifiers_co):
|
|
||||||
co_items = list(modifiers_co.items())
|
|
||||||
if mod.name == mod_name:
|
|
||||||
data = co_items[index - 1][1] if (
|
|
||||||
index or (index != 1)) else modifiers_co['co']
|
|
||||||
(up, down), (up_, down_) = cls.get_limits_pos(
|
|
||||||
mod, cls.co_to_direction(ob.matrix_world.copy(), data))
|
|
||||||
origin_mode = cls.get_origin_property_group(
|
|
||||||
mod, ob).origin_mode
|
|
||||||
cls.set_empty_obj_matrix(
|
|
||||||
origin_mode, empty_object, up_, down_, up, down)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def update_empty_matrix(cls):
|
|
||||||
ob = bpy.context.object
|
|
||||||
for mod in ob.modifiers:
|
|
||||||
if mod.type == 'SIMPLE_DEFORM':
|
|
||||||
cls.update_matrix(mod, ob)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_up_down_return_list(cls, mod, axis, up_, down_, data):
|
|
||||||
top, bottom, left, right, front, back = data
|
|
||||||
if 'BEND' == mod.deform_method:
|
|
||||||
if axis in ('X', 'Y'):
|
|
||||||
top = up_
|
|
||||||
bottom = down_
|
|
||||||
elif axis == 'Z':
|
|
||||||
right = up_
|
|
||||||
left = down_
|
|
||||||
else:
|
|
||||||
if axis == 'X':
|
|
||||||
right = up_
|
|
||||||
left = down_
|
|
||||||
elif axis == 'Y':
|
|
||||||
back = up_
|
|
||||||
front = down_
|
|
||||||
elif axis == 'Z':
|
|
||||||
top = up_
|
|
||||||
bottom = down_
|
|
||||||
return top, bottom, left, right, front, back
|
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
PublicClass.load_gizmo_data()
|
PublicData.load_gizmo_data()
|
||||||
|
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
|
Loading…
Reference in New Issue
Block a user