├── .gitignore
├── Addons
└── PolyQuilt
│ ├── QMesh
│ ├── ElementItem.py
│ ├── QMesh.py
│ ├── QMeshHighlight.py
│ ├── QMeshOperators.py
│ ├── QSnap.py
│ └── __init__.py
│ ├── __init__.py
│ ├── gizmo_preselect.py
│ ├── icons
│ ├── addon.poly_quilt_brush_icon.dat
│ ├── addon.poly_quilt_delete_icon.dat
│ ├── addon.poly_quilt_extrude_icon.dat
│ ├── addon.poly_quilt_frame_icon.dat
│ ├── addon.poly_quilt_icon.dat
│ ├── addon.poly_quilt_knife_icon.dat
│ ├── addon.poly_quilt_loopcut_icon.dat
│ ├── addon.poly_quilt_poly_icon.dat
│ ├── addon.poly_quilt_seam_icon.dat
│ ├── icon_brush_delete.png
│ ├── icon_brush_move.png
│ ├── icon_brush_relax.png
│ ├── icon_geom_edge.png
│ ├── icon_geom_polygon.png
│ ├── icon_geom_quad.png
│ ├── icon_geom_triangle.png
│ ├── icon_geom_vert.png
│ ├── icon_move_free.png
│ ├── icon_move_normal.png
│ ├── icon_move_tangent.png
│ ├── icon_move_x.png
│ ├── icon_move_y.png
│ ├── icon_move_z.png
│ ├── icon_opt_backcull.png
│ ├── icon_opt_mirror.png
│ └── icon_opt_x0.png
│ ├── pq_icon.py
│ ├── pq_keymap_editor.py
│ ├── pq_operator.py
│ ├── pq_operator_add_empty_object.py
│ ├── pq_preferences.py
│ ├── pq_tool.py
│ ├── pq_tool_ui.py
│ ├── subtools
│ ├── __init__.py
│ ├── maintool_brush.py
│ ├── maintool_default.py
│ ├── maintool_delete.py
│ ├── maintool_edgeloop_dissolve.py
│ ├── maintool_extrude.py
│ ├── maintool_hold.py
│ ├── maintool_knife.py
│ ├── maintool_loopcut.py
│ ├── maintool_lowpoly.py
│ ├── subtool.py
│ ├── subtool_autoquad.py
│ ├── subtool_brush_delete.py
│ ├── subtool_brush_move.py
│ ├── subtool_brush_relax.py
│ ├── subtool_brush_size.py
│ ├── subtool_delete.py
│ ├── subtool_edge_extrude.py
│ ├── subtool_edge_extrude_multi.py
│ ├── subtool_edge_slice.py
│ ├── subtool_edge_slide.py
│ ├── subtool_edgeloop_cut.py
│ ├── subtool_edgeloop_extrude.py
│ ├── subtool_fin_slice.py
│ ├── subtool_knife.py
│ ├── subtool_makepoly.py
│ ├── subtool_move.py
│ ├── subtool_polypen.py
│ ├── subtool_seam.py
│ ├── subtool_seam_loop.py
│ └── subtool_vert_extrude.py
│ ├── translation.py
│ └── utils
│ ├── __init__.py
│ ├── addon_updater.py
│ ├── dpi.py
│ ├── draw_util.py
│ ├── mouse_event_util.py
│ └── pqutil.py
├── Docs
└── en
│ ├── docs
│ ├── download.md
│ ├── index.md
│ └── manual
│ │ ├── Brush.md
│ │ ├── facecut.md
│ │ ├── geometry.md
│ │ ├── insert_loop.md
│ │ ├── knife.md
│ │ ├── move.md
│ │ ├── operation.md
│ │ ├── remove.md
│ │ └── remove_loop.md
│ └── mkdocs.yml
├── README.md
├── Resources
├── blender_icons_geom.py
├── icon_geom.blend
└── makeicon.bat
├── _config.yml
└── mkpackage.py
/.gitignore:
--------------------------------------------------------------------------------
1 | /Addons/PolyQuilt/QMesh/__pycache__
2 | /Addons/PolyQuilt/subtools/__pycache__
3 | /Addons/PolyQuilt/utils/__pycache__
4 | /Addons/PolyQuilt/__pycache__
5 | /Addons/PolyQuilt/.vscode
6 | /Docs/en/site
7 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/QMesh/QMesh.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import bpy
15 | import bmesh
16 | import math
17 | import copy
18 | import mathutils
19 | import bpy_extras
20 | import collections
21 | from mathutils import *
22 | from .QSnap import QSnap
23 | import numpy as np
24 | from ..utils import pqutil
25 | from ..utils import draw_util
26 | from ..utils.dpi import *
27 | from .ElementItem import ElementItem
28 | from .QMeshOperators import QMeshOperators
29 | from .QMeshHighlight import QMeshHighlight
30 |
31 | __all__ = ['QMesh','SelectStack']
32 |
33 | class QMesh(QMeshOperators) :
34 |
35 | def __init__(self , obj , preferences) :
36 | super().__init__(obj, preferences)
37 | self.highlight = QMeshHighlight(self)
38 | self.invalid = False
39 |
40 | def UpdateMesh( self , updateHighLight = True ) :
41 | super().UpdateMesh()
42 | if updateHighLight :
43 | self.highlight.setDirty()
44 |
45 | def CheckValid( self , context ) :
46 | val = super()._CheckValid(context)
47 | if val == False or self.invalid :
48 | self.highlight.setDirty()
49 | self.reload_obj(context)
50 | self.invalid = False
51 | return val
52 |
53 | def UpdateView( self ,context , forced = False ):
54 | self.highlight.UpdateView(context)
55 |
56 | def PickElement( self , coord , radius : float , ignore = [] , edgering = False , backface_culling = None , elements = ['FACE','EDGE','VERT'] ) -> ElementItem :
57 | if backface_culling == None :
58 | backface_culling = self.get_shading(bpy.context).show_backface_culling
59 | rv3d = bpy.context.region_data
60 | matrix = rv3d.perspective_matrix
61 | radius = radius * dpm()
62 |
63 | hitElement = ElementItem.Empty()
64 |
65 | ignoreFaces = [ i for i in ignore if isinstance( i , bmesh.types.BMFace ) ]
66 |
67 | # Hitする頂点を探す
68 | hitVert = ElementItem.Empty()
69 | if 'VERT' in elements :
70 | ignoreVerts = [ i for i in ignore if isinstance( i , bmesh.types.BMVert ) ]
71 | candidateVerts = self.highlight.CollectVerts( coord , radius , ignoreVerts , edgering , backface_culling = backface_culling )
72 | for vert in candidateVerts :
73 | # 各点からRayを飛ばす
74 | if QSnap.is_target( vert.hitPosition ) :
75 | hitTemp = self.highlight.PickFace( vert.coord , ignoreFaces , backface_culling = False )
76 | if hitTemp.isEmpty :
77 | # 何の面にもヒットしないなら採択
78 | hitVert = vert
79 | break
80 | else :
81 | if vert.element in hitTemp.element.verts :
82 | # ヒットした面に含まれているなら採択
83 | hitVert = vert
84 | break
85 | else :
86 | # ヒットしたポイントより後ろなら採択
87 | v1 = matrix @ vert.hitPosition
88 | v2 = matrix @ hitTemp.hitPosition
89 | if v1.z <= v2.z :
90 | hitVert = vert
91 | break
92 |
93 | # Todo:ヒットするエッジを探す
94 | hitEdge = ElementItem.Empty()
95 | if 'EDGE' in elements :
96 | ignoreEdges = [ i for i in ignore if isinstance( i , bmesh.types.BMEdge ) ]
97 | candidateEdges = self.highlight.CollectEdge( coord , radius , ignoreEdges , backface_culling = backface_culling , edgering= edgering )
98 |
99 | for edge in candidateEdges :
100 | if QSnap.is_target( edge.hitPosition ) :
101 | hitTemp = self.highlight.PickFace( edge.coord , ignoreFaces , backface_culling = False )
102 |
103 | if hitTemp.isEmpty :
104 | hitEdge = edge
105 | break
106 | else:
107 | if edge.element in hitTemp.element.edges :
108 | # ヒットした面に含まれているなら採択
109 | hitEdge = edge
110 | break
111 | else :
112 | # ヒットしたポイントより後ろなら採択
113 | v1 = matrix @ edge.hitPosition
114 | v2 = matrix @ hitTemp.hitPosition
115 | if v1.z <= v2.z :
116 | hitEdge = edge
117 | break
118 |
119 |
120 | if hitVert.isEmpty and hitEdge.isEmpty :
121 | if 'FACE' in elements :
122 | # hitする面を探す
123 | hitFace = self.highlight.PickFace( coord , ignoreFaces , backface_culling = backface_culling )
124 | # 候補頂点/エッジがないなら面を返す
125 | if hitFace.isNotEmpty :
126 | if QSnap.is_target( hitFace.hitPosition ) :
127 | hitElement = hitFace
128 | elif hitVert.isNotEmpty and hitEdge.isNotEmpty :
129 | if hitVert.element in hitEdge.element.verts :
130 | return hitVert
131 | v1 = matrix @ hitVert.hitPosition.to_4d()
132 | v2 = matrix @ hitEdge.hitPosition.to_4d()
133 | if v1.z <= v2.z :
134 | hitElement = hitVert
135 | else :
136 | hitElement = hitEdge
137 | elif hitVert.isNotEmpty :
138 | hitElement = hitVert
139 | elif hitEdge.isNotEmpty :
140 | hitElement = hitEdge
141 |
142 | return hitElement
143 |
144 |
145 | class SelectStack :
146 | def __init__(self, context , bm) :
147 | self.context = context
148 | self.bm = bm
149 | self.mesh_select_mode = context.tool_settings.mesh_select_mode[0:3]
150 |
151 | def push( self ) :
152 | self.mesh_select_mode = self.context.tool_settings.mesh_select_mode[0:3]
153 | self.vert_selection = [ v.select for v in self.bm.verts ]
154 | self.face_selection = [ f.select for f in self.bm.faces ]
155 | self.edge_selection = [ e.select for e in self.bm.edges ]
156 | self.select_history = self.bm.select_history[:]
157 | self.mesh_select_mode = self.context.tool_settings.mesh_select_mode[0:3]
158 |
159 | def select_mode( self , vert , edge , face ) :
160 | self.context.tool_settings.mesh_select_mode = (vert , edge , face)
161 |
162 |
163 | def pop( self ) :
164 | for select , v in zip( self.vert_selection , self.bm.verts ) :
165 | v.select = select
166 | for select , f in zip( self.face_selection , self.bm.faces ) :
167 | f.select = select
168 | for select , e in zip( self.edge_selection , self.bm.edges ) :
169 | e.select = select
170 |
171 | self.bm.select_history = self.select_history
172 |
173 | del self.vert_selection
174 | del self.face_selection
175 | del self.edge_selection
176 |
177 | self.context.tool_settings.mesh_select_mode = self.mesh_select_mode
178 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/QMesh/QSnap.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import bpy
15 | import bmesh
16 | import math
17 | import copy
18 | import mathutils
19 | import bpy_extras
20 | import collections
21 | from mathutils import *
22 | from .QMeshOperators import *
23 | from ..utils import pqutil
24 |
25 | class QSnap :
26 | instance = None
27 | ref = 0
28 |
29 | @classmethod
30 | def add_ref( cls , context ) :
31 | if cls.ref == 0 :
32 | cls.instance = cls(context)
33 | cls.update(context)
34 | cls.ref = cls.ref + 1
35 |
36 | @classmethod
37 | def remove_ref( cls ) :
38 | cls.ref = cls.ref - 1
39 | if cls.ref == 0 :
40 | if cls.instance :
41 | del cls.instance
42 | cls.instance = None
43 |
44 | @classmethod
45 | def is_active( cls ) :
46 | return cls.instance != None
47 |
48 | @classmethod
49 | def update(cls,context) :
50 | if cls.instance :
51 | cls.instance.__update(context)
52 |
53 | def __init__( self , context, snap_objects = 'Visible' ) :
54 | self.objects_array = None
55 | self.bvh_list = None
56 |
57 | def __update( self , context ) :
58 | if context.scene.tool_settings.use_snap \
59 | and 'FACE' in context.scene.tool_settings.snap_elements :
60 | if self.bvh_list == None :
61 | self.create_tree(context)
62 | else :
63 | if set( self.bvh_list.keys() ) != set(self.snap_objects(context)) :
64 | self.remove_tree()
65 | self.create_tree(context)
66 | else :
67 | if self.bvh_list != None :
68 | self.remove_tree()
69 |
70 | @staticmethod
71 | def snap_objects( context ) :
72 | active_obj = context.active_object
73 | objects = context.visible_objects
74 | # objects = context.selected_objects
75 | objects_array = [obj for obj in objects if obj != active_obj and obj.type == 'MESH']
76 | return objects_array
77 |
78 | def create_tree( self , context ) :
79 | if self.bvh_list == None :
80 | self.bvh_list = {}
81 | for obj in self.snap_objects(context):
82 | bvh = mathutils.bvhtree.BVHTree.FromObject(obj, context.evaluated_depsgraph_get() , epsilon = 0.0 )
83 | self.bvh_list[obj] = bvh
84 |
85 | def remove_tree( self ) :
86 | if self.bvh_list != None :
87 | for bvh in self.bvh_list.values():
88 | del bvh
89 | self.bvh_list = None
90 |
91 |
92 | @classmethod
93 | def view_adjust( cls , world_pos : mathutils.Vector ) -> mathutils.Vector :
94 | if cls.instance != None :
95 | ray = pqutil.Ray.from_world_to_screen( bpy.context , world_pos )
96 | if ray == None :
97 | return world_pos
98 | location , norm , obj = cls.instance.__raycast( ray )
99 | if location != None :
100 | return location
101 | return world_pos
102 |
103 | @classmethod
104 | def adjust_point( cls , world_pos : mathutils.Vector , is_fix_to_x_zero = False) :
105 | if cls.instance != None :
106 | location , norm , index = cls.instance.__find_nearest( world_pos )
107 | if is_fix_to_x_zero and QMeshOperators.is_x_zero_pos(location) :
108 | location.x = 0
109 | return location
110 | return world_pos
111 |
112 | @classmethod
113 | def adjust_local( cls , matrix_world : mathutils.Matrix , local_pos : mathutils.Vector , is_fix_to_x_zero ) :
114 | if cls.instance != None :
115 | location , norm , index = cls.instance.__find_nearest( matrix_world @ local_pos )
116 | lp = matrix_world.inverted() @ location
117 | if is_fix_to_x_zero and QMeshOperators.is_x_zero_pos(local_pos) :
118 | lp.x = 0
119 | return lp
120 | return local_pos
121 |
122 | @classmethod
123 | def adjust_local_to_world( cls , matrix_world : mathutils.Matrix , local_pos : mathutils.Vector , is_fix_to_x_zero ) :
124 | if cls.instance != None :
125 | location , norm , index = cls.instance.__find_nearest( matrix_world @ local_pos )
126 | lp = location
127 | if is_fix_to_x_zero and QMeshOperators.is_x_zero_pos(local_pos) :
128 | lp.x = 0
129 | return lp
130 | return local_pos
131 |
132 |
133 | @classmethod
134 | def adjust_verts( cls , obj , verts , is_fix_to_x_zero ) :
135 | if cls.instance != None and cls.instance.bvh_list :
136 | dist = bpy.context.scene.tool_settings.double_threshold
137 | find_nearest = cls.instance.__find_nearest
138 | matrix = obj.matrix_world
139 | for vert in verts :
140 | location , norm , index = find_nearest( matrix @ vert.co )
141 | if location != None :
142 | lp = obj.matrix_world.inverted() @ location
143 | if is_fix_to_x_zero and QMeshOperators.is_x_zero_pos(vert.co) :
144 | lp.x = 0
145 | vert.co = lp
146 |
147 | @classmethod
148 | def is_target( cls , world_pos : mathutils.Vector) -> bool :
149 | dist = bpy.context.scene.tool_settings.double_threshold
150 | if cls.instance != None :
151 | ray = pqutil.Ray.from_world_to_screen( bpy.context , world_pos )
152 | if ray == None :
153 | return False
154 | hit , normal , face = cls.instance.__raycast( ray )
155 | if hit != None :
156 | v2h = (ray.origin - hit).length
157 | v2w = (ray.origin - world_pos).length
158 |
159 | if abs(v2h - v2w) <= dist :
160 | return True
161 | else :
162 | ray2 = pqutil.Ray( hit + ray.vector * dist , ray.vector )
163 | hit2 , normal2 , face2 = cls.instance.__raycast( ray2 )
164 | if not hit2 :
165 | return False
166 | h2h = ( ray2.origin - hit2 ).length
167 | w2h0 = ( ray2.origin - world_pos ).length
168 | w2h1 = ( world_pos - hit2 ).length
169 | if w2h0 < h2h :
170 | if w2h0 < w2h1 :
171 | return True
172 | return False
173 | return True
174 |
175 | def __raycast( self , ray : pqutil.Ray ) :
176 | min_dist = math.inf
177 | location = None
178 | normal = None
179 | index = None
180 | if self.bvh_list :
181 | for obj , bvh in self.bvh_list.items():
182 | local_ray = ray.world_to_object( obj )
183 | hit = bvh.ray_cast( local_ray.origin , local_ray.vector )
184 | if None not in hit :
185 | if hit[3] < min_dist :
186 | matrix = obj.matrix_world
187 | location = pqutil.transform_position( hit[0] , matrix )
188 | normal = pqutil.transform_normal( hit[1] , matrix )
189 | index = hit[2] + obj.pass_index * 10000000
190 | min_dist = hit[3]
191 |
192 | return location , normal , index
193 |
194 | def __smart_find( self , ray : pqutil.Ray ) :
195 | location_i , normal_i , obj_i = self.__raycast_double( ray )
196 | if location_i == None :
197 | a,b,c = self.__find_nearest( ray.origin )
198 | return a,b,c
199 | location_r , normal_r , obj_r = self.__find_nearest( ray.origin )
200 | if location_r == None :
201 | return location_i , normal_i , obj_i
202 | if (location_r - ray.origin).length <= (location_i - ray.origin).length :
203 | return location_r , normal_r , obj_r
204 | else :
205 | return location_i , normal_i , obj_i
206 |
207 | def __raycast_double( self , ray : pqutil.Ray ) :
208 | # ターゲットからビュー方向にレイを飛ばす
209 | location_r , normal_r , face_r = self.__raycast( ray )
210 | location_i , normal_i , face_i = self.__raycast( ray.invert )
211 |
212 | if None in [face_i,face_r] :
213 | if face_i != None :
214 | return location_i , normal_i , face_i
215 | elif face_r != None :
216 | return location_r , normal_r , face_r
217 | else :
218 | if (location_r - ray.origin).length <= (location_i - ray.origin).length :
219 | return location_r , normal_r , face_r
220 | else :
221 | return location_i , normal_i , face_i
222 | return None , None , None
223 |
224 | def __find_nearest( self, pos : mathutils.Vector ) :
225 | min_dist = math.inf
226 | location = pos
227 | normal = None
228 | index = None
229 | hits = []
230 | if self.bvh_list :
231 | for obj , bvh in self.bvh_list.items():
232 | lp = obj.matrix_world.inverted() @ pos
233 | hit = bvh.find_nearest( lp )
234 | if None not in hit :
235 | wp = pqutil.transform_position( hit[0] , obj.matrix_world )
236 | dist = ( pos - wp ).length
237 | if min_dist > dist :
238 | min_dist = dist
239 | location = wp
240 | normal = pqutil.transform_normal( hit[1] , obj.matrix_world )
241 | index = hit[2] + obj.pass_index * 10000000
242 |
243 | return location , normal , index
244 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/QMesh/__init__.py:
--------------------------------------------------------------------------------
1 |
2 | # This program is free software; you can redistribute it and/or modify
3 | # it under the terms of the GNU General Public License as published by
4 | # the Free Software Foundation; either version 3 of the License, or
5 | # (at your option) any later version.
6 | #
7 | # This program is distributed in the hope that it will be useful, but
8 | # WITHOUT ANY WARRANTY; without even the implied warranty of
9 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 | # General Public License for more details.
11 | #
12 | # You should have received a copy of the GNU General Public License
13 | # along with this program. If not, see .
14 |
15 | from .QMesh import QMesh , SelectStack
16 | from .QSnap import QSnap
17 | from .ElementItem import ElementItem
18 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/__init__.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | bl_info = {
15 | "name" : "PolyQuilt",
16 | "author" : "Sakana3",
17 | "version": (1, 3, 1),
18 | "blender" : (2, 83, 0),
19 | "location": "View3D > Mesh > PolyQuilt",
20 | "description": "Lowpoly Tool",
21 | "warning" : "",
22 | "wiki_url": "",
23 | "category": "Mesh",
24 | }
25 |
26 | import bpy
27 | from bpy.utils.toolsystem import ToolDef
28 | from .pq_operator import *
29 | from .pq_operator_add_empty_object import *
30 | from .pq_icon import *
31 | from .pq_tool import PolyQuiltTools
32 | from .pq_tool_ui import VIEW3D_PT_tools_polyquilt_options
33 | from .pq_keymap_editor import PQ_OT_DirtyKeymap
34 | from .gizmo_preselect import *
35 | from .pq_preferences import *
36 | from .translation import pq_translation_dict
37 |
38 | classes = (
39 | MESH_OT_poly_quilt ,
40 | MESH_OT_poly_quilt_brush_size ,
41 | MESH_OT_poly_quilt_daemon ,
42 | PQ_OT_SetupUnityLikeKeymap ,
43 | PolyQuiltPreferences ,
44 | PQ_OT_CheckAddonUpdate ,
45 | PQ_OT_UpdateAddon ,
46 | VIEW3D_PT_tools_polyquilt_options ,
47 | PQ_OT_DirtyKeymap ,
48 | ) + gizmo_preselect.all_gizmos
49 |
50 |
51 | def register():
52 | bpy.app.translations.register(__name__, pq_translation_dict)
53 | register_icons()
54 | register_updater(bl_info)
55 |
56 | # 空メッシュ追加
57 | bpy.utils.register_class(pq_operator_add_empty_object.OBJECT_OT_add_object)
58 | bpy.utils.register_manual_map(pq_operator_add_empty_object.add_object_manual_map)
59 | bpy.types.VIEW3D_MT_mesh_add.append(pq_operator_add_empty_object.add_object_button)
60 |
61 | for cls in classes:
62 | bpy.utils.register_class(cls)
63 |
64 | for tool in PolyQuiltTools :
65 | bpy.utils.register_tool(tool['tool'] , after = tool['after'] , group = tool['group'] )
66 |
67 | def unregister():
68 | for tool in PolyQuiltTools :
69 | bpy.utils.unregister_tool(tool['tool'])
70 |
71 | for cls in reversed(classes):
72 | bpy.utils.unregister_class(cls)
73 |
74 | bpy.utils.unregister_class(pq_operator_add_empty_object.OBJECT_OT_add_object)
75 | bpy.utils.unregister_manual_map(pq_operator_add_empty_object.add_object_manual_map)
76 | bpy.types.VIEW3D_MT_mesh_add.remove(pq_operator_add_empty_object.add_object_button)
77 |
78 | unregister_icons()
79 | bpy.app.translations.unregister(__name__)
80 |
81 | if __name__ == "__main__":
82 | register()
83 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/gizmo_preselect.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import bpy
15 | import mathutils
16 | import time
17 | from .QMesh import *
18 | from .utils import draw_util
19 | from .subtools import *
20 | from .pq_tool import *
21 |
22 |
23 |
24 | class PQ_Gizmo_Preselect( bpy.types.Gizmo):
25 | bl_idname = "MESH_GT_PQ_Preselect"
26 |
27 | def __init__(self) :
28 | self.bmo = None
29 | self.currentElement = None
30 | self.preferences = bpy.context.preferences.addons[__package__].preferences
31 | self.DrawHighlight = None
32 | self.region = None
33 | self.subtool = None
34 | self.tool_table = [None,None,None,None]
35 | self.tool = None
36 |
37 | def __del__(self) :
38 | pass
39 |
40 | def setup(self):
41 | self.bmo = None
42 | self.currentElement = ElementItem.Empty()
43 |
44 | def init( self , context , tool ) :
45 | self.tool = tool
46 | self.maintool = maintools[tool.pq_main_tool]
47 | self.subtool = self.maintool
48 | self.region = context.region_data
49 | self.bmo = QMesh( context.active_object , self.preferences )
50 | self.keyitem = None
51 | if self.subtool :
52 | context.window.cursor_set( self.subtool.GetCursor() )
53 |
54 | def exit( self , context, cancel) :
55 | pass
56 |
57 | def test_select(self, context, location):
58 | if PQ_GizmoGroup_Base.running_polyquilt :
59 | self.DrawHighlight = None
60 | return -1
61 |
62 | if self.currentElement == None :
63 | self.currentElement = ElementItem.Empty()
64 |
65 | self.mouse_pos = mathutils.Vector(location)
66 | if context.region == self.region :
67 | return -1
68 | if self.bmo == None :
69 | self.bmo = QMesh( context.active_object , self.preferences )
70 | self.bmo.CheckValid( context )
71 | self.bmo.UpdateView(context)
72 | QSnap.update(context)
73 |
74 | if self.subtool != None :
75 | element = self.subtool.pick_element( self.bmo , location , self.preferences )
76 | element.set_snap_div( self.preferences.loopcut_division )
77 | if self.subtool.UpdateHighlight( self , element ) :
78 | context.area.tag_redraw()
79 | else :
80 | element = ElementItem.Empty()
81 |
82 | self.currentElement = element
83 |
84 | if self.subtool != None :
85 | self.DrawHighlight = self.subtool.DrawHighlight( self , self.currentElement )
86 | else :
87 | self.DrawHighlight = None
88 |
89 | return -1
90 |
91 | def draw(self, context):
92 | if PQ_GizmoGroup_Base.running_polyquilt :
93 | self.DrawHighlight = None
94 |
95 | if self.DrawHighlight != None :
96 | self.DrawHighlight()
97 |
98 | def refresh( self , context ) :
99 | if self.bmo != None :
100 | self.bmo.invalid = True
101 | self.currentElement = ElementItem.Empty()
102 | self.DrawHighlight = None
103 |
104 | def recive_event( self , context , event ) :
105 | subtool = self.maintool
106 |
107 | self.keyitem = self.get_keyitem( event.shift , event.ctrl , event.alt, event.oskey )
108 | if self.keyitem and hasattr( self.keyitem.properties , "tool_mode" ) :
109 | subtool = maintools[ self.keyitem.properties.tool_mode ]
110 |
111 | if self.subtool != subtool :
112 | self.subtool = subtool
113 | if context.area :
114 | context.area.tag_redraw()
115 | if self.subtool :
116 | PQ_GizmoGroup_Base.set_cursor( subtool.GetCursor() )
117 | else :
118 | PQ_GizmoGroup_Base.set_cursor( )
119 |
120 | if context.region_data == self.region and self.subtool:
121 | self.subtool.recive_event( self , context , event )
122 |
123 | def get_keyitem( self , shift , ctrl , alt, oskey ) :
124 | keymap = bpy.context.window_manager.keyconfigs.user.keymaps["3D View Tool: Edit Mesh, " + self.tool.bl_label]
125 | keyitems = [ item for item in keymap.keymap_items if item.idname == 'mesh.poly_quilt' ]
126 | for item in keymap.keymap_items :
127 | if item.idname == 'mesh.poly_quilt' and item.active :
128 | if [ item.shift , item.ctrl , item.alt, item.oskey ] == [ shift , ctrl , alt, oskey ] :
129 | if item.active :
130 | return item
131 | return None
132 |
133 | def get_attr( self , attr ) :
134 | if self.keyitem :
135 | if self.keyitem.properties.is_property_set(attr) :
136 | return getattr( self.keyitem.properties , attr )
137 |
138 | for tool in bpy.context.workspace.tools :
139 | if "mesh_tool.poly_quilt" in tool.idname :
140 | props = tool.operator_properties("mesh.poly_quilt")
141 | return getattr( props , attr )
142 |
143 | return None
144 |
145 | class PQ_GizmoGroup_Base(bpy.types.GizmoGroup):
146 | my_tool = ToolPolyQuiltBase
147 | bl_idname = "MESH_GGT_PQ_Preselect"
148 | bl_label = "PolyQuilt Preselect Gizmo"
149 | bl_options = {'3D'}
150 | bl_region_type = 'WINDOW'
151 | bl_space_type = 'VIEW_3D'
152 | bl_idname = my_tool.bl_widget
153 | child_gizmos = []
154 | cursor = 'DEFAULT'
155 |
156 | running_polyquilt = False
157 |
158 | def __init__(self) :
159 | self.gizmo = None
160 |
161 | def __del__(self) :
162 | if hasattr( self , "gizmo" ) :
163 | PQ_GizmoGroup_Base.child_gizmos.remove( self.gizmo )
164 | if not PQ_GizmoGroup_Base.child_gizmos :
165 | QSnap.remove_ref()
166 |
167 | @classmethod
168 | def poll(cls, context):
169 | if context.mode != 'EDIT_MESH' :
170 | return False
171 | # 自分を使っているツールを探す。
172 | workspace = context.workspace
173 | for tool in workspace.tools:
174 | if tool.widget == cls.bl_idname:
175 | break
176 | else:
177 | context.window_manager.gizmo_group_type_unlink_delayed(cls.bl_idname)
178 | return False
179 | if not PQ_GizmoGroup_Base.running_polyquilt :
180 | context.window.cursor_set( cls.cursor )
181 | return True
182 |
183 | def setup(self, context):
184 | QSnap.add_ref(context)
185 | self.gizmo = self.gizmos.new(PQ_Gizmo_Preselect.bl_idname)
186 | self.gizmo.init(context , self.my_tool )
187 | PQ_GizmoGroup_Base.child_gizmos.append(self.gizmo)
188 |
189 | def refresh( self , context ) :
190 | if hasattr( self , "gizmo" ) :
191 | self.gizmo.refresh(context)
192 |
193 | @classmethod
194 | def set_cursor(cls, cursor = 'DEFAULT' ):
195 | cls.cursor = cursor
196 |
197 | @classmethod
198 | def get_gizmo(cls, region ):
199 | gizmo = [ i for i in cls.child_gizmos if i.region == region ]
200 | if gizmo :
201 | return gizmo[0]
202 | return None
203 |
204 | @classmethod
205 | def recive_event( cls , context , event ) :
206 | for gizmo in cls.child_gizmos :
207 | gizmo.recive_event( context , event)
208 |
209 | @classmethod
210 | def depsgraph_update_post( cls , scene ) :
211 | for gizmo in cls.child_gizmos :
212 | gizmo.refresh( bpy.context )
213 |
214 |
215 | class PQ_GizmoGroup_Preselect(PQ_GizmoGroup_Base):
216 | my_tool = ToolPolyQuilt
217 | bl_idname = my_tool.bl_widget
218 | bl_label = "PolyQuilt Preselect Gizmo"
219 |
220 | class PQ_GizmoGroup_Lowpoly(PQ_GizmoGroup_Base):
221 | my_tool = ToolPolyQuiltPoly
222 | bl_idname = my_tool.bl_widget
223 | bl_label = "PolyQuilt Lowpoly Gizmo"
224 |
225 | class PQ_GizmoGroup_Knife(PQ_GizmoGroup_Base):
226 | my_tool = ToolPolyQuiltKnife
227 | bl_idname = my_tool.bl_widget
228 | bl_label = "PolyQuilt Knife Gizmo"
229 |
230 | class PQ_GizmoGroup_Delete(PQ_GizmoGroup_Base):
231 | my_tool = ToolPolyQuiltDelete
232 | bl_idname = my_tool.bl_widget
233 | bl_label = "PolyQuilt Delete Gizmo"
234 |
235 | class PQ_GizmoGroup_Extrude(PQ_GizmoGroup_Base):
236 | my_tool = ToolPolyQuiltExtrude
237 | bl_idname = my_tool.bl_widget
238 | bl_label = "PolyQuilt Extrude Gizmo"
239 |
240 | class PQ_GizmoGroup_LoopCut(PQ_GizmoGroup_Base):
241 | my_tool = ToolPolyQuiltLoopCut
242 | bl_idname = my_tool.bl_widget
243 | bl_label = "PolyQuilt LoopCut Gizmo"
244 |
245 | class PQ_GizmoGroup_Brush(PQ_GizmoGroup_Base):
246 | my_tool = ToolPolyQuiltBrush
247 | bl_idname = my_tool.bl_widget
248 | bl_label = "PolyQuilt Brush Gizmo"
249 |
250 | class PQ_GizmoGroup_Seam(PQ_GizmoGroup_Base):
251 | my_tool = ToolPolyQuiltSeam
252 | bl_idname = my_tool.bl_widget
253 | bl_label = "PolyQuilt Seam Gizmo"
254 |
255 |
256 | all_gizmos = ( PQ_Gizmo_Preselect , PQ_GizmoGroup_Preselect , PQ_GizmoGroup_Lowpoly , PQ_GizmoGroup_Knife , PQ_GizmoGroup_Delete, PQ_GizmoGroup_Extrude, PQ_GizmoGroup_LoopCut, PQ_GizmoGroup_Brush, PQ_GizmoGroup_Seam )
257 |
258 |
259 | # ursor (enum in ['DEFAULT', 'NONE', 'WAIT', 'CROSSHAIR', 'MOVE_X', 'MOVE_Y', 'KNIFE', 'TEXT', 'PAINT_BRUSH', 'PAINT_CROSS', 'DOT', 'ERASER', 'HAND', 'SCROLL_X', 'SCROLL_Y', 'SCROLL_XY', 'EYEDROPPER'], (optional)) – cursor
260 |
261 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/addon.poly_quilt_brush_icon.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/addon.poly_quilt_brush_icon.dat
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/addon.poly_quilt_delete_icon.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/addon.poly_quilt_delete_icon.dat
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/addon.poly_quilt_extrude_icon.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/addon.poly_quilt_extrude_icon.dat
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/addon.poly_quilt_frame_icon.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/addon.poly_quilt_frame_icon.dat
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/addon.poly_quilt_icon.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/addon.poly_quilt_icon.dat
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/addon.poly_quilt_knife_icon.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/addon.poly_quilt_knife_icon.dat
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/addon.poly_quilt_loopcut_icon.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/addon.poly_quilt_loopcut_icon.dat
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/addon.poly_quilt_poly_icon.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/addon.poly_quilt_poly_icon.dat
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/addon.poly_quilt_seam_icon.dat:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/addon.poly_quilt_seam_icon.dat
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/icon_brush_delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/icon_brush_delete.png
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/icon_brush_move.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/icon_brush_move.png
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/icon_brush_relax.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/icon_brush_relax.png
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/icon_geom_edge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/icon_geom_edge.png
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/icon_geom_polygon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/icon_geom_polygon.png
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/icon_geom_quad.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/icon_geom_quad.png
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/icon_geom_triangle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/icon_geom_triangle.png
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/icon_geom_vert.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/icon_geom_vert.png
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/icon_move_free.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/icon_move_free.png
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/icon_move_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/icon_move_normal.png
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/icon_move_tangent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/icon_move_tangent.png
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/icon_move_x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/icon_move_x.png
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/icon_move_y.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/icon_move_y.png
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/icon_move_z.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/icon_move_z.png
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/icon_opt_backcull.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/icon_opt_backcull.png
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/icon_opt_mirror.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/icon_opt_mirror.png
--------------------------------------------------------------------------------
/Addons/PolyQuilt/icons/icon_opt_x0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Addons/PolyQuilt/icons/icon_opt_x0.png
--------------------------------------------------------------------------------
/Addons/PolyQuilt/pq_icon.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import bpy
15 | import os
16 | import bpy.utils.previews
17 |
18 | __all__ = ['register_icons','unregister_icons','custom_icon']
19 |
20 | icons = [ "icon_geom_vert" , "icon_geom_edge" , "icon_geom_triangle" , "icon_geom_quad" , "icon_geom_polygon" ,
21 | "icon_move_free" , "icon_move_x" , "icon_move_y" , "icon_move_z" , "icon_move_normal", "icon_move_tangent" ,
22 | "icon_opt_backcull" , "icon_opt_mirror" , "icon_opt_x0" ,
23 | "icon_brush_move" , "icon_brush_relax", "icon_brush_delete" ]
24 |
25 | custom_icons = {}
26 |
27 | def register_icons():
28 | global custom_icons
29 | custom_icons = bpy.utils.previews.new()
30 | my_icons_dir = os.path.join(os.path.dirname(__file__), "icons")
31 | for icon in icons :
32 | custom_icons.load( icon , os.path.join(my_icons_dir, icon + ".png" ) , 'IMAGE')
33 |
34 | def unregister_icons():
35 | global custom_icons
36 | bpy.utils.previews.remove(custom_icons)
37 | custom_icons = None
38 |
39 | def custom_icon( name ) :
40 | global custom_icons
41 | return custom_icons[ name].icon_id
42 |
43 | def custom_icon_t( name ) :
44 | global custom_icons
45 | return custom_icons[ name]
46 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/pq_keymap_editor.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import os
15 | import bpy
16 | from bpy.types import WorkSpaceTool , Panel
17 | from bpy.utils.toolsystem import ToolDef
18 | from .pq_icon import *
19 | import inspect
20 | import rna_keymap_ui
21 | from bpy.types import AddonPreferences
22 |
23 | def draw_tool_keymap( layout ,keyconfing,keymapname ) :
24 | keymap = keyconfing.keymaps[keymapname]
25 | layout.context_pointer_set('keymap', keymap)
26 | cnt = 0
27 |
28 |
29 | for item in reversed(keymap.keymap_items) :
30 | cnt = max( cnt , (item.oskey,item.shift,item.ctrl,item.alt).count(True) )
31 |
32 | for item in reversed(keymap.keymap_items) :
33 | if True in (item.oskey,item.shift,item.ctrl,item.alt) :
34 | it = layout.row( )
35 | # it.prop(item , "active" , text = "" )
36 | if item.idname == 'mesh.poly_quilt' :
37 | # for i3d in keyconfing.keymaps["Mesh"].keymap_items :
38 | # if i3d.type == 'LEFTMOUSE' and i3d.shift == item.shift and i3d.ctrl == item.ctrl and i3d.alt == item.alt and i3d.oskey == item.oskey :
39 | # ic = layout.row(align = True)
40 | # ic.template_event_from_keymap_item(i3d)
41 | # ic.label( icon = 'ERROR' , text = i3d.name )
42 | # for i3d in keyconfing.keymaps["3D View"].keymap_items :
43 | # if i3d.type == 'LEFTMOUSE' and i3d.shift == item.shift and i3d.ctrl == item.ctrl and i3d.alt == item.alt and i3d.oskey == item.oskey :
44 | # ic = layout.row(align = True)
45 | # ic.template_event_from_keymap_item(i3d)
46 | # ic.label( icon = 'ERROR' , text = i3d.name )
47 |
48 | # ic = it.row(align = True)
49 | # ic.prop( item , icon = 'ERROR' )
50 |
51 | ic = it.row(align = True)
52 | ic.ui_units_x = cnt + 2
53 | ic.prop(item , "active" , text = "" , emboss = True )
54 | ic.template_event_from_keymap_item(item)
55 |
56 | ic = it.row(align = True)
57 | ic.prop(item.properties , "tool_mode" , text = "" , emboss = True )
58 |
59 | # op = it.popover(panel="VIEW3D_PT_tools_polyquilt_keymap_properties" , text = item.properties.tool_mode )
60 | # op.item_id = 0
61 |
62 | if( item.properties.tool_mode == 'LOWPOLY' ) :
63 | im = ic.row()
64 | im.active = item.properties.is_property_set("geometry_type")
65 | im.prop(item.properties, "geometry_type" , text = "" , emboss = True , expand = False , icon_only = False )
66 |
67 | if( item.properties.tool_mode == 'BRUSH' ) :
68 | im = ic.row()
69 | im.active = item.properties.is_property_set("brush_type")
70 | im.prop(item.properties, "brush_type" , text = "" , emboss = True , expand = False , icon_only = False )
71 |
72 | if( item.properties.tool_mode == 'LOOPCUT' ) :
73 | im = ic.row()
74 | im.active = item.properties.is_property_set("loopcut_mode")
75 | im.prop(item.properties, "loopcut_mode" , text = "" , emboss = True , expand = False , icon_only = False )
76 |
77 |
78 | if (not item.is_user_defined) and item.is_user_modified:
79 | it.operator("preferences.keyitem_restore", text="", icon='BACK').item_id = item.id
80 | elif item.is_user_defined :
81 | it.operator("preferences.keyitem_remove", text="", icon='X').item_id = item.id
82 |
83 | # layout.operator("preferences.keyitem_add", text="Add New", text_ctxt=i18n_contexts.id_windowmanager, icon='ADD')
84 | layout.operator(PQ_OT_DirtyKeymap.bl_idname)
85 | # popover_kw = {"space_type": 'VIEW_3D', "region_type": 'UI', "category": "Tool"}
86 | # op = layout.popover_group(context=".poly_quilt_keymap_properties", **popover_kw)
87 | # if it.active :
88 | # it.context_pointer_set( "brush_type" , item.properties )
89 | # it = layout.column()
90 | # rna_keymap_ui.draw_kmi(
91 | # [], keyconfing, keymap, item, it, 0)
92 | # it.template_keymap_item_properties(item)
93 | # for m in inspect.getmembers(item.properties):
94 | # print(m)
95 |
96 |
97 | def draw_tool_keymap_ui( context , _layout , text , tool) :
98 |
99 | preferences = context.preferences.addons[__package__].preferences
100 |
101 | column = _layout.box().column()
102 | row = column.row()
103 | row.prop( preferences, "keymap_setting_expanded", text="",
104 | icon='TRIA_DOWN' if preferences.keymap_setting_expanded else 'TRIA_RIGHT')
105 |
106 | row.label(text =text + " Setting")
107 |
108 | if preferences.keymap_setting_expanded :
109 | keyconfing = context.window_manager.keyconfigs.user
110 | draw_tool_keymap( column, keyconfing,"3D View Tool: Edit Mesh, " + tool.bl_label )
111 |
112 | class PQ_OT_DirtyKeymap(bpy.types.Operator) :
113 | bl_idname = "addon.polyquilt_dirty_keymap"
114 | bl_label = "Save Keymap"
115 |
116 | def execute(self, context):
117 | for keymap in [ k for k in context.window_manager.keyconfigs.user.keymaps if "PolyQuilt" in k.name ] :
118 | keymap.show_expanded_items = keymap.show_expanded_items
119 | for item in reversed(keymap.keymap_items) :
120 | if True in (item.oskey,item.shift,item.ctrl,item.alt) :
121 | if item.idname == 'mesh.poly_quilt' :
122 | item.active = item.active
123 |
124 | context.preferences.is_dirty = True
125 | # bpy.ops.wm.save_userpref()
126 | return {'FINISHED'}
127 |
128 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/pq_operator_add_empty_object.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 | import bpy
14 | from bpy.types import Operator
15 | from bpy.props import FloatVectorProperty
16 | from bpy_extras.object_utils import AddObjectHelper, object_data_add
17 |
18 | class OBJECT_OT_add_object(Operator, AddObjectHelper):
19 | """Create a new Empty Mesh Object"""
20 | bl_idname = "mesh.add_empty_mesh_object"
21 | bl_label = "Add Empty Mesh Object"
22 | bl_options = {'REGISTER', 'UNDO'}
23 |
24 | def invoke(self, context , event):
25 | mesh = bpy.data.meshes.new(name="New Empty Mesh")
26 | object_data_add(context, mesh, operator=self)
27 | return {'FINISHED'}
28 |
29 | def add_object_button(self, context):
30 | self.layout.operator(
31 | OBJECT_OT_add_object.bl_idname,
32 | text="Empty Mesh Object",
33 | icon='EMPTY_DATA')
34 |
35 | # This allows you to right click on a button and link to the manual
36 | def add_object_manual_map():
37 | url_manual_prefix = "https://docs.blender.org/manual/en/dev/"
38 | url_manual_mapping = (
39 | ("bpy.ops.mesh.add_object", "editors/3dview/object"),
40 | )
41 | return url_manual_prefix, url_manual_mapping
42 |
43 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/pq_tool.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import os
15 | import bpy
16 | from bpy.types import WorkSpaceTool , Panel
17 | from bpy.utils.toolsystem import ToolDef
18 | from .pq_icon import *
19 | from .pq_tool_ui import *
20 | from .pq_keymap_editor import draw_tool_keymap_ui
21 |
22 | class ToolPolyQuiltBase(WorkSpaceTool):
23 | pq_main_tool = 'MASTER'
24 | pq_description = 'Master Tool'
25 |
26 | bl_space_type='VIEW_3D'
27 | bl_context_mode='EDIT_MESH'
28 | bl_widget = "MESH_GGT_PQ_Preselect"
29 |
30 | @staticmethod
31 | def tool_keymaps( main_tool , shift = ["NONE"] , ctrl = ["NONE"] , alt = ["NONE"] ) :
32 | def keyitem( mods , tool ) :
33 | key = {"type": 'LEFTMOUSE', "value": 'PRESS', "shift" : 's' in mods , "ctrl" : 'c' in mods , "alt" : 'a' in mods , "oskey": 'o' in mods }
34 | prop = {"properties": [("tool_mode", tool[0] )]}
35 | if len(tool) > 1 and tool[0] == 'BRUSH' :
36 | prop["properties"].append( ('brush_type' , tool[1] ) )
37 | item = ("mesh.poly_quilt", key , prop )
38 | return item
39 |
40 | return (
41 | keyitem( "" , main_tool ) ,
42 | keyitem( "s" , shift ) ,
43 | keyitem( "c" , ctrl ) ,
44 | keyitem( "a" , alt ) ,
45 | keyitem( "cs" , ['NONE'] ) ,
46 | keyitem( "sa" , ['NONE'] ) ,
47 | keyitem( "ca" , ['NONE'] ) ,
48 | keyitem( "os" , ['NONE'] ) ,
49 | keyitem( "oc" , ['NONE'] ) ,
50 | keyitem( "oa" , ['NONE'] ) ,
51 |
52 | ("mesh.poly_quilt_daemon", {"type": 'MOUSEMOVE', "value": 'ANY' }, {"properties": []}),
53 | )
54 |
55 | @classmethod
56 | def draw_settings( cls ,context, layout, tool):
57 | reg = context.region.type
58 |
59 | # keyconfigs = context.window_manager.keyconfigs.user
60 | # keymap = keyconfigs.keymaps["3D View Tool: Edit Mesh, " + cls.bl_label ]
61 | # tools = [ item.properties.tool_mode for item in keymap.keymap_items if item.idname == 'mesh.poly_quilt' and hasattr( item.properties , "tool_mode" ) ]
62 | tools = [ "MASTER" ]
63 |
64 | if reg == 'UI' :
65 | draw_settings_ui( context , layout , tool , ui = tools)
66 | draw_tool_keymap_ui( context , layout , cls.pq_description , cls)
67 | elif reg == 'WINDOW' :
68 | draw_settings_ui( context , layout , tool , ui = tools)
69 | draw_tool_keymap_ui( context , layout , cls.pq_description , cls )
70 | elif reg == 'TOOL_HEADER' :
71 | draw_settings_toolheader( context , layout , tool , ui = tools )
72 |
73 | class ToolPolyQuilt(ToolPolyQuiltBase):
74 | pq_main_tool = 'MASTER'
75 | pq_description = 'Master Tool'
76 |
77 | # The prefix of the idname should be your add-on name.
78 | bl_idname = "mesh_tool.poly_quilt"
79 | bl_label = "PolyQuilt"
80 | bl_description = ( "Lowpoly Tool" )
81 | bl_icon = os.path.join(os.path.join(os.path.dirname(__file__), "icons") , "addon.poly_quilt_icon")
82 | bl_widget = "MESH_GGT_PQ_Preselect"
83 | bl_keymap = ToolPolyQuiltBase.tool_keymaps( [pq_main_tool] , shift = ['BRUSH'] )
84 |
85 | class ToolPolyQuiltPoly(ToolPolyQuiltBase):
86 | pq_main_tool = 'LOWPOLY'
87 | pq_description = 'LowPoly Tool'
88 |
89 | # The prefix of the idname should be your add-on name.
90 | bl_idname = "mesh_tool.poly_quilt_poly"
91 | bl_label = "PolyQuilt:Poly"
92 | bl_description = ( "Lowpoly Tool" )
93 | bl_icon = os.path.join(os.path.join(os.path.dirname(__file__), "icons") , "addon.poly_quilt_poly_icon")
94 | bl_widget = "MESH_GGT_PQ_Lowpoly"
95 | bl_keymap = ToolPolyQuiltBase.tool_keymaps( [pq_main_tool] , shift = ['BRUSH'] )
96 |
97 | class ToolPolyQuiltKnife(ToolPolyQuiltBase):
98 | pq_main_tool = 'KNIFE'
99 | pq_description = 'Knife Tool'
100 |
101 | # The prefix of the idname should be your add-on name.
102 | bl_idname = "mesh_tool.poly_quilt_knife"
103 | bl_label = "PolyQuilt:Knife"
104 | bl_description = ( "Quick Knife Tool" )
105 | bl_icon = os.path.join(os.path.join(os.path.dirname(__file__), "icons") , "addon.poly_quilt_knife_icon")
106 | bl_widget = "MESH_GGT_PQ_Knife"
107 | bl_keymap = ToolPolyQuiltBase.tool_keymaps( [pq_main_tool] , shift = ['BRUSH'] )
108 |
109 |
110 | class ToolPolyQuiltDelete(ToolPolyQuiltBase):
111 | pq_main_tool = 'DELETE'
112 | pq_description = 'Delete Tool'
113 |
114 | # The prefix of the idname should be your add-on name.
115 | bl_idname = "mesh_tool.poly_quilt_delete"
116 | bl_label = "PolyQuilt:Delete"
117 | bl_description = ( "Quick Delete Tool" )
118 | bl_icon = os.path.join(os.path.join(os.path.dirname(__file__), "icons") , "addon.poly_quilt_delete_icon")
119 | bl_widget = "MESH_GGT_PQ_Delete"
120 | bl_keymap = ToolPolyQuiltBase.tool_keymaps( [pq_main_tool] , shift = ['BRUSH','DELETE'] )
121 |
122 | class ToolPolyQuiltExtrude(ToolPolyQuiltBase):
123 | pq_main_tool = 'EXTRUDE'
124 | pq_description = 'Extrude Tool'
125 |
126 | # The prefix of the idname should be your add-on name.
127 | bl_idname = "mesh_tool.poly_quilt_extrude"
128 | bl_label = "PolyQuilt:Extrude"
129 | bl_description = ( "Edge Extrude Tool" )
130 | bl_icon = os.path.join(os.path.join(os.path.dirname(__file__), "icons") , "addon.poly_quilt_extrude_icon")
131 | bl_widget = "MESH_GGT_PQ_Extrude"
132 | bl_keymap = ToolPolyQuiltBase.tool_keymaps( [pq_main_tool] , shift = ['BRUSH'] )
133 |
134 | class ToolPolyQuiltLoopCut(ToolPolyQuiltBase):
135 | pq_main_tool = 'LOOPCUT'
136 | pq_description = 'LoopCut Tool'
137 |
138 | # The prefix of the idname should be your add-on name.
139 | bl_idname = "mesh_tool.poly_quilt_loopcut"
140 | bl_label = "PolyQuilt:LoopCut"
141 | bl_description = ( "LoopCut Tool" )
142 | bl_icon = os.path.join(os.path.join(os.path.dirname(__file__), "icons") , "addon.poly_quilt_loopcut_icon")
143 | bl_widget = "MESH_GGT_PQ_LoopCut"
144 | bl_keymap = ToolPolyQuiltBase.tool_keymaps( [pq_main_tool] , shift = ['BRUSH'])
145 |
146 | class ToolPolyQuiltBrush(ToolPolyQuiltBase):
147 | pq_main_tool = 'BRUSH'
148 | pq_description = 'Brush Tool'
149 |
150 | # The prefix of the idname should be your add-on name.
151 | bl_idname = "mesh_tool.poly_quilt_brush"
152 | bl_label = "PolyQuilt:Brush"
153 | bl_description = ( "Brush Tool" )
154 | bl_icon = os.path.join(os.path.join(os.path.dirname(__file__), "icons") , "addon.poly_quilt_brush_icon")
155 | bl_widget = "MESH_GGT_PQ_Brush"
156 | bl_keymap = ToolPolyQuiltBase.tool_keymaps( [pq_main_tool], shift = ['BRUSH'] )
157 |
158 | class ToolPolyQuiltSeam(ToolPolyQuiltBase):
159 | pq_main_tool = 'MARK_SEAM'
160 | pq_description = 'Seam Tool'
161 |
162 | # The prefix of the idname should be your add-on name.
163 | bl_idname = "mesh_tool.poly_quilt_seam"
164 | bl_label = "PolyQuilt:Seam"
165 | bl_description = ( "Seam Tool" )
166 | bl_icon = os.path.join(os.path.join(os.path.dirname(__file__), "icons") , "addon.poly_quilt_seam_icon")
167 | bl_widget = "MESH_GGT_PQ_Seam"
168 | bl_keymap = ToolPolyQuiltBase.tool_keymaps( [pq_main_tool], ctrl = ['MARK_SEAM_LOOP'] )
169 |
170 | PolyQuiltTools = (
171 | { 'tool' : ToolPolyQuilt , 'after' : {"builtin.poly_build"} , 'group' : True },
172 | { 'tool' : ToolPolyQuiltPoly , 'after' : {"mesh_tool.poly_quilt"} , 'group' : False },
173 | { 'tool' : ToolPolyQuiltExtrude , 'after' : {"mesh_tool.poly_quilt"} , 'group' : False },
174 | { 'tool' : ToolPolyQuiltLoopCut , 'after' : {"mesh_tool.poly_quilt"} , 'group' : False },
175 | { 'tool' : ToolPolyQuiltKnife , 'after' : {"mesh_tool.poly_quilt"} , 'group' : False },
176 | { 'tool' : ToolPolyQuiltDelete , 'after' : {"mesh_tool.poly_quilt"} , 'group' : False },
177 | { 'tool' : ToolPolyQuiltBrush , 'after' : {"mesh_tool.poly_quilt"} , 'group' : False },
178 | { 'tool' : ToolPolyQuiltSeam , 'after' : {"mesh_tool.poly_quilt"} , 'group' : False },
179 | )
180 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/pq_tool_ui.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import os
15 | import bpy
16 | from bpy.types import WorkSpaceTool , Panel
17 | from bpy.utils.toolsystem import ToolDef
18 | from .pq_icon import *
19 | import inspect
20 | import rna_keymap_ui
21 | from bpy.app.translations import pgettext_iface as iface_
22 | from bpy.app.translations import contexts as i18n_contexts
23 | from bpy.types import AddonPreferences
24 | def draw_settings_ui(context, layout, tool , ui ):
25 | props = tool.operator_properties("mesh.poly_quilt")
26 | preferences = bpy.context.preferences.addons[__package__].preferences
27 |
28 | hoge = bpy.props.EnumProperty(
29 | name="LoopCut Mode",
30 | description="LoopCut Mode",
31 | items=[('EQUAL' , "Equal", "" ),
32 | ('EVEN' , "Even", "" ) ],
33 | default='EQUAL',
34 | )
35 |
36 | # layout.label(text="Make",text_ctxt="Make", translate=True, icon='NORMALS_FACE')
37 |
38 | if "MASTER" in ui or "LOWPOLY" in ui :
39 | col = layout.column(align=True)
40 | col.prop(props, "geometry_type" , text = "Geom" , expand = True , icon_only = False )
41 |
42 | col = layout.column(align=True)
43 | col.prop(props, "plane_pivot" , text = "Pivot" , expand = True , icon_only = False )
44 |
45 | col = layout.column(align=True)
46 | col.prop(props, "move_type" , text = "Move" , expand = True , icon_only = False )
47 |
48 | row = layout.row(align=True)
49 | row.prop(props, "snap_mode" , text = "Snap" , expand = True , icon_only = False )
50 |
51 | # layout.prop(context.active_object.data, "use_mirror_x", toggle = toggle , icon_only = False, icon_value = custom_icon("icon_opt_mirror") )
52 | layout.prop(context.active_object.data, "use_mirror_x", toggle = True , icon_only = False , icon = "MOD_MIRROR" )
53 | layout.prop( preferences, "fix_to_x_zero", toggle = True , text = "Fix X=0" , icon_only = False, icon_value = custom_icon("icon_opt_x0") )
54 |
55 | if "MASTER" in ui or "EXTRUDE" in ui :
56 | row = layout.row(align=True)
57 | row.prop(props, "extrude_mode" , text = "EXTRUDE" , expand = True )
58 |
59 | if "MASTER" in ui or "LOOPCUT" in ui :
60 | layout.separator()
61 | row = layout.row(align=True)
62 | row.prop(props, "loopcut_mode" , text = "LOOPCUT" , expand = True )
63 |
64 | row = layout.row(align=True)
65 | row.prop( preferences, "loopcut_division" , text = "Edge Snap Div" , expand = True, slider = True )
66 |
67 | layout.separator()
68 | col = layout.column(align=True)
69 | col.prop( preferences, "vertex_dissolve_angle" , text = "Vertex Dissolve Angle", expand = True, slider = True , icon_only = False )
70 |
71 | if "MASTER" in ui or "BRUSH" in ui :
72 | layout.separator()
73 | col = layout.column(align=True)
74 | col.prop( props, "brush_type" , text = "Brush", toggle = True , expand = True, icon_only = False )
75 |
76 | col.prop( preferences, "brush_size" , text = "Brush Size" , expand = True, slider = True , icon_only = False )
77 | col.prop( preferences, "brush_strength" , text = "Brush Strength" , expand = True, slider = True , icon_only = False )
78 | # shading = get_shading()
79 | # if shading.type == 'SOLID':
80 | # layout.prop( shading , "show_backface_culling", icon_value = custom_icon("icon_opt_backcull"))
81 |
82 | # tool_settings = context.tool_settings
83 | # layout.prop(tool_settings, "use_edge_path_live_unwrap")
84 | # layout.prop(tool_settings, "use_mesh_automerge")
85 | # layout.prop(tool_settings, "double_threshold")
86 | # layout.prop(tool_settings, "edge_path_mode")
87 |
88 | def draw_settings_toolheader(context, layout, tool , ui = ['GEOM','BRUSH','OPTION'] ):
89 | props = tool.operator_properties("mesh.poly_quilt")
90 |
91 | if "MASTER" in ui or "LOWPOLY" in ui :
92 | row = layout.row( align=True)
93 | row.label( text = "Geom" )
94 | row.prop(props, "geometry_type" , text = "Geom" , expand = True , icon_only = True )
95 |
96 | if "MASTER" in ui or "BRUSH" in ui :
97 | row = layout.row( align=True)
98 | row.label( text = "Brush" )
99 | row.prop( props , "brush_type" , text = "Brush", toggle = True , expand = True, icon_only = True )
100 |
101 | # Expand panels from the side-bar as popovers.
102 | popover_kw = {"space_type": 'VIEW_3D', "region_type": 'UI', "category": "Tool"}
103 | op = layout.popover_group(context=".poly_quilt_option", **popover_kw)
104 |
105 |
106 | class VIEW3D_PT_tools_polyquilt_options( Panel):
107 | bl_space_type = 'VIEW_3D'
108 | bl_region_type = 'UI'
109 |
110 | bl_category = "Tool"
111 | bl_context = ".poly_quilt_option" # dot on purpose (access from topbar)
112 | bl_label = "Options"
113 | bl_options = {'DEFAULT_CLOSED'}
114 | # bl_ui_units_x = 8
115 |
116 | def draw(self, context):
117 | layout = self.layout
118 |
119 | # Active Tool
120 | # -----------
121 | from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
122 | tool = ToolSelectPanelHelper.tool_active_from_context(context)
123 | # print(tool.idname)
124 | props = tool.operator_properties("mesh.poly_quilt")
125 | preferences = bpy.context.preferences.addons[__package__].preferences
126 |
127 | col = layout.column()
128 | col.label( text = "Pivot" )
129 | col.prop(props, "plane_pivot" , text = "Pivot" , expand = True )
130 |
131 | col = layout.column()
132 | col.label( text = "Move" )
133 | row = layout.row()
134 | row.ui_units_x = 3.25
135 | row.prop(props, "move_type" , text = "" , expand = True , icon_only = True )
136 |
137 | col = layout.column()
138 | col.label( text = "Snap" )
139 | row = layout.row(align=True)
140 | row.prop(props, "snap_mode" , text = "Snap" , expand = True , icon_only = False )
141 |
142 | layout.label( text = "Fix X=0" )
143 | layout.prop( preferences, "fix_to_x_zero", toggle = True , text = "" , icon_only = True, icon_value = custom_icon("icon_opt_x0") )
144 |
145 | layout.label( text = "Extrude" )
146 | layout.prop(props, "extrude_mode" , text = "EXTRUDE" , expand = True )
147 |
148 | layout.label( text = "LOOPCUT" )
149 | layout.prop(props, "loopcut_mode" , text = "LOOPCUT" , expand = True )
150 | col = layout.column()
151 | col.label( text = "Edge Snap Div" )
152 | col.prop( preferences, "loopcut_division" , text = "Edge Snap Div" , expand = True, slider = True , icon_only = False )
153 |
154 | col.label( text = "Vertex Dissolve Angle" )
155 | col.prop( preferences, "vertex_dissolve_angle" , text = "Vertex Dissolve Angle", expand = True, slider = True , icon_only = False )
156 |
157 | col.label( text = "Brush" )
158 | col.prop( preferences, "brush_size" , text = "Brush Size" , expand = True, slider = True , icon_only = False )
159 | col.prop( preferences, "brush_strength" , text = "Brush Strength" , expand = True, slider = True , icon_only = False )
160 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/__init__.py:
--------------------------------------------------------------------------------
1 |
2 | # This program is free software; you can redistribute it and/or modify
3 | # it under the terms of the GNU General Public License as published by
4 | # the Free Software Foundation; either version 3 of the License, or
5 | # (at your option) any later version.
6 | #
7 | # This program is distributed in the hope that it will be useful, but
8 | # WITHOUT ANY WARRANTY; without even the implied warranty of
9 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 | # General Public License for more details.
11 | #
12 | # You should have received a copy of the GNU General Public License
13 | # along with this program. If not, see .
14 |
15 | from .maintool_default import MainToolDefault
16 | from .maintool_hold import MainToolHold
17 | from .maintool_brush import *
18 | from .maintool_lowpoly import MainToolLowPoly
19 | from .maintool_knife import MainToolKnife
20 | from .maintool_delete import MainToolDelete
21 | from .maintool_extrude import MainToolExtrude
22 | from .maintool_loopcut import MainToolLoopCut
23 | from .maintool_edgeloop_dissolve import MainToolEdgeLoopDissolve
24 | from .subtool_seam import SubToolSeam
25 | from .subtool_seam_loop import SubToolSeamLoop
26 |
27 | maintools = {
28 | 'NONE' : None ,
29 | 'MASTER' : MainToolDefault ,
30 | # 'HOLD' : MainToolHold ,
31 | 'LOWPOLY' : MainToolLowPoly ,
32 | 'BRUSH' : MainToolBrush ,
33 | # 'BRUSH_DELETE' : MainToolBrushDelete ,
34 | # 'BRUSH_RELAX' : MainToolBrushRelax ,
35 | # 'BRUSH_MOVE' : MainToolBrushMove ,
36 | 'EXTRUDE' : MainToolExtrude ,
37 | 'KNIFE' : MainToolKnife ,
38 | 'DELETE' : MainToolDelete ,
39 | 'LOOPCUT' : MainToolLoopCut ,
40 | 'EDGELOOP_DISSOLVE' : MainToolEdgeLoopDissolve ,
41 | 'MARK_SEAM' : SubToolSeam ,
42 | 'MARK_SEAM_LOOP' : SubToolSeamLoop ,
43 | }
44 |
45 |
46 | def enum_tool_callback(scene, context ):
47 | return ( ( tool , cls.name if cls else "None" , "" ,"", index ) for index , (tool,cls) in enumerate(maintools.items()) )
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/maintool_brush.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import bpy
15 | import math
16 | import mathutils
17 | import bmesh
18 | import bpy_extras
19 | import collections
20 | import copy
21 | from ..utils import pqutil
22 | from ..utils import draw_util
23 | from ..utils.dpi import *
24 | from ..QMesh import *
25 | from ..utils.mouse_event_util import ButtonEventUtil, MBEventType
26 | from .subtool import *
27 | from .subtool_makepoly import *
28 | from .subtool_knife import *
29 | from .subtool_edge_slice import *
30 | from .subtool_edgeloop_cut import *
31 | from .subtool_edge_extrude import *
32 | from .subtool_brush_relax import *
33 | from .subtool_brush_size import *
34 | from .subtool_brush_move import *
35 | from .subtool_brush_delete import *
36 | from .subtool_move import *
37 | from .subtool_fin_slice import *
38 | from .subtool_autoquad import *
39 | from ..utils.dpi import *
40 |
41 |
42 |
43 |
44 | class MainToolBrush(MainTool) :
45 | name = "Brush"
46 |
47 | def __init__(self,op,currentTarget, button) :
48 | super().__init__(op,currentTarget, button)
49 | brush_tbl = {
50 | 'SMOOTH' : SubToolBrushRelax ,
51 | 'MOVE' : SubToolBrushMove ,
52 | 'DELETE' : SubToolBrushDelete ,
53 | }
54 |
55 | brush = op.brush_type
56 | brush_type = brush_tbl[ brush ]
57 |
58 | self.callback = {
59 | MBEventType.Release : [] ,
60 | MBEventType.Click : [SubToolAutoQuad] ,
61 | MBEventType.LongClick : [] ,
62 | MBEventType.LongPressDrag : [SubToolBrushSize] ,
63 | MBEventType.Drag : [brush_type] ,
64 | }
65 |
66 | @staticmethod
67 | def LMBEventCallback(self , event ):
68 | self.debugStr = str(event.type)
69 | if event.type in self.callback.keys() :
70 | tools = [ t( event.event , self) for t in self.callback[event.type] if t.Check( self , self.currentTarget ) ]
71 | if tools :
72 | self.SetSubTool( tools )
73 | self.isExit = True
74 |
75 | @classmethod
76 | def DrawHighlight( cls , gizmo , element ) :
77 | if SubToolAutoQuad.Check( None , element ) :
78 | drawAutoQuad = SubToolAutoQuad.DrawHighlight(gizmo,element)
79 | else :
80 | drawAutoQuad = None
81 |
82 | def Draw() :
83 | radius = gizmo.preferences.brush_size * dpm()
84 | strength = gizmo.preferences.brush_strength
85 | if drawAutoQuad :
86 | drawAutoQuad()
87 | with draw_util.push_pop_projection2D() :
88 | draw_util.draw_circle2D( gizmo.mouse_pos , radius * strength , color = (1,0.25,0.25,0.25), fill = False , subdivide = 64 , dpi= False )
89 | draw_util.draw_circle2D( gizmo.mouse_pos , radius , color = (1,1,1,0.5), fill = False , subdivide = 64 , dpi= False )
90 | return Draw
91 |
92 | @classmethod
93 | def UpdateHighlight( cls , gizmo , element ) :
94 | return True
95 |
96 | def OnDraw( self , context ) :
97 | radius = self.preferences.brush_size * dpm()
98 | strength = self.preferences.brush_strength
99 | draw_util.draw_circle2D( self.mouse_pos , radius * strength , color = (1,0.25,0.25,0.25), fill = False , subdivide = 64 , dpi= False )
100 | draw_util.draw_circle2D( self.mouse_pos , radius , color = (1,1,1,0.5), fill = False , subdivide = 64 , dpi= False )
101 |
102 | self.LMBEvent.Draw( self.mouse_pos )
103 |
104 | if self.LMBEvent.is_hold :
105 | draw_util.DrawFont( "Strenght = " + '{:.0f}'.format(self.preferences.brush_strength * 100) , 10 , self.mouse_pos , (0,0) )
106 | draw_util.DrawFont( "Radius = " + '{:.0f}'.format(self.preferences.brush_size * dpm() ) , 10 , self.mouse_pos , (0,-8) )
107 |
108 | def OnDraw3D( self , context ) :
109 | if not self.LMBEvent.presureComplite :
110 | if SubToolAutoQuad.Check( self , self.currentTarget ) :
111 | draw = SubToolAutoQuad.DrawHighlight(self,self.currentTarget)
112 | if draw :
113 | draw()
114 |
115 | @classmethod
116 | def GetCursor(cls) :
117 | return 'CROSSHAIR'
118 |
119 | @classmethod
120 | def recive_event( cls , gizmo , context , event ) :
121 | if any( [ event.shift , event.ctrl , event.alt, event.oskey ] ) :
122 | if event.type == 'WHEELUPMOUSE' :
123 | cls.change_brush_size( gizmo.preferences , context ,-50 , 0 )
124 |
125 | if event.type == 'WHEELDOWNMOUSE' :
126 | cls.change_brush_size( gizmo.preferences , context , 50 , 0 )
127 |
128 | return {'PASS_THROUGH'}
129 |
130 | @classmethod
131 | def change_brush_size( cls , preferences , context , brush_size_value , brush_strong_value ):
132 | if context.area.type == 'VIEW_3D' :
133 | a = (preferences.brush_size * preferences.brush_size) / 40000.0 + 0.1
134 | preferences.brush_size = preferences.brush_size + brush_size_value * a
135 | strength = min( max( 0 , preferences.brush_strength + brush_strong_value ) , 1 )
136 | preferences.brush_strength = strength
137 | context.area.tag_redraw()
138 |
139 | class MainToolBrushDelete(MainToolBrush) :
140 | name = "BrushDeleteSubTool"
141 |
142 | def __init__(self,op,currentTarget, button) :
143 | super().__init__(op,currentTarget, button)
144 | self.callback = {
145 | MBEventType.Release : [] ,
146 | MBEventType.Click : [] ,
147 | MBEventType.LongClick : [] ,
148 | MBEventType.LongPressDrag : [SubToolBrushSize] ,
149 | MBEventType.Drag : [SubToolBrushDelete] ,
150 | }
151 |
152 | def OnDraw3D( self , context ) :
153 | pass
154 |
155 | @classmethod
156 | def DrawHighlight( cls , gizmo , element ) :
157 | return SubToolBrushDelete.DrawHighlight(gizmo,element)
158 |
159 | class MainToolBrushRelax(MainToolBrush) :
160 | name = "BrushRelaxSubTool"
161 |
162 | def __init__(self,op,currentTarget, button) :
163 | super().__init__(op,currentTarget, button)
164 | self.callback = {
165 | MBEventType.Release : [] ,
166 | MBEventType.Click : [] ,
167 | MBEventType.LongClick : [] ,
168 | MBEventType.LongPressDrag : [SubToolBrushSize] ,
169 | MBEventType.Drag : [SubToolBrushRelax] ,
170 | }
171 |
172 | def OnDraw3D( self , context ) :
173 | pass
174 |
175 | @classmethod
176 | def DrawHighlight( cls , gizmo , element ) :
177 | return SubToolBrushRelax.DrawHighlight(gizmo,element)
178 |
179 | class MainToolBrushMove(MainToolBrush) :
180 | name = "BrushMoveSubTool"
181 |
182 | def __init__(self,op,currentTarget, button) :
183 | super().__init__(op,currentTarget, button)
184 | self.callback = {
185 | MBEventType.Release : [] ,
186 | MBEventType.Click : [] ,
187 | MBEventType.LongClick : [] ,
188 | MBEventType.LongPressDrag : [SubToolBrushSize] ,
189 | MBEventType.Drag : [SubToolBrushMove] ,
190 | }
191 |
192 | def OnDraw3D( self , context ) :
193 | pass
194 |
195 | @classmethod
196 | def DrawHighlight( cls , gizmo , element ) :
197 | return SubToolBrushMove.DrawHighlight(gizmo,element)
198 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/maintool_default.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import bpy
15 | import math
16 | import mathutils
17 | import bmesh
18 | import bpy_extras
19 | import collections
20 | import copy
21 | from ..utils import pqutil
22 | from ..utils import draw_util
23 | from ..QMesh import *
24 | from ..utils.mouse_event_util import ButtonEventUtil, MBEventType
25 | from .subtool import *
26 | from .subtool_makepoly import *
27 | from .subtool_knife import *
28 | from .subtool_edge_slice import *
29 | from .subtool_edgeloop_cut import *
30 | from .subtool_edge_extrude import *
31 | from .subtool_vert_extrude import *
32 | from .subtool_move import *
33 | from .subtool_fin_slice import *
34 | from .subtool_polypen import *
35 |
36 | class MainToolDefault(MainTool) :
37 | name = "Master Tool"
38 |
39 | def __init__(self,op,currentTarget, button) :
40 | super().__init__(op,currentTarget, button)
41 |
42 | @staticmethod
43 | def LMBEventCallback(self , event ):
44 | self.debugStr = str(event.type)
45 |
46 | if event.type == MBEventType.Release :
47 | self.isExit = True
48 |
49 | elif event.type == MBEventType.Click :
50 | if self.currentTarget.isVert or self.currentTarget.isEmpty or self.currentTarget.isEdge:
51 | self.SetSubTool( SubToolMakePoly(self.operator,self.currentTarget , self.mouse_pos ) )
52 |
53 | elif event.type == MBEventType.LongClick :
54 | if self.currentTarget.isVert :
55 | self.bmo.dissolve_vert( self.currentTarget.element , False , False , dissolve_vert_angle=self.preferences.vertex_dissolve_angle )
56 | elif self.currentTarget.isEdge :
57 | self.bmo.dissolve_edge( self.currentTarget.element , use_verts = False , use_face_split = False , dissolve_vert_angle=self.preferences.vertex_dissolve_angle )
58 | elif self.currentTarget.isFace :
59 | self.bmo.Remove( self.currentTarget.element )
60 | self.bmo.UpdateMesh()
61 | self.currentTarget = ElementItem.Empty()
62 |
63 | elif event.type == MBEventType.LongPressDrag :
64 | if self.currentTarget.isEdge :
65 | tools = []
66 | if SubToolPolyPen.Check( self ,self.currentTarget) :
67 | tools.append(SubToolPolyPen(self.operator,self.currentTarget))
68 | else :
69 | if len(self.currentTarget.element.link_faces) > 0 :
70 | tools.append(SubToolEdgeSlice(self.operator,self.currentTarget, self.mouse_pos))
71 | if SubToolEdgeloopCut.Check( self ,self.currentTarget) :
72 | tools.append(SubToolEdgeloopCut(self.operator,self.currentTarget))
73 | if SubToolEdgeExtrude.Check( self ,self.currentTarget) :
74 | tools.append(SubToolEdgeExtrude(self.operator,self.currentTarget,False))
75 | self.SetSubTool( tools )
76 | elif self.currentTarget.isVert :
77 | tools = []
78 | tools.append(SubToolFinSlice(self.operator,self.currentTarget ))
79 | if SubToolVertExtrude.Check( self ,self.currentTarget ) :
80 | tools.append(SubToolVertExtrude(self.operator,self.currentTarget))
81 | self.SetSubTool( tools )
82 | elif self.currentTarget.isEmpty :
83 | self.SetSubTool( SubToolKnife(self.operator,self.currentTarget , self.LMBEvent.PressPos ) )
84 |
85 | elif event.type == MBEventType.Drag :
86 | if self.currentTarget.isEdge :
87 | if self.currentTarget.can_extrude() :
88 | self.SetSubTool( SubToolEdgeExtrude(self.operator,self.currentTarget , False ) )
89 | else :
90 | self.SetSubTool( SubToolMove(self.operator,self.currentTarget , self.mouse_pos ) )
91 | elif self.currentTarget.isNotEmpty :
92 | self.SetSubTool( SubToolMove(self.operator,self.currentTarget , self.mouse_pos ) )
93 | else :
94 | self.isExit = self.do_empty_space(event)
95 |
96 |
97 | @classmethod
98 | def DrawHighlight( cls , gizmo , element ) :
99 | if element != None and gizmo.bmo != None :
100 | return element.DrawFunc( gizmo.bmo.obj , gizmo.preferences.highlight_color , gizmo.preferences , True )
101 | return None
102 |
103 | def OnDraw( self , context ) :
104 | if self.LMBEvent.isPresure :
105 | if self.currentTarget.isNotEmpty :
106 | self.LMBEvent.Draw( self.currentTarget.coord )
107 | else:
108 | self.LMBEvent.Draw( None )
109 |
110 | def OnDraw3D( self , context ) :
111 | if self.currentTarget.isNotEmpty :
112 | color = self.color_highlight()
113 | if self.LMBEvent.is_hold :
114 | color = self.color_delete()
115 | self.currentTarget.Draw( self.bmo.obj , color , self.preferences )
116 |
117 | def OnExit( self ) :
118 | pass
119 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/maintool_delete.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import bpy
15 | import math
16 | import mathutils
17 | import bmesh
18 | import bpy_extras
19 | import collections
20 | import copy
21 | from ..utils import pqutil
22 | from ..utils import draw_util
23 | from ..QMesh import *
24 | from ..utils.mouse_event_util import ButtonEventUtil, MBEventType
25 | from .subtool import *
26 | from .subtool_knife import *
27 | from .subtool_edge_slice import *
28 | from .subtool_edgeloop_cut import *
29 | from .subtool_delete import *
30 |
31 | class MainToolDelete(MainTool) :
32 | name = "Delete"
33 |
34 | def __init__(self,op,currentTarget, button) :
35 | super().__init__(op,currentTarget, button , no_hold = True )
36 |
37 | @staticmethod
38 | def LMBEventCallback(self , event ):
39 | self.debugStr = str(event.type)
40 |
41 | if event.type == MBEventType.Release :
42 | self.isExit = True
43 | elif event.type == MBEventType.Down or event.type == MBEventType.Click or event.type == MBEventType.LongClick:
44 | self.SetSubTool(SubToolDelete( self , self.currentTarget))
45 | elif event.type == MBEventType.Drag or event.type == MBEventType.LongPressDrag :
46 | self.SetSubTool(SubToolDelete( self , self.currentTarget))
47 |
48 | @classmethod
49 | def DrawHighlight( cls , gizmo , element ) :
50 | return SubToolDelete.DrawHighlight( gizmo , element )
51 |
52 | def OnDraw( self , context ) :
53 | pass
54 |
55 | def OnDraw3D( self , context ) :
56 | if self.currentTarget.isNotEmpty :
57 | self.currentTarget.Draw( self.bmo.obj , self.preferences.delete_color , self.preferences , edge_pivot = False )
58 |
59 | def OnExit( self ) :
60 | pass
61 |
62 | @classmethod
63 | def GetCursor(cls) :
64 | return 'ERASER'
65 |
66 | @staticmethod
67 | def pick_element( qmesh , location , preferences ) :
68 | element = qmesh.PickElement( location , preferences.distance_to_highlight )
69 | return element
70 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/maintool_edgeloop_dissolve.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import bpy
15 | import math
16 | import mathutils
17 | import bmesh
18 | import bpy_extras
19 | import collections
20 | import copy
21 | from ..utils import pqutil
22 | from ..utils import draw_util
23 | from ..QMesh import *
24 | from ..utils.mouse_event_util import ButtonEventUtil, MBEventType
25 | from .subtool import *
26 | from .subtool_knife import *
27 | from .subtool_edge_slice import *
28 | from .subtool_edgeloop_cut import *
29 | from .subtool_delete import *
30 |
31 | class MainToolEdgeLoopDissolve(MainTool) :
32 | name = "Dissolve Loop"
33 |
34 | def __init__(self,op,currentTarget, button) :
35 | super().__init__(op,currentTarget, button , no_hold = True )
36 | self.currentTarget = currentTarget
37 | self.removes , v = self.bmo.calc_edge_loop( self.currentTarget.element )
38 |
39 | @staticmethod
40 | def Check( root , target ) :
41 | return target.isEdge
42 |
43 | def OnUpdate( self , context , event ) :
44 | if event.type == 'MOUSEMOVE':
45 | self.currentTarget = self.bmo.PickElement( self.mouse_pos , self.preferences.distance_to_highlight , elements = ['EDGE'] )
46 | if self.currentTarget.isEdge :
47 | self.removes , v = self.bmo.calc_edge_loop( self.currentTarget.element )
48 | else :
49 | self.removes = []
50 | elif event.type == self.buttonType :
51 | if event.value == 'RELEASE' :
52 | if self.removes :
53 | self.bmo.dissolve_edges( self.removes , use_verts = False , use_face_split = False , dissolve_vert_angle=0 )
54 | self.bmo.UpdateMesh()
55 | return 'FINISHED'
56 | elif event.type == 'RIGHTMOUSE':
57 | if event.value == 'RELEASE' :
58 | return 'FINISHED'
59 | return 'RUNNING_MODAL'
60 |
61 | @classmethod
62 | def DrawHighlight( cls , gizmo , element ) :
63 | e , v = gizmo.bmo.calc_edge_loop( element.element )
64 | alpha = gizmo.preferences.highlight_face_alpha
65 | vertex_size = gizmo.preferences.highlight_vertex_size
66 | width = gizmo.preferences.highlight_line_width
67 | color = gizmo.preferences.delete_color
68 | return draw_util.drawElementsHilight3DFunc( gizmo.bmo.obj , e , vertex_size , width , alpha , color )
69 |
70 | def OnDraw( self , context ) :
71 | pass
72 |
73 | def OnDraw3D( self , context ) :
74 | if self.currentTarget.isEdge :
75 | alpha = self.preferences.highlight_face_alpha
76 | vertex_size = self.preferences.highlight_vertex_size
77 | width = self.preferences.highlight_line_width
78 | color = self.preferences.delete_color
79 | draw_util.drawElementsHilight3D( self.bmo.obj , self.removes , vertex_size , width , alpha , color )
80 |
81 | def OnExit( self ) :
82 | pass
83 |
84 | @classmethod
85 | def GetCursor(cls) :
86 | return 'ERASER'
87 |
88 | @staticmethod
89 | def pick_element( qmesh , location , preferences ) :
90 | element = qmesh.PickElement( location , preferences.distance_to_highlight, elements = ['EDGE'] )
91 | return element
92 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/maintool_extrude.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import bpy
15 | import math
16 | import mathutils
17 | import bmesh
18 | import bpy_extras
19 | import collections
20 | import copy
21 | from ..utils import pqutil
22 | from ..utils import draw_util
23 | from ..QMesh import *
24 | from ..utils.mouse_event_util import ButtonEventUtil, MBEventType
25 | from .subtool import *
26 | from .subtool_makepoly import *
27 | from .subtool_knife import *
28 | from .subtool_edge_slice import *
29 | from .subtool_edgeloop_cut import *
30 | from .subtool_edge_extrude import *
31 | from .subtool_vert_extrude import *
32 | from .subtool_move import *
33 | from .subtool_fin_slice import *
34 | from .subtool_polypen import *
35 |
36 | class MainToolExtrude(MainTool) :
37 | name = "Edge Extrude"
38 |
39 | def __init__(self,op,currentTarget, button) :
40 | super().__init__(op,currentTarget, button , no_hold = True )
41 |
42 | @staticmethod
43 | def LMBEventCallback(self , event ):
44 | self.debugStr = str(event.type)
45 |
46 | if event.type == MBEventType.Release :
47 | self.isExit = True
48 |
49 | elif event.type == MBEventType.Click or event.type == MBEventType.LongClick :
50 | if self.currentTarget.isVert or self.currentTarget.isEmpty or self.currentTarget.isEdge:
51 | self.SetSubTool( SubToolMakePoly(self.operator,self.currentTarget , self.mouse_pos ) )
52 |
53 | elif event.type == MBEventType.Drag or event.type == MBEventType.LongPressDrag :
54 | if self.currentTarget.isEdge :
55 | self.SetSubTool( SubToolEdgeExtrude(self.operator,self.currentTarget,False))
56 | elif self.currentTarget.isVert :
57 | self.SetSubTool( SubToolVertExtrude(self.operator,self.currentTarget))
58 |
59 | @classmethod
60 | def DrawHighlight( cls , gizmo , element ) :
61 | if element != None and gizmo.bmo != None :
62 | return element.DrawFunc( gizmo.bmo.obj , gizmo.preferences.highlight_color , gizmo.preferences , marker = True )
63 | return None
64 |
65 | @staticmethod
66 | def pick_element( qmesh , location , preferences ) :
67 | element = qmesh.PickElement( location , preferences.distance_to_highlight, edgering = True, elements = ['EDGE','VERT'] )
68 | # element.set_snap_div( preferences.loopcut_division )
69 | return element
70 |
71 | def OnDraw( self , context ) :
72 | pass
73 |
74 | def OnDraw3D( self , context ) :
75 | self.currentTarget.Draw( self.bmo.obj , self.color_highlight() , self.preferences )
76 |
77 | def OnExit( self ) :
78 | pass
79 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/maintool_hold.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import bpy
15 | import math
16 | import mathutils
17 | import bmesh
18 | import bpy_extras
19 | import collections
20 | import copy
21 | from ..utils import pqutil
22 | from ..utils import draw_util
23 | from ..QMesh import *
24 | from ..utils.mouse_event_util import ButtonEventUtil, MBEventType
25 | from .subtool import *
26 | from .subtool_makepoly import *
27 | from .subtool_knife import *
28 | from .subtool_edge_slice import *
29 | from .subtool_edge_slide import *
30 | from .subtool_edgeloop_cut import *
31 | from .subtool_edge_extrude import *
32 | from .subtool_edge_extrude_multi import *
33 | from .subtool_vert_extrude import *
34 | from .subtool_autoquad import *
35 | from .subtool_move import *
36 | from .subtool_fin_slice import *
37 |
38 | class MainToolHold(MainTool) :
39 | name = "Hold"
40 |
41 | def __init__(self,op,currentTarget, button) :
42 | super().__init__(op,currentTarget, button)
43 |
44 | @staticmethod
45 | def LMBEventCallback(self , event ):
46 | self.debugStr = str(event.type)
47 | if event.type == MBEventType.Down :
48 | pass
49 |
50 | elif event.type == MBEventType.Release :
51 | self.isExit = True
52 |
53 | elif event.type == MBEventType.Click :
54 | if self.currentTarget.isVert or self.currentTarget.isEdge or self.currentTarget.isEmpty:
55 | if SubToolAutoQuad.Check( self , self.currentTarget) :
56 | self.SetSubTool( SubToolAutoQuad( self ))
57 | self.isExit = True
58 |
59 | elif event.type == MBEventType.LongClick :
60 | if self.currentTarget.isVert :
61 | self.bmo.dissolve_vert( self.currentTarget.element , False , False )
62 | elif self.currentTarget.isEdge :
63 | self.bmo.dissolve_edge( self.currentTarget.element , False , False )
64 | elif self.currentTarget.isFace :
65 | self.bmo.Remove( self.currentTarget.element )
66 | self.bmo.UpdateMesh()
67 | self.currentTarget = ElementItem.Empty()
68 |
69 | elif event.type == MBEventType.Drag :
70 | if self.currentTarget.isEdge :
71 | tools = []
72 | tools.append(SubToolEdgeSlide(self.operator,self.currentTarget))
73 | self.SetSubTool( tools )
74 | elif self.currentTarget.isVert :
75 | tools = []
76 | if SubToolVertExtrude.Check( self ,self.currentTarget ) :
77 | tools.append(SubToolVertExtrude(self.operator,self.currentTarget))
78 | if tools :
79 | self.SetSubTool( tools )
80 | elif self.currentTarget.isEmpty :
81 | self.SetSubTool( SubToolKnife(self.operator,self.currentTarget , self.LMBEvent.PressPos ) )
82 |
83 | elif event.type == MBEventType.LongPressDrag :
84 | if self.currentTarget.isEdge :
85 | tools = []
86 | if len(self.currentTarget.element.link_faces) > 0 :
87 | tools.append(SubToolEdgeSlice(self.operator,self.currentTarget, self.mouse_pos))
88 | if SubToolEdgeloopCut.Check(self ,self.currentTarget) :
89 | tools.append(SubToolEdgeloopCut(self.operator,self.currentTarget))
90 | if SubToolEdgeExtrudeMulti.Check(self ,self.currentTarget) :
91 | tools.append(SubToolEdgeExtrudeMulti(self.operator,self.currentTarget,True))
92 | self.SetSubTool( tools )
93 | elif self.currentTarget.isVert :
94 | tools = []
95 | if SubToolEdgeExtrudeMulti.Check( self ,self.currentTarget ) :
96 | tools.append(SubToolEdgeExtrudeMulti(self.operator,self.currentTarget))
97 | self.SetSubTool( tools )
98 | elif self.currentTarget.isEmpty :
99 | self.SetSubTool( SubToolKnife(self.operator,self.currentTarget , self.LMBEvent.PressPos ) )
100 |
101 |
102 |
103 | @classmethod
104 | def DrawHighlight( cls , gizmo , element ) :
105 | if SubToolAutoQuad.Check( None , element ) :
106 | drawAutoQuad = SubToolAutoQuad.DrawHighlight(gizmo,element)
107 | else :
108 | drawAutoQuad = None
109 |
110 | if element.isEdge :
111 | edges , verts = gizmo.bmo.findEdgeLoop( element.element )
112 | else :
113 | edges = []
114 |
115 | width = gizmo.preferences.highlight_line_width
116 | color = gizmo.preferences.highlight_color
117 | def Draw() :
118 | if drawAutoQuad:
119 | drawAutoQuad()
120 | for edge in edges :
121 | vs = [ gizmo.bmo.obj.matrix_world @ v.co for v in edge.verts ]
122 | draw_util.draw_lines3D( bpy.context , vs , color , width , primitiveType = 'LINES' , hide_alpha = 0.5 )
123 | return Draw
124 |
125 | def OnDraw( self , context ) :
126 | if self.LMBEvent.isPresure :
127 | if self.currentTarget.isNotEmpty :
128 | self.LMBEvent.Draw( self.currentTarget.coord )
129 | else:
130 | self.LMBEvent.Draw( None )
131 |
132 | def OnDraw3D( self , context ) :
133 | width = self.preferences.highlight_line_width
134 | color = self.preferences.highlight_color
135 | if self.LMBEvent.isPresure :
136 | color = self.preferences.makepoly_color
137 | else :
138 | if self.currentTarget.isEdge :
139 | if SubToolAutoQuad.Check( None , self.currentTarget ) :
140 | drawAutoQuad = SubToolAutoQuad.DrawHighlight( self,self.currentTarget )
141 | if drawAutoQuad :
142 | drawAutoQuad()
143 | if self.currentTarget.isEdge :
144 | edges , verts = self.bmo.findEdgeLoop( self.currentTarget.element )
145 | else :
146 | edges = []
147 | for edge in edges :
148 | vs = [ self.bmo.obj.matrix_world @ v.co for v in edge.verts ]
149 | draw_util.draw_lines3D( bpy.context , vs , color , width , primitiveType = 'LINES' , hide_alpha = 0.5 )
150 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/maintool_knife.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import bpy
15 | import math
16 | import mathutils
17 | import bmesh
18 | import bpy_extras
19 | import collections
20 | import copy
21 | from ..utils import pqutil
22 | from ..utils import draw_util
23 | from ..QMesh import *
24 | from ..utils.mouse_event_util import ButtonEventUtil, MBEventType
25 | from .subtool import *
26 | from .subtool_makepoly import *
27 | from .subtool_knife import *
28 | from .subtool_edge_slice import *
29 | from .subtool_edgeloop_cut import *
30 | from .subtool_edge_extrude import *
31 | from .subtool_vert_extrude import *
32 | from .subtool_move import *
33 | from .subtool_fin_slice import *
34 | from .subtool_polypen import *
35 |
36 | class MainToolKnife(MainTool) :
37 | name = "Knife"
38 |
39 | def __init__(self,op,currentTarget, button) :
40 | super().__init__(op,currentTarget, button , no_hold = True )
41 |
42 | @staticmethod
43 | def LMBEventCallback(self , event ):
44 | self.debugStr = str(event.type)
45 |
46 | if event.type == MBEventType.Release :
47 | self.isExit = True
48 | elif event.type == MBEventType.Click or event.type == MBEventType.LongClick:
49 | if self.currentTarget.isVert or self.currentTarget.isEmpty or self.currentTarget.isEdge:
50 | self.SetSubTool( SubToolMakePoly(self.operator,self.currentTarget , self.mouse_pos ) )
51 | elif event.type == MBEventType.Drag or event.type == MBEventType.LongPressDrag:
52 | self.SetSubTool( SubToolKnife(self.operator,self.currentTarget , self.LMBEvent.PressPos ) )
53 |
54 | @classmethod
55 | def DrawHighlight( cls , gizmo , element ) :
56 | if element != None and gizmo.bmo != None :
57 | return element.DrawFunc( gizmo.bmo.obj , gizmo.preferences.highlight_color , gizmo.preferences , False )
58 | return None
59 |
60 | def OnDraw( self , context ) :
61 | pass
62 |
63 | def OnDraw3D( self , context ) :
64 | if self.currentTarget.isNotEmpty :
65 | color = self.color_highlight()
66 | self.currentTarget.Draw( self.bmo.obj , color , self.preferences )
67 |
68 | def OnExit( self ) :
69 | pass
70 |
71 | @classmethod
72 | def GetCursor(cls) :
73 | return 'KNIFE'
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/maintool_loopcut.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import bpy
15 | import math
16 | import mathutils
17 | import bmesh
18 | import bpy_extras
19 | import collections
20 | import copy
21 | from ..utils import pqutil
22 | from ..utils import draw_util
23 | from ..QMesh import *
24 | from ..utils.mouse_event_util import ButtonEventUtil, MBEventType
25 | from .subtool import *
26 | from .subtool_makepoly import *
27 | from .subtool_knife import *
28 | from .subtool_edge_slice import *
29 | from .subtool_edgeloop_cut import *
30 | from .subtool_edge_extrude import *
31 | from .subtool_vert_extrude import *
32 | from .subtool_move import *
33 | from .subtool_fin_slice import *
34 | from .subtool_polypen import *
35 |
36 | class MainToolLoopCut(MainTool) :
37 | name = "Loop Cut"
38 |
39 | def __init__(self,op,currentTarget, button) :
40 | super().__init__(op,currentTarget, button , no_hold = True )
41 |
42 | @staticmethod
43 | def LMBEventCallback(self , event ):
44 | last_mouse_pos = event.mouse_pos
45 | self.debugStr = str(event.type)
46 |
47 | if event.type == MBEventType.Release :
48 | self.isExit = True
49 |
50 | elif event.type == MBEventType.Down :
51 | if SubToolEdgeSlice.Check( self , self.currentTarget ):
52 | self.SetSubTool( SubToolEdgeSlice(self.operator,self.currentTarget, self.mouse_pos ) )
53 |
54 | @classmethod
55 | def DrawHighlight( cls , gizmo , target ) :
56 | if SubToolEdgeSlice.Check( None , target ) :
57 | return SubToolEdgeSlice.DrawHighlight( gizmo , target )
58 | return None
59 |
60 | @staticmethod
61 | def pick_element( qmesh , location , preferences ) :
62 | element = qmesh.PickElement( location , preferences.distance_to_highlight, edgering = False, elements = ['EDGE'] )
63 | element.set_snap_div( preferences.loopcut_division )
64 | return element
65 |
66 | def OnDraw( self , context ) :
67 | pass
68 |
69 | def OnDraw3D( self , context ) :
70 | self.currentTarget.Draw( self.bmo.obj , self.color_highlight() , self.preferences )
71 |
72 | def OnExit( self ) :
73 | pass
74 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/maintool_lowpoly.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import bpy
15 | import math
16 | import mathutils
17 | import bmesh
18 | import bpy_extras
19 | import collections
20 | import copy
21 | from ..utils import pqutil
22 | from ..utils import draw_util
23 | from ..QMesh import *
24 | from ..utils.mouse_event_util import ButtonEventUtil, MBEventType
25 | from .subtool import *
26 | from .subtool_makepoly import *
27 | from .subtool_knife import *
28 | from .subtool_edge_slice import *
29 | from .subtool_edgeloop_cut import *
30 | from .subtool_edge_extrude import *
31 | from .subtool_vert_extrude import *
32 | from .subtool_move import *
33 | from .subtool_fin_slice import *
34 | from .subtool_polypen import *
35 |
36 | class MainToolLowPoly(MainTool) :
37 | name = "Make Polygon"
38 |
39 | def __init__(self,op,currentTarget, button) :
40 | super().__init__(op,currentTarget, button , no_hold = True )
41 |
42 | @staticmethod
43 | def LMBEventCallback(self , event ):
44 | self.debugStr = str(event.type)
45 |
46 | if event.type == MBEventType.Release :
47 | self.isExit = True
48 |
49 | elif event.type == MBEventType.Click or event.type == MBEventType.LongClick :
50 | if self.currentTarget.isVert or self.currentTarget.isEmpty or self.currentTarget.isEdge:
51 | self.SetSubTool( SubToolMakePoly(self.operator,self.currentTarget , self.mouse_pos ) )
52 |
53 | elif event.type == MBEventType.Drag or event.type == MBEventType.LongPressDrag :
54 | if self.currentTarget.isNotEmpty :
55 | self.SetSubTool( SubToolMove(self.operator,self.currentTarget , self.mouse_pos ) )
56 | else :
57 | self.isExit = self.do_empty_space(event)
58 |
59 | @classmethod
60 | def DrawHighlight( cls , gizmo , element ) :
61 | if element != None and gizmo.bmo != None :
62 | return element.DrawFunc( gizmo.bmo.obj , gizmo.preferences.highlight_color , gizmo.preferences , False )
63 | return None
64 |
65 | def OnDraw( self , context ) :
66 | pass
67 |
68 | def OnDraw3D( self , context ) :
69 | self.currentTarget.Draw( self.bmo.obj , self.color_highlight() , self.preferences )
70 |
71 | def OnExit( self ) :
72 | pass
73 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/subtool.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import bpy
15 | import blf
16 | import math
17 | import mathutils
18 | import bmesh
19 | from enum import Enum , auto
20 | import bpy_extras
21 | import collections
22 | from ..utils import pqutil
23 | from ..utils.mouse_event_util import ButtonEventUtil, MBEventType
24 | import time
25 | from ..QMesh import *
26 |
27 | class SubToolRoot :
28 | name = "None"
29 | __timer_handle = None
30 |
31 | def __init__(self,op, button = None) :
32 | self.operator = op
33 | self.bmo : QMesh = op.bmo
34 | self.debugStr = ""
35 | self.subTool = []
36 | self.__enterySubTool = None
37 | self.step = 0
38 | self.mouse_pos = mathutils.Vector((0,0))
39 | self.preferences = op.preferences
40 | self.activeSubTool = None
41 | self.buttonType = button
42 |
43 | @staticmethod
44 | def Check( root ,target ) :
45 | return True
46 |
47 | def Active(self) :
48 | return self if self.activeSubTool == None else self.activeSubTool
49 |
50 | @classmethod
51 | def GetCursor(cls) :
52 | return 'DEFAULT'
53 |
54 | def CurrentCursor( self ) :
55 | if self.activeSubTool is None :
56 | return self.GetCursor()
57 | return self.activeSubTool.CurrentCursor()
58 |
59 | def SetSubTool( self , subTool ) :
60 | if isinstance( subTool , list) :
61 | self.__enterySubTool = subTool
62 | else :
63 | self.__enterySubTool = [ subTool ]
64 |
65 | def OnInit( self , context ) :
66 | pass
67 |
68 | def OnExit( self ) :
69 | pass
70 |
71 | def OnForcus( self , context , event ) :
72 | return True
73 |
74 | def OnUpdate( self , context , event ) :
75 | return 'FINISHED'
76 |
77 | def OnDraw( self , context ) :
78 | pass
79 |
80 | def OnDraw3D( self , context ) :
81 | pass
82 |
83 | def Update( self , context , event ) :
84 |
85 | ret = None
86 | self.mouse_pos = mathutils.Vector((event.mouse_region_x, event.mouse_region_y))
87 |
88 | if self.__enterySubTool != None :
89 | self.subTool = self.__enterySubTool
90 | self.__enterySubTool = None
91 | for subTool in self.subTool :
92 | self.OnEnterSubTool( context , subTool)
93 |
94 | self.activeSubTool = None
95 | if self.subTool :
96 | for subTool in self.subTool :
97 | ret = subTool.Update(context , event)
98 | if ret == 'RUNNING_MODAL' :
99 | self.activeSubTool = subTool
100 | break
101 | elif ret == 'FINISHED' :
102 | break
103 | elif ret == 'PASS_THROUGH' :
104 | ret = None
105 |
106 | if ret == 'FINISHED' :
107 | for subTool in self.subTool :
108 | subTool.OnExit()
109 | self.OnExitSubTool( context , subTool)
110 |
111 | if ret == 'PASS_THROUGH' :
112 | ret = 'RUNNING_MODAL'
113 |
114 | if ret == None :
115 | if self.OnForcus(context , event) :
116 | ret = self.OnUpdate(context,event)
117 | else :
118 | return 'PASS_THROUGH'
119 |
120 | if ret != 'RUNNING_MODAL' :
121 | self.subTool = []
122 | self.OnExit()
123 |
124 | self.step += 1
125 | return ret
126 |
127 | def check_animated( self , context ) :
128 | if self.activeSubTool :
129 | return self.activeSubTool.is_animated(context)
130 | else :
131 | return self.is_animated(context)
132 | return False
133 |
134 | def is_animated( self , context ) :
135 | return False
136 |
137 | def Draw2D( self , context ) :
138 | if self.activeSubTool :
139 | self.activeSubTool.Draw2D(context )
140 | else :
141 | self.OnDraw(context)
142 |
143 | def Draw3D( self , context ) :
144 | if self.activeSubTool :
145 | self.activeSubTool.Draw3D(context )
146 | else :
147 | self.OnDraw3D(context)
148 |
149 | def OnEnterSubTool( self ,context,subTool ):
150 | pass
151 |
152 | def OnExitSubTool( self ,context,subTool ):
153 | return 'RUNNING_MODAL'
154 |
155 | def color_highlight( self , alpha = 1.0 ) :
156 | col = self.preferences.highlight_color
157 | return (col[0],col[1],col[2],col[3] * alpha)
158 |
159 | def color_create( self , alpha = 1.0 ) :
160 | col = self.preferences.makepoly_color
161 | return (col[0],col[1],col[2],col[3] * alpha)
162 |
163 | def color_split( self , alpha = 1.0 ) :
164 | col = self.preferences.split_color
165 | return (col[0],col[1],col[2],col[3] * alpha)
166 |
167 | def color_delete( self ,alpha = 1.0 ) :
168 | col = self.preferences.delete_color
169 | return (col[0],col[1],col[2],col[3] * alpha)
170 |
171 | @classmethod
172 | def DrawHighlight( cls , gizmo , element ) :
173 | if element != None and gizmo.bmo != None :
174 | return element.DrawFunc( gizmo.bmo.obj , gizmo.preferences.highlight_color , gizmo.preferences )
175 | return None
176 |
177 | @classmethod
178 | def UpdateHighlight( cls , gizmo , element ) :
179 | if gizmo.currentElement.element != element.element :
180 | return True
181 | elif element.isEdge :
182 | if element.coord != gizmo.currentElement.coord :
183 | return True
184 | return False
185 |
186 | @classmethod
187 | def recive_event( cls , gizmo , context , event ) :
188 | pass
189 |
190 | class MainTool(SubToolRoot) :
191 | def __init__(self,op,currentTarget, button , no_hold = False ) :
192 | super().__init__(op, button)
193 | self.currentTarget = currentTarget
194 | self.LMBEvent = ButtonEventUtil( button ,self, self.LMBEventCallback , op , True , no_hold )
195 | self.isExit = False
196 |
197 | def is_animated( self , context ) :
198 | return self.LMBEvent.is_animated()
199 |
200 | @staticmethod
201 | def LMBEventCallback(self , event ):
202 | pass
203 |
204 | def OnUpdate( self , context , event ) :
205 | if self.isExit :
206 | return 'FINISHED'
207 |
208 | self.LMBEvent.Update(context,event)
209 |
210 | return 'RUNNING_MODAL'
211 |
212 | def OnEnterSubTool( self ,context,subTool ):
213 | self.currentTarget = ElementItem.Empty()
214 | self.LMBEvent.Reset(context)
215 |
216 | def OnExitSubTool( self ,context,subTool ):
217 | self.currentTarget = ElementItem.Empty()
218 |
219 | def do_empty_space( self , event ) :
220 | if self.preferences.space_drag_op == "ORBIT" :
221 | bpy.ops.view3d.rotate('INVOKE_DEFAULT', use_cursor_init=True)
222 | return True
223 | elif self.preferences.space_drag_op == "PAN" :
224 | bpy.ops.view3d.move('INVOKE_DEFAULT', use_cursor_init=True)
225 | return True
226 | elif self.preferences.space_drag_op == "DOLLY" :
227 | bpy.ops.view3d.zoom('INVOKE_DEFAULT', use_cursor_init=True)
228 | return True
229 | elif self.preferences.space_drag_op == "KNIFE" :
230 | self.SetSubTool( SubToolKnife(self.operator,self.currentTarget , self.LMBEvent.PressPos ) )
231 | elif self.preferences.space_drag_op == "SELECT_BOX" :
232 | bpy.context.window.cursor_warp( event.PressPrevPos.x , event.PressPrevPos.y )
233 | bpy.ops.view3d.select_box('INVOKE_DEFAULT' ,wait_for_input=False, mode='SET')
234 | bpy.context.window.cursor_warp( event.event.mouse_prev_x ,event.event.mouse_prev_y )
235 | return True
236 | elif self.preferences.space_drag_op == "SELECT_LASSO" :
237 | bpy.context.window.cursor_warp( event.PressPrevPos.x , event.PressPrevPos.y )
238 | bpy.ops.view3d.select_lasso('INVOKE_DEFAULT' , path = [], mode='SET')
239 | bpy.context.window.cursor_warp( event.event.mouse_prev_x ,event.event.mouse_prev_y )
240 | return True
241 |
242 | return True
243 |
244 | @classmethod
245 | def UpdateHighlight( cls , gizmo , element ) :
246 | return True
247 |
248 | @staticmethod
249 | def pick_element( qmesh , location , preferences ) :
250 | element = qmesh.PickElement( location , preferences.distance_to_highlight )
251 | element.set_snap_div( preferences.loopcut_division )
252 | return element
253 |
254 |
255 | class SubTool(SubToolRoot) :
256 | def __init__( self, op ) :
257 | super().__init__(op )
258 |
259 | class SubToolEx(SubTool) :
260 | def __init__( self, root ) :
261 | super().__init__( root.operator )
262 | self.rootTool = root
263 | self.currentTarget = root.currentTarget
264 | self.startMousePos = root.mouse_pos.copy()
265 |
266 |
267 |
268 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/subtool_brush_delete.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import sys
15 | import bpy
16 | import math
17 | import mathutils
18 | import bmesh
19 | import copy
20 | import bpy_extras
21 | import collections
22 | from ..utils import pqutil
23 | from ..utils import draw_util
24 | from ..QMesh import *
25 | from .subtool import SubToolEx
26 | from ..utils.dpi import *
27 |
28 | class SubToolBrushDelete(SubToolEx) :
29 | name = "DeleteBrushTool"
30 |
31 | def __init__(self, event , root ) :
32 | super().__init__( root )
33 | self.radius = self.preferences.brush_size * dpm()
34 | self.strength = self.preferences.brush_strength
35 | self.mirror_tbl = {}
36 | matrix = self.bmo.obj.matrix_world
37 | self.remove_faces = self.collect_faces( bpy.context , self.startMousePos )
38 | if self.bmo.is_mirror_mode :
39 | mirror = { self.bmo.find_mirror( f ) for f in self.remove_faces }
40 | mirror = { m for m in mirror if m != None }
41 | self.remove_faces = self.remove_faces | mirror
42 |
43 | @staticmethod
44 | def Check( root , target ) :
45 | return True
46 |
47 | @classmethod
48 | def DrawHighlight( cls , gizmo , element ) :
49 | def Draw() :
50 | radius = gizmo.preferences.brush_size * dpm()
51 | strength = gizmo.preferences.brush_strength
52 | color = gizmo.preferences.delete_color
53 | with draw_util.push_pop_projection2D() :
54 | draw_util.draw_circle2D( gizmo.mouse_pos , radius * strength , color = color, fill = False , subdivide = 64 , dpi= False )
55 | draw_util.draw_circle2D( gizmo.mouse_pos , radius , color = color, fill = False , subdivide = 64 , dpi= False )
56 | return Draw
57 |
58 | def OnUpdate( self , context , event ) :
59 | if event.type == 'MOUSEMOVE':
60 | faces = self.collect_faces( context , self.mouse_pos )
61 | if self.bmo.is_mirror_mode :
62 | mirror = { self.bmo.find_mirror( f ) for f in faces if f not in self.remove_faces }
63 | mirror = { m for m in mirror if m != None }
64 | self.remove_faces = self.remove_faces | mirror
65 | self.remove_faces = self.remove_faces | faces
66 |
67 | elif event.type == self.rootTool.buttonType :
68 | if event.value == 'RELEASE' :
69 | if self.remove_faces :
70 | self.bmo.delete_faces( list( self.remove_faces ) )
71 | self.bmo.UpdateMesh()
72 | return 'FINISHED'
73 | return 'CANCELLED'
74 | elif event.value == 'RELEASE' :
75 | self.repeat = False
76 |
77 | return 'RUNNING_MODAL'
78 |
79 | def OnDraw( self , context ) :
80 | color = self.preferences.delete_color
81 | draw_util.draw_circle2D( self.mouse_pos , self.radius , color , fill = False , subdivide = 64 , dpi= False , width = 1.0 )
82 |
83 | def OnDraw3D( self , context ) :
84 | alpha = self.preferences.highlight_face_alpha
85 | vertex_size = self.preferences.highlight_vertex_size
86 | width = self.preferences.highlight_line_width
87 | color = self.preferences.delete_color
88 | draw_util.drawElementsHilight3D( self.bmo.obj , self.remove_faces , vertex_size , width , alpha , color )
89 |
90 | def collect_faces( self , context , coord ) :
91 | radius = self.radius
92 | bm = self.bmo.bm
93 |
94 | select_stack = SelectStack( context , bm )
95 | select_stack.push()
96 | select_stack.select_mode(False,False,True)
97 | bpy.ops.view3d.select_circle( x = coord.x , y = coord.y , radius = radius , wait_for_input=False, mode='SET' )
98 | faces = { f for f in self.bmo.bm.faces if f.select }
99 | select_stack.pop()
100 | return faces
101 |
102 | @classmethod
103 | def GetCursor(cls) :
104 | return 'ERASER'
105 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/subtool_brush_move.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import sys
15 | import bpy
16 | import math
17 | import mathutils
18 | import bmesh
19 | import copy
20 | import bpy_extras
21 | import collections
22 | from ..utils import pqutil
23 | from ..utils import draw_util
24 | from ..QMesh import *
25 | from .subtool import SubToolEx
26 | from ..utils.dpi import *
27 |
28 | class SubToolBrushMove(SubToolEx) :
29 | name = "MoveBrushTool"
30 |
31 | def __init__(self, event , root ) :
32 | super().__init__( root )
33 | self.radius = self.preferences.brush_size * dpm()
34 | self.strength = self.preferences.brush_strength
35 | self.mirror_tbl = {}
36 | matrix = self.bmo.obj.matrix_world
37 | self.verts = self.CollectVerts( bpy.context , self.startMousePos )
38 |
39 | if self.bmo.is_mirror_mode :
40 | self.mirrors = { vert : self.bmo.find_mirror( vert ) for vert in self.verts }
41 | else :
42 | self.mirrors = {}
43 |
44 | @staticmethod
45 | def Check( root , target ) :
46 | return True
47 |
48 | def OnUpdate( self , context , event ) :
49 | if event.type == 'MOUSEMOVE':
50 | self.UpdateVerts(context)
51 | elif event.type == self.rootTool.buttonType :
52 | if event.value == 'RELEASE' :
53 | if self.verts :
54 | self.bmo.UpdateMesh()
55 | return 'FINISHED'
56 | return 'CANCELLED'
57 | elif event.value == 'RELEASE' :
58 | self.repeat = False
59 |
60 | return 'RUNNING_MODAL'
61 |
62 | @classmethod
63 | def DrawHighlight( cls , gizmo , element ) :
64 | def Draw() :
65 | radius = gizmo.preferences.brush_size * dpm()
66 | strength = gizmo.preferences.brush_strength
67 | with draw_util.push_pop_projection2D() :
68 | draw_util.draw_circle2D( gizmo.mouse_pos , radius * strength , color = (1,0.25,0.25,0.25), fill = False , subdivide = 64 , dpi= False )
69 | draw_util.draw_circle2D( gizmo.mouse_pos , radius , color = (1,1,1,0.5), fill = False , subdivide = 64 , dpi= False )
70 | return Draw
71 |
72 | def OnDraw( self , context ) :
73 | radius = self.preferences.brush_size * dpm()
74 | strength = self.preferences.brush_strength
75 |
76 | draw_util.draw_circle2D( self.mouse_pos , radius * strength , color = (1,0.25,0.25,0.25), fill = False , subdivide = 64 , dpi= False )
77 | draw_util.draw_circle2D( self.startMousePos , self.radius , color = (0.75,0.75,1,1), fill = False , subdivide = 64 , dpi= False , width = 1.0 )
78 |
79 | def OnDraw3D( self , context ) :
80 | pass
81 |
82 | def CollectVerts( self , context , coord ) :
83 | rv3d = context.region_data
84 | region = context.region
85 | halfW = region.width / 2.0
86 | halfH = region.height / 2.0
87 | matrix_world = self.bmo.obj.matrix_world
88 | matrix = rv3d.perspective_matrix @ matrix_world
89 | radius = self.radius
90 | bm = self.bmo.bm
91 | verts = bm.verts
92 |
93 | select_stack = SelectStack( context , bm )
94 |
95 | select_stack.push()
96 | select_stack.select_mode(True,False,False)
97 | bpy.ops.view3d.select_circle( x = coord.x , y = coord.y , radius = radius , wait_for_input=False, mode='SET' )
98 | # bm.select_flush(False)
99 |
100 | is_target = QSnap.is_target
101 | new_vec = mathutils.Vector
102 | pw = (self.strength * self.strength ) * 8
103 |
104 | def ProjVert( vt ) :
105 | co = vt.co
106 | if not is_target(matrix_world @ co) :
107 | return None
108 |
109 | pv = matrix @ co.to_4d()
110 | w = pv.w
111 | if w < 0.0 :
112 | return None
113 | p = new_vec( (pv.x * halfW / w + halfW , pv.y * halfH / w + halfH ) )
114 | r = (coord - p).length
115 | if r > radius :
116 | return None
117 |
118 | x = (radius - r) / radius
119 | r = (1-x) ** pw
120 | return ( p , r , matrix_world @ co , co )
121 |
122 | coords = { vert : ProjVert(vert) for vert in verts if vert.select }
123 |
124 | select_stack.pop()
125 |
126 | return { v : x for v,x in coords.items() if x }
127 |
128 | def UpdateVerts( self , context ) :
129 | is_fix_zero = self.preferences.fix_to_x_zero or self.bmo.is_mirror_mode
130 | region = context.region
131 | rv3d = context.region_data
132 | move = self.mouse_pos - self.startMousePos
133 | matrix = self.bmo.obj.matrix_world
134 | matrix_inv = self.bmo.obj.matrix_world.inverted()
135 | region_2d_to_location_3d = pqutil.region_2d_to_location_3d
136 | is_x_zero_pos = self.bmo.is_x_zero_pos
137 | zero_pos = self.bmo.zero_pos
138 | mirror_pos = self.bmo.mirror_pos
139 |
140 | for v,(p,r,co,orig) in self.verts.items() :
141 | coord = p + move
142 | x = region_2d_to_location_3d( region = region , rv3d = rv3d , coord = coord , depth_location = co)
143 | x = co.lerp( x , 1 - r )
144 | x = QSnap.adjust_point( x )
145 | x = matrix_inv @ x
146 | if is_fix_zero and is_x_zero_pos(orig) :
147 | x.x = 0
148 | v.co = x
149 |
150 | if self.bmo.is_mirror_mode :
151 | for vert , mirror in self.mirrors.items() :
152 | if mirror != None :
153 | if mirror in self.verts.keys() :
154 | ms = self.verts[mirror][1]
155 | vs = self.verts[vert][1]
156 | if vs <= ms :
157 | mirror.co = mirror_pos(vert.co)
158 | else :
159 | vert.co = mirror_pos(mirror.co)
160 | else :
161 | mirror.co = mirror_pos(vert.co)
162 |
163 | self.bmo.UpdateMesh()
164 |
165 | @classmethod
166 | def GetCursor(cls) :
167 | return 'SCROLL_XY'
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/subtool_brush_relax.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import sys
15 | import bpy
16 | import math
17 | import mathutils
18 | import bmesh
19 | import copy
20 | import bpy_extras
21 | import collections
22 | from ..utils import pqutil
23 | from ..utils import draw_util
24 | from ..QMesh import *
25 | from .subtool import SubToolEx
26 | from ..utils.dpi import *
27 |
28 | class SubToolBrushRelax(SubToolEx) :
29 | name = "RelaxBrushTool"
30 |
31 | def __init__(self, event , root) :
32 | super().__init__(root)
33 | self.radius = self.preferences.brush_size * dpm()
34 | self.occlusion_tbl = {}
35 | self.mirror_tbl = {}
36 | self.dirty = False
37 | if self.currentTarget.isEmpty or ( self.currentTarget.isEdge and self.currentTarget.element.is_boundary ) :
38 | self.effective_boundary = True
39 | else :
40 | self.effective_boundary = False
41 |
42 | @staticmethod
43 | def Check( root , target ) :
44 | return True
45 |
46 | @classmethod
47 | def DrawHighlight( cls , gizmo , element ) :
48 | def Draw() :
49 | radius = gizmo.preferences.brush_size * dpm()
50 | strength = gizmo.preferences.brush_strength
51 | with draw_util.push_pop_projection2D() :
52 | draw_util.draw_circle2D( gizmo.mouse_pos , radius * strength , color = (1,0.25,0.25,0.25), fill = False , subdivide = 64 , dpi= False )
53 | draw_util.draw_circle2D( gizmo.mouse_pos , radius , color = (1,1,1,0.5), fill = False , subdivide = 64 , dpi= False )
54 | return Draw
55 |
56 | def OnUpdate( self , context , event ) :
57 | if event.type == 'MOUSEMOVE':
58 | self.DoRelax( context ,self.mouse_pos )
59 | elif event.type == self.rootTool.buttonType :
60 | if event.value == 'RELEASE' :
61 | if self.dirty :
62 | self.bmo.UpdateMesh()
63 | return 'FINISHED'
64 | return 'CANCELLED'
65 | elif event.value == 'RELEASE' :
66 | self.repeat = False
67 |
68 | return 'RUNNING_MODAL'
69 |
70 | def OnDraw( self , context ) :
71 | width = 2.0 if self.effective_boundary else 1.0
72 | draw_util.draw_circle2D( self.mouse_pos , self.radius , color = (1,1,1,1), fill = False , subdivide = 64 , dpi= False , width = width )
73 | pass
74 |
75 | def OnDraw3D( self , context ) :
76 | pass
77 |
78 | def CollectVerts( self , context , coord ) :
79 | rv3d = context.region_data
80 | region = context.region
81 | halfW = region.width / 2.0
82 | halfH = region.height / 2.0
83 | matrix_world = self.bmo.obj.matrix_world
84 | matrix = rv3d.perspective_matrix @ matrix_world
85 | radius = self.radius
86 | bm = self.bmo.bm
87 | verts = bm.verts
88 |
89 | select_stack = SelectStack( context , bm )
90 |
91 | select_stack.push()
92 | select_stack.select_mode(True,False,False)
93 | bpy.ops.view3d.select_circle( x = coord.x , y = coord.y , radius = radius , wait_for_input=False, mode='SET' )
94 | # bm.select_flush(False)
95 |
96 | occlusion_tbl_get = self.occlusion_tbl.get
97 | is_target = QSnap.is_target
98 | new_vec = mathutils.Vector
99 | def ProjVert( vt ) :
100 | co = vt.co
101 | is_occlusion = occlusion_tbl_get(vt)
102 | if is_occlusion == None :
103 | is_occlusion = is_target(matrix_world @ co)
104 | self.occlusion_tbl[vt] = is_occlusion
105 |
106 | if not is_occlusion :
107 | return None
108 |
109 | pv = matrix @ co.to_4d()
110 | w = pv.w
111 | if w < 0.0 :
112 | return None
113 | p = new_vec( (pv.x * halfW / w + halfW , pv.y * halfH / w + halfH ) )
114 | r = (coord - p).length
115 | if r > radius :
116 | return None
117 |
118 | x = (radius - r) / radius
119 | return ( vt , x ** 2 , co.copy())
120 |
121 | coords = [ ProjVert(vert) for vert in verts if vert.select ]
122 |
123 | select_stack.pop()
124 |
125 | return { c[0]: [c[1],c[2]] for c in coords if c != None }
126 |
127 | def MirrorVert( self , context , coords ) :
128 | # ミラー頂点を検出
129 | find_mirror = self.bmo.find_mirror
130 | mirrors = { vert : find_mirror( vert ) for vert , coord in coords.items() }
131 |
132 | # 重複する場合は
133 | for (vert , coord) , mirror in zip( coords.items() , mirrors.values() ) :
134 | if mirror != None :
135 | cur = coord[0]
136 | dst = coords[mirror][0]
137 | coord[0] = cur if dst <= cur else dst
138 |
139 | # 重複しないものを列挙
140 | mirrors = { vert : [coords[vert][0] , mirror.co.copy() ] for vert , mirror in mirrors.items() if mirror != None and mirror not in coords }
141 |
142 | coords.update(mirrors)
143 | return coords
144 |
145 | def DoRelax( self , context , coord ) :
146 | is_fix_zero = self.preferences.fix_to_x_zero or self.bmo.is_mirror_mode
147 | coords = self.CollectVerts( context, coord )
148 | if coords :
149 | self.dirty = True
150 | if self.bmo.is_mirror_mode :
151 | mirrors = { vert : self.bmo.find_mirror( vert ) for vert , coord in coords.items() }
152 |
153 | if not self.effective_boundary :
154 | bmesh.ops.smooth_vert( self.bmo.bm , verts = [ c for c in coords.keys() if not c.is_boundary ] , factor = self.preferences.brush_strength ,
155 | mirror_clip_x = is_fix_zero, mirror_clip_y = False, mirror_clip_z = False, clip_dist = 0.0001 ,
156 | use_axis_x = True, use_axis_y = True, use_axis_z = True)
157 | else :
158 | boundary = { c for c in coords.keys() if c.is_boundary }
159 |
160 | result = {}
161 | for v in boundary :
162 | if len(v.link_faces) != 1 :
163 | tv = mathutils.Vector( (0,0,0) )
164 | le = [ e.other_vert( v ).co for e in v.link_edges if e.is_boundary ]
165 | for te in le :
166 | tv = tv + te
167 | result[v] = tv * (1/ len(le) )
168 | for v , co in result.items() :
169 | v.co = co
170 |
171 | bmesh.ops.smooth_vert( self.bmo.bm , verts = list( coords.keys() - boundary ) , factor = self.preferences.brush_strength ,
172 | mirror_clip_x = is_fix_zero, mirror_clip_y = False, mirror_clip_z = False, clip_dist = 0.0001 ,
173 | use_axis_x = True, use_axis_y = True, use_axis_z = True)
174 |
175 | # bmesh.ops.smooth_laplacian_vert(self.bmo.bm , verts = hits , lambda_factor = 1.0 , lambda_border = 0.0 ,
176 | # use_x = True, use_y = True, use_z = True, preserve_volume = False )
177 |
178 | matrix_world = self.bmo.obj.matrix_world
179 | is_x_zero_pos = self.bmo.is_x_zero_pos
180 | zero_pos = self.bmo.zero_pos
181 | mirror_pos = self.bmo.mirror_pos
182 | # matrix_world_inv = matrix_world.inverted()
183 | for v , (f,orig) in coords.items() :
184 | p = QSnap.adjust_local( matrix_world , v.co , is_fix_zero )
185 | s = orig.lerp( p , f )
186 | if is_fix_zero and is_x_zero_pos(s) :
187 | s = zero_pos(s)
188 | v.co = s
189 |
190 | if self.bmo.is_mirror_mode :
191 | for vert , mirror in mirrors.items() :
192 | if mirror != None :
193 | if mirror in coords :
194 | ms = coords[mirror][0]
195 | vs = coords[vert][0]
196 | if vs >= ms :
197 | mirror.co = mirror_pos(vert.co)
198 | else :
199 | vert.co = mirror_pos(mirror.co)
200 | else :
201 | mirror.co = mirror_pos(vert.co)
202 |
203 | # self.bmo.bm.normal_update()
204 | # self.bmo.obj.data.update_gpu_tag()
205 | # self.bmo.obj.data.update_tag()
206 | # self.bmo.obj.update_from_editmode()
207 | # self.bmo.obj.update_tag()
208 | bmesh.update_edit_mesh(self.bmo.obj.data , loop_triangles = False,destructive = False )
209 |
210 | @classmethod
211 | def GetCursor(cls) :
212 | return 'CROSSHAIR'
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/subtool_brush_size.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import sys
15 | import bpy
16 | import math
17 | import mathutils
18 | import bmesh
19 | import copy
20 | import bpy_extras
21 | import collections
22 | from ..utils import pqutil
23 | from ..utils import draw_util
24 | from ..QMesh import *
25 | from .subtool import SubToolEx
26 | from ..utils.dpi import *
27 |
28 | class SubToolBrushSize(SubToolEx) :
29 | name = "BrushSizeTool"
30 |
31 | def __init__(self, event , root ) :
32 | super().__init__(root)
33 | self.preMousePos = self.startMousePos
34 | self.start_radius = self.preferences.brush_size * dpm()
35 | self.radius = self.start_radius
36 | self.strength = self.preferences.brush_strength
37 | self.start_strength = self.strength
38 | self.PressPrevPos = mathutils.Vector( (event.mouse_prev_x , event.mouse_prev_y) )
39 |
40 | def OnUpdate( self , context , event ) :
41 | if event.type == 'MOUSEMOVE':
42 | vec = self.mouse_pos - self.preMousePos
43 | self.preMousePos = self.mouse_pos
44 | nrm = vec.normalized()
45 | ang = 0.8
46 | if nrm.x > ang or nrm.x < -ang :
47 | self.radius = self.radius + vec.x
48 | self.radius = min( max( 50 , self.radius ) , 500 )
49 | self.preferences.brush_size = self.radius / dpm()
50 |
51 | if nrm.y > ang or nrm.y < -ang :
52 | self.strength = self.strength + vec.y / self.radius
53 | self.strength = min( max( 0 , self.strength ) , 1 )
54 | self.preferences.brush_strength = self.strength
55 |
56 | elif event.type == self.rootTool.buttonType :
57 | if event.value == 'RELEASE' :
58 | bpy.context.window.cursor_warp( self.PressPrevPos.x , self.PressPrevPos.y )
59 | return 'FINISHED'
60 |
61 | return 'RUNNING_MODAL'
62 |
63 | def OnDraw( self , context ) :
64 | draw_util.draw_circle2D( self.startMousePos , self.radius * self.strength , color = (1,0.25,0.25,0.5), fill = False , subdivide = 64 , dpi= False )
65 | draw_util.draw_circle2D( self.startMousePos , self.radius , color = (1,1,1,1), fill = False , subdivide = 64 , dpi= False )
66 | draw_util.DrawFont( "Strenght = " + '{:.0f}'.format(self.strength * 100) , 10 , self.startMousePos , (0,0) )
67 | draw_util.DrawFont( "Radius = " + '{:.0f}'.format(self.radius ) , 10 , self.startMousePos , (0,-8) )
68 |
69 | def OnDraw3D( self , context ) :
70 | pass
71 |
72 | def resetMouse(self, context, event):
73 | context.window.cursor_warp(context.region.x + context.region.width / 2 - 0.5*(event.mouse_x - event.mouse_prev_x), \
74 | context.region.y + context.region.height / 2 - 0.5*(event.mouse_y - event.mouse_prev_y))
75 |
76 | @classmethod
77 | def GetCursor(cls) :
78 | return 'NONE'
79 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/subtool_delete.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import sys
15 | import bpy
16 | import math
17 | import mathutils
18 | import bmesh
19 | import copy
20 | import bpy_extras
21 | import collections
22 | from ..utils import pqutil
23 | from ..utils import draw_util
24 | from ..QMesh import *
25 | from .subtool import SubToolEx
26 | from ..utils.dpi import *
27 |
28 | class SubToolDelete(SubToolEx) :
29 | name = "DeleteTool"
30 |
31 | def __init__(self,root,currentTarget) :
32 | super().__init__( root )
33 | self.currentTarget = currentTarget
34 | self.startTarget = currentTarget
35 | self.removes = ( [ currentTarget.element ] , [] )
36 |
37 | @staticmethod
38 | def Check( root , target ) :
39 | return target.isNotEmpty
40 |
41 | def OnUpdate( self , context , event ) :
42 | if event.type == 'MOUSEMOVE':
43 | preTarget = self.currentTarget
44 | self.currentTarget = self.bmo.PickElement( self.mouse_pos , self.preferences.distance_to_highlight , elements = [self.startTarget.type_name] )
45 | if self.currentTarget.isNotEmpty :
46 | vt = None
47 | if self.startTarget.isEdge :
48 | vt = self.bmo.highlight.check_hit_element_vert( self.startTarget.element , self.mouse_pos , self.preferences.distance_to_highlight * dpm())
49 | ed = None
50 | if self.startTarget.isFace :
51 | ed = self.bmo.highlight.check_hit_element_edge( self.startTarget.element , self.mouse_pos , self.preferences.distance_to_highlight * dpm())
52 | if vt :
53 | e , v = self.bmo.calc_edge_loop( self.startTarget.element )
54 | self.removes = (e,v)
55 | self.currentTarget = self.startTarget
56 | elif ed :
57 | self.removes = ( self.bmo.calc_loop_face(ed) , [] )
58 | elif self.startTarget.element != self.currentTarget.element and self.startTarget.type == self.currentTarget.type :
59 | self.removes = self.bmo.calc_shortest_pass( self.bmo.bm , self.startTarget.element , self.currentTarget.element )
60 | else :
61 | self.removes = ([self.startTarget.element],[])
62 | else :
63 | self.removes = ([self.startTarget.element],[])
64 | elif event.type == self.rootTool.buttonType :
65 | if event.value == 'RELEASE' :
66 | self.RemoveElement(self.currentTarget )
67 | return 'FINISHED'
68 | elif event.type == 'RIGHTMOUSE':
69 | if event.value == 'RELEASE' :
70 | return 'FINISHED'
71 | return 'RUNNING_MODAL'
72 |
73 | @classmethod
74 | def DrawHighlight( cls , gizmo , element ) :
75 | if element != None and gizmo.bmo != None :
76 | return element.DrawFunc( gizmo.bmo.obj , gizmo.preferences.delete_color , gizmo.preferences , marker = False , edge_pivot = False )
77 | return None
78 |
79 | def OnDraw( self , context ) :
80 | pass
81 |
82 | def OnDraw3D( self , context ) :
83 | if self.removes[0] :
84 | alpha = self.preferences.highlight_face_alpha
85 | vertex_size = self.preferences.highlight_vertex_size
86 | width = self.preferences.highlight_line_width
87 | color = self.preferences.delete_color
88 | draw_util.drawElementsHilight3D( self.bmo.obj , self.removes[0] , vertex_size , width , alpha , color )
89 | if self.bmo.is_mirror_mode :
90 | mirrors = [ self.bmo.find_mirror(m) for m in self.removes[0] ]
91 | mirrors = [ m for m in mirrors if m ]
92 | if mirrors :
93 | draw_util.drawElementsHilight3D( self.bmo.obj , mirrors , vertex_size , width , alpha * 0.5 , color )
94 |
95 | if self.startTarget.element == self.currentTarget.element :
96 | self.startTarget.Draw( self.bmo.obj , self.preferences.delete_color , self.preferences , marker = False , edge_pivot = True )
97 | else :
98 | self.startTarget.Draw( self.bmo.obj , self.preferences.delete_color , self.preferences , marker = False , edge_pivot = False )
99 | if self.currentTarget.isNotEmpty :
100 | if self.startTarget.element != self.currentTarget.element :
101 | self.currentTarget.Draw( self.bmo.obj , self.preferences.delete_color , self.preferences , marker = False , edge_pivot = False )
102 |
103 | @classmethod
104 | def GetCursor(cls) :
105 | return 'ERASER'
106 |
107 | def RemoveElement( self , element ) :
108 | def dissolve_edges( edges ) :
109 | single_edges = [ e for e in edges if len(e.link_faces) == 0 ]
110 | edges = [ e for e in edges if len(e.link_faces) > 0 ]
111 |
112 | if single_edges :
113 | self.bmo.delete_edges( list(single_edges) )
114 |
115 | if all( e.is_boundary for e in edges ) :
116 | faces = set()
117 | for e in edges :
118 | for f in e.link_faces :
119 | faces.add(f)
120 | self.bmo.delete_faces( list(faces) )
121 | else :
122 | self.bmo.dissolve_edges( edges , use_verts = False , use_face_split = False , dissolve_vert_angle=self.preferences.vertex_dissolve_angle )
123 |
124 | if element.isNotEmpty :
125 | if self.removes[0] and self.removes[1] :
126 | self.bmo.do_edge_loop_cut( self.removes[0] , self.removes[1] )
127 | elif element.isVert :
128 | edges = [ r for r in self.removes[0] if isinstance( r , bmesh.types.BMEdge ) ]
129 | if edges :
130 | dissolve_edges( edges )
131 | else :
132 | self.bmo.dissolve_vert( element.element , False , False , dissolve_vert_angle=self.preferences.vertex_dissolve_angle )
133 | elif element.isEdge :
134 | dissolve_edges( self.removes[0] )
135 | elif element.isFace :
136 | self.bmo.delete_faces( self.removes[0] )
137 | self.bmo.UpdateMesh()
138 |
139 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/subtool_edge_slide.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import sys
15 | import bpy
16 | import math
17 | import mathutils
18 | import bmesh
19 | import bpy_extras
20 | import collections
21 | from ..utils import pqutil
22 | from ..utils import draw_util
23 | from ..QMesh import *
24 | from ..utils.dpi import *
25 | from .subtool import SubTool
26 |
27 | class SubToolEdgeSlide(SubTool) :
28 | name = "EdgeLoopSlideTool"
29 |
30 | def __init__(self,op, target : ElementItem ) :
31 | super().__init__(op)
32 | self.currentEdge = target.element
33 | self.edges , self.vetrs = self.bmo.findEdgeLoop( self.currentEdge )
34 | l2w = self.bmo.local_to_world_pos
35 | t = [ [ l2w( e.other_vert(v).co) for e in self.vetrs[v] ] for v in self.currentEdge.verts ]
36 | v0 = l2w(self.currentEdge.verts[0].co)
37 | v1 = l2w(self.currentEdge.verts[1].co)
38 | rate = ( v0 - target.hitPosition ).length / ( v0 - v1 ).length
39 | self.center_pos = target.hitPosition
40 | self.target_poss = [ t[0][i].lerp( t[1][i] , rate ) for i in range( len(self.currentEdge.link_faces) ) ]
41 | self.initial_poss = { v : v.co.copy() for v in self.vetrs }
42 | self.other_poss = { v : [ e.other_vert(v).co.copy() for e in edges] for v,edges in self.vetrs.items() }
43 | self.rates = [0,0]
44 |
45 | def OnUpdate( self , context , event ) :
46 | if event.type == 'MOUSEMOVE':
47 | self.rates = self.calcRate( context , self.mouse_pos )
48 | max_rate = max( self.rates )
49 | if max_rate <= sys.float_info.epsilon :
50 | for vert in self.vetrs :
51 | vert.co = self.initial_poss[vert]
52 | else :
53 | for vert in self.vetrs :
54 | edges = self.vetrs[vert]
55 | for i in range( len(edges)) :
56 | if self.rates[i] >= sys.float_info.epsilon and self.rates[i] == max_rate :
57 | p = self.initial_poss[vert].lerp( self.other_poss[vert][i] , self.rates[i] )
58 | vert.co = p
59 | self.bmo.UpdateMesh()
60 |
61 | elif event.type == 'RIGHTMOUSE' :
62 | if event.value == 'PRESS' :
63 | pass
64 | elif event.value == 'RELEASE' :
65 | pass
66 | elif event.type == 'LEFTMOUSE' :
67 | if event.value == 'RELEASE' :
68 | return 'FINISHED'
69 | return 'RUNNING_MODAL'
70 |
71 | def OnDraw( self , context ) :
72 | pass
73 |
74 | def calcRate( self , context , location ) :
75 | ray = pqutil.Ray.from_screen( context , location )
76 | r = [ ray.hit_to_line( self.center_pos , p ) for p in self.target_poss ]
77 | return r
78 |
79 | def OnDraw3D( self , context ) :
80 | for t in self.target_poss :
81 | draw_util.draw_lines3D( context , [self.center_pos,t] , width = 1 , color = self.color_create() , primitiveType = 'LINES' , hide_alpha = 1 )
82 |
83 | for (t,r) in zip( self.target_poss , self.rates ) :
84 | draw_util.draw_pivots3D( ( self.center_pos.lerp( t , r ) ,) , self.preferences.highlight_vertex_size / 2 , self.color_split(0.5) )
85 |
86 | lines = []
87 | for e in self.edges :
88 | lines.append( self.bmo.local_to_world_pos( e.verts[0].co ) )
89 | lines.append( self.bmo.local_to_world_pos( e.verts[1].co ) )
90 | draw_util.draw_lines3D( context , lines , width = 3 , color = self.color_highlight() , primitiveType = 'LINES' , hide_alpha = 1 )
91 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/subtool_edgeloop_cut.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import sys
15 | import bpy
16 | import math
17 | import mathutils
18 | import bmesh
19 | import bpy_extras
20 | import collections
21 | from ..utils import pqutil
22 | from ..utils import draw_util
23 | from ..QMesh import *
24 | from ..utils.dpi import *
25 | from .subtool import SubTool
26 |
27 | class SubToolEdgeloopCut(SubTool) :
28 | name = "EdgeLoopCutTool"
29 |
30 | def __init__(self,op, target : ElementItem ) :
31 | super().__init__(op)
32 | self.currentEdge = target
33 | self.is_forcus = False
34 | self.EdgeLoops = None
35 | self.VertLoops = None
36 |
37 | def Check( root ,target : ElementItem ) :
38 | if len( target.element.link_faces ) == 2 :
39 | return True
40 | return False
41 |
42 | def OnForcus( self , context , event ) :
43 | if event.type == 'MOUSEMOVE':
44 | self.is_forcus = False
45 | p0 = self.bmo.local_to_2d( self.currentEdge.element.verts[0].co )
46 | p1 = self.bmo.local_to_2d( self.currentEdge.element.verts[1].co )
47 | if self.bmo.is_snap2D( self.mouse_pos , p0 ) or self.bmo.is_snap2D( self.mouse_pos , p1 ) :
48 | self.is_forcus = True
49 | if self.EdgeLoops == None :
50 | self.EdgeLoops , self.VertLoops = self.bmo.calc_edge_loop( self.currentEdge.element )
51 | return self.is_forcus
52 |
53 | def OnUpdate( self , context , event ) :
54 | if event.type == 'RIGHTMOUSE' :
55 | if event.value == 'PRESS' :
56 | pass
57 | elif event.value == 'RELEASE' :
58 | pass
59 | elif event.type == 'LEFTMOUSE' :
60 | if event.value == 'RELEASE' :
61 | if self.EdgeLoops != None :
62 | # bpy.ops.mesh.select_all(action='DESELECT')
63 | self.bmo.do_edge_loop_cut( self.EdgeLoops , self.VertLoops )
64 | self.bmo.UpdateMesh()
65 | self.currentTarget = ElementItem.Empty()
66 | return 'FINISHED'
67 | return 'CANCELLED'
68 | return 'RUNNING_MODAL'
69 |
70 | def OnDraw( self , context ) :
71 | pass
72 |
73 | def OnDraw3D( self , context ) :
74 | if self.EdgeLoops != None :
75 | alpha = self.preferences.highlight_face_alpha
76 | vertex_size = self.preferences.highlight_vertex_size
77 | width = self.preferences.highlight_line_width
78 | color = self.color_delete()
79 | draw_util.drawElementsHilight3D( self.bmo.obj , self.EdgeLoops , vertex_size ,width,alpha, color )
80 |
81 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/subtool_fin_slice.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import sys
15 | import bpy
16 | import math
17 | import mathutils
18 | import bmesh
19 | import copy
20 | import bpy_extras
21 | import collections
22 | from ..utils import pqutil
23 | from ..utils import draw_util
24 | from ..QMesh import *
25 | from ..utils.dpi import *
26 | from .subtool import SubTool
27 |
28 | class SubToolFinSlice(SubTool) :
29 | name = "FinSliceTool"
30 |
31 | def __init__(self,op, target ) :
32 | super().__init__(op)
33 | self.currentTarget = target
34 | self.slice_rate = 0.0
35 | self.slice_dist = 0.0
36 |
37 | def OnForcus( self , context , event ) :
38 | if event.type == 'MOUSEMOVE':
39 | self.slice_rate , self.slice_dist = self.CalcRate(context,self.mouse_pos)
40 | return self.slice_rate > 0
41 |
42 | def OnUpdate( self , context , event ) :
43 | if event.type == 'RIGHTMOUSE' :
44 | return 'FINISHED'
45 | elif event.type == 'LEFTMOUSE' :
46 | if event.value == 'RELEASE' :
47 | if self.slice_rate == 1 :
48 | self.DoSplit()
49 | elif self.slice_rate > 0 :
50 | self.DoSlice()
51 | return 'FINISHED'
52 | return 'RUNNING_MODAL'
53 |
54 |
55 | def OnDraw( self , context ) :
56 | pass
57 |
58 | def OnDraw3D( self , context ) :
59 | self.currentTarget.Draw( self.bmo.obj , self.color_split() , self.preferences )
60 |
61 | for edge in self.currentTarget.element.link_edges :
62 | v0 = self.bmo.local_to_world_pos(edge.verts[0].co)
63 | v1 = self.bmo.local_to_world_pos(edge.verts[1].co)
64 | draw_util.draw_lines3D( context , (v0,v1) , self.color_split(0.25) , self.preferences.highlight_line_width , 1.0 , primitiveType = 'LINES' )
65 | if self.slice_rate == 1.0 :
66 | p = self.bmo.local_to_world_pos(edge.other_vert(self.currentTarget.element).co)
67 | draw_util.draw_pivots3D( ( p , ) , 1 , self.color_split() )
68 |
69 | rate = self.slice_rate
70 | virts , edges = self.collect_geom( self.currentTarget.element , self.currentTarget.mirror )
71 |
72 | for vert in virts :
73 | for face in vert.link_faces :
74 | links = [ e for e in face.edges if e in edges ]
75 | if len(links) == 2 :
76 | for v in virts :
77 | p0 = self.bmo.local_to_world_pos(v.co)
78 | if v in links[0].verts :
79 | p1 = self.bmo.local_to_world_pos(links[0].other_vert(v).co)
80 | v0 = p0 *(1-rate) + p1 * rate
81 | if v in links[1].verts :
82 | p1 = self.bmo.local_to_world_pos(links[1].other_vert(v).co)
83 | v1 = p0 *(1-rate) + p1 * rate
84 | draw_util.draw_lines3D( context , (v0,v1) , self.color_split() , self.preferences.highlight_line_width , 1.0 , primitiveType = 'LINES' )
85 |
86 | def collect_geom( self , element , mirror ) :
87 | if self.bmo.is_mirror_mode and mirror is not None :
88 | s0 = set(element.link_edges)
89 | s1 = set(mirror.link_edges)
90 | edges = ( s0 | s1 ) - ( s0 & s1 )
91 | virts = [ element, mirror]
92 | else :
93 | edges = element.link_edges
94 | virts = [element]
95 | return virts , edges
96 |
97 | def CalcRate( self , context , coord ):
98 | rate = 0.0
99 | dist = 0.0
100 | ray = pqutil.Ray.from_screen( context , coord ).world_to_object( self.bmo.obj )
101 | d = self.preferences.distance_to_highlight* dpm()
102 | for edge in self.currentTarget.element.link_edges :
103 | r = pqutil.CalcRateEdgeRay( self.bmo.obj , context , edge , self.currentTarget.element , coord , ray , d )
104 | if r > rate :
105 | rate = r
106 | dist = d * (edge.verts[0].co - edge.verts[1].co).length
107 | return rate , dist
108 |
109 | def DoSplit( self ) :
110 | verts , edges = self.collect_geom( self.currentTarget.element , self.currentTarget.mirror )
111 |
112 | for vert in verts :
113 | for face in vert.link_faces :
114 | links = [ e for e in face.edges if e in edges ]
115 | if len(links) == 2 :
116 | v0 = links[0].other_vert(vert)
117 | v1 = links[1].other_vert(vert)
118 |
119 | if self.bmo.bm.edges.get( (v0 , v1) ) == None :
120 | bmesh.utils.face_split( face , v0 , v1 )
121 | self.bmo.UpdateMesh()
122 |
123 | def DoSlice( self ) :
124 | _slice = {}
125 | _edges = []
126 |
127 | def append( vert , other_vert ) :
128 | for edge in vert.link_edges :
129 | if other_vert is not None :
130 | if edge not in other_vert.link_edges :
131 | _edges.append( edge )
132 | else :
133 | _edges.append( edge )
134 | _slice[ edge ] = self.slice_rate if ( edge.verts[0] == vert ) else (1.0 - self.slice_rate)
135 |
136 | if self.bmo.is_mirror_mode and self.currentTarget.mirror is not None :
137 | append( self.currentTarget.element , self.currentTarget.mirror )
138 | append( self.currentTarget.mirror , self.currentTarget.element )
139 | else :
140 | append( self.currentTarget.element , None )
141 |
142 | ret = bmesh.ops.subdivide_edges(
143 | self.bmo.bm ,
144 | edges = _edges ,
145 | edge_percents = _slice ,
146 | smooth = 0 ,
147 | smooth_falloff = 'SMOOTH' ,
148 | use_smooth_even = False ,
149 | fractal = 0.0 ,
150 | along_normal = 0.0 ,
151 | cuts = 1 ,
152 | quad_corner_type = 'STRAIGHT_CUT' ,
153 | use_single_edge = False ,
154 | use_grid_fill=False,
155 | use_only_quads = False ,
156 | seed = 0 ,
157 | use_sphere = False
158 | )
159 |
160 | for e in ret['geom_inner'] :
161 | e.select_set(True)
162 | if QSnap.is_active() :
163 | QSnap.adjust_verts( self.bmo.obj , [ v for v in ret['geom_inner'] if isinstance( v , bmesh.types.BMVert ) ] , self.preferences.fix_to_x_zero )
164 |
165 | self.bmo.UpdateMesh()
166 |
167 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/subtool_knife.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import sys
15 | import bpy
16 | import blf
17 | import math
18 | import mathutils
19 | import bmesh
20 | import bpy_extras
21 | import collections
22 | from ..utils import pqutil
23 | from ..utils import draw_util
24 | from ..QMesh import *
25 | from .subtool import SubTool
26 |
27 | class SubToolKnife(SubTool) :
28 | name = "KnifeTool2"
29 |
30 | def __init__(self,op, currentElement : ElementItem ,startPos) :
31 | super().__init__(op)
32 | if currentElement.isNotEmpty and not currentElement.isFace :
33 | self.startPos = currentElement.coord
34 | else :
35 | self.startPos = startPos
36 | self.endPos = self.startPos
37 | self.cut_edges = {}
38 | self.cut_edges_mirror = {}
39 | self.startElement = currentElement
40 | self.goalElement = ElementItem.Empty()
41 |
42 | def OnUpdate( self , context , event ) :
43 | self.goalElement = self.bmo.PickElement( self.mouse_pos , self.preferences.distance_to_highlight, elements = ['EDGE','VERT'] )
44 | if self.goalElement.isNotEmpty and not self.goalElement.isFace :
45 | self.goalElement.set_snap_div( self.preferences.loopcut_division )
46 | self.endPos = self.goalElement.coord
47 | else :
48 | self.endPos = self.mouse_pos
49 |
50 | if event.type == 'MOUSEMOVE':
51 | if( self.startPos - self.endPos ).length > 2 :
52 | self.CalcKnife( context,self.startPos,self.endPos )
53 | elif event.type == 'RIGHTMOUSE' :
54 | if event.value == 'RELEASE' :
55 | return 'FINISHED'
56 | elif event.type == 'LEFTMOUSE' :
57 | if event.value == 'RELEASE' :
58 | if self.cut_edges or self.cut_edges_mirror :
59 | self.DoKnife(context,self.startPos,self.endPos)
60 | self.bmo.UpdateMesh()
61 | return 'FINISHED'
62 | return 'CANCELLED'
63 | return 'RUNNING_MODAL'
64 |
65 | def OnDraw( self , context ) :
66 | draw_util.draw_lines2D( (self.startPos,self.endPos) , self.color_delete() , self.preferences.highlight_line_width )
67 |
68 | def OnDraw3D( self , context ) :
69 | if self.goalElement.isNotEmpty :
70 | self.goalElement.Draw( self.bmo.obj , self.color_highlight() , self.preferences )
71 |
72 | if self.cut_edges :
73 | draw_util.draw_pivots3D( list(self.cut_edges.values()) , 1 , self.color_delete() )
74 | if self.cut_edges_mirror :
75 | draw_util.draw_pivots3D( list(self.cut_edges_mirror.values()) , 1 , self.color_delete(0.5) )
76 |
77 | def CalcKnife( self ,context,startPos , endPos ) :
78 | slice_plane , plane0 , plane1 = self.make_slice_planes(context,startPos , endPos)
79 | self.cut_edges = self.calc_slice( slice_plane , plane0 , plane1 )
80 | if self.bmo.is_mirror_mode :
81 | slice_plane.x_mirror()
82 | plane0.x_mirror()
83 | plane1.x_mirror()
84 | self.cut_edges_mirror = self.calc_slice( slice_plane , plane0 , plane1 )
85 |
86 | def make_slice_planes( self , context,startPos , endPos ):
87 | slice_plane_world = pqutil.Plane.from_screen_slice( context,startPos , endPos )
88 | slice_plane_object = slice_plane_world.world_to_object( self.bmo.obj )
89 |
90 | ray0 = pqutil.Ray.from_screen( context , startPos ).world_to_object( self.bmo.obj )
91 | vec0 = slice_plane_object.vector.cross(ray0.vector).normalized()
92 | ofs0 = vec0 * 0.001
93 | plane0 = pqutil.Plane( ray0.origin - ofs0 , vec0 )
94 | ray1 = pqutil.Ray.from_screen( context ,endPos ).world_to_object( self.bmo.obj )
95 | vec1 = slice_plane_object.vector.cross(ray1.vector).normalized()
96 | ofs1 = vec1 * 0.001
97 | plane1 = pqutil.Plane( ray1.origin + ofs1 , vec1 )
98 |
99 | return slice_plane_object , plane0 , plane1
100 |
101 | def calc_slice( self ,slice_plane , plane0 , plane1 ) :
102 | edges = [ edge for edge in self.bmo.edges if edge.hide is False ]
103 | slice_plane_intersect_line = slice_plane.intersect_line
104 | plane0_distance_point = plane0.distance_point
105 | plane1_distance_point = plane1.distance_point
106 | epsilon = sys.float_info.epsilon
107 |
108 | def chk( edge ) :
109 | p0 = edge.verts[0].co
110 | p1 = edge.verts[1].co
111 | p = slice_plane_intersect_line( p0 , p1 )
112 |
113 | if p != None :
114 | a0 = plane0_distance_point( p )
115 | a1 = plane1_distance_point( p )
116 | if (a0 > epsilon and a1 > epsilon ) or (a0 < -epsilon and a1 < -epsilon ):
117 | return None
118 | return p
119 |
120 | matrix = self.bmo.obj.matrix_world
121 | cut_edges = { edge : chk( edge ) for edge in edges }
122 | cut_edges = { e : matrix @ p for e,p in cut_edges.items() if p != None }
123 | # for add_edge in add_edges :
124 | # cut_edges[add_edge] = slice_plane_intersect_line( add_edge.verts[0].co , add_edge.verts[1].co )
125 | return cut_edges
126 |
127 | def DoKnife( self ,context,startPos , endPos ) :
128 | bm = self.bmo.bm
129 | threshold = bpy.context.scene.tool_settings.double_threshold
130 | plane , plane0 , plane1 = self.make_slice_planes(context,startPos , endPos)
131 | faces = [ face for face in self.bmo.faces if not face.hide ]
132 | # if self.startElement.isVert :
133 | # for edge in self.startElement.element.link_edges :
134 | # self.cut_edges[edge] = self.startElement.coord
135 | # if self.goalElement.isVert :
136 | # for edge in self.goalElement.element.link_edges :
137 | # self.cut_edges[edge] = self.goalElement.coord
138 | elements = list(self.cut_edges.keys()) + faces
139 |
140 | ret = bmesh.ops.bisect_plane(bm,geom=elements,dist=threshold,plane_co= plane.origin ,plane_no= plane.vector ,use_snap_center=True,clear_outer=False,clear_inner=False)
141 | for e in ret['geom_cut'] :
142 | e.select_set(True)
143 | if QSnap.is_active() :
144 | QSnap.adjust_verts( self.bmo.obj , [ v for v in ret['geom_cut'] if isinstance( v , bmesh.types.BMVert ) ] , self.preferences.fix_to_x_zero )
145 |
146 | if self.bmo.is_mirror_mode :
147 | slice_plane , plane0 , plane1 = self.make_slice_planes(context,startPos , endPos)
148 | slice_plane.x_mirror()
149 | plane0.x_mirror()
150 | plane1.x_mirror()
151 | self.bmo.UpdateMesh()
152 | cut_edges_mirror = self.calc_slice( slice_plane , plane0 , plane1 )
153 | if cut_edges_mirror :
154 | faces = [ face for face in self.bmo.faces if face.hide is False ]
155 | elements = list(cut_edges_mirror.keys()) + faces[:]
156 | ret = bmesh.ops.bisect_plane(bm,geom=elements,dist=threshold,plane_co= slice_plane.origin ,plane_no= slice_plane.vector ,use_snap_center=False,clear_outer=False,clear_inner=False)
157 | for e in ret['geom_cut'] :
158 | e.select_set(True)
159 | if QSnap.is_active() :
160 | QSnap.adjust_verts( self.bmo.obj , [ v for v in ret['geom_cut'] if isinstance( v , bmesh.types.BMVert ) ] , self.preferences.fix_to_x_zero )
161 |
162 | @classmethod
163 | def GetCursor(cls) :
164 | return 'KNIFE'
165 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/subtool_polypen.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import sys
15 | import bpy
16 | import math
17 | import mathutils
18 | import bmesh
19 | import bpy_extras
20 | import collections
21 | import mathutils
22 | import copy
23 | from ..utils import pqutil
24 | from ..utils import draw_util
25 | from ..QMesh import *
26 | from ..utils.dpi import *
27 | from .subtool import SubTool
28 | from collections import namedtuple
29 |
30 | class SubToolPolyPen(SubTool) :
31 | name = "PolyPenTool"
32 |
33 | def __init__(self , op , target : ElementItem ) :
34 | super().__init__(op)
35 | self.currentEdge = target
36 | self.startPos = target.hitPosition.copy()
37 | self.targetPos = target.hitPosition.copy()
38 | self.startMousePos = copy.copy(target.coord)
39 |
40 | self.startEdge = target.element
41 | self.startData = [ self.CalcHead( target.verts ) ]
42 | self.endData = None
43 |
44 | @staticmethod
45 | def Check( root ,target ) :
46 | return target.isEdge and target.can_extrude()
47 |
48 | def OnUpdate( self , context , event ) :
49 | if event.type == 'MOUSEMOVE':
50 | dist = self.preferences.distance_to_highlight
51 | hitEdge = self.bmo.PickElement( self.mouse_pos , dist , edgering=True , backface_culling = True , elements=['EDGE'], ignore=[self.startEdge] )
52 | if hitEdge.isEdge :
53 | self.endData = self.AdsorptionEdge( self.startData[-1].WorldPos[0] , self.startData[-1].WorldPos[1] , hitEdge.element )
54 | else :
55 | while( True ) :
56 | sd = self.startData[-1]
57 | v = ( self.mouse_pos - sd.Center )
58 | pos = sd.Center + v.normalized() * min( sd.Witdh , v.length )
59 | self.endData = self.CalcEnd( pos )
60 | self.ReCalcFin(pos)
61 | if self.startData != None and self.endData != None :
62 | if v.length >= sd.Witdh :
63 | self.MakePoly()
64 | continue
65 | break
66 |
67 | elif event.type == 'RIGHTMOUSE' :
68 | if event.value == 'PRESS' :
69 | pass
70 | elif event.value == 'RELEASE' :
71 | pass
72 | elif event.type == 'LEFTMOUSE' :
73 | if event.value == 'RELEASE' :
74 | if self.endData != None :
75 | if (self.startData[-1].Center - self.mouse_pos).length / self.startData[-1].Witdh > 0.2 :
76 | self.MakePoly()
77 | return 'FINISHED'
78 | return 'RUNNING_MODAL'
79 |
80 | def OnDraw( self , context ) :
81 | pass
82 |
83 | def OnDraw3D( self , context ) :
84 | startData = self.startData[-1]
85 | if startData != None and self.endData != None :
86 | alpha = (startData.Center - self.mouse_pos).length / startData.Witdh
87 | if alpha >= 0.2 :
88 | verts = [ startData.WorldPos[0] , startData.WorldPos[1] , self.endData.WorldPos[1] , self.endData.WorldPos[0] ]
89 | draw_util.draw_Poly3D( context , verts , color = self.color_create(alpha / 2) )
90 | verts.append( verts[-1] )
91 | draw_util.draw_lines3D( context , verts , color = self.color_create(alpha ) , width = 2 )
92 |
93 | def CalcHead( self , verts , center = None ) :
94 | wpos = [ self.bmo.obj.matrix_world @ v.co for v in verts ]
95 | planes = [ pqutil.Plane.from_screen( bpy.context , v ) for v in wpos ]
96 | vpos = [ pqutil.location_3d_to_region_2d(v) for v in wpos ]
97 | if center == None :
98 | center = ( vpos[0] + vpos[1] ) / 2
99 | vec = ( vpos[0] - vpos[1] )
100 | width = vec.length
101 | nrm = vec.normalized()
102 |
103 | perpendicular = mathutils.Vector( (0,0,1) ).cross( mathutils.Vector( (nrm.x,nrm.y,0) ) ).normalized()
104 |
105 | Ret = namedtuple('Ret', ('Verts','WorldPos', 'Plane' , 'ViewPos' , 'Center' , 'Witdh', 'Perpendicular' ))
106 | ret = Ret(Verts=verts[:],WorldPos=wpos,Plane=planes, ViewPos = vpos , Center = center , Witdh = width , Perpendicular = perpendicular )
107 | return ret
108 |
109 | def CalcEnd( self , mouse_pos ) :
110 | Ret = namedtuple('Ret', ('WorldPos', 'ViewPos' , 'Center', 'Verts' ))
111 |
112 | start = self.startData[-1]
113 | context = bpy.context
114 |
115 | q = self.CalcRot( start , mouse_pos )
116 |
117 | f = [ v - start.Center for v in start.ViewPos ]
118 | ft = [ q.to_matrix() @ v.to_3d() for v in f ]
119 | pt = [ mouse_pos + v.xy for v in ft ]
120 | rt = [ pqutil.Ray.from_screen( context , v ) for v in pt ]
121 | vt = [ p.intersect_ray( r ) for r,p in zip(rt , start.Plane) ]
122 | vt = [ QSnap.view_adjust( p ) for p in vt ]
123 |
124 | ret = Ret(WorldPos = vt , ViewPos = pt , Center = mouse_pos , Verts = [None,None] )
125 | return ret
126 |
127 |
128 | def ReCalcFin( self , mouse_pos ) :
129 | if len( self.startData ) < 2 :
130 | return
131 | context = bpy.context
132 | finP = self.startData[-2]
133 | fin1 = self.startData[-1]
134 |
135 | q = self.CalcRot( finP , self.endData.Center )
136 |
137 | f = [ v - finP.Center for v in finP.ViewPos ]
138 | ft = [ q.to_matrix() @ v.to_3d() for v in f ]
139 | pt = [ fin1.Center + v.xy for v in ft ]
140 | rt = [ pqutil.Ray.from_screen( context , v ) for v in pt ]
141 | vt = [ p.intersect_ray( r ) for r,p in zip(rt , finP.Plane) ]
142 | vt = [ QSnap.view_adjust( p ) for p in vt ]
143 |
144 | for v , p in zip( fin1.Verts , vt ) :
145 | if self.bmo.is_mirror_mode :
146 | mirror = self.bmo.find_mirror(v)
147 |
148 | self.bmo.set_positon( v , p , is_world = True )
149 |
150 | if self.bmo.is_mirror_mode and mirror != None :
151 | self.bmo.set_positon( mirror , self.bmo.mirror_world_pos( p ) , is_world = True )
152 | self.bmo.UpdateMesh()
153 |
154 | self.startData[-1] = self.CalcHead( fin1.Verts , fin1.Center )
155 |
156 | def MakePoly( self ) :
157 | startData = self.startData[-1]
158 | verts = [ startData.Verts[0] , startData.Verts[1] ]
159 | nv = []
160 |
161 | for p , vt in zip( self.endData.WorldPos[::-1] , self.endData.Verts[::-1] ) :
162 | if vt != None :
163 | v = vt
164 | else :
165 | v = self.bmo.AddVertexWorld( p )
166 | verts.append( v )
167 | nv.append( v )
168 |
169 | normal = pqutil.getViewDir()
170 |
171 | face = self.bmo.AddFace( verts , normal )
172 | face.normal_update()
173 | self.bmo.UpdateMesh()
174 |
175 | for e in face.edges :
176 | if set( e.verts ) == set( nv ) :
177 | self.startEdge = e
178 | self.startData.append( self.CalcHead( nv[::-1] ) )
179 | self.endData = None
180 |
181 |
182 | def AdsorptionEdge( self , p0 , p1 , edge ) :
183 | Ret = namedtuple('Ret', ('WorldPos', 'ViewPos' , 'Center' , 'Verts' ))
184 |
185 | st0 = pqutil.location_3d_to_region_2d(p0)
186 | st1 = pqutil.location_3d_to_region_2d(p1)
187 | se0 = pqutil.location_3d_to_region_2d(self.bmo.local_to_world_pos(edge.verts[0].co))
188 | se1 = pqutil.location_3d_to_region_2d(self.bmo.local_to_world_pos(edge.verts[1].co))
189 | if (st0-se0).length + (st1-se1).length > (st0-se1).length + (st1-se0).length :
190 | wp = [ edge.verts[1] , edge.verts[0] ]
191 | else :
192 | wp = [ edge.verts[0] , edge.verts[1] ]
193 |
194 | vp = [ pqutil.location_3d_to_region_2d(v.co) for v in wp ]
195 |
196 | ret = Ret(WorldPos = [ self.bmo.obj.matrix_world @ v.co for v in wp] , ViewPos = vp , Center = (vp[0]+vp[1]) / 2 , Verts = wp )
197 |
198 | return ret
199 |
200 | def CalcRot( self , start , v0 ) :
201 | vec = (v0 - start.Center )
202 | nrm = vec.normalized()
203 |
204 | r0 = math.atan2( nrm.x , nrm.y )
205 | if nrm.dot(start.Perpendicular) > 0 :
206 | r1 = math.atan2( start.Perpendicular.x , start.Perpendicular.y )
207 | else :
208 | r1 = math.atan2( -start.Perpendicular.x , -start.Perpendicular.y )
209 |
210 | q0 = mathutils.Quaternion( mathutils.Vector( (0,0,1) ) , r0 )
211 | q1 = mathutils.Quaternion( mathutils.Vector( (0,0,1) ) , r1 )
212 |
213 | q = q0.rotation_difference(q1)
214 |
215 | return q
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/subtool_seam.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import sys
15 | import bpy
16 | import math
17 | import mathutils
18 | import bmesh
19 | import copy
20 | import bpy_extras
21 | import collections
22 | from ..utils import pqutil
23 | from ..utils import draw_util
24 | from ..QMesh import *
25 | from .subtool import MainTool
26 | from ..utils.dpi import *
27 |
28 | class SubToolSeam(MainTool) :
29 | name = "Mark Seam"
30 |
31 | def __init__(self,op,currentTarget, button) :
32 | super().__init__(op,currentTarget, button , no_hold = True )
33 |
34 | self.currentTarget = currentTarget
35 | self.startTarget = currentTarget
36 | self.removes = ( [ currentTarget.element ] , [] )
37 |
38 | @staticmethod
39 | def Check( root , target ) :
40 | return target.isEdge or target.isVert
41 |
42 | @staticmethod
43 | def pick_element( qmesh , location , preferences ) :
44 | element = qmesh.PickElement( location , preferences.distance_to_highlight , elements = ["EDGE","VERT"] )
45 | return element
46 |
47 | def OnUpdate( self , context , event ) :
48 | if event.type == 'MOUSEMOVE':
49 | preTarget = self.currentTarget
50 | self.currentTarget = self.bmo.PickElement( self.mouse_pos , self.preferences.distance_to_highlight , elements = ["VERT","EDGE"] )
51 | if self.currentTarget.isNotEmpty :
52 | vt = None
53 | if self.startTarget.isEdge :
54 | vt = self.bmo.highlight.check_hit_element_vert( self.startTarget.element , self.mouse_pos , self.preferences.distance_to_highlight * dpm())
55 | if vt :
56 | e = self.find_seam_loop( self.bmo , self.startTarget.element )
57 | self.removes = (e,[])
58 | self.currentTarget = self.startTarget
59 | elif self.startTarget.element != self.currentTarget.element :
60 | self.removes = self.bmo.calc_shortest_pass( self.bmo.bm , self.startTarget.element , self.currentTarget.element )
61 | else :
62 | self.removes = ([self.startTarget.element],[])
63 | else :
64 | self.removes = ([self.startTarget.element],[])
65 | elif event.type == self.buttonType :
66 | if event.value == 'RELEASE' :
67 | self.SeamElement(self.currentTarget )
68 | return 'FINISHED'
69 | elif event.type == 'RIGHTMOUSE':
70 | if event.value == 'RELEASE' :
71 | return 'FINISHED'
72 | return 'RUNNING_MODAL'
73 |
74 | @classmethod
75 | def DrawHighlight( cls , gizmo , element ) :
76 | if element != None and gizmo.bmo != None :
77 | if element.isEdge and element.element.seam :
78 | color = (0,0,0,1)
79 | else :
80 | color = bpy.context.preferences.themes["Default"].view_3d.edge_seam
81 | return element.DrawFunc( gizmo.bmo.obj , (color[0],color[1],color[2],1) , gizmo.preferences , marker = False , edge_pivot = False , width = 5 )
82 | return None
83 |
84 | def OnDraw( self , context ) :
85 | pass
86 |
87 | def OnDraw3D( self , context ) :
88 | if self.removes[0] :
89 | alpha = self.preferences.highlight_face_alpha
90 | vertex_size = self.preferences.highlight_vertex_size
91 | width = 5
92 | if all( isinstance( e , bmesh.types.BMEdge ) and e.seam for e in self.removes[0] ) :
93 | color = (0,0,0,1)
94 | else :
95 | color = bpy.context.preferences.themes["Default"].view_3d.edge_seam
96 | color = (color[0],color[1],color[2],1)
97 |
98 | draw_util.drawElementsHilight3D( self.bmo.obj , self.removes[0] , vertex_size , width , alpha , color )
99 | if self.bmo.is_mirror_mode :
100 | mirrors = [ self.bmo.find_mirror(m) for m in self.removes[0] ]
101 | mirrors = [ m for m in mirrors if m ]
102 | if mirrors :
103 | draw_util.drawElementsHilight3D( self.bmo.obj , mirrors , vertex_size , width , alpha * 0.5 , color )
104 |
105 | if self.startTarget.element == self.currentTarget.element :
106 | self.startTarget.Draw( self.bmo.obj , self.preferences.highlight_color , self.preferences , marker = False , edge_pivot = True )
107 | else :
108 | self.startTarget.Draw( self.bmo.obj , self.preferences.highlight_color , self.preferences , marker = False , edge_pivot = False )
109 | if self.currentTarget.isNotEmpty :
110 | if self.startTarget.element != self.currentTarget.element :
111 | self.currentTarget.Draw( self.bmo.obj , self.preferences.highlight_color , self.preferences , marker = False , edge_pivot = False )
112 |
113 | @classmethod
114 | def GetCursor(cls) :
115 | return 'CROSSHAIR'
116 |
117 | def SeamElement( self , element ) :
118 | edges = [ e for e in self.removes[0] if isinstance( e , bmesh.types.BMEdge ) ]
119 | seam = True
120 | if all( [ e.seam for e in edges] ) :
121 | seam = False
122 |
123 | for edge in edges :
124 | edge.seam = seam
125 | if self.bmo.is_mirror_mode :
126 | mirror = self.bmo.find_mirror( edge )
127 | if mirror :
128 | mirror.seam = seam
129 |
130 | if bpy.context.tool_settings.use_edge_path_live_unwrap :
131 | bpy.ops.uv.unwrap()
132 | self.bmo.UpdateMesh()
133 |
134 | @classmethod
135 | def find_seam_loop( cls , bmo , edge ) :
136 | loop = [edge]
137 |
138 | if not edge.seam :
139 | def check_func( edge , vert ) :
140 | return any( [ e.seam for e in vert.link_edges if e != edge ] ) == False
141 | loop , v = bmo.calc_edge_loop( edge , check_func )
142 | else :
143 | def find_loop( start , head ) :
144 | while(True) :
145 | link_seams = [ e for e in head.link_edges if e != start and e.seam ]
146 | if len( [ e for e in link_seams ] ) != 1 :
147 | return
148 | start = link_seams[0]
149 | if start in loop :
150 | break
151 | loop.append( start )
152 | head = start.other_vert(head)
153 |
154 | find_loop( edge , edge.verts[0] )
155 | find_loop( edge , edge.verts[1] )
156 |
157 | return loop
158 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/subtool_seam_loop.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import sys
15 | import bpy
16 | import math
17 | import mathutils
18 | import bmesh
19 | import copy
20 | import bpy_extras
21 | import collections
22 | from ..utils import pqutil
23 | from ..utils import draw_util
24 | from ..QMesh import *
25 | from .subtool import MainTool
26 | from ..utils.dpi import *
27 | from .subtool_seam import SubToolSeam
28 |
29 | class SubToolSeamLoop(SubToolSeam) :
30 | name = "Mark Seam Loop"
31 |
32 | @staticmethod
33 | def Check( root , target ) :
34 | return target.isEdge
35 |
36 | @staticmethod
37 | def pick_element( qmesh , location , preferences ) :
38 | element = qmesh.PickElement( location , preferences.distance_to_highlight , elements = ["EDGE"] )
39 | return element
40 |
41 | def OnUpdate( self , context , event ) :
42 | if event.type == 'MOUSEMOVE':
43 | preTarget = self.currentTarget
44 | self.currentTarget = self.bmo.PickElement( self.mouse_pos , self.preferences.distance_to_highlight , elements = ["VERT","EDGE"] )
45 | if self.currentTarget.isEdge :
46 | e = self.find_seam_loop( self.bmo , self.currentTarget.element )
47 | self.removes = (e,[])
48 | self.startTarget = self.currentTarget
49 | else :
50 | self.removes = ([self.currentTarget.element],[])
51 | elif event.type == self.buttonType :
52 | if event.value == 'RELEASE' :
53 | if self.currentTarget.isEdge :
54 | e = self.find_seam_loop( self.bmo , self.currentTarget.element )
55 | self.removes = (e,[])
56 | self.startTarget = self.currentTarget
57 | else :
58 | self.removes = ([self.currentTarget.element],[])
59 | self.SeamElement(self.currentTarget )
60 | return 'FINISHED'
61 | elif event.type == 'RIGHTMOUSE':
62 | if event.value == 'RELEASE' :
63 | return 'FINISHED'
64 | return 'RUNNING_MODAL'
65 |
66 | @classmethod
67 | def DrawHighlight( cls , gizmo , element ) :
68 | if element != None and gizmo.bmo != None :
69 | edges = cls.find_seam_loop( gizmo.bmo , element.element )
70 | if edges :
71 | alpha = gizmo.preferences.highlight_face_alpha
72 | vertex_size = gizmo.preferences.highlight_vertex_size
73 | width = 5
74 | if not element.element.seam :
75 | color = bpy.context.preferences.themes["Default"].view_3d.edge_seam
76 | color = (color[0],color[1],color[2],1)
77 | else :
78 | color = ( 0, 0 , 0 ,1)
79 |
80 | mirrors = []
81 | if gizmo.bmo.is_mirror_mode :
82 | mirrors = [ gizmo.bmo.find_mirror(m) for m in edges ]
83 | mirrors = [ m for m in mirrors if m ]
84 | def func() :
85 | draw_util.drawElementsHilight3D( gizmo.bmo.obj , edges , vertex_size , width , alpha , color )
86 | if mirrors :
87 | draw_util.drawElementsHilight3D( gizmo.bmo.obj , mirrors , vertex_size , width , alpha * 0.5 , color )
88 | return func
89 | return None
90 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/subtools/subtool_vert_extrude.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import sys
15 | import bpy
16 | import math
17 | import mathutils
18 | import bmesh
19 | import bpy_extras
20 | import collections
21 | import mathutils
22 | import copy
23 | from ..utils import pqutil
24 | from ..utils import draw_util
25 | from ..QMesh import *
26 | from ..utils.dpi import *
27 | from .subtool import SubTool
28 |
29 | class SubToolVertExtrude(SubTool) :
30 | name = "VertExtrudeTool"
31 |
32 | def __init__(self,op, target ) :
33 | super().__init__(op)
34 | self.currentVert = target
35 | self.startPos = target.hitPosition
36 | self.targetPos = target.hitPosition
37 | self.screen_space_plane = pqutil.Plane.from_screen( bpy.context , target.hitPosition )
38 | self.move_plane = self.screen_space_plane
39 | self.startMousePos = copy.copy(target.coord)
40 | self.snapTarget = ElementItem.Empty()
41 | self.is_snap_center = False
42 | self.ignore = []
43 | for face in self.currentVert.element.link_faces :
44 | self.ignore.extend(face.verts)
45 | self.ignore = set(self.ignore )
46 |
47 | @staticmethod
48 | def Check( root ,target ) :
49 | edges = [ e for e in target.element.link_edges if len(e.link_faces) == 1 ]
50 | if len(edges) == 2 :
51 | if set(edges[0].link_faces) == set(edges[1].link_faces) :
52 | return False
53 | return True
54 | return False
55 |
56 | def OnUpdate( self , context , event ) :
57 | if event.type == 'MOUSEMOVE':
58 | rayS = pqutil.Ray.from_screen( context , self.startMousePos )
59 | rayG = pqutil.Ray.from_screen( context , self.mouse_pos )
60 | vS = self.move_plane.intersect_ray( rayS )
61 | vG = self.move_plane.intersect_ray( rayG )
62 | move = (vG - vS)
63 | self.targetPos = self.startPos + move
64 | self.targetPos = QSnap.view_adjust(self.targetPos)
65 | self.snapTarget = self.bmo.PickElement( self.mouse_pos , self.preferences.distance_to_highlight , edgering=True , backface_culling = True , elements=['VERT'] , ignore = self.ignore )
66 | if self.snapTarget.isEmpty :
67 | self.is_snap_center = self.bmo.is_x0_snap( self.targetPos )
68 | if self.is_snap_center :
69 | self.targetPos = self.bmo.zero_pos_w2l(self.targetPos)
70 | else :
71 | self.is_snap_center = False
72 |
73 | elif event.type == 'RIGHTMOUSE' :
74 | if event.value == 'PRESS' :
75 | pass
76 | elif event.value == 'RELEASE' :
77 | pass
78 | elif event.type == 'LEFTMOUSE' :
79 | if event.value == 'RELEASE' :
80 | self.MakePoly()
81 | return 'FINISHED'
82 | return 'RUNNING_MODAL'
83 |
84 | def OnDraw( self , context ) :
85 | size = self.preferences.highlight_vertex_size
86 | if self.snapTarget.isVert :
87 | pos = pqutil.location_3d_to_region_2d( self.bmo.local_to_world_pos( self.snapTarget.element.co ) )
88 | draw_util.draw_circle2D( pos , size , (1,1,1,1) , False )
89 | if self.is_snap_center :
90 | p = self.bmo.zero_pos_w2l( self.targetPos )
91 | pos = pqutil.location_3d_to_region_2d( p )
92 | draw_util.draw_circle2D( pos , size , (1,1,1,1) , False )
93 |
94 | def OnDraw3D( self , context ) :
95 | vert = self.currentVert.element
96 | edges = [ edge for edge in vert.link_edges if edge.is_boundary ]
97 |
98 | p0 = self.bmo.local_to_world_pos(vert.co)
99 | p1 = self.bmo.local_to_world_pos(edges[0].other_vert(vert).co)
100 | p3 = self.bmo.local_to_world_pos(edges[1].other_vert(vert).co)
101 | if self.snapTarget.isVert :
102 | p2 = self.bmo.local_to_world_pos(self.snapTarget.element.co)
103 | else :
104 | p2 = self.targetPos
105 |
106 | lines = (p0,p1,p2,p3,p0)
107 | polys = (p0,p1,p2,p3)
108 |
109 | draw_util.draw_Poly3D( self.bmo.obj , polys , self.color_create(0.5), hide_alpha = 0.25 )
110 | draw_util.draw_lines3D( context , lines , self.color_create(1.0) , 2 , primitiveType = 'LINE_STRIP' , hide_alpha = 0 )
111 |
112 | if self.bmo.is_mirror_mode :
113 | lines = [ self.bmo.mirror_pos_w2l(p) for p in lines ]
114 | polys = [ self.bmo.mirror_pos_w2l(p) for p in polys ]
115 | draw_util.draw_Poly3D( self.bmo.obj , polys , self.color_create(0.5), hide_alpha = 0.25 )
116 | draw_util.draw_lines3D( context , lines , self.color_create(1.0) , 1 , primitiveType = 'LINE_STRIP' , hide_alpha = 0 )
117 |
118 | def MakePoly( self ) :
119 | vert = self.currentVert.element
120 | edges = [ edge for edge in vert.link_edges if edge.is_boundary ]
121 | if self.snapTarget.isVert :
122 | v0 = self.snapTarget.element
123 | else :
124 | v0 = self.bmo.AddVertexWorld( self.targetPos )
125 | self.bmo.UpdateMesh()
126 | v1 = edges[0].other_vert(vert)
127 | v2 = edges[1].other_vert(vert)
128 |
129 | verts = [v2,vert,v1,v0]
130 |
131 | normal = None
132 | edge = edges[0]
133 | if edge.link_faces :
134 | for loop in edge.link_faces[0].loops :
135 | if edge == loop.edge :
136 | if loop.vert == vert :
137 | verts.reverse()
138 | break
139 | else :
140 | normal = pqutil.getViewDir()
141 |
142 | self.bmo.AddFace( verts , normal )
143 | self.bmo.UpdateMesh()
144 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/translation.py:
--------------------------------------------------------------------------------
1 | pq_translation_dict = {
2 | "ja_JP" :
3 | {
4 | ("*", "Fix X=0") : "X=0を固定" ,
5 | ("*", "Tool settings:") : "ツールセッティング" ,
6 | ("*", "Long Press Time") : "長押し判定の時間" ,
7 | ("*", "Distance to Highlight") : "ハイライト距離" ,
8 | ("*", "Highlight Vertex Size") : "ハイライト点のサイズ" ,
9 | ("*", "Highlight Line Width") : "ハイライト線の太さ" ,
10 | ("*", "Highlight Face Alpha") : "ハイライト面のアルファ" ,
11 | ("*", "Color settings:") : "カラーセッティング" ,
12 | ("*", "HighlightColor") : "ハイライト色" ,
13 | ("*", "MakePolyColor") : "面作成色" ,
14 | ("*", "SplitColor") : "分割色" ,
15 | ("*", "DeleteColor") : "削除色" ,
16 | ("*", "Edge Snap Div") : "辺スナップ分割数" ,
17 | ("*", "Vertex Dissolve Angle") : "頂点融解角度" ,
18 | ("*", "Setup GameEngine like Keymaps") : "ゲームエンジン風キーマップをセットアップ" ,
19 | ("*", "Empty Mesh Object") : "空メッシュ" ,
20 | ("*", "Brush Strength") : "ブラシ強度" ,
21 | ("*", "Flex") : "自由" ,
22 | ("*", "Lowpoly") : "ローポリ" ,
23 | ("*", "Knife") : "ナイフ" ,
24 | ("*", "LoopCut") : "ループカット" ,
25 | ("*", "Brush_Delete") : "削除ブラシ" ,
26 | ("*", "Space Drag Operation") : "スペースでドラッグした時の操作" ,
27 | ("*", "Loop Cut") : "ループカット" ,
28 | ("*", "Dissolve Loop") : "ループ消去" ,
29 | ("*", "Make Polygon") : "ポリゴン作成" ,
30 | ("*", "Master Tool") : "マスターツール" ,
31 | ("*", "Edge Extrude") : "エッジ押し出し" ,
32 | ("*", "Mark Seam") : "シームをマーク" ,
33 | ("*", "Mark Seam Loop") : "シームループをマーク" ,
34 | }
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/utils/__init__.py:
--------------------------------------------------------------------------------
1 | #
2 |
3 | # ##### BEGIN GPL LICENSE BLOCK #####
4 | #
5 | # This program is free software; you can redistribute it and/or
6 | # modify it under the terms of the GNU General Public License
7 | # as published by the Free Software Foundation; either version 2
8 | # of the License, or (at your option) any later version.
9 | #
10 | # This program is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with this program; if not, write to the Free Software Foundation,
17 | # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 | #
19 | # ##### END GPL LICENSE BLOCK #####
20 |
21 | from . import dpi
22 | from . import addon_updater
23 | from . import pqutil
24 | from . import mouse_event_util
25 |
26 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/utils/dpi.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import bpy
15 |
16 | def dpi() :
17 | return bpy.context.preferences.system.dpi
18 |
19 | def dpc() :
20 | return dpi() / 2.54
21 |
22 | def dpm() :
23 | return dpc() / 10
24 |
--------------------------------------------------------------------------------
/Addons/PolyQuilt/utils/mouse_event_util.py:
--------------------------------------------------------------------------------
1 | # This program is free software; you can redistribute it and/or modify
2 | # it under the terms of the GNU General Public License as published by
3 | # the Free Software Foundation; either version 3 of the License, or
4 | # (at your option) any later version.
5 | #
6 | # This program is distributed in the hope that it will be useful, but
7 | # WITHOUT ANY WARRANTY; without even the implied warranty of
8 | # MERCHANTIBILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 | # General Public License for more details.
10 | #
11 | # You should have received a copy of the GNU General Public License
12 | # along with this program. If not, see .
13 |
14 | import bpy
15 | import math
16 | import mathutils
17 | import time
18 | import collections
19 | from enum import Enum , auto
20 | from . import draw_util
21 |
22 | __all__ = ['MBEventType','ButtonEventUtil']
23 |
24 | class MBEventType(Enum) :
25 | Noting = auto()
26 | Click = auto()
27 | Drag = auto()
28 | Down = auto()
29 | Move = auto()
30 | LongPress = auto()
31 | LongClick = auto()
32 | LongPressDrag = auto()
33 | Release = auto()
34 |
35 | class ButtonEventUtil :
36 | def __init__( self , button : str , cls , func , op , use_hold_lock = False , no_hold = False ) :
37 | self.op = op
38 | self.button : str = button
39 | self.eventFunc = func
40 | self.eventClass = cls
41 | self.Press : bool = False
42 | self.Presure : bool = False
43 | self.PressTime : float = 0.0
44 | self.type : MBEventType = MBEventType.Noting
45 | self.mouse_pos = mathutils.Vector((0.0,0.0))
46 | self.event = None
47 | self.PressPos = mathutils.Vector((0.0,0.0))
48 | self.PressPrevPos = mathutils.Vector((0.0,0.0))
49 | self.presureCompOnce = False
50 | self.preferences = op.preferences
51 | self.no_hold = no_hold
52 |
53 | @property
54 | def presureValue(self) -> float :
55 | if self.Presure == False :
56 | return 0.0
57 | else :
58 | lpt = self.preferences.longpress_time
59 | cur = time.time()-self.PressTime
60 | m = max( lpt / 3.0 , 0.125 )
61 | t = ( cur - m ) / (lpt - m )
62 | return min( 1.0 , max( 0.0 , t ) )
63 |
64 | @property
65 | def presureComplite(self) -> bool :
66 | return self.presureValue >= 1.0
67 |
68 | @property
69 | def isPresure(self) -> bool :
70 | return self.presureValue >= 0.0001
71 |
72 | @property
73 | def is_hold(self) -> bool :
74 | hold = self.presureCompOnce
75 | return hold
76 |
77 | def is_animated( self ) :
78 | if self.Presure and not self.presureCompOnce :
79 | return True
80 | return False
81 |
82 | def Update( self , context , event ) :
83 | self.mouse_pos = mathutils.Vector((event.mouse_region_x, event.mouse_region_y))
84 | if event.type == self.button:
85 | if event.value == 'PRESS':
86 | if self.Press is False :
87 | self.PressPos = self.mouse_pos
88 | self.PressTime = time.time()
89 | if not self.no_hold :
90 | self.Press = True
91 | self.Presure = True
92 | self.presureCompOnce = False
93 | self.PressPrevPos = mathutils.Vector((event.mouse_prev_x, event.mouse_prev_y))
94 | self.OnEvent( event , MBEventType.Down )
95 | else :
96 | self.OnEvent( event , MBEventType.Press )
97 | elif event.value == 'RELEASE':
98 | if not self.no_hold :
99 | if self.presureComplite :
100 | self.presureCompOnce = True
101 | if self.Presure :
102 | if self.is_hold :
103 | self.OnEvent( event , MBEventType.LongClick )
104 | else :
105 | self.OnEvent( event , MBEventType.Click )
106 | self.Presure = False
107 | self.Press = False
108 | else :
109 | self.OnEvent( event , MBEventType.Click )
110 | self.OnEvent( event , MBEventType.Release )
111 | self.PressTime = 0.0
112 | self.presureCompOnce = False
113 | elif event.type == 'MOUSEMOVE':
114 | drag_threshold = context.preferences.inputs.drag_threshold_mouse
115 | if not self.no_hold :
116 | if self.Press :
117 | if self.presureComplite :
118 | self.presureCompOnce = True
119 | if event.is_tablet :
120 | drag_threshold = context.preferences.inputs.drag_threshold_tablet
121 | if (time.time()-self.PressTime ) > 0.15 and (self.mouse_pos-self.PressPos ).length > drag_threshold:
122 | self.Presure = False
123 | if self.Presure is False :
124 | if self.is_hold :
125 | self.OnEvent( event , MBEventType.LongPressDrag )
126 | else :
127 | self.OnEvent( event , MBEventType.Drag )
128 | else :
129 | if (time.time()-self.PressTime ) > 0.15 and (self.mouse_pos-self.PressPos ).length > drag_threshold:
130 | self.OnEvent( event , MBEventType.Drag )
131 |
132 | self.OnEvent( event , MBEventType.Move )
133 | elif event.type == 'TIMER':
134 | if self.presureComplite :
135 | self.presureCompOnce = True
136 | self.OnEvent( event , MBEventType.LongPress )
137 |
138 | def OnEvent( self ,event , type : MBEventType ) :
139 | self.type = type
140 | if self.eventFunc != None :
141 | self.event = event
142 | self.eventFunc( self.eventClass , self)
143 | self.event = None
144 |
145 | def Reset(self, context ) :
146 | self.Presure = False
147 | self.Press = False
148 | self.presureCompOnce = False
149 |
150 | def Draw( self , coord = None , text = None ) :
151 | if self.presureValue > 0.001 :
152 | if coord != None :
153 | draw_util.draw_donuts2D( coord , 4 , 1 , self.presureValue, (1,1,1,self.presureValue) )
154 | if self.presureComplite and text != None :
155 | draw_util.DrawFont(text, 12 , coord , (0,4) )
156 | else:
157 | draw_util.draw_donuts2D( self.PressPos , 4 , 1 , self.presureValue, (1,1,1,self.presureValue) )
158 | if self.presureComplite and text != None :
159 | draw_util.DrawFont( text , 12 , self.PressPos , (0,4) )
160 | return self.presureComplite
161 |
162 |
--------------------------------------------------------------------------------
/Docs/en/docs/download.md:
--------------------------------------------------------------------------------
1 | # Download and Install
2 |
3 | ## Install
4 |
5 | - [Download](##download) the zip to your local storage
6 | - Open Preferences form edit menu
7 | - Change to Add-ons Tab
8 | - Click Install and select zip
9 | - Activate PolyQuilt
10 | - Into Editmode and Select PolyQuilt from Toolbar
11 | - Enjoy!
12 |
13 | ## Update
14 |
15 | Online updater is built in. You can automatically update to the latest version by selecting **Check PolyQuilt add-on update** from ExtraSetting in the add-on menu.
16 |
17 | !!! Note
18 | Reboot required after update
19 |
20 | ## Download
21 |
22 | ### [PolyQuilt 1.1.5](https://github.com/sakana3/PolyQuilt/releases/download/1.1.5/PolyQuilt_v1.1.5.zip)
23 |
24 |
--------------------------------------------------------------------------------
/Docs/en/docs/index.md:
--------------------------------------------------------------------------------
1 | # Welcome to PolyQuilt
2 |
3 | ## What is PolyQuilt?
4 |
5 | PolyQuilt is an add-on for Blender 2.8 that supports low-poly modeling.
6 | In addition to simple modeling capabilities, it supports fast and intuitive polygon modeling with minimal interaction by assigning various functions to mouse actions such as clicking, dragging and holding.
7 | It also works well with tablets and tablet PCs with digitizers.
8 |
9 | I hope this add-on will make your modeling life happy!
10 |
11 | ## If you are in trouble
12 |
13 | Please send bug reports or requests to a [Twitter](https://twitter.com/sakanaya) or [Github](https://github.com/sakana3/PolyQuilt) or [BlenderArtist PolyQuilt thread](https://blenderartists.org/t/polyquilt-addon-for-blender-2-8/1168918
14 | ).
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Docs/en/docs/manual/Brush.md:
--------------------------------------------------------------------------------
1 | # Brush Tools
2 |
3 | ## Change size and intensity
4 |
5 | ## Move Brush
6 |
7 | ## Relax Brush
8 |
--------------------------------------------------------------------------------
/Docs/en/docs/manual/facecut.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Docs/en/docs/manual/facecut.md
--------------------------------------------------------------------------------
/Docs/en/docs/manual/geometry.md:
--------------------------------------------------------------------------------
1 | # Draw Geometry
2 |
3 | ## Vertex
4 |
5 | ### Click
6 |
7 | ## Edge
8 |
9 | ### Click and Draw
10 |
11 | ## Face
12 |
13 | ### Click and Draw
14 | ### Extrude
15 | ### AutoQuad
16 |
17 |
--------------------------------------------------------------------------------
/Docs/en/docs/manual/insert_loop.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Docs/en/docs/manual/insert_loop.md
--------------------------------------------------------------------------------
/Docs/en/docs/manual/knife.md:
--------------------------------------------------------------------------------
1 | # Knife
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Docs/en/docs/manual/move.md:
--------------------------------------------------------------------------------
1 | # Move and Snap
--------------------------------------------------------------------------------
/Docs/en/docs/manual/operation.md:
--------------------------------------------------------------------------------
1 | # Basic Operation
2 |
3 | PolyQuilt allows you to perform a variety of polygon edits using a combination of highlighted elements, mouse actions, and modifier keys.
4 |
5 | ## Action List
6 |
7 | | Opetation |Description |
8 | |:-:|:-|
9 | | Click |Press and release the left mouse button in a short period of time|
10 | | Drag|Press and move the left mouse button|
11 | | Hold|Press the left mouse button for a long time. When the marker becomes a circle, holding is completed.|
12 | | Hold Drag|Move the mouse from the hold state.|
13 |
14 | ## Modifier key
15 |
16 | | Key |Description |
17 | |:-:|:-|
18 | | Alt | Delete Geometry |
19 | | Ctrl | Preparing |
20 | | Shift | AutoQuad and Brushs |
21 | | OS Key | Lock Hold |
22 |
23 | ## Caution
24 |
25 | Until you get used to it, holding may cause a lot of malfunction. If there are many mistakes in operation, it is better to set the hold time longer from the setting screen. The default is 0.4 seconds.
26 |
--------------------------------------------------------------------------------
/Docs/en/docs/manual/remove.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Docs/en/docs/manual/remove.md
--------------------------------------------------------------------------------
/Docs/en/docs/manual/remove_loop.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Docs/en/docs/manual/remove_loop.md
--------------------------------------------------------------------------------
/Docs/en/mkdocs.yml:
--------------------------------------------------------------------------------
1 | # Project information
2 | site_name: 'PolyQuilt'
3 | site_description: 'Lowpoly modeling assist blender-addon PolyQuilt'
4 | site_author: 'sakana3'
5 | site_url: 'https://sakana3.github.io/PolyQuilt/'
6 |
7 | # Repository
8 | repo_name: 'sakana3/PolyQuilt'
9 | repo_url: 'https://github.com/sakana3/PolyQuilt'
10 |
11 | # Copyright
12 | copyright: 'Copyright © 2016 - 2017 Martin Donath'
13 |
14 | # Configuration
15 | theme:
16 | name: 'material'
17 | language: 'en'
18 | feature:
19 | tabs: true
20 | palette:
21 | primary: 'Teal'
22 | accent: 'Red'
23 | font:
24 | text: 'Roboto'
25 | code: 'Roboto Mono'
26 |
27 | # Customization
28 | extra:
29 | manifest: 'manifest.webmanifest'
30 | social:
31 | - type: 'github'
32 | link: 'https://github.com/sakana3/PolyQuilt'
33 | - type: 'twitter'
34 | link: 'https://twitter.com/sakanaya'
35 | - type: 'globe'
36 | link: 'https://blenderartists.org/t/polyquilt-addon-for-blender-2-8/1168918/'
37 |
38 | # Page tree
39 | nav:
40 | - About: index.md
41 | - Download and Install: download.md
42 | - Manual :
43 | - Basic operation: manual/operation.md
44 | - Move and Snap : manual/move.md
45 | - Make Geometry: manual/geometry.md
46 | - Remove Geometry: manual/remove.md
47 | - Face Cut: manual/facecut.md
48 | - Insert Loop: manual/insert_loop.md
49 | - Remove Loop: manual/remove_loop.md
50 | - Knife : manual/knife.md
51 | - Brush Tools : manual/brush.md
52 |
53 | # Extensions
54 | markdown_extensions:
55 | - markdown.extensions.admonition
56 | - markdown.extensions.codehilite:
57 | guess_lang: false
58 | - markdown.extensions.def_list
59 | - markdown.extensions.footnotes
60 | - markdown.extensions.meta
61 | - markdown.extensions.toc:
62 | permalink: true
63 | - pymdownx.arithmatex
64 | - pymdownx.betterem:
65 | smart_enable: all
66 | - pymdownx.caret
67 | - pymdownx.critic
68 | - pymdownx.details
69 | - pymdownx.emoji:
70 | emoji_index: !!python/name:pymdownx.emoji.twemoji
71 | emoji_generator: !!python/name:pymdownx.emoji.to_svg
72 | - pymdownx.inlinehilite
73 | - pymdownx.keys
74 | - pymdownx.magiclink:
75 | repo_url_shorthand: true
76 | user: squidfunk
77 | repo: mkdocs-material
78 | - pymdownx.mark
79 | - pymdownx.smartsymbols
80 | - pymdownx.superfences
81 | - pymdownx.tasklist:
82 | custom_checkbox: true
83 | - pymdownx.tilde
84 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | I am currently working on an English document. [Click here](https://github.com/sakana3/PolyQuilt/releases/download/1.3.1/PolyQuilt_v1.3.1.zip) to download the latest version.
2 | If you are using Blender2.82 and earlier. [Click here](#OldVersion)
3 |
4 | # PolyQuilt(ポリキルト)
5 | ---
6 | PolyQuiltはローポリモデリングをサポートするBlender2.8用アドオンです。シンプルでオーソドックスでオールドスクールな面張り機能に加えてドラッグや長押し等のマウスオペレーションに様々な機能を割り振ることでツールの切り替えを最小限にしてポリゴンモデリングを支援します。
7 | またタブレットやデジタイザ付きタブレットPCとの相性も良く、操作量を減らしつつ直感的にモデリングを行う事ができます。
8 |
9 | # 導入方法
10 |
11 | 最新版ダウンロードは[こちら](https://github.com/sakana3/PolyQuilt/releases/download/1.3.1/PolyQuilt_v1.3.1.zip)から(Blender2.83 LTS以降)
12 | Blender2.82以前の方は[こちら](#OldVersion)から
13 |
14 | ダウンロードして編集→設定→アドオン→インストールよりダウンロード先を指定してインストールしてください。インストールした段階ではまだ使えませんのでその後検索バーよりPolyQuiltを検索しチェックボックスをOnにしてください。
15 |
16 | 編集モードに入るとPolyBuildの下にアイコンが追加されているのでこれをクリックでPolyQuiltがアクティブツールになります。
17 |
18 | # 機能一覧
19 |
20 | 各種機能はクリック、ドラッグ、長押し、長押し後ドラッグの操作に割り振られています。空間、点辺面の要素との組み合わせで以下の機能が割り振られています。
21 | Altでホールドと同等の動作になります。Altキーダブルクリックで長押しをロックします。
22 |
23 | ||クリック|ドラッグ|ホールド|ホールドドラッグ|
24 | |:-:|:-:|:-:|:-:|:-:|
25 | |空間|[頂点作成](#頂点作成)
→[面張り](#面張り)
→[ループカット](#ループカット)|ビュー回転(暫定)||[ナイフ](#ナイフ)||
26 | |点|[面張り](#面張り)|[移動(結合)](#移動)|[削除/融解](#削除/融解)|[扇カット](#扇カット)
[エッジ押し出し](#エッジ押し出し)|
27 | |辺|[頂点挿入→面張り](#面張り)|[移動](#移動)|[削除/融解](#削除/融解)|[ループ挿入](#ループ挿入)
[エッジ押し出し](#エッジ押し出し)
[ループカット](#ループカット)|
28 | |面||[移動](#移動)|[削除](#削除/融解)||
29 |
30 | |Shiftを押しながら| 機能 |
31 | |:-:|:-:|
32 | |Shift + クリック| AutoQuad |
33 | |Shift + ドラッグ| Brush |
34 | |Shift + ミドルボタン+ドラッグ| 2ndBrush |
35 | |Shift + ホールドドラック| ブラシサイズ/強度 +/- |
36 | |Shift + Wheel Up/ Down| ブラシサイズ +/- |
37 | |Shift + Ctrl + Wheel Up/ Down| ブラシ強度 +/- |
38 |
39 | ---
40 | ## 移動
41 |
42 | 点、線、面の上でマウスドラッグでビュー平面状の移動ができます。エッジの頂点は別エッジの頂点に近づける事で結合する事ができます。
43 |
44 | ## 頂点作成
45 | 何もない空間をクリックで頂点が作成されます。通常はそのまま面張りモードになりますがジオメトリを点モードにすれば連続で点を作れます。
46 |
47 | ## 面張り
48 |
49 | 何もないエリアか点の上でクリックで面張りを開始します。クリックする度に点、線、面が追加されます。最後に配置した点の上でクリック(ダブルクリックと同意)か右クリックで終了しまた新たな面を作ることができます。
50 |
51 | 画面上部のツールメニューより作成するジオメトリを点、線、三角形、四角形、ポリゴンから選ぶ事ができます。
52 |
53 | また同一面の頂点でクリックすると面の分断になります。
54 |
55 | ## 削除/融解
56 |
57 | 点、線、面の上で長押しでその要素を消去or融解します。共有する線や面がある場合は融解、そうでない場合は削除になります。
58 |
59 | ## ナイフ
60 |
61 | 空間で長押し後にドラッグでナイフを実行できます。
62 |
63 | ## ループカット
64 |
65 | 辺の上で長押し後ドラッグで辺をループカットできます。
66 |
67 | ## 扇カット
68 |
69 | 点の上で長押し後ドラッグで辺を扇状にループカットできます。
70 |
71 | ## エッジ押し出し
72 |
73 | 辺または点の上で長押し後ドラッグでエッジの押し出しをします。
74 |
75 | ## ループ挿入
76 |
77 | 辺ループを挿入します
78 |
79 | ## ループカット
80 |
81 | 辺ループをカットします
82 |
83 | # 環境設定
84 |
85 | ドキュメント準備中
86 |
87 | ## Extra Setting
88 | 特別な設定群です。上級者向け
89 |
90 | ### Check PolyQuilt add-on update
91 |
92 | ポリキルトを更新をチェックし最新版にアップデートする事ができます。上手くいかない場合は従来通りの方法でお願いします。
93 |
94 | ### ゲームエンジン風キーマップをセットアップ
95 |
96 | 右マウスボタン+ドラッグでの視点変更にキーマップをセットアップします。標準で割り振られているコンテキストメニューはクリックで開く為、操作が被る事なくUnityやUE4といった主要ゲームエンジン風の操作を行う事ができます。キーマップを書き換える為、独自カスタムや他のアドオンとの衝突する場合があるので注意してください。
97 |
98 | # フィードバック
99 |
100 | フィードバックはGithubの[Issues](https://github.com/sakana3/PolyQuilt/issues)かTwitterアカウント[sakana3](https://twitter.com/sakanaya) まで宜しくお願い致します。
101 |
102 | # 実装予定
103 |
104 | - [ ] 分離(Rip)
105 | - [ ] LoopCutの末端三角形対応
106 | - [ ] 選択
107 | - [x] ハンドリトポ編集
108 |
109 | # OldVersion
110 | For Blender2.82 and earlier. [Click here](https://github.com/sakana3/PolyQuilt/releases/download/1.2.0/PolyQuilt_v1.2.0.zip)
111 |
--------------------------------------------------------------------------------
/Resources/icon_geom.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sakana3/PolyQuilt/5ed6c9ac20b04008e726299ea4e3a36080024f11/Resources/icon_geom.blend
--------------------------------------------------------------------------------
/Resources/makeicon.bat:
--------------------------------------------------------------------------------
1 | blender.exe icon_geom.blend --background --python blender_icons_geom.py -- --output-dir=./icons
2 |
3 | pause
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
2 |
--------------------------------------------------------------------------------
/mkpackage.py:
--------------------------------------------------------------------------------
1 | # coding: UTF-8
2 | import os
3 | import re
4 | import zipfile
5 | import shutil
6 |
7 | modulename = "PolyQuilt"
8 | package_folder = os.getcwd() + "/Addons/PolyQuilt"
9 |
10 | version = "0.0.0"
11 | with open( package_folder + "/__init__.py", encoding= "utf-8" ) as f:
12 | line = f.readline()
13 | while line :
14 | if "\"version\"" in line :
15 | vtext = line.split('(')[-1].split(')')[0]
16 | vtext = [ v.replace(" ","") for v in vtext.split(',') ]
17 | version = '.'.join(vtext)
18 | line = f.readline()
19 |
20 | filename = modulename + "_v" + version
21 |
22 | shutil.make_archive( filename , 'zip', root_dir= package_folder )
23 |
24 |
--------------------------------------------------------------------------------