new addon simple_deform_helper #104464
339
simple_deform_helper/LICENSE
Normal file
339
simple_deform_helper/LICENSE
Normal file
@ -0,0 +1,339 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
34
simple_deform_helper/README.md
Normal file
34
simple_deform_helper/README.md
Normal file
@ -0,0 +1,34 @@
|
||||
# SimpleDeformHelper
|
||||
# Move to [AIGODLIKE](https://github.com/AIGODLIKE/popoti_align_helper.git)
|
||||
|
||||
Simple Deform Modifier Visual adjustment tool
|
||||
|
||||
You can modify parameters more intuitively by dragging the gizmo directly
|
||||
|
||||
![emm.jpeg](image/emm.jpeg)
|
||||
|
||||
To display the gizmo, you need to select the active object and the active modifier is a simple deformation modifier
|
||||
|
||||
1. Angle control gizmo
|
||||
|
||||
You can drag to control the modifier's value
|
||||
|
||||
2. Axial control gizmo
|
||||
|
||||
Modify the deformation axis of the active modifier by clicking
|
||||
|
||||
|
||||
|
||||
Shortcut keys (when dragging the gizmo):
|
||||
|
||||
X Y Z: modify the modifier's deformation axis
|
||||
|
||||
A: Show deformation axis gizmo
|
||||
|
||||
Scroll wheel: switch the origin control mode
|
||||
|
||||
Toggle the axial gizmo (displayed only when the mode is bending):
|
||||
|
||||
6 directions, each direction has two directions that can be switched
|
||||
|
||||
While dragging the upper and lower limits gizmo, you can hold down Ctrl to maintain the relative distance between the upper and lower limits
|
31
simple_deform_helper/README_CN.md
Normal file
31
simple_deform_helper/README_CN.md
Normal file
@ -0,0 +1,31 @@
|
||||
# SimpleDeformHelper
|
||||
|
||||
Simple Deform Modifier Visual adjustment tool
|
||||
|
||||
You can modify parameters more intuitively by dragging the gizmo directly
|
||||
|
||||
![emm.jpeg](image/emm.jpeg)
|
||||
|
||||
显示Gizmo需要选择活动物体并且活动修改器为简易形变修改器
|
||||
|
||||
1.角度控制Gizmo
|
||||
|
||||
可通过拖动来控制修改器的值
|
||||
|
||||
2.轴向控制Gizmo
|
||||
|
||||
通过单击修改活动修改器的形变轴向
|
||||
|
||||
快捷键(在拖动Gizmo时):
|
||||
|
||||
X Y Z:修改修改器的变形轴
|
||||
|
||||
A:显示变形轴Gizmo
|
||||
|
||||
滚轮滚动:切换原点控制模式
|
||||
|
||||
切换轴向Gizmo(只有在模式为弯曲时才可显示):
|
||||
|
||||
6个方向每个方向有两个方向可切换
|
||||
|
||||
在拖动上下限Gizmo时可以按住Ctrl 保持上下限之间的相对距离
|
34
simple_deform_helper/__init__.py
Normal file
34
simple_deform_helper/__init__.py
Normal file
@ -0,0 +1,34 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
from . import gizmo, operators, preferences, data,timers,translate
|
||||
|
||||
bl_info = {
|
||||
"name": "SimpleDeformHelper",
|
||||
"author": "AIGODLIKE Community(BlenderCN辣椒,小萌新)",
|
||||
"version": (0, 1, 1),
|
||||
"blender": (3, 0, 0),
|
||||
"location": "3D View -> Select an object and the active modifier is simple deformation",
|
||||
"description": "Simple Deform visualization adjustment tool",
|
||||
"doc_url": "https://github.com/Yorha4D/simple_deform_helper/blob/main/README_CN.md",
|
||||
"wiki_url": "",
|
||||
"category": "3D View"
|
||||
}
|
||||
|
||||
module_tuple = (
|
||||
gizmo,
|
||||
timers,
|
||||
translate,
|
||||
operators,
|
||||
preferences,
|
||||
)
|
||||
|
||||
|
||||
def register():
|
||||
data.Data.load_gizmo_data()
|
||||
|
||||
for item in module_tuple:
|
||||
item.register()
|
||||
|
||||
|
||||
def unregister():
|
||||
for item in module_tuple:
|
||||
item.unregister()
|
57
simple_deform_helper/data.py
Normal file
57
simple_deform_helper/data.py
Normal file
@ -0,0 +1,57 @@
|
||||
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
|
212
simple_deform_helper/draw.py
Normal file
212
simple_deform_helper/draw.py
Normal file
@ -0,0 +1,212 @@
|
||||
import bgl
|
||||
import blf
|
||||
import bpy
|
||||
import gpu
|
||||
from gpu_extras.batch import batch_for_shader
|
||||
from mathutils import Vector
|
||||
|
||||
from .data import G_INDICES, G_MODIFIERS_PROPERTY, G_NAME, Data
|
||||
from .utils import Pref, Utils
|
||||
|
||||
|
||||
class Handler(Data):
|
||||
@classmethod
|
||||
def add_handler(cls):
|
||||
"""向3d视图添加绘制handler
|
||||
并将其存储下来
|
||||
"""
|
||||
if 'handler' not in cls.G_SimpleDeformGizmoHandlerDit:
|
||||
cls.G_SimpleDeformGizmoHandlerDit['handler'] = bpy.types.SpaceView3D.draw_handler_add(
|
||||
Draw3D.draw_bound_box, (), 'WINDOW', 'POST_VIEW')
|
||||
|
||||
@classmethod
|
||||
def del_handler_text(cls):
|
||||
|
||||
if 'handler_text' in cls.G_SimpleDeformGizmoHandlerDit:
|
||||
bpy.types.SpaceView3D.draw_handler_remove(
|
||||
cls.G_SimpleDeformGizmoHandlerDit['handler_text'], 'WINDOW')
|
||||
cls.G_SimpleDeformGizmoHandlerDit.pop('handler_text')
|
||||
|
||||
@classmethod
|
||||
def del_handler(cls):
|
||||
data = bpy.data
|
||||
if data.meshes.get(G_NAME):
|
||||
data.meshes.remove(data.meshes.get(G_NAME))
|
||||
|
||||
if data.objects.get(G_NAME):
|
||||
data.objects.remove(data.objects.get(G_NAME))
|
||||
|
||||
cls.del_handler_text()
|
||||
|
||||
if 'handler' in cls.G_SimpleDeformGizmoHandlerDit:
|
||||
bpy.types.SpaceView3D.draw_handler_remove(
|
||||
cls.G_SimpleDeformGizmoHandlerDit['handler'], 'WINDOW')
|
||||
cls.G_SimpleDeformGizmoHandlerDit.clear()
|
||||
|
||||
|
||||
class Draw3D(Pref, Data):
|
||||
|
||||
@classmethod
|
||||
def draw_3d_shader(cls, pos, indices, color=None, *, shader_name='3D_UNIFORM_COLOR', draw_type='LINES'):
|
||||
"""
|
||||
:param draw_type:
|
||||
:param shader_name:
|
||||
:param color:
|
||||
:param indices:
|
||||
:param pos:
|
||||
:type pos:list ((0,0,0),(1,1,1))
|
||||
2D_FLAT_COLOR - 2D_IMAGE - 2D_SMOOTH_COLOR - 2D_UNIFORM_COLOR - 3D_FLAT_COLOR - 3D_SMOOTH_COLOR - 3D_UNIFORM_COLOR - 3D_POLYLINE_FLAT_COLOR - 3D_POLYLINE_SMOOTH_COLOR - 3D_POLYLINE_UNIFORM_COLOR
|
||||
('POINTS', 'LINES', 'TRIS', 'LINE_STRIP', 'LINE_LOOP','TRI_STRIP',
|
||||
'TRI_FAN', 'LINES_ADJ', 'TRIS_ADJ', 'LINE_STRIP_ADJ')
|
||||
`NONE`, `ALWAYS`, `LESS`, `LESS_EQUAL`, `EQUAL`, `GREATER` and `GREATER_EQUAL`
|
||||
"""
|
||||
|
||||
shader = gpu.shader.from_builtin(shader_name)
|
||||
if draw_type == 'POINTS':
|
||||
batch = batch_for_shader(shader, draw_type, {'pos': pos})
|
||||
else:
|
||||
batch = batch_for_shader(
|
||||
shader, draw_type, {'pos': pos}, indices=indices)
|
||||
|
||||
shader.bind()
|
||||
if color:
|
||||
shader.uniform_float('color', color)
|
||||
|
||||
batch.draw(shader)
|
||||
|
||||
font_info = {
|
||||
'font_id': 0,
|
||||
'handler': None,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def draw_str(cls):
|
||||
obj = bpy.context.object
|
||||
font_id = cls.font_info['font_id']
|
||||
|
||||
blf.position(font_id, 200, 80, 0)
|
||||
blf.size(font_id, 15, 72)
|
||||
blf.color(font_id, 1, 1, 1, 1)
|
||||
blf.draw(
|
||||
font_id,
|
||||
f'The scaling value of the object {obj.name_full} is not 1,'
|
||||
f' which will cause the deformation of the simple deformation modifier.'
|
||||
f' Please apply the scaling before deformation')
|
||||
if obj.scale == Vector((1, 1, 1)):
|
||||
Handler.del_handler_text()
|
||||
|
||||
@classmethod
|
||||
def draw_text(cls, x, y, text='Hello Word', font_id=0, size=10, *, color=(0.5, 0.5, 0.5, 1), dpi=72, column=0):
|
||||
blf.position(font_id, x, y - (size * (column + 1)), 0)
|
||||
blf.size(font_id, size, dpi)
|
||||
blf.draw(font_id, text)
|
||||
blf.color(font_id, *color)
|
||||
|
||||
@classmethod
|
||||
def draw_box(cls, data, mat):
|
||||
pref = cls._pref()
|
||||
coords = Utils.matrix_calculation(mat,
|
||||
cls.data_to_calculation(data))
|
||||
cls.draw_3d_shader(coords, G_INDICES, pref.bound_box_color)
|
||||
|
||||
@classmethod
|
||||
def data_to_calculation(cls, data):
|
||||
((min_x, min_y, min_z), (max_x, max_y, max_z)) = data
|
||||
return (
|
||||
(max_x, min_y, min_z),
|
||||
(min_x, min_y, min_z),
|
||||
(max_x, max_y, min_z),
|
||||
(min_x, max_y, min_z),
|
||||
(max_x, min_y, max_z),
|
||||
(min_x, min_y, max_z),
|
||||
(max_x, max_y, max_z),
|
||||
(min_x, max_y, max_z))
|
||||
|
||||
@classmethod
|
||||
def draw_limits_bound_box(cls):
|
||||
|
||||
pref = cls._pref()
|
||||
handler_dit = cls.G_SimpleDeformGizmoHandlerDit
|
||||
if 'draw_limits_bound_box' in handler_dit:
|
||||
# draw limits_bound_box
|
||||
mat, data = handler_dit['draw_limits_bound_box']
|
||||
bgl.glEnable(bgl.GL_DEPTH_TEST)
|
||||
coords = Utils.matrix_calculation(mat, cls.data_to_calculation(data))
|
||||
cls.draw_3d_shader(coords,
|
||||
G_INDICES,
|
||||
pref.limits_bound_box_color)
|
||||
|
||||
@classmethod
|
||||
def draw_limits_line(cls):
|
||||
handler_dit = cls.G_SimpleDeformGizmoHandlerDit
|
||||
if 'draw_line' in handler_dit:
|
||||
line_pos, limits_pos, = handler_dit['draw_line']
|
||||
bgl.glDisable(bgl.GL_DEPTH_TEST)
|
||||
# draw limits line
|
||||
cls.draw_3d_shader(limits_pos, ((1, 0),), (1, 1, 0, 0.5))
|
||||
# draw line
|
||||
cls.draw_3d_shader(line_pos, ((1, 0),), (1, 1, 0, 0.3))
|
||||
# draw pos
|
||||
cls.draw_3d_shader([line_pos[1]], (), (0, 1, 0, 0.5),
|
||||
shader_name='3D_UNIFORM_COLOR', draw_type='POINTS')
|
||||
|
||||
@classmethod
|
||||
def draw_deform_mesh(cls, ob, context):
|
||||
pref = cls._pref()
|
||||
handler_dit = cls.G_SimpleDeformGizmoHandlerDit
|
||||
active = context.object.modifiers.active
|
||||
# draw deform mesh
|
||||
if 'draw' in handler_dit:
|
||||
pos, indices, mat, mod_data, limits = handler_dit['draw']
|
||||
if ([getattr(active, i) for i in G_MODIFIERS_PROPERTY] == mod_data) and (
|
||||
ob.matrix_world == mat) and limits == active.limits[:]:
|
||||
bgl.glEnable(bgl.GL_DEPTH_TEST)
|
||||
cls.draw_3d_shader(
|
||||
pos, indices, pref.deform_wireframe_color)
|
||||
|
||||
@classmethod
|
||||
def draw_scale_text(cls, ob):
|
||||
if (ob.scale != Vector((1, 1, 1))) and ('handler_text' not in cls.G_SimpleDeformGizmoHandlerDit):
|
||||
cls.G_SimpleDeformGizmoHandlerDit['handler_text'] = bpy.types.SpaceView3D.draw_handler_add(
|
||||
cls.draw_str, (), 'WINDOW', 'POST_PIXEL')
|
||||
|
||||
@classmethod
|
||||
def is_draw_box(cls, context):
|
||||
obj = context.object # 活动物体
|
||||
matrix = obj.matrix_world # 活动物体矩阵
|
||||
modifier = context.object.modifiers.active # 活动修改器
|
||||
|
||||
pref = cls._pref()
|
||||
simple_poll = Utils.simple_deform_poll(context)
|
||||
bend = modifier and (modifier.deform_method == 'BEND')
|
||||
display_switch_axis = False == pref.display_bend_axis_switch_gizmo
|
||||
|
||||
cls.draw_scale_text(obj)
|
||||
Utils.update_co_data(obj, modifier)
|
||||
|
||||
co_data = Utils.generate_co_data()
|
||||
|
||||
if simple_poll and ((not bend) or display_switch_axis):
|
||||
# draw bound box
|
||||
cls.draw_box(co_data, matrix)
|
||||
cls.draw_deform_mesh(obj, context)
|
||||
cls.draw_limits_line()
|
||||
cls.draw_limits_bound_box()
|
||||
elif simple_poll and (bend and not display_switch_axis):
|
||||
bgl.glDisable(bgl.GL_DEPTH_TEST)
|
||||
cls.draw_box(co_data, matrix)
|
||||
Utils.new_empty(obj, modifier)
|
||||
|
||||
@classmethod
|
||||
def draw_bound_box(cls):
|
||||
gpu.state.blend_set('ALPHA')
|
||||
gpu.state.line_width_set(1)
|
||||
bgl.glEnable(bgl.GL_BLEND)
|
||||
bgl.glEnable(bgl.GL_ALPHA)
|
||||
bgl.glDisable(bgl.GL_DEPTH_TEST)
|
||||
|
||||
context = bpy.context
|
||||
if Utils.simple_deform_poll(context):
|
||||
cls.is_draw_box(context)
|
||||
else:
|
||||
Handler.del_handler()
|
1
simple_deform_helper/gizmo.json
Normal file
1
simple_deform_helper/gizmo.json
Normal file
File diff suppressed because one or more lines are too long
619
simple_deform_helper/gizmo.py
Normal file
619
simple_deform_helper/gizmo.py
Normal file
@ -0,0 +1,619 @@
|
||||
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, Pref
|
||||
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, Pref):
|
||||
"""显示轴向切换拖动点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, Pref, 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, Pref):
|
||||
"""绘制切换变型轴的
|
||||
变换方向
|
||||
"""
|
||||
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()
|
BIN
simple_deform_helper/image/emm.jpeg
Normal file
BIN
simple_deform_helper/image/emm.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 155 KiB |
68
simple_deform_helper/operators.py
Normal file
68
simple_deform_helper/operators.py
Normal file
@ -0,0 +1,68 @@
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import FloatProperty, StringProperty, BoolProperty
|
||||
|
||||
from .utils import Pref
|
||||
|
||||
|
||||
class DeformAxisOperator(Operator,Pref):
|
||||
bl_idname = 'simple_deform_gizmo.deform_axis'
|
||||
bl_label = 'deform_axis'
|
||||
bl_description = 'deform_axis operator'
|
||||
bl_options = {'REGISTER'}
|
||||
|
||||
Deform_Axis: StringProperty(default='X', options={'SKIP_SAVE'})
|
||||
|
||||
X_Value: FloatProperty(default=-0, options={'SKIP_SAVE'})
|
||||
Y_Value: FloatProperty(default=-0, options={'SKIP_SAVE'})
|
||||
Z_Value: FloatProperty(default=-0, options={'SKIP_SAVE'})
|
||||
|
||||
Is_Positive: BoolProperty(default=True, options={'SKIP_SAVE'})
|
||||
|
||||
def execute(self, context):
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.modal_handler_add(self)
|
||||
return {'RUNNING_MODAL'}
|
||||
|
||||
def modal(self, context, event):
|
||||
from .gizmo import Utils
|
||||
|
||||
mod = context.object.modifiers.active
|
||||
mod.deform_axis = self.Deform_Axis
|
||||
empty, con_limit_name = Utils.new_empty(context.object, mod)
|
||||
is_positive = Utils.is_positive(mod.angle)
|
||||
|
||||
for limit, value in (('max_x', self.X_Value),
|
||||
('min_x', self.X_Value),
|
||||
('max_y', self.Y_Value),
|
||||
('min_y', self.Y_Value),
|
||||
('max_z', self.Z_Value),
|
||||
('min_z', self.Z_Value),
|
||||
):
|
||||
setattr(empty.constraints[con_limit_name], limit, value)
|
||||
|
||||
if ((not is_positive) and self.Is_Positive) or (is_positive and (not self.Is_Positive)):
|
||||
mod.angle = mod.angle * -1
|
||||
|
||||
if not event.ctrl:
|
||||
self.pref.display_bend_axis_switch_gizmo = False
|
||||
|
||||
Utils.update_bound_box(context.object)
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
class_list = (
|
||||
DeformAxisOperator,
|
||||
)
|
||||
|
||||
register_class, unregister_class = bpy.utils.register_classes_factory(class_list)
|
||||
|
||||
|
||||
def register():
|
||||
register_class()
|
||||
|
||||
|
||||
def unregister():
|
||||
unregister_class()
|
177
simple_deform_helper/preferences.py
Normal file
177
simple_deform_helper/preferences.py
Normal file
@ -0,0 +1,177 @@
|
||||
import os
|
||||
import bpy
|
||||
from bpy.props import (FloatProperty,
|
||||
PointerProperty,
|
||||
FloatVectorProperty,
|
||||
EnumProperty,
|
||||
BoolProperty)
|
||||
from bpy.types import (
|
||||
AddonPreferences,
|
||||
PropertyGroup,
|
||||
)
|
||||
|
||||
from .data import G_ADDON_NAME
|
||||
from .utils import Pref, Utils
|
||||
|
||||
|
||||
class SimpleDeformGizmoAddonPreferences(AddonPreferences, Pref):
|
||||
bl_idname = G_ADDON_NAME
|
||||
|
||||
deform_wireframe_color: FloatVectorProperty(
|
||||
name='Deform Wireframe',
|
||||
description='Draw Deform Wireframe Color',
|
||||
default=(1, 1, 1, 0.3),
|
||||
soft_max=1,
|
||||
soft_min=0,
|
||||
size=4, subtype='COLOR')
|
||||
bound_box_color: FloatVectorProperty(
|
||||
name='Bound Box',
|
||||
description='Draw Bound Box Color',
|
||||
default=(1, 0, 0, 0.1),
|
||||
soft_max=1,
|
||||
soft_min=0,
|
||||
size=4,
|
||||
subtype='COLOR')
|
||||
limits_bound_box_color: FloatVectorProperty(
|
||||
name='Upper and lower limit Bound Box Color',
|
||||
description='Draw Upper and lower limit Bound Box Color',
|
||||
default=(0.3, 1, 0.2, 0.5),
|
||||
soft_max=1,
|
||||
soft_min=0,
|
||||
size=4,
|
||||
subtype='COLOR')
|
||||
modifiers_limits_tolerance: FloatProperty(
|
||||
name='Upper and lower limit tolerance',
|
||||
description='Minimum value between upper and lower limits',
|
||||
default=0.05,
|
||||
max=1,
|
||||
min=0.0001
|
||||
)
|
||||
display_bend_axis_switch_gizmo: BoolProperty(
|
||||
name='Show Toggle Axis Gizmo',
|
||||
default=False,
|
||||
options={'SKIP_SAVE'})
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
if __name__ is None:
|
||||
from bpy.types import Panel
|
||||
layout = Panel.layout
|
||||
layout.prop(self, 'deform_wireframe_color')
|
||||
layout.prop(self, 'bound_box_color')
|
||||
layout.prop(self, 'limits_bound_box_color')
|
||||
layout.prop(self, 'modifiers_limits_tolerance')
|
||||
layout.prop(self, 'display_bend_axis_switch_gizmo')
|
||||
|
||||
def draw_header_tool_settings(self, context):
|
||||
layout = self.layout
|
||||
if Utils.simple_deform_poll(context):
|
||||
layout.separator(factor=5)
|
||||
active_mod = context.object.modifiers.active
|
||||
prop = context.object.SimpleDeformGizmo_PropertyGroup
|
||||
pref = Pref._pref()
|
||||
|
||||
if active_mod.origin:
|
||||
layout.prop(active_mod.origin.SimpleDeformGizmo_PropertyGroup,
|
||||
'origin_mode',
|
||||
text='')
|
||||
else:
|
||||
layout.prop(prop,
|
||||
'origin_mode',
|
||||
text='')
|
||||
layout.prop(pref,
|
||||
'modifiers_limits_tolerance',
|
||||
text='')
|
||||
if active_mod.deform_method == 'BEND':
|
||||
layout.prop(pref,
|
||||
'display_bend_axis_switch_gizmo',
|
||||
toggle=1)
|
||||
layout.separator(factor=0.5)
|
||||
layout.prop(active_mod,
|
||||
'deform_method',
|
||||
expand=True)
|
||||
layout.prop(active_mod,
|
||||
'deform_axis',
|
||||
expand=True)
|
||||
layout.prop(active_mod,
|
||||
'angle')
|
||||
layout.prop(active_mod,
|
||||
'factor')
|
||||
|
||||
|
||||
class SimpleDeformGizmoObjectPropertyGroup(PropertyGroup):
|
||||
|
||||
def _limits_up(self, context):
|
||||
mod = context.object.modifiers
|
||||
if mod and (mod.active.type == 'SIMPLE_DEFORM'):
|
||||
mod = mod.active
|
||||
mod.limits[1] = self.up_limits
|
||||
|
||||
up_limits: FloatProperty(name='up',
|
||||
description='UP Limits(Red)',
|
||||
default=1,
|
||||
update=_limits_up,
|
||||
max=1,
|
||||
min=0)
|
||||
|
||||
def _limits_down(self, context):
|
||||
mod = context.object.modifiers
|
||||
if mod and (mod.active.type == 'SIMPLE_DEFORM'):
|
||||
mod = mod.active
|
||||
mod.limits[0] = self.down_limits
|
||||
|
||||
down_limits: FloatProperty(name='down',
|
||||
description='Lower limit(Green)',
|
||||
default=0,
|
||||
update=_limits_down,
|
||||
max=1,
|
||||
min=0)
|
||||
|
||||
origin_mode_items = (
|
||||
('UP_LIMITS',
|
||||
'Follow Upper Limit(Red)',
|
||||
'Add an empty object origin as the rotation axis (if there is an origin, do not add it), and set the origin '
|
||||
'position as the upper limit during operation'),
|
||||
('DOWN_LIMITS',
|
||||
'Follow Lower Limit(Green)',
|
||||
'Add an empty object origin as the rotation axis (if there is an origin, do not add it), and set the origin '
|
||||
'position as the lower limit during operation'),
|
||||
('LIMITS_MIDDLE',
|
||||
'Middle',
|
||||
'Add an empty object origin as the rotation axis (if there is an origin, do not add it), and set the '
|
||||
'origin position between the upper and lower limits during operation'),
|
||||
('MIDDLE',
|
||||
'Bound Middle',
|
||||
'Add an empty object origin as the rotation axis (if there is an origin, do not add it), and set the origin '
|
||||
'position as the position between the bounding boxes during operation'),
|
||||
('NOT', 'No origin operation', ''),
|
||||
)
|
||||
|
||||
origin_mode: EnumProperty(name='Origin control mode',
|
||||
default='NOT',
|
||||
items=origin_mode_items)
|
||||
|
||||
|
||||
class_list = (
|
||||
SimpleDeformGizmoAddonPreferences,
|
||||
SimpleDeformGizmoObjectPropertyGroup,
|
||||
)
|
||||
|
||||
register_class, unregister_class = bpy.utils.register_classes_factory(class_list)
|
||||
|
||||
|
||||
def register():
|
||||
register_class()
|
||||
|
||||
Pref._pref().display_bend_axis_switch_gizmo = False
|
||||
bpy.types.Object.SimpleDeformGizmo_PropertyGroup = PointerProperty(
|
||||
type=SimpleDeformGizmoObjectPropertyGroup,
|
||||
name='SimpleDeformGizmo_PropertyGroup')
|
||||
bpy.types.VIEW3D_MT_editor_menus.append(
|
||||
SimpleDeformGizmoAddonPreferences.draw_header_tool_settings)
|
||||
|
||||
|
||||
def unregister():
|
||||
unregister_class()
|
||||
bpy.types.VIEW3D_MT_editor_menus.remove(
|
||||
SimpleDeformGizmoAddonPreferences.draw_header_tool_settings)
|
28
simple_deform_helper/timers.py
Normal file
28
simple_deform_helper/timers.py
Normal file
@ -0,0 +1,28 @@
|
||||
import bpy
|
||||
from bpy.app import timers
|
||||
|
||||
|
||||
def remove_not_use_empty(remove_name: str = "ViewSimpleDeformGizmo__Empty_"):
|
||||
"""循环场景内的所有物体,找出没用的空物体并删掉
|
||||
"""
|
||||
context = bpy.context
|
||||
for obj in context.scene.objects:
|
||||
is_empty = obj.type == "EMPTY"
|
||||
not_parent = not obj.parent
|
||||
name_ok = obj.name.count(remove_name)
|
||||
if name_ok and not_parent and is_empty:
|
||||
bpy.data.objects.remove(obj) # remove object
|
||||
|
||||
|
||||
def update_timers() -> float:
|
||||
remove_not_use_empty()
|
||||
return 3
|
||||
|
||||
|
||||
def register():
|
||||
timers.register(update_timers, persistent=True)
|
||||
|
||||
|
||||
def unregister():
|
||||
if timers.is_registered(update_timers):
|
||||
timers.unregister(update_timers)
|
55
simple_deform_helper/translate.py
Normal file
55
simple_deform_helper/translate.py
Normal file
@ -0,0 +1,55 @@
|
||||
import bpy
|
||||
|
||||
|
||||
def origin_text(a, b):
|
||||
return "Add an empty object origin as the rotation axis (if there is an origin, " + a + \
|
||||
"), and set the origin position " + b + " during operation"
|
||||
|
||||
|
||||
translations_dict = {
|
||||
"zh_CN": {
|
||||
("上下文", "原文"): "翻译文字",
|
||||
("*", "Show Toggle Axis Gizmo"): "显示切换轴向Gizmo",
|
||||
|
||||
("*", "Follow Upper Limit(Red)"): "跟随上限(红色)",
|
||||
("*", "Follow Lower Limit(Green)"): "跟随下限(绿色)",
|
||||
("*", "Lower limit(Green)"): "下限(绿色)",
|
||||
("*", "UP Limits(Red)"): "上限(红色)",
|
||||
("*", "Minimum value between upper and lower limits"): "上限与下限之间的最小值",
|
||||
("*", "Upper and lower limit tolerance"): "上下限容差",
|
||||
("*", "Draw Upper and lower limit Bound Box Color"): "绘制网格上限下限边界线框的颜色",
|
||||
("*", "Upper and lower limit Bound Box Color"): "上限下限边界框颜色",
|
||||
("*", "Draw Bound Box Color"): "绘制网格边界框的颜色",
|
||||
("*", "Bound Box"): "边界框颜色",
|
||||
("*", "Draw Deform Wireframe Color"): "绘制网格形变形状线框的颜色",
|
||||
("*", "Deform Wireframe"): "形变线框颜色",
|
||||
("*", "Simple Deform visualization adjustment tool"): "简易形变可视化工具",
|
||||
("*", "Select an object and the active modifier is Simple Deform"): "选择物体并且活动修改器为简易形变",
|
||||
("*", "Bound Middle"): "边界框中心",
|
||||
("*", origin_text("do not add it", "as the lower limit")):
|
||||
"添加一个空物体原点作为旋转轴(如果已有原点则不添加),并在操作时设置原点位置为下限位置",
|
||||
("*", origin_text("do not add it", "as the upper limit")):
|
||||
"添加一个空物体原点作为旋转轴(如果已有原点则不添加),并在操作时设置原点位置为上限位置",
|
||||
("*", origin_text("it will not be added", "between the upper and lower limits")):
|
||||
"添加一个空物体原点作为旋转轴(如果已有原点则不添加),并在操作时设置原点位置为上下限之间的位置",
|
||||
("*", origin_text("do not add it", "as the position between the bounding boxes")):
|
||||
"添加一个空物体原点作为旋转轴(如果已有原点则不添加),并在操作时设置原点位置为边界框之间的位置",
|
||||
("*", "No origin operation"): "不进行原点操作",
|
||||
("*", "Origin control mode"): "原点控制模式",
|
||||
("*", "Down limit"): "下限",
|
||||
("*", "Coefficient"): "系数",
|
||||
("*", "Upper limit"): "上限",
|
||||
("*", "3D View -> Select an object and the active modifier is simple deformation"): "3D视图 -> 选择一个物体,"
|
||||
"并且活动修改器为简易形修改器",
|
||||
("*", "3D View: SimpleDeformHelper"): "3D 视图: SimpleDeformHelper 简易形变助手",
|
||||
("*", ""): "",
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def register():
|
||||
bpy.app.translations.register(__name__, translations_dict)
|
||||
|
||||
|
||||
def unregister():
|
||||
bpy.app.translations.unregister(__name__)
|
510
simple_deform_helper/utils.py
Normal file
510
simple_deform_helper/utils.py
Normal file
@ -0,0 +1,510 @@
|
||||
import math
|
||||
import uuid
|
||||
|
||||
import bpy
|
||||
import numpy as np
|
||||
from bpy.types import AddonPreferences
|
||||
from mathutils import Vector, Matrix
|
||||
|
||||
from .data import G_ADDON_NAME, G_NAME, G_INDICES, G_MODIFIERS_PROPERTY, G_CON_LIMIT_NAME, Data
|
||||
|
||||
|
||||
class Pref:
|
||||
@staticmethod
|
||||
def _pref() -> "AddonPreferences":
|
||||
return bpy.context.preferences.addons[G_ADDON_NAME].preferences
|
||||
|
||||
@property
|
||||
def pref(self=None) -> 'AddonPreferences':
|
||||
"""
|
||||
:return: AddonPreferences
|
||||
"""
|
||||
return Pref._pref()
|
||||
|
||||
|
||||
class Utils(Data):
|
||||
@classmethod
|
||||
def set_reduce(cls, list_a, list_b, operation_type='-') -> list:
|
||||
"""
|
||||
:param list_a: 列表a
|
||||
:type list_a: list or set
|
||||
:param list_b: 列表b
|
||||
:type list_b:list or set
|
||||
:param operation_type :运算方法Enumerator in ['+','-','*','/'].
|
||||
: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:
|
||||
return max_value
|
||||
elif value < min_value:
|
||||
return min_value
|
||||
else:
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def is_positive(cls, number: 'int') -> bool:
|
||||
"""return bool value
|
||||
if number is positive return True else return False
|
||||
"""
|
||||
return number == abs(number)
|
||||
|
||||
@classmethod
|
||||
def get_depsgraph(cls, obj: 'bpy.context.object'):
|
||||
"""
|
||||
:param obj: 要被评估的物体
|
||||
:type obj: bpy.types.Object
|
||||
:return bpy.types.Object: 反回评估后的物体,计算应用修改器和实例化的数据
|
||||
如果未输入物休将会评估活动物体
|
||||
"""
|
||||
context = bpy.context
|
||||
if obj is None:
|
||||
obj = context.object
|
||||
depsgraph = context.evaluated_depsgraph_get()
|
||||
return obj.evaluated_get(depsgraph)
|
||||
|
||||
@classmethod
|
||||
def link_active_collection(cls,
|
||||
obj: 'bpy.context.object') -> \
|
||||
'bpy.context.view_layer.active_layer_collection.collection.objects':
|
||||
context = bpy.context
|
||||
if obj.name not in context.view_layer.active_layer_collection.collection.objects:
|
||||
context.view_layer.active_layer_collection.collection.objects.link(
|
||||
obj)
|
||||
return context.view_layer.active_layer_collection.collection.objects
|
||||
|
||||
@classmethod
|
||||
def properties_is_modifier(cls) -> bool:
|
||||
"""
|
||||
反回活动窗口内是否有修改器属性面板被打开,如果打开则反回True else False
|
||||
"""
|
||||
for area in bpy.context.screen.areas:
|
||||
if area.type == 'PROPERTIES':
|
||||
for space in area.spaces:
|
||||
if space.type == 'PROPERTIES' and space.context == 'MODIFIER':
|
||||
return True
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def simple_deform_poll(cls, context: 'bpy.context') -> bool:
|
||||
"""
|
||||
:param context:输入一个上下文
|
||||
:type context:bpy.context
|
||||
:return bool:反回布尔值,如果活动物体为网格或晶格并且活动修改器为简易形变反回 True else False
|
||||
"""
|
||||
obj = context.object
|
||||
mesh = (obj.type in ('MESH', 'LATTICE')) if obj else False
|
||||
modifiers_type = (obj.modifiers.active.type ==
|
||||
'SIMPLE_DEFORM') if (obj and (obj.modifiers.active is not None)) else False
|
||||
obj_ok = context and obj and modifiers_type and mesh
|
||||
module_ok = (context.mode == 'OBJECT')
|
||||
view = context.space_data
|
||||
show_gizmo = view.show_gizmo
|
||||
return obj_ok and module_ok and show_gizmo
|
||||
|
||||
@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)
|
||||
|
||||
@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
|
||||
def get_mesh_max_min_co(cls, obj: 'bpy.context.object') -> tuple:
|
||||
if obj.type == 'MESH':
|
||||
ver_len = obj.data.vertices.__len__()
|
||||
list_vertices = np.zeros(ver_len * 3, dtype=np.float32)
|
||||
obj.data.vertices.foreach_get('co', list_vertices)
|
||||
list_vertices = list_vertices.reshape(ver_len, 3)
|
||||
elif obj.type == 'LATTICE':
|
||||
ver_len = obj.data.points.__len__()
|
||||
list_vertices = np.zeros(ver_len * 3, dtype=np.float32)
|
||||
obj.data.points.foreach_get('co', list_vertices)
|
||||
list_vertices = list_vertices.reshape(ver_len, 3)
|
||||
return tuple(list_vertices.min(axis=0)), tuple(list_vertices.max(axis=0))
|
||||
|
||||
@classmethod
|
||||
def matrix_calculation(cls, mat: 'Matrix', calculation_list: 'list') -> 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
|
||||
def point_to_angle(cls, i, j, f, axis_):
|
||||
if i == j:
|
||||
if f == 0:
|
||||
i[0] += 0.1
|
||||
j[0] -= 0.1
|
||||
elif f == 1:
|
||||
i[1] -= 0.1
|
||||
j[1] += 0.1
|
||||
|
||||
else:
|
||||
i[2] -= 0.1
|
||||
j[2] += 0.1
|
||||
vector_value = i - j
|
||||
angle = (180 * vector_value.angle(axis_) / math.pi)
|
||||
return angle
|
||||
|
||||
@classmethod
|
||||
def get_up_down(cls, mod, axis, top, bottom, left, right, front, back):
|
||||
if 'BEND' == mod.deform_method:
|
||||
if axis in ('X', 'Y'):
|
||||
return top, bottom
|
||||
elif axis == 'Z':
|
||||
return right, left
|
||||
else:
|
||||
if axis == 'X':
|
||||
return right, left
|
||||
elif axis == 'Y':
|
||||
return back, front
|
||||
elif axis == 'Z':
|
||||
return top, bottom
|
||||
|
||||
@classmethod
|
||||
def get_limits_pos(cls, mod, data):
|
||||
top, bottom, left, right, front, back = data
|
||||
up_limits = mod.limits[1]
|
||||
down_limits = mod.limits[0]
|
||||
axis = mod.deform_axis
|
||||
|
||||
if mod.origin:
|
||||
vector_axis = cls.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 = cls.point_to_angle(i, j, f, axis_)
|
||||
if abs(angle - 180) < 0.00001:
|
||||
up, down = j, i
|
||||
elif abs(angle) < 0.00001:
|
||||
up, down = i, j
|
||||
else:
|
||||
up, down = cls.get_up_down(mod, axis, top, bottom,
|
||||
left, right, front, back)
|
||||
|
||||
ex = lambda a: cls.set_reduce(down, cls.set_reduce(cls.set_reduce(
|
||||
up, down, '-'), (a, a, a), '*'), '+')
|
||||
|
||||
up_ = ex(up_limits)
|
||||
down_ = ex(down_limits)
|
||||
return (up, down), (up_, down_)
|
||||
|
||||
@classmethod
|
||||
def update_bound_box(cls, object):
|
||||
|
||||
context = bpy.context
|
||||
data = bpy.data
|
||||
obj = object
|
||||
matrix = obj.matrix_world.copy() # 物体矩阵
|
||||
|
||||
# add simple_deform mesh
|
||||
(min_x, min_y, min_z), (max_x, max_y,
|
||||
max_z) = cls.get_mesh_max_min_co(object)
|
||||
vertexes = ((max_x, min_y, min_z),
|
||||
(min_x, min_y, min_z),
|
||||
(max_x, max_y, min_z),
|
||||
(min_x, max_y, min_z),
|
||||
(max_x, min_y, max_z),
|
||||
(min_x, min_y, max_z),
|
||||
(max_x, max_y, max_z),
|
||||
(min_x, max_y, max_z))
|
||||
if data.objects.get(G_NAME):
|
||||
data.objects.remove(data.objects.get(G_NAME))
|
||||
|
||||
if data.meshes.get(G_NAME):
|
||||
data.meshes.remove(data.meshes.get(G_NAME))
|
||||
mesh = data.meshes.new(G_NAME)
|
||||
mesh.from_pydata(vertexes, G_INDICES, [])
|
||||
mesh.update()
|
||||
|
||||
new_object = data.objects.new(G_NAME, mesh)
|
||||
|
||||
cls.link_active_collection(new_object)
|
||||
|
||||
if new_object.parent != obj:
|
||||
new_object.parent = obj
|
||||
|
||||
new_object.modifiers.clear()
|
||||
subdivision = new_object.modifiers.new('1', 'SUBSURF')
|
||||
subdivision.levels = 7
|
||||
cls.G_SimpleDeformGizmoHandlerDit['modifiers_co'] = {}
|
||||
cls.G_SimpleDeformGizmoHandlerDit['modifiers_co']['co'] = (
|
||||
min_x, min_y, min_z), (max_x, max_y, max_z)
|
||||
for mo in context.object.modifiers:
|
||||
if mo.type == 'SIMPLE_DEFORM':
|
||||
simple_deform = new_object.modifiers.new(
|
||||
mo.name, 'SIMPLE_DEFORM')
|
||||
simple_deform.deform_method = mo.deform_method
|
||||
simple_deform.deform_axis = mo.deform_axis
|
||||
simple_deform.lock_x = mo.lock_x
|
||||
simple_deform.lock_y = mo.lock_y
|
||||
simple_deform.lock_z = mo.lock_z
|
||||
simple_deform.origin = mo.origin
|
||||
simple_deform.limits[1] = mo.limits[1]
|
||||
simple_deform.limits[0] = mo.limits[0]
|
||||
simple_deform.angle = mo.angle
|
||||
simple_deform.show_viewport = mo.show_viewport
|
||||
obj = Utils.get_depsgraph(new_object)
|
||||
cls.G_SimpleDeformGizmoHandlerDit['modifiers_co'][mo.name] = cls.get_mesh_max_min_co(
|
||||
obj)
|
||||
new_object.hide_set(True)
|
||||
new_object.hide_viewport = False
|
||||
new_object.hide_select = True
|
||||
new_object.hide_render = True
|
||||
new_object.hide_viewport = True
|
||||
new_object.hide_set(True)
|
||||
ver_len = obj.data.vertices.__len__()
|
||||
edge_len = obj.data.edges.__len__()
|
||||
|
||||
if 'numpy_data' not in cls.G_SimpleDeformGizmoHandlerDit:
|
||||
cls.G_SimpleDeformGizmoHandlerDit['numpy_data'] = {}
|
||||
|
||||
numpy_data = cls.G_SimpleDeformGizmoHandlerDit['numpy_data']
|
||||
key = (ver_len, edge_len)
|
||||
if key in numpy_data:
|
||||
list_edges, list_vertices = numpy_data[key]
|
||||
else:
|
||||
list_edges = np.zeros(edge_len * 2, dtype=np.int32)
|
||||
list_vertices = np.zeros(ver_len * 3, dtype=np.float32)
|
||||
numpy_data[key] = (list_edges, list_vertices)
|
||||
obj.data.vertices.foreach_get('co', list_vertices)
|
||||
ver = list_vertices.reshape((ver_len, 3))
|
||||
ver = np.insert(ver, 3, 1, axis=1).T
|
||||
ver[:] = np.dot(matrix, ver)
|
||||
|
||||
ver /= ver[3, :]
|
||||
ver = ver.T
|
||||
ver = ver[:, :3]
|
||||
obj.data.edges.foreach_get('vertices', list_edges)
|
||||
indices = list_edges.reshape((edge_len, 2))
|
||||
|
||||
limits = context.object.modifiers.active.limits[:]
|
||||
modifiers = [getattr(context.object.modifiers.active, i)
|
||||
for i in G_MODIFIERS_PROPERTY]
|
||||
|
||||
cls.G_SimpleDeformGizmoHandlerDit['draw'] = (ver, indices, matrix, modifiers, limits)
|
||||
|
||||
@classmethod
|
||||
def update_co_data(cls, ob, mod):
|
||||
handler_dit = cls.G_SimpleDeformGizmoHandlerDit
|
||||
|
||||
if 'modifiers_co' in cls.G_SimpleDeformGizmoHandlerDit and ob.type in ('MESH', 'LATTICE'):
|
||||
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:
|
||||
cls.G_SimpleDeformGizmoHandlerDit['co'] = co_items[index - 1][1] if (index or (index != 1)) else \
|
||||
modifiers_co['co']
|
||||
|
||||
@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
|
||||
def new_empty(cls, obj, mod):
|
||||
origin = mod.origin
|
||||
if origin is None:
|
||||
new_name = G_NAME + '_Empty_' + str(uuid.uuid4())
|
||||
origin_object = bpy.data.objects.new(new_name, None)
|
||||
cls.link_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
|
||||
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():
|
||||
Pref.load_gizmo_data()
|
||||
|
||||
|
||||
def unregister():
|
||||
...
|
Loading…
Reference in New Issue
Block a user