Export_3ds: Added distance cue chunk export
[blender-addons.git] / mesh_tissue / dual_mesh.py
blob8439d1ecbe41242645b38f742582650cce3a4784
1 # SPDX-FileCopyrightText: 2017 Alessandro Zomparelli
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # --------------------------------- DUAL MESH -------------------------------- #
6 # -------------------------------- version 0.3 ------------------------------- #
7 # #
8 # Convert a generic mesh to its dual. With open meshes it can get some wired #
9 # effect on the borders. #
10 # #
11 # (c) Alessandro Zomparelli #
12 # (2017) #
13 # #
14 # http://www.co-de-it.com/ #
15 # #
16 # ############################################################################ #
19 import bpy
20 from bpy.types import Operator
21 from bpy.props import (
22 BoolProperty,
23 EnumProperty,
25 import bmesh
26 from .utils import *
29 class dual_mesh_tessellated(Operator):
30 bl_idname = "object.dual_mesh_tessellated"
31 bl_label = "Dual Mesh"
32 bl_description = ("Generate a polygonal mesh using Tessellate. (Non-destructive)")
33 bl_options = {'REGISTER', 'UNDO'}
35 apply_modifiers : BoolProperty(
36 name="Apply Modifiers",
37 default=True,
38 description="Apply object's modifiers"
41 source_faces : EnumProperty(
42 items=[
43 ('QUAD', 'Quad Faces', ''),
44 ('TRI', 'Triangles', '')],
45 name="Source Faces",
46 description="Triangles works with any geometry." \
47 "Quad option is faster when the object has only Quads",
48 default="TRI",
49 options={'LIBRARY_EDITABLE'}
52 link_component : BoolProperty(
53 name="Editable Component",
54 default=False,
55 description="Add Component Object to the Scene"
58 def execute(self, context):
59 auto_layer_collection()
60 ob0 = context.object
61 name1 = "DualMesh_{}_Component".format(self.source_faces)
62 # Generate component
63 if self.source_faces == 'QUAD':
64 verts = [(1.0, 0.0, 0.0), (0.5, 0.0, 0.0),
65 (0.0, 0.0, 0.0), (0.0, 0.5, 0.0),
66 (0.0, 1.0, 0.0), (0.5, 1.0, 0.0),
67 (1.0, 1.0, 0.0), (1.0, 0.5, 0.0),
68 (2/3, 1/3, 0.0), (1/3, 2/3, 0.0)]
69 edges = [(0,1), (1,2), (2,3), (3,4), (4,5), (5,6), (6,7),
70 (7,0), (1,8), (8,7), (3,9), (9,5), (8,9)]
71 faces = [(7,8,1,0), (8,9,3,2,1), (9,5,4,3), (9,8,7,6,5)]
72 else:
73 verts = [(0.0, 0.0, 0.0), (1.0, 0.0, 0.0),
74 (0.0, 1.0, 0.0), (1.0, 1.0, 0.0),
75 (0.5, 1/3, 0.0), (0.0, 0.5, 0.0),
76 (1.0, 0.5, 0.0), (0.5, 0.0, 0.0)]
77 edges = [(0,5), (1,7), (3,6), (2,3), (2,5), (1,6), (0,7),
78 (4,5), (4,7), (4,6)]
79 faces = [(5,0,7,4), (7,1,6,4), (3,2,5,4,6)]
81 # check pre-existing component
82 try:
83 _verts = [0]*len(verts)*3
84 __verts = [c for co in verts for c in co]
85 ob1 = bpy.data.objects[name1]
86 ob1.data.vertices.foreach_get("co",_verts)
87 for a, b in zip(_verts, __verts):
88 if abs(a-b) > 0.0001:
89 raise ValueError
90 except:
91 me = bpy.data.meshes.new("Dual-Mesh") # add a new mesh
92 me.from_pydata(verts, edges, faces)
93 me.update(calc_edges=True, calc_edges_loose=True)
94 if self.source_faces == 'QUAD': seams = (0,1,2,3,4,5,6,9)
95 else: seams = (0,1,2,3,4,5,7)
96 for i in seams: me.edges[i].use_seam = True
97 ob1 = bpy.data.objects.new(name1, me)
98 # fix visualization issue
99 if self.link_component:
100 context.collection.objects.link(ob1)
101 context.view_layer.objects.active = ob1
102 ob1.select_set(True)
103 bpy.ops.object.editmode_toggle()
104 bpy.ops.object.editmode_toggle()
105 ob1.select_set(False)
106 ob1.hide_render = True
107 ob = convert_object_to_mesh(ob0,False,False)
108 ob.name = 'DualMesh'
109 ob.tissue.tissue_type = 'TESSELLATE'
110 ob.tissue.bool_lock = True
111 ob.tissue_tessellate.component = ob1
112 ob.tissue_tessellate.generator = ob0
113 ob.tissue_tessellate.gen_modifiers = self.apply_modifiers
114 ob.tissue_tessellate.merge = True
115 ob.tissue_tessellate.bool_dissolve_seams = True
116 if self.source_faces == 'TRI': ob.tissue_tessellate.fill_mode = 'TRI'
117 bpy.ops.object.tissue_update_tessellate()
118 ob.tissue.bool_lock = False
119 ob.location = ob0.location
120 ob.matrix_world = ob0.matrix_world
121 return {'FINISHED'}
123 def invoke(self, context, event):
124 return context.window_manager.invoke_props_dialog(self)
126 class dual_mesh(Operator):
127 bl_idname = "object.dual_mesh"
128 bl_label = "Convert to Dual Mesh"
129 bl_description = ("Convert a generic mesh into a polygonal mesh. (Destructive)")
130 bl_options = {'REGISTER', 'UNDO'}
132 quad_method : EnumProperty(
133 items=[('BEAUTY', 'Beauty',
134 'Split the quads in nice triangles, slower method'),
135 ('FIXED', 'Fixed',
136 'Split the quads on the 1st and 3rd vertices'),
137 ('FIXED_ALTERNATE', 'Fixed Alternate',
138 'Split the quads on the 2nd and 4th vertices'),
139 ('SHORTEST_DIAGONAL', 'Shortest Diagonal',
140 'Split the quads based on the distance between the vertices')
142 name="Quad Method",
143 description="Method for splitting the quads into triangles",
144 default="FIXED",
145 options={'LIBRARY_EDITABLE'}
147 polygon_method : EnumProperty(
148 items=[
149 ('BEAUTY', 'Beauty', 'Arrange the new triangles evenly'),
150 ('CLIP', 'Clip',
151 'Split the N-gon with an ear clipping algorithm')],
152 name="N-gon Method",
153 description="Method for splitting the N-gons into triangles",
154 default="BEAUTY",
155 options={'LIBRARY_EDITABLE'}
157 preserve_borders : BoolProperty(
158 name="Preserve Borders",
159 default=True,
160 description="Preserve original borders"
162 apply_modifiers : BoolProperty(
163 name="Apply Modifiers",
164 default=True,
165 description="Apply object's modifiers"
168 def execute(self, context):
169 mode = context.mode
170 if mode == 'EDIT_MESH':
171 mode = 'EDIT'
172 act = context.active_object
173 if mode != 'OBJECT':
174 sel = [act]
175 bpy.ops.object.mode_set(mode='OBJECT')
176 else:
177 sel = context.selected_objects
178 doneMeshes = []
180 for ob0 in sel:
181 if ob0.type != 'MESH':
182 continue
183 if ob0.data.name in doneMeshes:
184 continue
185 ob = ob0
186 mesh_name = ob0.data.name
188 # store linked objects
189 clones = []
190 n_users = ob0.data.users
191 count = 0
192 for o in bpy.data.objects:
193 if o.type != 'MESH':
194 continue
195 if o.data.name == mesh_name:
196 count += 1
197 clones.append(o)
198 if count == n_users:
199 break
201 if self.apply_modifiers:
202 bpy.ops.object.convert(target='MESH')
203 ob.data = ob.data.copy()
204 bpy.ops.object.select_all(action='DESELECT')
205 ob.select_set(True)
206 context.view_layer.objects.active = ob0
207 bpy.ops.object.mode_set(mode='EDIT')
209 # prevent borders erosion
210 bpy.ops.mesh.select_mode(
211 use_extend=False, use_expand=False, type='EDGE'
213 bpy.ops.mesh.select_non_manifold(
214 extend=False, use_wire=False, use_boundary=True,
215 use_multi_face=False, use_non_contiguous=False,
216 use_verts=False
218 bpy.ops.mesh.extrude_region_move(
219 MESH_OT_extrude_region={"mirror": False},
220 TRANSFORM_OT_translate={"value": (0, 0, 0)}
223 bpy.ops.mesh.select_mode(
224 use_extend=False, use_expand=False, type='VERT',
225 action='TOGGLE'
227 bpy.ops.mesh.select_all(action='SELECT')
228 bpy.ops.mesh.quads_convert_to_tris(
229 quad_method=self.quad_method, ngon_method=self.polygon_method
231 bpy.ops.mesh.select_all(action='DESELECT')
232 bpy.ops.object.mode_set(mode='OBJECT')
233 bpy.ops.object.modifier_add(type='SUBSURF')
234 ob.modifiers[-1].name = "dual_mesh_subsurf"
235 while True:
236 bpy.ops.object.modifier_move_up(modifier="dual_mesh_subsurf")
237 if ob.modifiers[0].name == "dual_mesh_subsurf":
238 break
240 bpy.ops.object.modifier_apply(modifier='dual_mesh_subsurf')
242 bpy.ops.object.mode_set(mode='EDIT')
243 bpy.ops.mesh.select_all(action='DESELECT')
245 verts = ob.data.vertices
247 bpy.ops.object.mode_set(mode='OBJECT')
248 verts[-1].select = True
249 bpy.ops.object.mode_set(mode='EDIT')
250 bpy.ops.mesh.select_more(use_face_step=False)
252 bpy.ops.mesh.select_similar(
253 type='VERT_EDGES', compare='EQUAL', threshold=0.01)
254 bpy.ops.mesh.select_all(action='INVERT')
256 bpy.ops.mesh.dissolve_verts()
257 bpy.ops.mesh.select_all(action='DESELECT')
259 bpy.ops.mesh.select_non_manifold(
260 extend=False, use_wire=False, use_boundary=True,
261 use_multi_face=False, use_non_contiguous=False, use_verts=False)
262 bpy.ops.mesh.select_more()
264 # find boundaries
265 bpy.ops.object.mode_set(mode='OBJECT')
266 bound_v = [v.index for v in ob.data.vertices if v.select]
267 bound_e = [e.index for e in ob.data.edges if e.select]
268 bound_p = [p.index for p in ob.data.polygons if p.select]
269 bpy.ops.object.mode_set(mode='EDIT')
271 # select quad faces
272 context.tool_settings.mesh_select_mode = (False, False, True)
273 bpy.ops.mesh.select_face_by_sides(number=4, extend=False)
275 # deselect boundaries
276 bpy.ops.object.mode_set(mode='OBJECT')
277 for i in bound_v:
278 context.active_object.data.vertices[i].select = False
279 for i in bound_e:
280 context.active_object.data.edges[i].select = False
281 for i in bound_p:
282 context.active_object.data.polygons[i].select = False
284 bpy.ops.object.mode_set(mode='EDIT')
286 context.tool_settings.mesh_select_mode = (False, False, True)
287 bpy.ops.mesh.edge_face_add()
288 context.tool_settings.mesh_select_mode = (True, False, False)
289 bpy.ops.mesh.select_all(action='DESELECT')
291 # delete boundaries
292 bpy.ops.mesh.select_non_manifold(
293 extend=False, use_wire=True, use_boundary=True,
294 use_multi_face=False, use_non_contiguous=False, use_verts=True
296 bpy.ops.mesh.delete(type='VERT')
298 # remove middle vertices
299 bm = bmesh.from_edit_mesh(ob.data)
300 for v in bm.verts:
301 if len(v.link_edges) == 2 and len(v.link_faces) < 3:
302 v.select = True
304 # dissolve
305 bpy.ops.mesh.dissolve_verts()
306 bpy.ops.mesh.select_all(action='DESELECT')
308 # remove border faces
309 if not self.preserve_borders:
310 bpy.ops.mesh.select_non_manifold(
311 extend=False, use_wire=False, use_boundary=True,
312 use_multi_face=False, use_non_contiguous=False, use_verts=False
314 bpy.ops.mesh.select_more()
315 bpy.ops.mesh.delete(type='FACE')
317 # clean wires
318 bpy.ops.mesh.select_non_manifold(
319 extend=False, use_wire=True, use_boundary=False,
320 use_multi_face=False, use_non_contiguous=False, use_verts=False
322 bpy.ops.mesh.delete(type='EDGE')
324 bpy.ops.object.mode_set(mode='OBJECT')
325 ob0.data.name = mesh_name
326 doneMeshes.append(mesh_name)
328 for o in clones:
329 o.data = ob.data
330 bm.free()
332 for o in sel:
333 o.select_set(True)
335 context.view_layer.objects.active = act
336 bpy.ops.object.mode_set(mode=mode)
338 return {'FINISHED'}